This commit is contained in:
Javier Casares 2026-06-09 12:52:52 +00:00
commit 19ef0566df
610 changed files with 21958 additions and 3112 deletions

View file

@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
<https://www.gnu.org/licenses/why-not-lgpl.html>.

View file

@ -1,3 +1,11 @@
= 2.1.3 =
* Fixed nonce handling in manual quota refresh to correctly handle false values returned by filter_input().
* Fixed admin notices display to skip non-array notice entries.
* Fixed input normalization for recipients, attachments, and headers to properly skip non-string values instead of attempting unsafe casts.
* Removed unused internal method (dead code cleanup).
* Updated compatibility: tested up to WordPress 7.1.
= 2.1.2 =
* Fixed charset encoding issue that caused "expected parameter value, got null" error when sending emails through Amazon SES.

400
class-robotstxt-updater.php Normal file
View file

@ -0,0 +1,400 @@
<?php
/**
* Generic JSON-based updater for ROBOTSTXT plugins.
*
* This file is designed to be copied to any ROBOTSTXT plugin.
* It auto-configures itself by reading the plugin headers.
*
* @package ROBOTSTXT
* @version 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'Robotstxt_Updater' ) ) {
/**
* Class Robotstxt_Updater
*
* Generic updater that works with any plugin.
* Reads plugin headers and constructs update URL automatically.
*/
class Robotstxt_Updater {
/**
* Plugin file path.
*
* @var string
*/
private string $plugin_file_path;
/**
* Plugin basename (e.g., 'my-plugin/my-plugin.php').
*
* @var string
*/
private string $plugin_basename;
/**
* Plugin slug (directory name).
*
* @var string
*/
private string $plugin_slug;
/**
* Remote JSON URL.
*
* @var string
*/
private string $json_url;
/**
* Cache key.
*
* @var string
*/
private string $cache_key;
/**
* Plugin headers.
*
* @var array<string, string|bool>
*/
private array $plugin_data;
/**
* Initialize the updater.
*
* Usage in your main plugin file:
* require_once __DIR__ . '/robotstxt-updater.php';
* Robotstxt_Updater::init( __FILE__ );
*
* @param string $plugin_file_path Absolute path to the main plugin file.
*/
public static function init( string $plugin_file_path ): void {
$instance = new self( $plugin_file_path );
$instance->register();
}
/**
* Constructor.
*
* @param string $plugin_file_path Absolute path to the main plugin file.
*/
private function __construct( string $plugin_file_path ) {
$this->plugin_file_path = $plugin_file_path;
$this->plugin_basename = plugin_basename( $plugin_file_path );
$this->plugin_slug = dirname( $this->plugin_basename );
$this->plugin_data = $this->get_plugin_data();
$this->json_url = $this->build_json_url();
$this->cache_key = 'robotstxt_updater_' . md5( $this->plugin_basename );
}
/**
* Register WordPress hooks.
*/
private function register(): void {
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'inject_update_info' ) );
add_filter( 'plugins_api', array( $this, 'provide_plugin_details' ), 10, 3 );
add_action( 'admin_init', array( $this, 'handle_cache_clear' ) );
add_action( 'robotstxt_updater_clear_cache', array( $this, 'clear_cache' ) );
}
/**
* Get plugin headers.
*
* @return array<string, string|bool> Plugin data.
*/
private function get_plugin_data(): array {
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
return get_plugin_data( $this->plugin_file_path, false, false );
}
/**
* Build JSON URL from plugin headers.
*
* Tries to use "Gitea Plugin URI" header to construct the URL.
* Falls back to Plugin URI if Gitea URI is not available.
*
* @return string JSON URL.
*/
private function build_json_url(): string {
// Try Gitea Plugin URI (format: "OWNER/REPO" or full URL).
$gitea_uri_raw = $this->plugin_data['Gitea Plugin URI'] ?? '';
if ( ! empty( $gitea_uri_raw ) && is_string( $gitea_uri_raw ) ) {
$gitea_uri = $gitea_uri_raw;
// If it's already a full URL, use it.
if ( str_starts_with( $gitea_uri, 'http' ) ) {
// Extract base URL and construct JSON path.
return rtrim( $gitea_uri, '/' ) . '/raw/branch/main/update.json';
}
// If it's in format "OWNER/REPO", construct full URL.
if ( preg_match( '#^[^/]+/[^/]+$#', $gitea_uri ) ) {
return "https://git.robotstxt.es/{$gitea_uri}/raw/branch/main/update.json";
}
}
// Fallback: try to extract from Plugin URI.
$plugin_uri_raw = $this->plugin_data['PluginURI'] ?? '';
if ( ! empty( $plugin_uri_raw ) && is_string( $plugin_uri_raw ) ) {
$plugin_uri = $plugin_uri_raw;
if ( str_contains( $plugin_uri, 'git.robotstxt.es' ) ) {
return rtrim( $plugin_uri, '/' ) . '/raw/branch/main/update.json';
}
}
// Last resort: construct from plugin slug.
return "https://git.robotstxt.es/ROBOTSTXT/{$this->plugin_slug}/raw/branch/main/update.json";
}
/**
* Inject update info into WP's plugin update transient.
*
* @param object|mixed $transient The update_plugins transient.
*
* @return object The modified transient.
*/
public function inject_update_info( $transient ) {
if ( ! $transient instanceof stdClass ) {
$transient = new stdClass();
}
if ( empty( $transient->checked ) || ! is_array( $transient->checked ) ) {
return $transient;
}
if ( empty( $transient->checked[ $this->plugin_basename ] ) ) {
return $transient;
}
$current_version_raw = $transient->checked[ $this->plugin_basename ] ?? '';
$current_version = is_string( $current_version_raw ) ? $current_version_raw : '';
$remote = $this->get_remote_data();
$remote_version = isset( $remote['version'] ) && is_string( $remote['version'] ) ? $remote['version'] : '';
$remote_download = isset( $remote['download_url'] ) && is_string( $remote['download_url'] ) ? $remote['download_url'] : '';
if ( '' === $remote_version || '' === $remote_download ) {
return $transient;
}
if ( ! $this->is_compatible( $remote ) ) {
return $transient;
}
if ( version_compare( $remote_version, $current_version, '>' ) ) {
$plugin_uri = $this->plugin_data['PluginURI'] ?? '';
$update = (object) array(
'slug' => isset( $remote['slug'] ) && is_string( $remote['slug'] ) ? $remote['slug'] : $this->plugin_slug,
'plugin' => $this->plugin_basename,
'new_version' => $remote_version,
'url' => isset( $remote['homepage'] ) && is_string( $remote['homepage'] ) ? $remote['homepage'] : ( is_string( $plugin_uri ) ? $plugin_uri : '' ),
'package' => $remote_download,
'tested' => isset( $remote['tested'] ) && is_string( $remote['tested'] ) ? $remote['tested'] : '',
'requires' => isset( $remote['requires'] ) && is_string( $remote['requires'] ) ? $remote['requires'] : '',
'requires_php' => isset( $remote['requires_php'] ) && is_string( $remote['requires_php'] ) ? $remote['requires_php'] : '',
);
$transient->response[ $this->plugin_basename ] = $update;
}
return $transient;
}
/**
* Provide "View details" modal content.
*
* @param false|object|array<string, mixed> $result The result object or array.
* @param string $action The type of information being requested.
* @param object $args Plugin API arguments.
*
* @return false|object|array<string, mixed>
*/
public function provide_plugin_details( $result, string $action, object $args ) {
if ( 'plugin_information' !== $action ) {
return $result;
}
if ( empty( $args->slug ) || $args->slug !== $this->plugin_slug ) {
return $result;
}
$remote = $this->get_remote_data();
$remote_version = isset( $remote['version'] ) && is_string( $remote['version'] ) ? $remote['version'] : '';
if ( '' === $remote_version ) {
return $result;
}
$pd_name = $this->plugin_data['Name'] ?? '';
$pd_author = $this->plugin_data['Author'] ?? '';
$pd_plugin_uri = $this->plugin_data['PluginURI'] ?? '';
$pd_description = $this->plugin_data['Description'] ?? '';
return (object) array(
'name' => isset( $remote['name'] ) && is_string( $remote['name'] ) ? $remote['name'] : ( is_string( $pd_name ) ? $pd_name : $this->plugin_slug ),
'slug' => isset( $remote['slug'] ) && is_string( $remote['slug'] ) ? $remote['slug'] : $this->plugin_slug,
'version' => $remote_version,
'author' => isset( $remote['author'] ) && is_string( $remote['author'] ) ? $remote['author'] : ( is_string( $pd_author ) ? $pd_author : '' ),
'homepage' => isset( $remote['homepage'] ) && is_string( $remote['homepage'] ) ? $remote['homepage'] : ( is_string( $pd_plugin_uri ) ? $pd_plugin_uri : '' ),
'requires' => isset( $remote['requires'] ) && is_string( $remote['requires'] ) ? $remote['requires'] : '',
'tested' => isset( $remote['tested'] ) && is_string( $remote['tested'] ) ? $remote['tested'] : '',
'requires_php' => isset( $remote['requires_php'] ) && is_string( $remote['requires_php'] ) ? $remote['requires_php'] : '',
'sections' => array(
'description' => isset( $remote['description'] ) && is_string( $remote['description'] ) ? $remote['description'] : ( is_string( $pd_description ) ? $pd_description : '' ),
'changelog' => isset( $remote['changelog'] ) && is_string( $remote['changelog'] ) ? $remote['changelog'] : '',
),
'download_link' => isset( $remote['download_url'] ) && is_string( $remote['download_url'] ) ? $remote['download_url'] : '',
);
}
/**
* Get remote data with caching and HMAC signature verification.
*
* @return array<string, mixed> Remote data.
*/
private function get_remote_data(): array {
$cached = get_site_transient( $this->cache_key );
// Verify HMAC signature if AUTH_SALT is defined and cache has signature.
if ( false !== $cached && defined( 'AUTH_SALT' ) && '' !== AUTH_SALT ) {
if ( is_array( $cached ) && isset( $cached['signature'], $cached['data'] ) ) {
$expected_sig = hash_hmac( 'sha256', $this->cache_key . wp_json_encode( $cached['data'] ), AUTH_SALT );
if ( hash_equals( $expected_sig, $cached['signature'] ) ) {
// Signature valid, return data.
return is_array( $cached['data'] ) ? $cached['data'] : array();
}
// Signature invalid, delete corrupted cache.
delete_site_transient( $this->cache_key );
$cached = false;
}
}
// If no valid cache, fetch fresh data.
if ( false === $cached ) {
$remote = $this->fetch_json();
// Store with HMAC signature if AUTH_SALT is available.
if ( defined( 'AUTH_SALT' ) && '' !== AUTH_SALT ) {
$remote_data = ! empty( $remote ) ? $remote : array();
$payload = array(
'data' => $remote_data,
'timestamp' => time(),
'signature' => hash_hmac( 'sha256', $this->cache_key . wp_json_encode( $remote_data ), AUTH_SALT ),
);
set_site_transient( $this->cache_key, $payload, 6 * HOUR_IN_SECONDS );
} else {
// Fallback to standard caching.
set_site_transient( $this->cache_key, ( ! empty( $remote ) ? $remote : array() ), 6 * HOUR_IN_SECONDS );
}
return $remote;
}
// Legacy cache format without signature (backward compatibility).
return is_array( $cached ) ? $cached : array();
}
/**
* Fetch JSON from remote URL.
*
* @return array<string, mixed> Decoded JSON data.
*/
private function fetch_json(): array {
$response = wp_remote_get(
$this->json_url,
array(
'timeout' => 10,
'headers' => array(
'Accept' => 'application/json',
),
)
);
if ( is_wp_error( $response ) ) {
return array();
}
$code = (int) wp_remote_retrieve_response_code( $response );
if ( $code < 200 || $code >= 300 ) {
return array();
}
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
return is_array( $data ) ? $data : array();
}
/**
* Check compatibility.
*
* @param array<string, mixed> $remote Remote data.
*
* @return bool True if compatible.
*/
private function is_compatible( array $remote ): bool {
$requires_php = isset( $remote['requires_php'] ) && is_string( $remote['requires_php'] ) ? $remote['requires_php'] : '';
if ( '' !== $requires_php ) {
if ( version_compare( PHP_VERSION, $requires_php, '<' ) ) {
return false;
}
}
$requires = isset( $remote['requires'] ) && is_string( $remote['requires'] ) ? $remote['requires'] : '';
if ( '' !== $requires ) {
if ( version_compare( get_bloginfo( 'version' ), $requires, '<' ) ) {
return false;
}
}
return true;
}
/**
* Handle manual cache clear via URL parameter.
*/
public function handle_cache_clear(): void {
// Check if this is a cache clear request first.
$clear_cache = filter_input( INPUT_GET, 'robotstxt_clear_update_cache', FILTER_UNSAFE_RAW );
if ( null === $clear_cache ) {
return;
}
// This is a cache clear request - now verify nonce.
$nonce_raw = filter_input( INPUT_GET, '_wpnonce', FILTER_UNSAFE_RAW );
$nonce = $nonce_raw ? sanitize_text_field( wp_unslash( $nonce_raw ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'robotstxt_clear_update_cache' ) ) {
wp_die( esc_html__( 'Security check failed', 'robotstxt-smtp-amazonses' ) );
}
// Check permissions.
if ( ! current_user_can( 'update_plugins' ) ) {
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'robotstxt-smtp-amazonses' ) );
}
$this->clear_cache();
wp_safe_redirect( remove_query_arg( array( 'robotstxt_clear_update_cache', '_wpnonce' ) ) );
exit;
}
/**
* Clear update cache.
*/
public function clear_cache(): void {
delete_site_transient( $this->cache_key );
delete_site_transient( 'update_plugins' );
}
}
}

View file

@ -126,12 +126,10 @@ class Plugin {
$secret_key = (string) ( $settings[ self::OPTION_SECRET_KEY ] ?? '' );
$region = (string) ( $settings[ self::OPTION_REGION ] ?? '' );
if ( '' === $access_key || '' === $secret_key || '' === $region ) {
return $pre_wp_mail;
}
$mail_data = \wp_parse_args(
$mail_data,
array(
@ -198,12 +196,12 @@ class Plugin {
/**
* Sends the email data to Amazon SES using the SDK client.
*
* @param array<string, mixed> $mail_context Normalized mail data.
* @param array<string, mixed> $settings Stored plugin settings.
* @param array<string, mixed> $parsed_headers Structured header data.
* @param string $access_key AWS access key ID.
* @param string $secret_key AWS secret access key.
* @param string $region AWS region identifier.
* @param array{to: array<int, string>, subject: string, message: string, headers: mixed, attachments: array<int, string>} $mail_context Normalized mail data.
* @param array{host: string, username: string, password: string, from_email: string, from_name: string, reply_to_email: string, reply_to_name: string, security: string, port: int, amazon_ses_access_key: string, amazon_ses_secret_key: string, amazon_ses_region: string, logs_enabled: bool, logs_retention_mode: string, logs_retention_count: int, logs_retention_days: int, stats_retention_days: int, rate_limit_per_second: int, rate_limit_per_hour: int, rate_limit_per_day: int, delete_data_on_uninstall: bool} $settings Stored plugin settings.
* @param array{content_type: string|null, charset: string|null, cc: array<int, string>, bcc: array<int, string>, reply_to: array<int, string>, custom: array<int, array{name: string, value: string}>} $parsed_headers Structured header data.
* @param string $access_key AWS access key ID.
* @param string $secret_key AWS secret access key.
* @param string $region AWS region identifier.
*
* @return WP_Error|null
*/
@ -278,15 +276,15 @@ class Plugin {
\esc_html( (string) $message )
),
array(
'to' => $mail_context['to'],
'subject' => $mail_context['subject'],
'message' => $mail_context['message'],
'headers' => $mail_context['headers'],
'attachments' => $mail_context['attachments'],
'ses_error_code' => $exception->getAwsErrorCode(),
'ses_request_id' => $exception->getAwsRequestId(),
'ses_http_status_code' => $exception->getStatusCode(),
'ses_debug' => $debug_context,
'to' => $mail_context['to'],
'subject' => $mail_context['subject'],
'message' => $mail_context['message'],
'headers' => $mail_context['headers'],
'attachments' => $mail_context['attachments'],
'ses_error_code' => $exception->getAwsErrorCode(),
'ses_request_id' => $exception->getAwsRequestId(),
'ses_http_status_code' => $exception->getStatusCode(),
'ses_debug' => $debug_context,
)
);
} catch ( Throwable $exception ) {
@ -314,9 +312,9 @@ class Plugin {
/**
* Builds a PHPMailer instance configured with the provided mail data.
*
* @param array<string, mixed> $mail_context Normalized mail data.
* @param array<string, mixed> $settings Stored plugin settings.
* @param array<string, mixed> $parsed_headers Structured header data.
* @param array{to: array<int, string>, subject: string, message: string, headers: mixed, attachments: array<int, string>} $mail_context Normalized mail data.
* @param array{host: string, username: string, password: string, from_email: string, from_name: string, reply_to_email: string, reply_to_name: string, security: string, port: int, amazon_ses_access_key: string, amazon_ses_secret_key: string, amazon_ses_region: string, logs_enabled: bool, logs_retention_mode: string, logs_retention_count: int, logs_retention_days: int, stats_retention_days: int, rate_limit_per_second: int, rate_limit_per_hour: int, rate_limit_per_day: int, delete_data_on_uninstall: bool} $settings Stored plugin settings.
* @param array{content_type: string|null, charset: string|null, cc: array<int, string>, bcc: array<int, string>, reply_to: array<int, string>, custom: array<int, array{name: string, value: string}>} $parsed_headers Structured header data.
*
* @return PHPMailer|WP_Error
*/
@ -326,14 +324,21 @@ class Plugin {
$default_content_type = \apply_filters( 'wp_mail_content_type', 'text/plain' );
$default_charset = \apply_filters( 'wp_mail_charset', \get_bloginfo( 'charset' ) );
$content_type = is_string( $parsed_headers['content_type'] ?? '' ) && '' !== $parsed_headers['content_type'] ? $parsed_headers['content_type'] : ( is_string( $default_content_type ) ? $default_content_type : 'text/plain' );
$charset = is_string( $parsed_headers['charset'] ?? '' ) && '' !== $parsed_headers['charset'] ? $parsed_headers['charset'] : ( is_string( $default_charset ) ? $default_charset : 'utf-8' );
$content_type_header = $parsed_headers['content_type'];
$content_type = ( null !== $content_type_header && '' !== $content_type_header )
? $content_type_header
: ( is_string( $default_content_type ) ? $default_content_type : 'text/plain' );
$charset_header = $parsed_headers['charset'];
$charset = ( null !== $charset_header && '' !== $charset_header )
? $charset_header
: ( is_string( $default_charset ) ? $default_charset : 'utf-8' );
// Ensure charset is never empty, null, or the string "null".
$charset = \trim( (string) $charset );
if ( '' === $charset || 'null' === \strtolower( $charset ) ) {
$charset = 'utf-8';
}
$charset = \trim( $charset );
if ( '' === $charset || 'null' === \strtolower( $charset ) ) {
$charset = 'utf-8';
}
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
$phpmailer->CharSet = $charset;
@ -538,43 +543,43 @@ class Plugin {
/**
* Determines the From email address based on the stored settings and filters.
*
* @param array<string, mixed> $settings Stored plugin settings.
* @param array{host: string, username: string, password: string, from_email: string, from_name: string, reply_to_email: string, reply_to_name: string, security: string, port: int, amazon_ses_access_key: string, amazon_ses_secret_key: string, amazon_ses_region: string, logs_enabled: bool, logs_retention_mode: string, logs_retention_count: int, logs_retention_days: int, stats_retention_days: int, rate_limit_per_second: int, rate_limit_per_hour: int, rate_limit_per_day: int, delete_data_on_uninstall: bool} $settings Stored plugin settings.
*
* @return string
*/
private function determine_from_email( array $settings ): string {
$default_email = \get_bloginfo( 'admin_email' );
if ( isset( $settings['from_email'] ) && \is_email( $settings['from_email'] ) ) {
if ( \is_email( $settings['from_email'] ) ) {
$default_email = $settings['from_email'];
}
$filtered = \apply_filters( 'wp_mail_from', $default_email );
if ( ! \is_email( $filtered ) ) {
return (string) $default_email;
if ( ! is_string( $filtered ) || ! \is_email( $filtered ) ) {
return $default_email;
}
return (string) $filtered;
return $filtered;
}
/**
* Determines the From name based on stored settings and filters.
*
* @param array<string, mixed> $settings Stored plugin settings.
* @param array{host: string, username: string, password: string, from_email: string, from_name: string, reply_to_email: string, reply_to_name: string, security: string, port: int, amazon_ses_access_key: string, amazon_ses_secret_key: string, amazon_ses_region: string, logs_enabled: bool, logs_retention_mode: string, logs_retention_count: int, logs_retention_days: int, stats_retention_days: int, rate_limit_per_second: int, rate_limit_per_hour: int, rate_limit_per_day: int, delete_data_on_uninstall: bool} $settings Stored plugin settings.
*
* @return string
*/
private function determine_from_name( array $settings ): string {
$default_name = \get_bloginfo( 'name', 'display' );
if ( isset( $settings['from_name'] ) && '' !== $settings['from_name'] ) {
if ( '' !== $settings['from_name'] ) {
$default_name = $settings['from_name'];
}
$filtered = \apply_filters( 'wp_mail_from_name', $default_name );
return (string) $filtered;
return is_string( $filtered ) ? $filtered : $default_name;
}
/**
@ -600,11 +605,11 @@ class Plugin {
$normalized = array();
foreach ( $headers as $header ) {
if ( is_array( $header ) ) {
if ( ! is_string( $header ) ) {
continue;
}
$line = trim( (string) $header );
$line = trim( $header );
if ( '' !== $line ) {
$normalized[] = $line;
@ -619,7 +624,14 @@ class Plugin {
*
* @param array<int, string> $headers Normalized header lines.
*
* @return array<string, mixed>
* @return array{
* content_type: string|null,
* charset: string|null,
* cc: array<int, string>,
* bcc: array<int, string>,
* reply_to: array<int, string>,
* custom: array<int, array{name: string, value: string}>
* }
*/
private function parse_headers( array $headers ): array {
$parsed = array(
@ -701,17 +713,17 @@ class Plugin {
}
if ( ! is_array( $recipients ) ) {
$recipients = explode( ',', (string) $recipients );
$recipients = is_string( $recipients ) ? explode( ',', $recipients ) : array();
}
$normalized = array();
foreach ( $recipients as $recipient ) {
if ( is_array( $recipient ) ) {
if ( ! is_string( $recipient ) ) {
continue;
}
$address = trim( (string) $recipient );
$address = trim( $recipient );
if ( '' !== $address ) {
$normalized[] = $address;
@ -740,11 +752,11 @@ class Plugin {
$normalized = array();
foreach ( $attachments as $attachment ) {
if ( is_array( $attachment ) ) {
if ( ! is_string( $attachment ) ) {
continue;
}
$path = trim( (string) $attachment );
$path = trim( $attachment );
if ( '' !== $path ) {
$normalized[] = $path;
@ -810,11 +822,11 @@ class Plugin {
$endpoint = \sprintf( 'https://email.%s.amazonaws.com', $region );
return array(
'region' => $region,
'access_key' => $masked_key,
'sdk_version' => $sdk_version,
'endpoint' => $endpoint,
'timestamp' => \gmdate( 'Y-m-d H:i:s' ) . ' UTC',
'region' => $region,
'access_key' => $masked_key,
'sdk_version' => $sdk_version,
'endpoint' => $endpoint,
'timestamp' => \gmdate( 'Y-m-d H:i:s' ) . ' UTC',
);
}
@ -828,7 +840,6 @@ class Plugin {
public function declare_active( bool $is_active ): bool {
unset( $is_active );
return true;
}
@ -854,8 +865,12 @@ class Plugin {
\delete_transient( 'robotstxt_smtp_admin_notices' );
foreach ( $notices as $notice ) {
$type = $notice['type'] ?? 'info';
$message = $notice['message'] ?? '';
if ( ! is_array( $notice ) ) {
continue;
}
$type = isset( $notice['type'] ) && is_string( $notice['type'] ) ? $notice['type'] : 'info';
$message = isset( $notice['message'] ) && is_string( $notice['message'] ) ? $notice['message'] : '';
if ( $message ) {
printf(
@ -888,7 +903,8 @@ class Plugin {
// This is a refresh quota request - now verify nonce.
$nonce_post = \filter_input( INPUT_POST, 'robotstxt_smtp_refresh_quota_nonce', FILTER_UNSAFE_RAW );
$nonce_get = \filter_input( INPUT_GET, 'robotstxt_smtp_refresh_quota_nonce', FILTER_UNSAFE_RAW );
$nonce = $nonce_post ?? $nonce_get ?? '';
$nonce_raw = $nonce_post ?? $nonce_get ?? '';
$nonce = is_string( $nonce_raw ) ? $nonce_raw : '';
if ( ! \wp_verify_nonce( $nonce, 'robotstxt_smtp_refresh_quota' ) ) {
\wp_die( \esc_html__( 'Security check failed.', 'robotstxt-smtp-amazonses' ) );
@ -1007,7 +1023,7 @@ class Plugin {
* @return void
*/
public function render_access_key_field(): void {
$settings = $this->get_stored_settings();
$settings = $this->get_stored_settings();
$has_access_key = '' !== $settings[ self::OPTION_ACCESS_KEY ];
$placeholder_label = $has_access_key ? \__( 'Leave empty to keep the stored access key.', 'robotstxt-smtp-amazonses' ) : '';
?>
@ -1079,8 +1095,6 @@ class Plugin {
public function sanitize_settings( array $clean, array $options, Settings_Page $settings ): array {
unset( $settings );
// Debug: Log incoming $clean array.
if ( ! SMTP_Plugin::is_amazon_ses_integration_active() ) {
return $clean;
}
@ -1094,7 +1108,8 @@ class Plugin {
$credentials_changed = false;
if ( array_key_exists( self::OPTION_ACCESS_KEY, $options ) ) {
$submitted_access_key = \trim( (string) $options[ self::OPTION_ACCESS_KEY ] );
$raw_access_key = $options[ self::OPTION_ACCESS_KEY ] ?? null;
$submitted_access_key = \trim( is_string( $raw_access_key ) ? $raw_access_key : '' );
if ( '' !== $submitted_access_key ) {
$sanitized_access_key = \sanitize_text_field( $submitted_access_key );
@ -1108,7 +1123,8 @@ class Plugin {
}
if ( array_key_exists( self::OPTION_SECRET_KEY, $options ) ) {
$submitted_secret_key = \trim( (string) $options[ self::OPTION_SECRET_KEY ] );
$raw_secret_key = $options[ self::OPTION_SECRET_KEY ] ?? null;
$submitted_secret_key = \trim( is_string( $raw_secret_key ) ? $raw_secret_key : '' );
if ( '' !== $submitted_secret_key ) {
$sanitized_secret_key = \sanitize_text_field( $submitted_secret_key );
@ -1122,7 +1138,8 @@ class Plugin {
}
if ( array_key_exists( self::OPTION_REGION, $options ) ) {
$submitted_region = \sanitize_text_field( (string) $options[ self::OPTION_REGION ] );
$raw_region = $options[ self::OPTION_REGION ] ?? null;
$submitted_region = \sanitize_text_field( is_string( $raw_region ) ? $raw_region : '' );
if ( '' === $submitted_region ) {
if ( '' !== $region ) {
@ -1152,10 +1169,6 @@ class Plugin {
$clean[ self::OPTION_SECRET_KEY ] = $secret_key;
$clean[ self::OPTION_REGION ] = $region;
// Temporary debug logging
// Debug: Check credentials_changed and values
if ( $credentials_changed && ( '' === $access_key || '' === $secret_key || '' === $region ) ) {
\add_settings_error(
$settings_error_slug,
@ -1197,16 +1210,14 @@ class Plugin {
}
}
// Debug: Log final $clean array before return.
return $clean;
return $clean;
}
/**
* Restores the previously stored Amazon SES values in case validation fails.
*
* @param array<string, mixed> $clean Current sanitized values.
* @param array<string, mixed> $stored_settings Stored settings merged with defaults.
* @param array<string, mixed> $clean Current sanitized values.
* @param array{amazon_ses_access_key: string, amazon_ses_secret_key: string, amazon_ses_region: string, host: string, username: string, password: string, from_email: string, from_name: string, reply_to_email: string, reply_to_name: string, security: string, port: int, logs_enabled: bool, logs_retention_mode: string, logs_retention_count: int, logs_retention_days: int, stats_retention_days: int, rate_limit_per_second: int, rate_limit_per_hour: int, rate_limit_per_day: int, delete_data_on_uninstall: bool} $stored_settings Stored settings merged with defaults.
*
* @return array<string, mixed>
*/
@ -1221,7 +1232,29 @@ class Plugin {
/**
* Retrieves the stored settings merged with defaults for the active scope.
*
* @return array<string, mixed>
* @return array{
* host: string,
* username: string,
* password: string,
* from_email: string,
* from_name: string,
* reply_to_email: string,
* reply_to_name: string,
* security: string,
* port: int,
* amazon_ses_access_key: string,
* amazon_ses_secret_key: string,
* amazon_ses_region: string,
* logs_enabled: bool,
* logs_retention_mode: string,
* logs_retention_count: int,
* logs_retention_days: int,
* stats_retention_days: int,
* rate_limit_per_second: int,
* rate_limit_per_hour: int,
* rate_limit_per_day: int,
* delete_data_on_uninstall: bool
* }
*/
private function get_stored_settings(): array {
$option_name = $this->get_settings_option_name();
@ -1238,16 +1271,43 @@ class Plugin {
$settings = \wp_parse_args( $settings, Settings_Page::get_default_settings() );
$s = (array) $settings;
$access_key = is_string( $s[ self::OPTION_ACCESS_KEY ] ?? null ) ? $s[ self::OPTION_ACCESS_KEY ] : '';
$secret_key = is_string( $s[ self::OPTION_SECRET_KEY ] ?? null ) ? $s[ self::OPTION_SECRET_KEY ] : '';
// Decrypt Amazon SES credentials after loading from database.
if ( isset( $settings[ self::OPTION_ACCESS_KEY ] ) && '' !== $settings[ self::OPTION_ACCESS_KEY ] ) {
$settings[ self::OPTION_ACCESS_KEY ] = \Robotstxt_SMTP_Encryption::decrypt( $settings[ self::OPTION_ACCESS_KEY ] );
if ( '' !== $access_key ) {
$access_key = \Robotstxt_SMTP_Encryption::decrypt( $access_key );
}
if ( isset( $settings[ self::OPTION_SECRET_KEY ] ) && '' !== $settings[ self::OPTION_SECRET_KEY ] ) {
$settings[ self::OPTION_SECRET_KEY ] = \Robotstxt_SMTP_Encryption::decrypt( $settings[ self::OPTION_SECRET_KEY ] );
if ( '' !== $secret_key ) {
$secret_key = \Robotstxt_SMTP_Encryption::decrypt( $secret_key );
}
return $settings;
return array(
'host' => is_string( $s['host'] ?? null ) ? $s['host'] : '',
'username' => is_string( $s['username'] ?? null ) ? $s['username'] : '',
'password' => is_string( $s['password'] ?? null ) ? $s['password'] : '',
'from_email' => is_string( $s['from_email'] ?? null ) ? $s['from_email'] : '',
'from_name' => is_string( $s['from_name'] ?? null ) ? $s['from_name'] : '',
'reply_to_email' => is_string( $s['reply_to_email'] ?? null ) ? $s['reply_to_email'] : '',
'reply_to_name' => is_string( $s['reply_to_name'] ?? null ) ? $s['reply_to_name'] : '',
'security' => is_string( $s['security'] ?? null ) ? $s['security'] : 'none',
'port' => is_int( $s['port'] ?? null ) ? $s['port'] : 25,
self::OPTION_ACCESS_KEY => $access_key,
self::OPTION_SECRET_KEY => $secret_key,
self::OPTION_REGION => is_string( $s[ self::OPTION_REGION ] ?? null ) ? $s[ self::OPTION_REGION ] : '',
'logs_enabled' => is_bool( $s['logs_enabled'] ?? null ) ? $s['logs_enabled'] : false,
'logs_retention_mode' => is_string( $s['logs_retention_mode'] ?? null ) ? $s['logs_retention_mode'] : 'count',
'logs_retention_count' => is_int( $s['logs_retention_count'] ?? null ) ? $s['logs_retention_count'] : 1024,
'logs_retention_days' => is_int( $s['logs_retention_days'] ?? null ) ? $s['logs_retention_days'] : 28,
'stats_retention_days' => is_int( $s['stats_retention_days'] ?? null ) ? $s['stats_retention_days'] : 28,
'rate_limit_per_second' => is_int( $s['rate_limit_per_second'] ?? null ) ? $s['rate_limit_per_second'] : 0,
'rate_limit_per_hour' => is_int( $s['rate_limit_per_hour'] ?? null ) ? $s['rate_limit_per_hour'] : 0,
'rate_limit_per_day' => is_int( $s['rate_limit_per_day'] ?? null ) ? $s['rate_limit_per_day'] : 0,
'delete_data_on_uninstall' => is_bool( $s['delete_data_on_uninstall'] ?? null ) ? $s['delete_data_on_uninstall'] : false,
);
}
/**
@ -1303,7 +1363,7 @@ class Plugin {
$regions = $partition->getAvailableEndpoints( 'ses' );
foreach ( $regions as $region ) {
if ( is_string( $region ) && '' !== $region ) {
if ( '' !== $region ) {
$discovered_list[ $region ] = $labels[ $region ] ?? $this->format_region_label( $region );
}
}
@ -1561,17 +1621,21 @@ class Plugin {
$send_quota = $result->get( 'SendQuota' );
if ( ! $send_quota ) {
if ( ! is_array( $send_quota ) ) {
return new WP_Error(
'no_quota_data',
__( 'Amazon SES did not return quota data.', 'robotstxt-smtp-amazonses' )
);
}
$raw_max_24 = $send_quota['Max24HourSend'] ?? 0;
$raw_rate = $send_quota['MaxSendRate'] ?? 0;
$raw_sent_24 = $send_quota['SentLast24Hours'] ?? 0;
return array(
'max_24_hour_send' => (int) ( $send_quota['Max24HourSend'] ?? 0 ),
'max_send_rate' => (float) ( $send_quota['MaxSendRate'] ?? 0 ),
'sent_last_24_hours' => (int) ( $send_quota['SentLast24Hours'] ?? 0 ),
'max_24_hour_send' => is_numeric( $raw_max_24 ) ? (int) $raw_max_24 : 0,
'max_send_rate' => is_numeric( $raw_rate ) ? (float) $raw_rate : 0.0,
'sent_last_24_hours' => is_numeric( $raw_sent_24 ) ? (int) $raw_sent_24 : 0,
'fetched_at' => time(),
);
@ -1606,56 +1670,6 @@ class Plugin {
}
}
/**
* Applies SES quota limits to the SMTP plugin rate limiting settings.
*
* @since 1.0.1
*
* @param array<string, mixed> $quota Quota data from SES.
* @return void
*/
private function apply_ses_quota_to_rate_limits( array $quota ): void {
$max_send_rate = (float) ( $quota['max_send_rate'] ?? 0 );
$max_24_hour_send = (int) ( $quota['max_24_hour_send'] ?? 0 );
$settings_page = \Robotstxt_SMTP\Admin\Settings_Page::class;
if ( ! class_exists( $settings_page ) ) {
return;
}
$is_network = ROBOTSTXT_SMTP_IS_MULTISITE && $settings_page::is_network_mode_enabled();
$option_name = $is_network ? $settings_page::NETWORK_OPTION_NAME : $settings_page::OPTION_NAME;
$settings = $is_network ? get_site_option( $option_name, array() ) : get_option( $option_name, array() );
if ( ! is_array( $settings ) ) {
$settings = array();
}
$settings['rate_limit_per_second'] = (int) floor( $max_send_rate );
$settings['rate_limit_per_hour'] = (int) floor( $max_send_rate * 3600 * 0.9 );
$settings['rate_limit_per_day'] = $max_24_hour_send;
if ( $is_network ) {
update_site_option( $option_name, $settings );
} else {
update_option( $option_name, $settings );
}
if ( defined( 'WP_DEBUG' ) && WP_DEBUG && defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
error_log(
sprintf(
'ROBOTSTXT SMTP: Updated rate limits from Amazon SES quota - Per second: %d, Per hour: %d, Per day: %d',
$settings['rate_limit_per_second'],
$settings['rate_limit_per_hour'],
$settings['rate_limit_per_day']
)
);
}
}
/**
* Gets the transient cache key for Amazon SES quota data.
*
@ -1682,9 +1696,10 @@ class Plugin {
*
* @since 1.0.1
*
* @return array<string, mixed>|false Quota data or false if not cached.
* @return array<string|int, mixed>|false Quota data or false if not cached.
*/
public function get_cached_ses_quota() {
return get_site_transient( self::get_quota_cache_key() );
public function get_cached_ses_quota(): array|false {
$result = \get_site_transient( self::get_quota_cache_key() );
return is_array( $result ) ? $result : false;
}
}

View file

@ -2,10 +2,10 @@
Contributors: javiercasares
Tags: amazon-ses, smtp, email, mail
Requires at least: 6.5
Tested up to: 6.9
Stable tag: 2.1.2
Tested up to: 7.1
Stable tag: 2.1.3
Requires PHP: 8.2
Version: 2.1.2
Version: 2.1.3
License: GPL-3.0-or-later
License URI: https://www.gnu.org/licenses/gpl-3.0.html
@ -46,6 +46,14 @@ Yes. Network administrators can manage credentials centrally or allow individual
== Changelog ==
= 2.1.3 =
* Fixed nonce handling in manual quota refresh to correctly handle false values returned by filter_input().
* Fixed admin notices display to skip non-array notice entries.
* Fixed input normalization for recipients, attachments, and headers to properly skip non-string values instead of attempting unsafe casts.
* Removed unused internal method (dead code cleanup).
* Updated compatibility: tested up to WordPress 7.1.
* Internal: renamed updater file to follow WordPress naming conventions.
= 2.1.2 =
* Fixed charset encoding issue that caused "expected parameter value, got null" error when sending emails through Amazon SES.
* Added robust charset validation in header parsing to prevent empty or invalid charset values.

View file

@ -3,7 +3,7 @@
* Plugin Name: SMTP (by ROBOTSTXT) Amazon SES
* Plugin URI: https://git.robotstxt.es/ROBOTSTXT/robotstxt-smtp-amazonses
* Description: Adds Amazon SES configuration support to the ROBOTSTXT SMTP plugin.
* Version: 2.1.2
* Version: 2.1.3
* Requires at least: 6.5
* Requires PHP: 8.2
* Network: true
@ -26,7 +26,7 @@ if ( ! defined( 'ABSPATH' ) ) {
}
if ( ! defined( 'ROBOTSTXT_SMTP_AMAZONSES_VERSION' ) ) {
define( 'ROBOTSTXT_SMTP_AMAZONSES_VERSION', '2.1.2' );
define( 'ROBOTSTXT_SMTP_AMAZONSES_VERSION', '2.1.3' );
}
if ( ! defined( 'ROBOTSTXT_SMTP_AMAZONSES_FILE' ) ) {
@ -62,5 +62,5 @@ require_once ROBOTSTXT_SMTP_AMAZONSES_PATH . 'includes/class-plugin.php';
\Robotstxt_SMTP_AmazonSES\Plugin::get_instance()->run();
// Initialize ROBOTSTXT updater (auto-configures from plugin headers).
require_once __DIR__ . '/robotstxt-updater.php';
require_once __DIR__ . '/class-robotstxt-updater.php';
Robotstxt_Updater::init( __FILE__ );

View file

@ -1,383 +0,0 @@
<?php
/**
* Generic JSON-based updater for ROBOTSTXT plugins.
*
* This file is designed to be copied to any ROBOTSTXT plugin.
* It auto-configures itself by reading the plugin headers.
*
* @package ROBOTSTXT
* @version 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'Robotstxt_Updater' ) ) {
/**
* Class Robotstxt_Updater
*
* Generic updater that works with any plugin.
* Reads plugin headers and constructs update URL automatically.
*/
class Robotstxt_Updater {
/**
* Plugin file path.
*
* @var string
*/
private string $plugin_file_path;
/**
* Plugin basename (e.g., 'my-plugin/my-plugin.php').
*
* @var string
*/
private string $plugin_basename;
/**
* Plugin slug (directory name).
*
* @var string
*/
private string $plugin_slug;
/**
* Remote JSON URL.
*
* @var string
*/
private string $json_url;
/**
* Cache key.
*
* @var string
*/
private string $cache_key;
/**
* Plugin headers.
*
* @var array
*/
private array $plugin_data;
/**
* Initialize the updater.
*
* Usage in your main plugin file:
* require_once __DIR__ . '/robotstxt-updater.php';
* Robotstxt_Updater::init( __FILE__ );
*
* @param string $plugin_file_path Absolute path to the main plugin file.
*/
public static function init( string $plugin_file_path ): void {
$instance = new self( $plugin_file_path );
$instance->register();
}
/**
* Constructor.
*
* @param string $plugin_file_path Absolute path to the main plugin file.
*/
private function __construct( string $plugin_file_path ) {
$this->plugin_file_path = $plugin_file_path;
$this->plugin_basename = plugin_basename( $plugin_file_path );
$this->plugin_slug = dirname( $this->plugin_basename );
$this->plugin_data = $this->get_plugin_data();
$this->json_url = $this->build_json_url();
$this->cache_key = 'robotstxt_updater_' . md5( $this->plugin_basename );
}
/**
* Register WordPress hooks.
*/
private function register(): void {
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'inject_update_info' ) );
add_filter( 'plugins_api', array( $this, 'provide_plugin_details' ), 10, 3 );
add_action( 'admin_init', array( $this, 'handle_cache_clear' ) );
add_action( 'robotstxt_updater_clear_cache', array( $this, 'clear_cache' ) );
}
/**
* Get plugin headers.
*
* @return array Plugin data.
*/
private function get_plugin_data(): array {
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
return get_plugin_data( $this->plugin_file_path, false, false );
}
/**
* Build JSON URL from plugin headers.
*
* Tries to use "Gitea Plugin URI" header to construct the URL.
* Falls back to Plugin URI if Gitea URI is not available.
*
* @return string JSON URL.
*/
private function build_json_url(): string {
// Try Gitea Plugin URI (format: "OWNER/REPO" or full URL).
if ( ! empty( $this->plugin_data['Gitea Plugin URI'] ) ) {
$gitea_uri = $this->plugin_data['Gitea Plugin URI'];
// If it's already a full URL, use it.
if ( str_starts_with( $gitea_uri, 'http' ) ) {
// Extract base URL and construct JSON path.
return rtrim( $gitea_uri, '/' ) . '/raw/branch/main/update.json';
}
// If it's in format "OWNER/REPO", construct full URL.
if ( preg_match( '#^[^/]+/[^/]+$#', $gitea_uri ) ) {
return "https://git.robotstxt.es/{$gitea_uri}/raw/branch/main/update.json";
}
}
// Fallback: try to extract from Plugin URI.
if ( ! empty( $this->plugin_data['PluginURI'] ) ) {
$plugin_uri = $this->plugin_data['PluginURI'];
if ( str_contains( $plugin_uri, 'git.robotstxt.es' ) ) {
return rtrim( $plugin_uri, '/' ) . '/raw/branch/main/update.json';
}
}
// Last resort: construct from plugin slug.
return "https://git.robotstxt.es/ROBOTSTXT/{$this->plugin_slug}/raw/branch/main/update.json";
}
/**
* Inject update info into WP's plugin update transient.
*
* @param object|mixed $transient The update_plugins transient.
*
* @return object The modified transient.
*/
public function inject_update_info( $transient ) {
if ( ! is_object( $transient ) ) {
$transient = new stdClass();
}
if ( empty( $transient->checked ) || ! is_array( $transient->checked ) ) {
return $transient;
}
if ( empty( $transient->checked[ $this->plugin_basename ] ) ) {
return $transient;
}
$current_version = $transient->checked[ $this->plugin_basename ];
$remote = $this->get_remote_data();
if ( empty( $remote['version'] ) || empty( $remote['download_url'] ) ) {
return $transient;
}
if ( ! $this->is_compatible( $remote ) ) {
return $transient;
}
if ( version_compare( $remote['version'], $current_version, '>' ) ) {
$update = (object) array(
'slug' => $remote['slug'] ?? $this->plugin_slug,
'plugin' => $this->plugin_basename,
'new_version' => $remote['version'],
'url' => $remote['homepage'] ?? $this->plugin_data['PluginURI'] ?? '',
'package' => $remote['download_url'],
'tested' => $remote['tested'] ?? '',
'requires' => $remote['requires'] ?? '',
'requires_php' => $remote['requires_php'] ?? '',
);
$transient->response[ $this->plugin_basename ] = $update;
}
return $transient;
}
/**
* Provide "View details" modal content.
*
* @param false|object|array $result The result object or array.
* @param string $action The type of information being requested.
* @param object $args Plugin API arguments.
*
* @return false|object The plugin information object or false.
*/
public function provide_plugin_details( $result, string $action, object $args ) {
if ( 'plugin_information' !== $action ) {
return $result;
}
if ( empty( $args->slug ) || $args->slug !== $this->plugin_slug ) {
return $result;
}
$remote = $this->get_remote_data();
if ( empty( $remote['version'] ) ) {
return $result;
}
return (object) array(
'name' => $remote['name'] ?? $this->plugin_data['Name'] ?? $this->plugin_slug,
'slug' => $remote['slug'] ?? $this->plugin_slug,
'version' => $remote['version'],
'author' => $remote['author'] ?? $this->plugin_data['Author'] ?? '',
'homepage' => $remote['homepage'] ?? $this->plugin_data['PluginURI'] ?? '',
'requires' => $remote['requires'] ?? '',
'tested' => $remote['tested'] ?? '',
'requires_php' => $remote['requires_php'] ?? '',
'sections' => array(
'description' => $remote['description'] ?? $this->plugin_data['Description'] ?? '',
'changelog' => $remote['changelog'] ?? '',
),
'download_link' => $remote['download_url'] ?? '',
);
}
/**
* Get remote data with caching and HMAC signature verification.
*
* @return array Remote data.
*/
private function get_remote_data(): array {
$cached = get_site_transient( $this->cache_key );
// Verify HMAC signature if AUTH_SALT is defined and cache has signature.
if ( false !== $cached && defined( 'AUTH_SALT' ) && '' !== AUTH_SALT ) {
if ( is_array( $cached ) && isset( $cached['signature'], $cached['data'] ) ) {
$expected_sig = hash_hmac( 'sha256', $this->cache_key . serialize( $cached['data'] ), AUTH_SALT );
if ( hash_equals( $expected_sig, $cached['signature'] ) ) {
// Signature valid, return data.
return is_array( $cached['data'] ) ? $cached['data'] : array();
}
// Signature invalid, delete corrupted cache.
delete_site_transient( $this->cache_key );
$cached = false;
}
}
// If no valid cache, fetch fresh data.
if ( false === $cached ) {
$remote = $this->fetch_json();
// Store with HMAC signature if AUTH_SALT is available.
if ( defined( 'AUTH_SALT' ) && '' !== AUTH_SALT ) {
$payload = array(
'data' => $remote ?: array(),
'timestamp' => time(),
'signature' => hash_hmac( 'sha256', $this->cache_key . serialize( $remote ?: array() ), AUTH_SALT ),
);
set_site_transient( $this->cache_key, $payload, 6 * HOUR_IN_SECONDS );
} else {
// Fallback to standard caching.
set_site_transient( $this->cache_key, $remote ?: array(), 6 * HOUR_IN_SECONDS );
}
return is_array( $remote ) ? $remote : array();
}
// Legacy cache format without signature (backward compatibility).
return is_array( $cached ) ? $cached : array();
}
/**
* Fetch JSON from remote URL.
*
* @return array Decoded JSON data.
*/
private function fetch_json(): array {
$response = wp_remote_get(
$this->json_url,
array(
'timeout' => 10,
'headers' => array(
'Accept' => 'application/json',
),
)
);
if ( is_wp_error( $response ) ) {
return array();
}
$code = (int) wp_remote_retrieve_response_code( $response );
if ( $code < 200 || $code >= 300 ) {
return array();
}
$body = wp_remote_retrieve_body( $response );
$data = json_decode( $body, true );
return is_array( $data ) ? $data : array();
}
/**
* Check compatibility.
*
* @param array $remote Remote data.
*
* @return bool True if compatible.
*/
private function is_compatible( array $remote ): bool {
if ( ! empty( $remote['requires_php'] ) ) {
if ( version_compare( PHP_VERSION, $remote['requires_php'], '<' ) ) {
return false;
}
}
if ( ! empty( $remote['requires'] ) ) {
if ( version_compare( get_bloginfo( 'version' ), $remote['requires'], '<' ) ) {
return false;
}
}
return true;
}
/**
* Handle manual cache clear via URL parameter.
*/
public function handle_cache_clear(): void {
// Check if this is a cache clear request first.
$clear_cache = filter_input( INPUT_GET, 'robotstxt_clear_update_cache', FILTER_UNSAFE_RAW );
if ( null === $clear_cache ) {
return;
}
// This is a cache clear request - now verify nonce.
$nonce_raw = filter_input( INPUT_GET, '_wpnonce', FILTER_UNSAFE_RAW );
$nonce = $nonce_raw ? sanitize_text_field( wp_unslash( $nonce_raw ) ) : '';
if ( ! wp_verify_nonce( $nonce, 'robotstxt_clear_update_cache' ) ) {
wp_die( esc_html__( 'Security check failed', 'robotstxt-smtp-amazonses' ) );
}
// Check permissions.
if ( ! current_user_can( 'update_plugins' ) ) {
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'robotstxt-smtp-amazonses' ) );
}
$this->clear_cache();
wp_safe_redirect( remove_query_arg( array( 'robotstxt_clear_update_cache', '_wpnonce' ) ) );
exit;
}
/**
* Clear update cache.
*/
public function clear_cache(): void {
delete_site_transient( $this->cache_key );
delete_site_transient( 'update_plugins' );
}
}
}

View file

@ -1,21 +1,21 @@
{
"name": "SMTP (by ROBOTSTXT) Amazon SES",
"slug": "robotstxt-smtp-amazonses",
"version": "2.1.2",
"download_url": "https://git.robotstxt.es/ROBOTSTXT/robotstxt-smtp-amazonses/releases/download/2.1.2/robotstxt-smtp-amazonses-2.1.2.zip",
"version": "2.1.3",
"download_url": "https://git.robotstxt.es/ROBOTSTXT/robotstxt-smtp-amazonses/releases/download/2.1.3/robotstxt-smtp-amazonses-2.1.3.zip",
"requires": "6.5",
"requires_php": "8.2",
"tested": "6.9",
"last_updated": "2026-02-12",
"tested": "7.1",
"last_updated": "2026-06-09",
"author": "ROBOTSTXT",
"author_profile": "https://www.robotstxt.es/",
"homepage": "https://git.robotstxt.es/ROBOTSTXT/robotstxt-smtp-amazonses",
"requires_plugins": ["robotstxt-smtp"],
"description": "Adds Amazon SES (Simple Email Service) configuration support to the ROBOTSTXT SMTP plugin. Integrates seamlessly with AWS SDK for PHP to send emails through Amazon SES infrastructure.",
"changelog": "<h3>2.1.1 - 2026-02-09</h3><ul><li><strong>Added:</strong> Comprehensive Amazon SES debug context to test email error reports</li><li><strong>Added:</strong> Debug information now includes AWS region, masked access key, SDK version, endpoint URL, and UTC timestamp</li><li><strong>Improved:</strong> Enhanced AWS error reporting with request IDs, HTTP status codes, and error codes for easier troubleshooting</li><li><strong>Security:</strong> Access keys are now masked in error output (shows only first 4 and last 4 characters)</li></ul><h3>2.1.0 - 2026-02-09</h3><ul><li><strong>Changed:</strong> Uninstall behavior: plugin no longer performs cleanup on uninstall, all data cleanup is now handled by the core SMTP plugin</li><li><strong>Changed:</strong> This ensures Amazon SES credentials are preserved when only the add-on is removed, and properly cleaned when the core plugin is uninstalled</li><li><strong>Technical:</strong> Simplified uninstall.php to delegate all cleanup responsibility to core SMTP plugin</li><li><strong>Technical:</strong> Improved data preservation and cleanup consistency across the plugin ecosystem</li></ul><h3>2.0.0 - 2026-01-29</h3><p><strong>⚠️ BREAKING CHANGE:</strong> This version requires PHP 8.2 or higher due to AWS SDK requirements.</p><ul><li><strong>Added:</strong> Reply-To header support for emails sent via Amazon SES</li><li><strong>Fixed:</strong> Improved error handling and validation</li><li><strong>Fixed:</strong> Enhanced compatibility with core SMTP plugin 2.0.0</li><li><strong>Security:</strong> Improved credential handling and encryption</li><li><strong>Security:</strong> Updated AWS SDK for PHP to latest version (3.369.20) with security patches</li><li><strong>Changed:</strong> Minimum PHP version increased to 8.2</li><li><strong>Improved:</strong> Code quality and documentation</li></ul><h3>1.0.0 - 2026-01-27</h3><ul><li>Initial release</li><li>Amazon SES integration via AWS SDK v3</li><li>Full compatibility with ROBOTSTXT SMTP plugin</li><li>Support for AWS regions and credentials configuration</li><li>Automatic dependency management with Composer</li></ul>",
"changelog": "<h3>2.1.3 - 2026-06-09</h3><ul><li><strong>Fixed:</strong> Nonce handling in manual quota refresh to correctly handle false values from filter_input()</li><li><strong>Fixed:</strong> Admin notices display now skips non-array notice entries</li><li><strong>Fixed:</strong> Input normalization for recipients, attachments, and headers now properly skips non-string values</li><li><strong>Removed:</strong> Unused internal rate limit method (dead code cleanup)</li><li><strong>Compatibility:</strong> Tested up to WordPress 7.1</li></ul><h3>2.1.2 - 2026-02-12</h3><ul><li><strong>Fixed:</strong> Charset encoding issue that caused \"expected parameter value, got null\" error when sending emails through Amazon SES</li><li><strong>Improved:</strong> Added robust charset validation in header parsing to prevent empty or invalid charset values</li><li><strong>Improved:</strong> Enhanced charset handling with automatic fallback to UTF-8 when no valid charset is provided</li></ul><h3>2.1.1 - 2026-02-09</h3><ul><li><strong>Added:</strong> Comprehensive Amazon SES debug context to test email error reports</li><li><strong>Added:</strong> Debug information now includes AWS region, masked access key, SDK version, endpoint URL, and UTC timestamp</li><li><strong>Improved:</strong> Enhanced AWS error reporting with request IDs, HTTP status codes, and error codes for easier troubleshooting</li><li><strong>Security:</strong> Access keys are now masked in error output (shows only first 4 and last 4 characters)</li></ul>",
"sections": {
"description": "Adds Amazon SES (Simple Email Service) configuration support to the ROBOTSTXT SMTP plugin. Integrates seamlessly with AWS SDK for PHP to send emails through Amazon SES infrastructure.",
"changelog": "<h3>2.1.2 - 2026-02-12</h3><ul><li><strong>Fixed:</strong> Charset encoding issue that caused \"expected parameter value, got null\" error when sending emails through Amazon SES</li><li><strong>Improved:</strong> Added robust charset validation in header parsing to prevent empty or invalid charset values</li><li><strong>Improved:</strong> Enhanced charset handling with automatic fallback to UTF-8 when no valid charset is provided</li></ul><h3>2.1.1 - 2026-02-09</h3><ul><li><strong>Added:</strong> Comprehensive Amazon SES debug context to test email error reports</li><li><strong>Added:</strong> Debug information now includes AWS region, masked access key, SDK version, endpoint URL, and UTC timestamp</li><li><strong>Improved:</strong> Enhanced AWS error reporting with request IDs, HTTP status codes, and error codes for easier troubleshooting</li><li><strong>Security:</strong> Access keys are now masked in error output (shows only first 4 and last 4 characters)</li></ul><h3>2.1.0 - 2026-02-09</h3><ul><li><strong>Changed:</strong> Uninstall behavior: plugin no longer performs cleanup on uninstall, all data cleanup is now handled by the core SMTP plugin</li><li><strong>Changed:</strong> This ensures Amazon SES credentials are preserved when only the add-on is removed, and properly cleaned when the core plugin is uninstalled</li><li><strong>Technical:</strong> Simplified uninstall.php to delegate all cleanup responsibility to core SMTP plugin</li><li><strong>Technical:</strong> Improved data preservation and cleanup consistency across the plugin ecosystem</li></ul><h3>2.0.0 - 2026-01-29</h3><p><strong>⚠️ BREAKING CHANGE:</strong> This version requires PHP 8.2 or higher due to AWS SDK requirements.</p><ul><li><strong>Added:</strong> Reply-To header support for emails sent via Amazon SES</li><li><strong>Fixed:</strong> Improved error handling and validation</li><li><strong>Fixed:</strong> Enhanced compatibility with core SMTP plugin 2.0.0</li><li><strong>Security:</strong> Improved credential handling and encryption</li><li><strong>Security:</strong> Updated AWS SDK for PHP to latest version (3.369.20) with security patches</li><li><strong>Changed:</strong> Minimum PHP version increased to 8.2</li><li><strong>Improved:</strong> Code quality and documentation</li></ul><h3>1.0.0 - 2026-01-27</h3><ul><li>Initial release</li><li>Amazon SES integration via AWS SDK v3</li><li>Full compatibility with ROBOTSTXT SMTP plugin</li><li>Support for AWS regions and credentials configuration</li><li>Automatic dependency management with Composer</li></ul>"
"changelog": "<h3>2.1.3 - 2026-06-09</h3><ul><li><strong>Fixed:</strong> Nonce handling in manual quota refresh to correctly handle false values from filter_input()</li><li><strong>Fixed:</strong> Admin notices display now skips non-array notice entries</li><li><strong>Fixed:</strong> Input normalization for recipients, attachments, and headers now properly skips non-string values</li><li><strong>Removed:</strong> Unused internal rate limit method (dead code cleanup)</li><li><strong>Compatibility:</strong> Tested up to WordPress 7.1</li></ul><h3>2.1.2 - 2026-02-12</h3><ul><li><strong>Fixed:</strong> Charset encoding issue that caused \"expected parameter value, got null\" error when sending emails through Amazon SES</li><li><strong>Improved:</strong> Added robust charset validation in header parsing to prevent empty or invalid charset values</li><li><strong>Improved:</strong> Enhanced charset handling with automatic fallback to UTF-8 when no valid charset is provided</li></ul><h3>2.1.1 - 2026-02-09</h3><ul><li><strong>Added:</strong> Comprehensive Amazon SES debug context to test email error reports</li><li><strong>Added:</strong> Debug information now includes AWS region, masked access key, SDK version, endpoint URL, and UTC timestamp</li><li><strong>Improved:</strong> Enhanced AWS error reporting with request IDs, HTTP status codes, and error codes for easier troubleshooting</li><li><strong>Security:</strong> Access keys are now masked in error output (shows only first 4 and last 4 characters)</li></ul><h3>2.1.0 - 2026-02-09</h3><ul><li><strong>Changed:</strong> Uninstall behavior: plugin no longer performs cleanup on uninstall, all data cleanup is now handled by the core SMTP plugin</li></ul>"
},
"banners": {
"low": "",

5
vendor/autoload.php vendored
View file

@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
echo $err;
}
}
trigger_error(
$err,
E_USER_ERROR
);
throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';

View file

@ -21,10 +21,14 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createAnalyzerAsync(array $args = [])
* @method \Aws\Result createArchiveRule(array $args = [])
* @method \GuzzleHttp\Promise\Promise createArchiveRuleAsync(array $args = [])
* @method \Aws\Result createServiceLinkedAnalyzer(array $args = [])
* @method \GuzzleHttp\Promise\Promise createServiceLinkedAnalyzerAsync(array $args = [])
* @method \Aws\Result deleteAnalyzer(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAnalyzerAsync(array $args = [])
* @method \Aws\Result deleteArchiveRule(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteArchiveRuleAsync(array $args = [])
* @method \Aws\Result deleteServiceLinkedAnalyzer(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteServiceLinkedAnalyzerAsync(array $args = [])
* @method \Aws\Result generateFindingRecommendation(array $args = [])
* @method \GuzzleHttp\Promise\Promise generateFindingRecommendationAsync(array $args = [])
* @method \Aws\Result getAccessPreview(array $args = [])

View file

@ -36,6 +36,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise resendValidationEmailAsync(array $args = [])
* @method \Aws\Result revokeCertificate(array $args = [])
* @method \GuzzleHttp\Promise\Promise revokeCertificateAsync(array $args = [])
* @method \Aws\Result searchCertificates(array $args = [])
* @method \GuzzleHttp\Promise\Promise searchCertificatesAsync(array $args = [])
* @method \Aws\Result updateCertificateOptions(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateCertificateOptionsAsync(array $args = [])
*/

View file

@ -0,0 +1,664 @@
<?php
namespace Aws\Api\Cbor;
use Aws\Api\Cbor\Exception\CborException;
/**
* Decodes Concise Binary Object Representation encoded strings
* into PHP values according to RFC 8949
*
* https://www.rfc-editor.org/rfc/rfc8949.html
*
* Supports Major types 0-7 including:
* - Type 0: Unsigned integers
* - Type 1: Negative integers
* - Type 2: Byte strings
* - Type 3: Text strings (UTF-8)
* - Type 4: Arrays
* - Type 5: Maps
* - Type 6: Tagged values (timestamps)
* - Type 7: Simple values (null, bool, float)
*
* @internal
*/
final class CborDecoder
{
private int $offset;
private int $length;
/**
* Decode CBOR binary data to PHP value
*
* @param string $data The CBOR-encoded binary data to decode
*
* @return mixed The decoded PHP value (can be any type: int, string, array, bool, null, float)
* @throws CborException If data is empty or malformed CBOR
*/
public function decode(string $data): mixed
{
if ($data === '') {
throw new CborException("No data to decode");
}
$this->offset = 0;
$this->length = strlen($data);
return $this->decodeValue($data);
}
/**
* Decode multiple CBOR values from sequential binary data
*
* @param string $data The CBOR-encoded binary data containing multiple values
*
* @return array Array of decoded PHP values in the order they appear in the data
* @throws CborException If data is malformed CBOR
*/
public function decodeAll(string $data): array
{
$this->length = strlen($data);
$this->offset = 0;
$values = [];
while ($this->offset < $this->length) {
$values[] = $this->decodeValue($data);
}
return $values;
}
/**
* Decodes a single CBOR value at the current offset
*
* @param string $data Reference to the CBOR data being decoded
*
* @return mixed The decoded value
* @throws CborException If unexpected end of data or invalid CBOR format
*/
private function decodeValue(string &$data): mixed
{
$offset = $this->offset;
$length = $this->length;
if ($offset >= $length) {
throw new CborException("Unexpected end of data");
}
$byte = ord($data[$offset++]);
$majorType = $byte >> 5;
$info = $byte & 0x1F;
switch ($majorType) {
case 0: // Unsigned integer
if ($info < 24) {
$this->offset = $offset;
return $info;
}
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 1;
return ord($data[$offset]);
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 2;
return (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 4;
return unpack('N', $data, $offset)[1];
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 8;
return unpack('J', $data, $offset)[1];
default:
throw new CborException("Invalid additional info for integer: $info");
}
case 1: // Negative integer
if ($info < 24) {
$this->offset = $offset;
return -1 - $info;
}
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 1;
return -1 - ord($data[$offset]);
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 2;
return -1 - ((ord($data[$offset]) << 8) | ord($data[$offset + 1]));
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 4;
return -1 - unpack('N', $data, $offset)[1];
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 8;
$unsigned = unpack('J', $data, $offset)[1];
return ($unsigned === 9223372036854775807) ? PHP_INT_MIN : -1 - $unsigned;
default:
throw new CborException("Invalid additional info for integer: $info");
}
case 2: // Byte string
if ($info < 24) {
$len = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$len = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('J', $data, $offset)[1];
$offset += 8;
break;
case 31:
$this->offset = $offset;
return $this->decodeIndefiniteString($data, 0x40);
default:
throw new CborException("Invalid additional info for byte string: $info");
}
}
if ($offset + $len > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + $len;
return substr($data, $offset, $len);
case 3: // Text string
if ($info < 24) {
$len = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$len = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('J', $data, $offset)[1];
$offset += 8;
break;
case 31:
$this->offset = $offset;
return $this->decodeIndefiniteString($data, 0x60);
default:
throw new CborException("Invalid additional info for text string: $info");
}
}
if ($offset + $len > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + $len;
return substr($data, $offset, $len);
case 4: // Array
if ($info < 24) {
$count = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$count = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$count = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$count = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$count = unpack('J', $data, $offset)[1];
$offset += 8;
break;
case 31:
$this->offset = $offset;
return $this->decodeIndefiniteArray($data);
default:
throw new CborException("Invalid additional info for array: $info");
}
}
$this->offset = $offset;
$arr = [];
for ($i = 0; $i < $count; $i++) {
$arr[] = $this->decodeValue($data);
}
return $arr;
case 5: // Map
if ($info < 24) {
$count = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$count = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$count = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$count = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$count = unpack('J', $data, $offset)[1];
$offset += 8;
break;
case 31:
$this->offset = $offset;
return $this->decodeIndefiniteMap($data);
default:
throw new CborException("Invalid additional info for map: $info");
}
}
$this->offset = $offset;
$map = [];
for ($i = 0; $i < $count; $i++) {
$key = $this->decodeValue($data);
$map[$key] = $this->decodeValue($data);
}
return $map;
case 6: // Tag
switch ($info) {
case 24:
$offset++;
break;
case 25:
$offset += 2;
break;
case 26:
$offset += 4;
break;
case 27:
$offset += 8;
break;
}
$this->offset = $offset;
return $this->decodeValue($data);
case 7: // Simple/float
switch ($info) {
case 20:
$this->offset = $offset;
return false;
case 21:
$this->offset = $offset;
return true;
case 22:
case 23:
$this->offset = $offset;
return null;
case 25: // Half-precision float
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 2;
$half = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$sign = ($half >> 15) & 0x01;
$exp = ($half >> 10) & 0x1F;
$mant = $half & 0x3FF;
if ($exp === 0) {
return $mant === 0
? ($sign ? -0.0 : 0.0)
: ($sign ? -1 : 1) * pow(2, -14) * ($mant / 1024);
}
if ($exp === 31) {
return $mant === 0 ? ($sign ? -INF : INF) : NAN;
}
return (float) (($sign ? -1 : 1) * pow(2, $exp - 15) * (1 + $mant / 1024));
case 26: // Single-precision float
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 4;
return unpack('G', $data, $offset)[1];
case 27: // Double-precision float
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 8;
return unpack('E', $data, $offset)[1];
case 31:
throw new CborException("Unexpected break");
default:
throw new CborException("Unknown simple value: $info");
}
default:
throw new CborException("Unknown major type: $majorType");
}
}
/**
* Decode indefinite-length string (byte or text)
*
* @param string $data Reference to the CBOR data being decoded
* @param int $expectedMajor Expected major type (0x40 for byte string, 0x60 for text string)
*
* @return string The concatenated string from all chunks
* @throws CborException If invalid chunk format or unexpected end of data
*/
private function decodeIndefiniteString(string &$data, int $expectedMajor): string
{
$chunks = [];
while (true) {
$offset = $this->offset;
$length = $this->length;
if ($offset >= $length) {
throw new CborException("Unexpected end of data");
}
$byte = ord($data[$offset++]);
if ($byte === 0xFF) {
$this->offset = $offset;
return implode('', $chunks);
}
if (($byte & 0xE0) !== $expectedMajor) {
throw new CborException("Invalid chunk in indefinite string");
}
$info = $byte & 0x1F;
if ($info === 31) {
throw new CborException("Nested indefinite string");
}
if ($info < 24) {
$len = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$len = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('J', $data, $offset)[1];
$offset += 8;
break;
default:
throw new CborException("Invalid chunk length info: $info");
}
}
if ($offset + $len > $length) {
throw new CborException("Not enough data for chunk");
}
$chunks[] = substr($data, $offset, $len);
$this->offset = $offset + $len;
}
}
/**
* Decode indefinite-length array
*
* @param string $data Reference to the CBOR data being decoded
*
* @return array The decoded array elements
* @throws CborException If unexpected end of data
*/
private function decodeIndefiniteArray(string &$data): array
{
$result = [];
while (true) {
if ($this->offset >= $this->length) {
throw new CborException("Unexpected end of data");
}
if (ord($data[$this->offset]) === 0xFF) {
$this->offset++;
return $result;
}
$result[] = $this->decodeValue($data);
}
}
/**
* Decode indefinite-length map
*
* @param string $data Reference to the CBOR data being decoded
*
* @return array The decoded map as associative array
* @throws CborException If unexpected end of data or odd number of items
*/
private function decodeIndefiniteMap(string &$data): array
{
$result = [];
while (true) {
if ($this->offset >= $this->length) {
throw new CborException("Unexpected end of data");
}
if (ord($data[$this->offset]) === 0xFF) {
$this->offset++;
return $result;
}
$key = $this->decodeValue($data);
$result[$key] = $this->decodeValue($data);
}
}
}

View file

@ -0,0 +1,345 @@
<?php
namespace Aws\Api\Cbor;
use Aws\Api\Cbor\Exception\CborException;
/**
* Encodes PHP values to Concise Binary Object Representation according to RFC 8949
* https://www.rfc-editor.org/rfc/rfc8949.html
*
* Supports Major types 0-7 including:
* - Type 0: Unsigned integers
* - Type 1: Negative integers
* - Type 2: Byte strings (via ['__cbor_bytes' => $data] wrappers)
* - Type 3: Text strings (UTF-8)
* - Type 4: Arrays
* - Type 5: Maps
* - Type 6: Tagged values (timestamps)
* - Type 7: Simple values (null, bool, float)
*
* @internal
*/
final class CborEncoder
{
/**
* Pre-encoded integers 0-23 (single byte) and common larger values
* CBOR major type 0 (unsigned integer)
*/
private const INT_CACHE = [
0 => "\x00", 1 => "\x01", 2 => "\x02", 3 => "\x03",
4 => "\x04", 5 => "\x05", 6 => "\x06", 7 => "\x07",
8 => "\x08", 9 => "\x09", 10 => "\x0A", 11 => "\x0B",
12 => "\x0C", 13 => "\x0D", 14 => "\x0E", 15 => "\x0F",
16 => "\x10", 17 => "\x11", 18 => "\x12", 19 => "\x13",
20 => "\x14", 21 => "\x15", 22 => "\x16", 23 => "\x17",
24 => "\x18\x18", 25 => "\x18\x19", 26 => "\x18\x1A",
32 => "\x18\x20", 50 => "\x18\x32", 64 => "\x18\x40",
100 => "\x18\x64", 128 => "\x18\x80", 200 => "\x18\xC8",
255 => "\x18\xFF", 256 => "\x19\x01\x00", 500 => "\x19\x01\xF4",
1000 => "\x19\x03\xE8", 1023 => "\x19\x03\xFF",
];
/**
* Pre-encoded negative integers -1 to -24 and common larger values
* CBOR major type 1 (negative integer)
*/
private const NEG_CACHE = [
-1 => "\x20", -2 => "\x21", -3 => "\x22", -4 => "\x23",
-5 => "\x24", -10 => "\x29", -20 => "\x33", -24 => "\x37",
-25 => "\x38\x18", -50 => "\x38\x31", -100 => "\x38\x63",
];
/**
* Encode a PHP value to CBOR binary string
*
* @param mixed $value The value to encode
*
* @return string
*/
public function encode(mixed $value): string
{
return $this->encodeValue($value);
}
/**
* Recursively encode a value to CBOR
*
* @param mixed $value Value to encode
* @return string Encoded CBOR bytes
*/
private function encodeValue(mixed $value): string
{
switch (gettype($value)) {
case 'string':
$len = strlen($value);
if ($len < 24) {
return chr(0x60 | $len) . $value;
}
if ($len < 0x100) {
return "\x78" . chr($len) . $value;
}
return $this->encodeTextString($value);
case 'array':
if (isset($value['__cbor_timestamp'])) {
return "\xC1\xFB" . pack('E', $value['__cbor_timestamp']);
}
// Encode a byte string (major type 2)
if (isset($value['__cbor_bytes'])) {
$bytes = $value['__cbor_bytes'];
$len = strlen($bytes);
if ($len < 24) {
return chr(0x40 | $len) . $bytes;
}
if ($len < 0x100) {
return "\x58" . chr($len) . $bytes;
}
if ($len < 0x10000) {
return "\x59" . pack('n', $len) . $bytes;
}
return "\x5A" . pack('N', $len) . $bytes;
}
if (array_is_list($value)) {
return $this->encodeArray($value);
}
return $this->encodeMap($value);
case 'integer':
if (isset(self::INT_CACHE[$value])) {
return self::INT_CACHE[$value];
}
if (isset(self::NEG_CACHE[$value])) {
return self::NEG_CACHE[$value];
}
// Fast path for positive integers
// Major type 0: unsigned integer
if ($value >= 0) {
if ($value < 24) {
return chr($value);
}
if ($value < 0x100) {
return "\x18" . chr($value);
}
if ($value < 0x10000) {
return "\x19" . pack('n', $value);
}
if ($value < 0x100000000) {
return "\x1A" . pack('N', $value);
}
return "\x1B" . pack('J', $value);
}
return $this->encodeInteger($value);
case 'double':
// Encode a float (major type 7, float 64)
return "\xFB" . pack('E', $value);
case 'boolean':
// Encode a boolean (major type 7, simple)
return $value ? "\xF5" : "\xF4";
case 'NULL':
// Encode null (major type 7, simple)
return "\xF6";
case 'object':
throw new CborException("Cannot encode object of type: " . get_class($value));
default:
throw new CborException("Cannot encode value of type: " . gettype($value));
}
}
/**
* Encode an integer (major type 0 or 1)
*
* @param int $value
* @return string
*/
private function encodeInteger(int $value): string
{
if (isset(self::INT_CACHE[$value])) {
return self::INT_CACHE[$value];
}
if (isset(self::NEG_CACHE[$value])) {
return self::NEG_CACHE[$value];
}
if ($value >= 0) {
// Major type 0: unsigned integer
if ($value < 24) {
return chr($value);
}
if ($value < 0x100) {
return "\x18" . chr($value);
}
if ($value < 0x10000) {
return "\x19" . pack('n', $value);
}
if ($value < 0x100000000) {
return "\x1A" . pack('N', $value);
}
return "\x1B" . pack('J', $value);
}
// Major type 1: negative integer (-1 - n)
$value = -1 - $value;
if ($value < 24) {
return chr(0x20 | $value);
}
if ($value < 0x100) {
return "\x38" . chr($value);
}
if ($value < 0x10000) {
return "\x39" . pack('n', $value);
}
if ($value < 0x100000000) {
return "\x3A" . pack('N', $value);
}
return "\x3B" . pack('J', $value);
}
/**
* Encode a text string (major type 3)
*
* @param string $value
* @return string
*/
private function encodeTextString(string $value): string
{
$len = strlen($value);
if ($len < 24) {
return chr(0x60 | $len) . $value;
}
if ($len < 0x100) {
return "\x78" . chr($len) . $value;
}
if ($len < 0x10000) {
return "\x79" . pack('n', $len) . $value;
}
if ($len < 0x100000000) {
return "\x7A" . pack('N', $len) . $value;
}
return "\x7B" . pack('J', $len) . $value;
}
/**
* Encode an array (major type 4)
*
* @param array $value
* @return string
*/
private function encodeArray(array $value): string
{
$count = count($value);
if ($count < 24) {
$result = chr(0x80 | $count);
} elseif ($count < 0x100) {
$result = "\x98" . chr($count);
} elseif ($count < 0x10000) {
$result = "\x99" . pack('n', $count);
} elseif ($count < 0x100000000) {
$result = "\x9A" . pack('N', $count);
} else {
$result = "\x9B" . pack('J', $count);
}
foreach ($value as $item) {
$result .= $this->encodeValue($item);
}
return $result;
}
/**
* Encode a map (major type 5)
*
* @param array $value
* @return string
*/
private function encodeMap(array $value): string
{
$count = count($value);
if ($count < 24) {
$result = chr(0xA0 | $count);
} elseif ($count < 0x100) {
$result = "\xB8" . chr($count);
} elseif ($count < 0x10000) {
$result = "\xB9" . pack('n', $count);
} elseif ($count < 0x100000000) {
$result = "\xBA" . pack('N', $count);
} else {
$result = "\xBB" . pack('J', $count);
}
foreach ($value as $k => $v) {
if (is_int($k)) {
$result .= $this->encodeInteger($k);
} else {
$len = strlen($k);
if ($len < 24) {
$result .= chr(0x60 | $len) . $k;
} elseif ($len < 0x100) {
$result .= "\x78" . chr($len) . $k;
} else {
$result .= "\x79" . pack('n', $len) . $k;
}
}
$result .= $this->encodeValue($v);
}
return $result;
}
/**
* Create an empty map (major type 5 with 0 elements)
*
* @return string
*/
public function encodeEmptyMap(): string
{
return "\xA0";
}
/**
* Create an empty indefinite map (major type 5 indefinite length)
*
* @return string
*/
public function encodeEmptyIndefiniteMap(): string
{
return "\xBF\xFF";
}
}

View file

@ -0,0 +1,6 @@
<?php
namespace Aws\Api\Cbor\Exception;
use RuntimeException;
class CborException extends RuntimeException {}

View file

@ -30,11 +30,6 @@ class DateTimeResult extends \DateTime implements \JsonSerializable
throw new ParserException('Invalid timestamp value passed to DateTimeResult::fromEpoch');
}
// PHP 5.5 does not support sub-second precision
if (\PHP_VERSION_ID < 56000) {
return new self(gmdate('c', $unixTimestamp));
}
$decimalSeparator = isset(localeconv()['decimal_point']) ? localeconv()['decimal_point'] : ".";
$formatString = "U" . $decimalSeparator . "u";
$dateTime = DateTime::createFromFormat(

View file

@ -31,19 +31,6 @@ abstract class AbstractErrorParser
StructureShape $member
);
protected function extractPayload(
StructureShape $member,
ResponseInterface $response
) {
if ($member instanceof StructureShape) {
// Structure members parse top-level data into a specific key.
return $this->payload($response, $member);
} else {
// Streaming data is just the stream from the response body.
return $response->getBody();
}
}
protected function populateShape(
array &$data,
ResponseInterface $response,
@ -57,16 +44,15 @@ abstract class AbstractErrorParser
if (!empty($data['code'])) {
$errors = $this->api->getOperation($command->getName())->getErrors();
foreach ($errors as $key => $error) {
foreach ($errors as $error) {
// If error code matches a known error shape, populate the body
if ($this->errorCodeMatches($data, $error)) {
$modeledError = $error;
$data['body'] = $this->extractPayload(
$modeledError,
$response
$data['body'] = $this->payload(
$response,
$error
);
$data['error_shape'] = $modeledError;
$data['error_shape'] = $error;
foreach ($error->getMembers() as $name => $member) {
switch ($member['location']) {

View file

@ -0,0 +1,159 @@
<?php
namespace Aws\Api\ErrorParser;
use Aws\Api\Parser\AbstractParser;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
/**
* Base implementation for Smithy RPC V2 protocol error parsers.
*
* @internal
*/
abstract class AbstractRpcV2ErrorParser extends AbstractErrorParser
{
private const HEADER_QUERY_ERROR = 'x-amzn-query-error';
private const HEADER_ERROR_TYPE = 'x-amzn-errortype';
private const HEADER_REQUEST_ID = 'x-amzn-requestid';
/**
* @param ResponseInterface $response
* @param CommandInterface|null $command
*
* @return array
*/
public function __invoke(
ResponseInterface $response,
?CommandInterface $command = null
) {
$response = AbstractParser::getResponseWithCachingStream($response);
$data = $this->parseError($response);
if (isset($data['parsed']['__type'])) {
$data['message'] = $data['parsed']['message'] ?? null;
}
$this->populateShape($data, $response, $command);
return $data;
}
/**
* @param ResponseInterface $response
* @param StructureShape $member
*
* @return array
*/
abstract protected function payload(
ResponseInterface $response,
StructureShape $member
): array;
/**
* @param StreamInterface $body
* @param ResponseInterface $response
*
* @return mixed
*/
abstract protected function parseBody(
StreamInterface $body,
ResponseInterface $response
): mixed;
/**
* @param ResponseInterface $response
*
* @return array
*/
private function parseError(ResponseInterface $response): array
{
$statusCode = (string) $response->getStatusCode();
$errorCode = null;
$errorType = null;
if ($this->api?->getMetadata('awsQueryCompatible') !== null
&& $response->hasHeader(self::HEADER_QUERY_ERROR)
&& $awsQueryError = $this->parseQueryCompatibleHeader($response)
) {
$errorCode = $awsQueryError['code'];
$errorType = $awsQueryError['type'];
}
if (!$errorCode && $response->hasHeader(self::HEADER_ERROR_TYPE)) {
$errorCode = $this->extractErrorCode(
$response->getHeaderLine(self::HEADER_ERROR_TYPE)
);
}
$parsedBody = null;
$body = $response->getBody();
if ($body->getSize()) {
//TODO handle unseekable streams with CachingStream
$parsedBody = array_change_key_case($this->parseBody($body, $response));
}
if (!$errorCode && $parsedBody) {
$errorCode = $this->extractErrorCode(
$parsedBody['code'] ?? $parsedBody['__type'] ?? ''
);
}
return [
'request_id' => $response->getHeaderLine(self::HEADER_REQUEST_ID),
'code' => $errorCode ?: null,
'message' => null,
'type' => $errorType ?? ($statusCode[0] === '4' ? 'client' : 'server'),
'parsed' => $parsedBody,
];
}
/**
* Parse AWS Query Compatible error from header
*
* @param ResponseInterface $response
*
* @return array|null Returns ['code' => string, 'type' => string] or null
*/
private function parseQueryCompatibleHeader(ResponseInterface $response): ?array
{
$parts = explode(';', $response->getHeaderLine(self::HEADER_QUERY_ERROR));
if (count($parts) === 2 && $parts[0] && $parts[1]) {
return [
'code' => $parts[0],
'type' => $parts[1],
];
}
return null;
}
/**
* Extract error code from raw error string containing # and/or : delimiters
*
* @param string $rawErrorCode
* @return string
*/
private function extractErrorCode(string $rawErrorCode): string
{
// Handle format with both # and uri (e.g., "namespace#ErrorCode:http://foo-bar")
if (str_contains($rawErrorCode, ':') && str_contains($rawErrorCode, '#')) {
$start = strpos($rawErrorCode, '#') + 1;
$end = strpos($rawErrorCode, ':', $start);
return substr($rawErrorCode, $start, $end - $start);
}
// Handle format with uri only : (e.g., "ErrorCode:http://foo-bar.com/baz")
if (str_contains($rawErrorCode, ':')) {
return substr($rawErrorCode, 0, strpos($rawErrorCode, ':'));
}
// Handle format with only # (e.g., "namespace#ErrorCode")
if (str_contains($rawErrorCode, '#')) {
return substr($rawErrorCode, strpos($rawErrorCode, '#') + 1);
}
return $rawErrorCode;
}
}

View file

@ -1,6 +1,7 @@
<?php
namespace Aws\Api\ErrorParser;
use Aws\Api\Parser\AbstractParser;
use Aws\Api\Parser\PayloadParserTrait;
use Aws\Api\StructureShape;
use Psr\Http\Message\ResponseInterface;
@ -38,9 +39,10 @@ trait JsonParserTrait
}
$parsedBody = null;
$body = $response->getBody();
if (!$body->isSeekable() || $body->getSize()) {
$parsedBody = $this->parseJson((string) $body, $response);
$rawBody = AbstractParser::getBodyContents($response);
if (!empty($rawBody)) {
$parsedBody = $this->parseJson($rawBody, $response);
}
// Parse error code from response body
@ -132,11 +134,12 @@ trait JsonParserTrait
ResponseInterface $response,
StructureShape $member
) {
$body = $response->getBody();
if (!$body->isSeekable() || $body->getSize()) {
$jsonBody = $this->parseJson($body, $response);
$rawBody = AbstractParser::getBodyContents($response);
if (!empty($rawBody)) {
$jsonBody = $this->parseJson($rawBody, $response);
} else {
$jsonBody = (string) $body;
$jsonBody = $rawBody;
}
return $this->parser->parse($member, $jsonBody);

View file

@ -1,6 +1,7 @@
<?php
namespace Aws\Api\ErrorParser;
use Aws\Api\Parser\AbstractParser;
use Aws\Api\Parser\JsonParser;
use Aws\Api\Service;
use Aws\CommandInterface;
@ -25,6 +26,7 @@ class JsonRpcErrorParser extends AbstractErrorParser
ResponseInterface $response,
?CommandInterface $command = null
) {
$response = AbstractParser::getResponseWithCachingStream($response);
$data = $this->genericHandler($response);
// Make the casing consistent across services.

View file

@ -1,6 +1,7 @@
<?php
namespace Aws\Api\ErrorParser;
use Aws\Api\Parser\AbstractParser;
use Aws\Api\Parser\JsonParser;
use Aws\Api\Service;
use Aws\Api\StructureShape;
@ -26,6 +27,7 @@ class RestJsonErrorParser extends AbstractErrorParser
ResponseInterface $response,
?CommandInterface $command = null
) {
$response = AbstractParser::getResponseWithCachingStream($response);
$data = $this->genericHandler($response);
// Merge in error data from the JSON body
@ -40,7 +42,9 @@ class RestJsonErrorParser extends AbstractErrorParser
// Retrieve error message directly
$data['message'] = $data['parsed']['message']
?? ($data['parsed']['Message'] ?? null);
?? $data['parsed']['Message']
?? $data['parsed']['error_description']
?? null;
$this->populateShape($data, $response, $command);

View file

@ -0,0 +1,65 @@
<?php
namespace Aws\Api\ErrorParser;
use Aws\Api\Cbor\CborDecoder;
use Aws\Api\Parser\RpcV2ParserTrait;
use Aws\Api\Service;
use Aws\Api\StructureShape;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
/**
* Parses errors according to Smithy RPC V2 CBOR protocol standards.
*
* https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html
*
* @internal
*/
final class RpcV2CborErrorParser extends AbstractRpcV2ErrorParser
{
/** @var CborDecoder */
private CborDecoder $decoder;
use RpcV2ParserTrait;
/**
* @param Service|null $api
*/
public function __construct(?Service $api = null)
{
$this->decoder = new CborDecoder();
parent::__construct($api);
}
/**
* @param ResponseInterface $response
* @param StructureShape $member
*
* @return array
* @throws \Exception
*/
protected function payload(
ResponseInterface $response,
StructureShape $member
): array
{
$body = $response->getBody();
$cborBody = $this->parseCbor($body, $response);
return $this->resolveOutputShape($member, $cborBody);
}
/**
* @param StreamInterface $body
* @param ResponseInterface $response
*
* @return mixed
*/
protected function parseBody(
StreamInterface $body,
ResponseInterface $response
): mixed
{
return $this->parseCbor($body, $response);
}
}

View file

@ -1,6 +1,7 @@
<?php
namespace Aws\Api\ErrorParser;
use Aws\Api\Parser\AbstractParser;
use Aws\Api\Parser\PayloadParserTrait;
use Aws\Api\Parser\XmlParser;
use Aws\Api\Service;
@ -27,6 +28,7 @@ class XmlErrorParser extends AbstractErrorParser
ResponseInterface $response,
?CommandInterface $command = null
) {
$response = AbstractParser::getResponseWithCachingStream($response);
$code = (string) $response->getStatusCode();
$data = [
@ -37,9 +39,9 @@ class XmlErrorParser extends AbstractErrorParser
'parsed' => null
];
$body = $response->getBody();
if ($body->getSize() > 0) {
$this->parseBody($this->parseXml($body, $response), $data);
$rawBody = AbstractParser::getBodyContents($response);
if (!empty($rawBody)) {
$this->parseBody($this->parseXml($rawBody, $response), $data);
} else {
$this->parseHeaders($response, $data);
}
@ -100,12 +102,20 @@ class XmlErrorParser extends AbstractErrorParser
ResponseInterface $response,
StructureShape $member
) {
$xmlBody = $this->parseXml($response->getBody(), $response);
$rawBody = AbstractParser::getBodyContents($response);
if (empty($rawBody)) {
return $rawBody;
}
$xmlBody = $this->parseXml($rawBody, $response);
$prefix = $this->registerNamespacePrefix($xmlBody);
$errorBody = $xmlBody->xpath("//{$prefix}Error");
if (is_array($errorBody) && !empty($errorBody[0])) {
return $this->parser->parse($member, $errorBody[0]);
}
return $rawBody;
}
}

View file

@ -0,0 +1,11 @@
<?php
namespace Aws\Api\Exception;
use Aws\HasMonitoringEventsTrait;
use Aws\MonitoringEventsInterface;
class RpcV2CborException extends \RuntimeException implements
MonitoringEventsInterface
{
use HasMonitoringEventsTrait;
}

View file

@ -89,7 +89,7 @@ class Operation extends AbstractModel
/**
* Get an array of operation error shapes.
*
* @return Shape[]
* @return StructureShape[]
*/
public function getErrors()
{

View file

@ -5,6 +5,7 @@ use Aws\Api\Service;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Aws\ResultInterface;
use GuzzleHttp\Psr7\CachingStream;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
@ -43,4 +44,27 @@ abstract class AbstractParser
StructureShape $member,
$response
);
public static function getBodyContents(ResponseInterface $response): string
{
$body = $response->getBody();
if ($body->isSeekable()) {
$body->rewind();
}
return $body->getContents();
}
public static function getResponseWithCachingStream(
ResponseInterface $response
): ResponseInterface
{
if (!$response->getBody()->isSeekable()) {
return $response->withBody(
new CachingStream($response->getBody())
);
}
return $response;
}
}

View file

@ -39,6 +39,21 @@ abstract class AbstractRestParser extends AbstractParser
if ($payload = $output['payload']) {
$this->extractPayload($payload, $output, $response, $result);
} else {
$response = AbstractParser::getResponseWithCachingStream($response);
if ($response->getBody()->getSize() === null) {
$rawBody = AbstractParser::getBodyContents($response);
$isEmpty = empty($rawBody);
} else {
$isEmpty = $response->getBody()->getSize() === 0;
}
if (!$isEmpty && count($output->getMembers()) > 0
) {
// if no payload was found, then parse the contents of the body
$this->payload($response, $output, $result);
}
}
foreach ($output->getMembers() as $name => $member) {
@ -55,15 +70,6 @@ abstract class AbstractRestParser extends AbstractParser
}
}
$body = $response->getBody();
if (!$payload
&& (!$body->isSeekable() || $body->getSize())
&& count($output->getMembers()) > 0
) {
// if no payload was found, then parse the contents of the body
$this->payload($response, $output, $result);
}
return new Result($result);
}
@ -75,17 +81,29 @@ abstract class AbstractRestParser extends AbstractParser
) {
$member = $output->getMember($payload);
$body = $response->getBody();
if (!empty($member['eventstream'])) {
$result[$payload] = new EventParsingIterator(
$body,
$member,
$this
);
} elseif ($member instanceof StructureShape) {
return;
}
$response = AbstractParser::getResponseWithCachingStream($response);
if ($member instanceof StructureShape) {
//Unions must have at least one member set to a non-null value
// If the body is empty, we can assume it is unset
if (!empty($member['union']) && ($body->isSeekable() && !$body->getSize())) {
if ($response->getBody()->getSize() === null) {
$rawBody = AbstractParser::getBodyContents($response);
$isEmpty = empty($rawBody);
} else {
$isEmpty = $response->getBody()->getSize() === 0;
}
if (!empty($member['union']) && $isEmpty) {
return;
}

View file

@ -0,0 +1,83 @@
<?php
namespace Aws\Api\Parser;
use Aws\Api\Operation;
use Aws\Api\Parser\Exception\ParserException;
use Aws\Result;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;
/**
* Base implementation for Smithy RPC V2 protocol parsers.
*
* Implementers MUST define the following static property representing
* the `Smithy-Protocol` header value:
* self::HEADER_SMITHY_PROTOCOL => static::$smithyProtocol
*
* @internal
*/
abstract class AbstractRpcV2Parser extends AbstractParser
{
private const HEADER_SMITHY_PROTOCOL = 'Smithy-Protocol';
/** @var string */
protected static string $smithyProtocol;
public function __invoke(
CommandInterface $command,
ResponseInterface $response
) {
$operation = $this->api->getOperation($command->getName());
return $this->parseResponse($response, $operation);
}
/**
* Parses a response according to Smithy RPC V2 protocol standards.
*
* @param ResponseInterface $response the response to parse.
* @param Operation $operation the operation which holds information for
* parsing the response.
*
* @return Result
*/
private function parseResponse(
ResponseInterface $response,
Operation $operation
): Result
{
$smithyProtocolHeader = $response->getHeaderLine(self::HEADER_SMITHY_PROTOCOL);
if ($smithyProtocolHeader !== static::$smithyProtocol) {
$statusCode = $response->getStatusCode();
throw new ParserException(
"Malformed response: Smithy-Protocol header mismatch (HTTP {$statusCode}). "
. 'Expected ' . static::$smithyProtocol
);
}
if ($operation['output'] === null) {
return new Result([]);
}
$outputShape = $operation->getOutput();
foreach ($outputShape->getMembers() as $memberName => $memberProps) {
if (!empty($memberProps['eventstream'])) {
return new Result([
$memberName => new EventParsingIterator(
$response->getBody(),
$outputShape->getMember($memberName),
$this
)
]);
}
}
$result = $this->parseMemberFromStream(
$response->getBody(),
$outputShape,
$response
);
return new Result(is_null($result) ? [] : $result);
}
}

View file

@ -63,11 +63,16 @@ class JsonRpcParser extends AbstractParser
}
}
$body = $response->getBody();
if ($body->isSeekable()) {
$body->rewind();
}
$result = $this->parseMemberFromStream(
$response->getBody(),
$operation->getOutput(),
$response
);
$body,
$operation->getOutput(),
$response
);
return new Result(is_null($result) ? [] : $result);
}

View file

@ -2,7 +2,6 @@
namespace Aws\Api\Parser;
use Aws\Api\Parser\Exception\ParserException;
use Psr\Http\Message\ResponseInterface;
trait PayloadParserTrait
{

View file

@ -40,9 +40,11 @@ class QueryParser extends AbstractParser
ResponseInterface $response
) {
$output = $this->api->getOperation($command->getName())->getOutput();
$body = $response->getBody();
$xml = !$body->isSeekable() || $body->getSize()
? $this->parseXml($body, $response)
// Read the full payload, even in non-seekable streams
$rawBody = AbstractParser::getBodyContents($response);
// Just parse when the body is not empty
$xml = !empty($rawBody)
? $this->parseXml($rawBody, $response)
: null;
// Empty request bodies should not be deserialized.

View file

@ -28,15 +28,14 @@ class RestJsonParser extends AbstractRestParser
StructureShape $member,
array &$result
) {
$responseBody = (string) $response->getBody();
$rawBody = AbstractParser::getBodyContents($response);
// Parse JSON if we have content
$parsedJson = null;
if (!empty($responseBody)) {
$parsedJson = $this->parseJson($responseBody, $response);
if (!empty($rawBody)) {
$parsedJson = $this->parseJson($rawBody, $response);
} else {
// An empty response body should be deserialized as null
$result = $parsedJson;
$result = null;
return;
}

View file

@ -28,7 +28,12 @@ class RestXmlParser extends AbstractRestParser
StructureShape $member,
array &$result
) {
$result += $this->parseMemberFromStream($response->getBody(), $member, $response);
$body = $response->getBody();
if ($body->isSeekable()) {
$body->rewind();
}
$result += $this->parseMemberFromStream($body, $member, $response);
}
public function parseMemberFromStream(

View file

@ -0,0 +1,50 @@
<?php
namespace Aws\Api\Parser;
use Aws\Api\Cbor\CborDecoder;
use Aws\Api\Service;
use Aws\Api\StructureShape;
use Psr\Http\Message\StreamInterface;
/**
* Parses responses according to Smithy RPC V2 CBOR protocol standards.
*
* https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html
*
* @internal
*/
final class RpcV2CborParser extends AbstractRpcV2Parser
{
/** @var string */
protected static string $smithyProtocol = 'rpc-v2-cbor';
/** @var CborDecoder */
private CborDecoder $decoder;
use RpcV2ParserTrait;
/**
* @param Service $api Service description
*/
public function __construct(Service $api)
{
$this->decoder = new CborDecoder();
parent::__construct($api);
}
/**
* @param StreamInterface $stream
* @param StructureShape $member
* @param $response
*
* @return mixed
*/
public function parseMemberFromStream(
StreamInterface $stream,
StructureShape $member,
$response
): mixed
{
return $this->resolveOutputShape($member, $this->parseCbor($stream, $response));
}
}

View file

@ -0,0 +1,105 @@
<?php
namespace Aws\Api\Parser;
use Aws\Api\Cbor\Exception\CborException;
use Aws\Api\DateTimeResult;
use Aws\Api\Parser\Exception\ParserException;
use Aws\Api\Shape;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
/**
* Shared parsing logic for RPC V2 Parsers.
*
* @internal
*/
trait RpcV2ParserTrait
{
/**
* Resolves output shape fields that are present in the response
*
* @param Shape $shape
* @param mixed $value
*
* @return mixed
*/
protected function resolveOutputShape(Shape $shape, mixed $value): mixed
{
if ($value === null) {
return $value;
}
switch ($shape['type']) {
case 'structure':
$target = [];
foreach ($shape->getMembers() as $name => $member) {
$locationName = $member['locationName'] ?: $name;
if (isset($value[$locationName])) {
$target[$name] = $this->resolveOutputShape($member, $value[$locationName]);
}
}
return $target;
case 'list':
$target = [];
foreach ($value as $v) {
$target[] = $this->resolveOutputShape($shape->getMember(), $v);
}
return $target;
case 'map':
$target = [];
foreach ($value as $k => $v) {
if ($v !== null) {
$target[$k] = $this->resolveOutputShape($shape->getValue(), $v);
}
}
return $target;
case 'timestamp':
try {
$value = DateTimeResult::fromEpoch($value);
} catch (\Exception $e) {
trigger_error(
'Unable to parse timestamp value for '
. $shape->getName()
. ': ' . $e->getMessage(),
E_USER_WARNING
);
}
return $value;
default:
return $value;
}
}
/**
* Parses CBOR-encoded response data from RPC V2 CBOR services.
*
* @param StreamInterface $stream
* @param ResponseInterface $response
*
* @return mixed
*/
protected function parseCbor(
StreamInterface $stream,
ResponseInterface $response
): mixed
{
try {
$cborString = (string) $stream;
return empty($cborString)
? null
: $this->decoder->decode($cborString);
} catch (CborException $e) {
throw new ParserException(
"Malformed Response: error parsing CBOR: {$e->getMessage()}",
0,
$e,
['response' => $response]
);
}
}
}

View file

@ -0,0 +1,220 @@
<?php
namespace Aws\Api\Serializer;
use Aws\Api\Service;
use Aws\Api\Shape;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Aws\EndpointV2\EndpointV2SerializerTrait;
use Aws\EndpointV2\Ruleset\RulesetEndpoint;
use DateTimeInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\RequestInterface;
/**
* Base implementation for Smithy RPC V2 protocol serializers.
*
* Implementers MUST override the defaultHeader property to represent
* protocol-specific default header values:
* self::HEADER_SMITHY_PROTOCOL => static::SMITHY_PROTOCOL,
* self::HEADER_CONTENT_TYPE => static::DEFAULT_CONTENT_TYPE,
* self::HEADER_ACCEPT => static::DEFAULT_ACCEPT
*
* Implementers must also implement `serialize()`, `resolveBlob()`, and `resolveTimestamp()
* according to their respective protocol specifications.
*
* @internal
*/
abstract class AbstractRpcV2Serializer
{
protected const HEADER_SMITHY_PROTOCOL = 'Smithy-Protocol';
protected const HEADER_CONTENT_TYPE = 'Content-Type';
protected const HEADER_ACCEPT = 'Accept';
/** @var array */
protected static array $defaultHeaders;
/** @var Service */
private Service $api;
/** @var string|Uri */
private string|Uri $endpoint;
/** @var bool */
private bool $isUseEndpointV2;
use EndpointV2SerializerTrait;
/**
* @param Service $api Service API description
* @param string $endpoint Endpoint to connect to
*/
public function __construct(Service $api, string|Uri $endpoint)
{
$this->api = $api;
$this->endpoint = Psr7\Utils::uriFor($endpoint);
}
/**
* @param CommandInterface $command Command to serialize into a request.
* @param mixed|null $endpoint
*
* @return RequestInterface
*/
public function __invoke(
CommandInterface $command,
mixed $endpoint = null
)
{
$commandArgs = $command->toArray();
$commandName = $command->getName();
$operation = $this->api->getOperation($commandName);
$headers = static::$defaultHeaders;
// Operations with no defined input type must not contain bodies
// Content-Type must not be set
if ($operation['input'] !== null) {
$body = $this->serialize($operation->getInput(), $commandArgs);
$headers['Content-Length'] = (string) strlen($body);
} else {
unset($headers['Content-Type']);
}
if ($endpoint instanceof RulesetEndpoint) {
$this->isUseEndpointV2 = true;
$this->setEndpointV2RequestOptions($endpoint, $headers);
$this->endpoint = $endpoint->getUrl();
}
$requestTarget = $this->buildRequestTarget(
$commandName,
$operation['http']['requestUri'] ?? ''
);
$uri = new Uri($this->endpoint . $requestTarget);
return new Request(
$operation['http']['method'],
$uri,
$headers,
$body ?? null
);
}
/**
* @param StructureShape $inputShape
* @param array $commandArgs
*
* @return string
*/
abstract public function serialize(
StructureShape $inputShape,
array $commandArgs
): string;
/**
* Resolves arguments for blob shapes present in the request arguments
* into a protocol-specific format.
*
* @param mixed $value
*
* @return array
*/
abstract protected function resolveBlob(mixed $value): array;
/**
* Resolves arguments for timestamp shapes present in the request arguments
* into a protocol-specific format.
*
* @param mixed $value
*
* @return array
*/
abstract protected function resolveTimestamp(
int|float|string|DateTimeInterface $value
): array;
/**
* Resolves input shape fields that are present in the request arguments
*
* @param Shape $shape
* @param mixed $value
*
* @return mixed
*/
protected function resolveInputShape(Shape $shape, mixed $value): mixed
{
switch ($shape->getType()) {
case 'structure':
$data = [];
foreach ($value as $k => $v) {
if ($v !== null && $shape->hasMember($k)) {
$valueShape = $shape->getMember($k);
$data[$valueShape['locationName'] ?: $k]
= $this->resolveInputShape($valueShape, $v);
}
}
return $data;
case 'list':
$items = $shape->getMember();
foreach ($value as $k => $v) {
$value[$k] = $this->resolveInputShape($items, $v);
}
return $value;
case 'map':
$values = $shape->getValue();
foreach ($value as $k => $v) {
$value[$k] = $this->resolveInputShape($values, $v);
}
return $value;
case 'timestamp':
return $this->resolveTimestamp($value);
case 'string':
return (string) $value;
case 'integer':
case 'long':
return (int) $value;
case 'double':
case 'float':
return (float) $value;
case 'blob':
return $this->resolveBlob($value);
default:
return $value;
}
}
/**
* Builds request URI absolute path
*
* @param string $commandName
* @param string $requestUri
*
* @return string
*/
private function buildRequestTarget(
string $commandName,
string $requestUri
): string
{
$requestUri = str_ends_with($requestUri, '/')
? $requestUri
: $requestUri . '/';
$targetPrefix = $this->api->getMetadata('targetPrefix');
return "{$requestUri}service/{$targetPrefix}/operation/{$commandName}";
}
}

View file

@ -66,7 +66,7 @@ class JsonRpcSerializer
$headers = [
'X-Amz-Target' => $this->api->getMetadata('targetPrefix') . '.' . $operationName,
'Content-Type' => $this->contentType,
'Content-Length' => strlen($body)
'Content-Length' => (string) strlen($body)
];
if ($endpoint instanceof RulesetEndpoint) {

View file

@ -61,7 +61,7 @@ class QuerySerializer
}
$body = http_build_query($body, '', '&', PHP_QUERY_RFC3986);
$headers = [
'Content-Length' => strlen($body),
'Content-Length' => (string) strlen($body),
'Content-Type' => 'application/x-www-form-urlencoded'
];
$requestUri = $operation['http']['requestUri'] ?? null;

View file

@ -35,7 +35,7 @@ class RestJsonSerializer extends RestSerializer
{
$opts['headers']['Content-Type'] = $this->contentType;
$body = $this->jsonFormatter->build($member, $value);
$opts['headers']['Content-Length'] = strlen($body);
$opts['headers']['Content-Length'] = (string) strlen($body);
$opts['body'] = $body;
}
}

View file

@ -159,7 +159,7 @@ abstract class RestSerializer
$body = $args[$name];
if (!$m['streaming'] && is_string($body)) {
$opts['headers']['Content-Length'] = strlen($body);
$opts['headers']['Content-Length'] = (string) strlen($body);
}
// Streaming bodies or payloads that are strings are
@ -173,20 +173,36 @@ abstract class RestSerializer
private function applyHeader($name, Shape $member, $value, array &$opts)
{
// Handle lists by recursively applying header logic to each element
if ($value === null) {
return;
}
// Handle lists by applying header logic to each element
if ($member instanceof ListShape) {
if (!is_array($value)) {
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
}
$listMember = $member->getMember();
$headerValues = [];
foreach ($value as $listValue) {
if ($listValue === null) {
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
}
$tempOpts = ['headers' => []];
$this->applyHeader('temp', $listMember, $listValue, $tempOpts);
if (!array_key_exists('temp', $tempOpts['headers'])) {
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
}
$convertedValue = $tempOpts['headers']['temp'];
$headerValues[] = $convertedValue;
}
$value = $headerValues;
} elseif (!is_null($value)) {
} else {
switch ($member->getType()) {
case 'timestamp':
$timestampFormat = $member['timestampFormat'] ?? 'rfc822';
@ -208,7 +224,7 @@ abstract class RestSerializer
$value = base64_encode($value);
}
$opts['headers'][$member['locationName'] ?: $name] = $value;
$opts['headers'][$member['locationName'] ?: $name] = self::prepareHeaderValue($value);
}
/**
@ -218,10 +234,42 @@ abstract class RestSerializer
{
$prefix = $member['locationName'];
foreach ($value as $k => $v) {
$opts['headers'][$prefix . $k] = $v;
if ($v === null) {
continue;
}
$opts['headers'][$prefix . $k] = self::prepareHeaderValue($v);
}
}
/**
* @return string|string[]
*/
private static function prepareHeaderValue($value)
{
if (is_scalar($value)) {
return (string) $value;
}
if (is_array($value)) {
if ($value === []) {
return '';
}
foreach ($value as $key => $item) {
if (!is_scalar($item)) {
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
}
$value[$key] = (string) $item;
}
return $value;
}
throw new \InvalidArgumentException('Header values must be scalar or an array of scalars.');
}
private function applyQuery($name, Shape $member, $value, array &$opts)
{
if ($member instanceof MapShape) {

View file

@ -30,7 +30,7 @@ class RestXmlSerializer extends RestSerializer
{
$opts['headers']['Content-Type'] = 'application/xml';
$body = $this->getXmlBody($member, $value);
$opts['headers']['Content-Length'] = strlen($body);
$opts['headers']['Content-Length'] = (string) strlen($body);
$opts['body'] = $body;
}

View file

@ -0,0 +1,124 @@
<?php
namespace Aws\Api\Serializer;
use Aws\Api\Cbor\CborEncoder;
use Aws\Api\Cbor\Exception\CborException;
use Aws\Api\Exception\RpcV2CborException;
use Aws\Api\Service;
use Aws\Api\StructureShape;
use DateTimeInterface;
/**
* Serializes requests according to Smithy RPC-V2 CBOR protocol standards.
*
* https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html
*
* @internal
*/
final class RpcV2CborSerializer extends AbstractRpcV2Serializer
{
/** @var array|string[] */
protected static array $defaultHeaders = [
self::HEADER_SMITHY_PROTOCOL => 'rpc-v2-cbor',
self::HEADER_CONTENT_TYPE => 'application/cbor',
self::HEADER_ACCEPT => 'application/cbor',
];
/** @var CborEncoder */
private CborEncoder $encoder;
/**
* @param Service $api Service API description
* @param string $endpoint Endpoint to connect to
*/
public function __construct(Service $api, string $endpoint)
{
$this->encoder = new CborEncoder();
parent::__construct($api, $endpoint);
}
/**
* @param StructureShape $inputShape
* @param array $commandArgs
*
* @return string
* @throws RpcV2CborException
*/
public function serialize(
StructureShape $inputShape,
array $commandArgs
): string
{
try {
$resolvedInput = $this->resolveInputShape($inputShape, $commandArgs);
return !empty($resolvedInput)
? $this->encoder->encode($resolvedInput)
: $this->encoder->encodeEmptyIndefiniteMap();
} catch (CborException $e) {
throw new RpcV2CborException(
'Unable to encode CBOR document ' . $inputShape->getName() . ': ' .
$e->getMessage() . PHP_EOL
);
}
}
/**
* Wraps blob values in order to be encoded properly into
* byte strings.
*
* @param mixed $value
*
* @return string[]
* @throws RpcV2CborException
*/
protected function resolveBlob(mixed $value): array
{
if (is_resource($value)) {
$value = stream_get_contents($value);
if ($value === false) {
throw new RpcV2CborException(
'Failed to read resource stream value during serialization',
);
}
}
// Wrapper to differentiate byte string values during encoding
return ['__cbor_bytes' => (string) $value];
}
/**
* Wraps timestamp values in order to be encoded properly into
* value tag 1.
*
* @param mixed $value
*
* @return string[]
* @throws RpcV2CborException
*/
protected function resolveTimestamp(
int|float|string|DateTimeInterface $value
): array
{
if (is_numeric($value)) {
return ['__cbor_timestamp' => $value];
}
if ($value instanceof DateTimeInterface) {
// Preserve milliseconds
$micro = (int) $value->format('u');
$value = $value->getTimestamp() + $micro / 1e6;
} else {
$timestamp = strtotime($value);
if ($timestamp === false) {
throw new RpcV2CborException(
'Request serialization failed: Invalid date/time: ' . $value,
);
}
$value = $timestamp;
}
// Wrapper to differentiate timestamp values during encoding
return ['__cbor_timestamp' => $value];
}
}

View file

@ -91,7 +91,8 @@ class Service extends AbstractModel
'json' => Serializer\JsonRpcSerializer::class,
'query' => Serializer\QuerySerializer::class,
'rest-json' => Serializer\RestJsonSerializer::class,
'rest-xml' => Serializer\RestXmlSerializer::class
'rest-xml' => Serializer\RestXmlSerializer::class,
'smithy-rpc-v2-cbor' => Serializer\RpcV2CborSerializer::class
];
$proto = $api->getProtocol();
@ -126,7 +127,8 @@ class Service extends AbstractModel
'query' => ErrorParser\XmlErrorParser::class,
'rest-json' => ErrorParser\RestJsonErrorParser::class,
'rest-xml' => ErrorParser\XmlErrorParser::class,
'ec2' => ErrorParser\XmlErrorParser::class
'ec2' => ErrorParser\XmlErrorParser::class,
'smithy-rpc-v2-cbor' => ErrorParser\RpcV2CborErrorParser::class
];
if (isset($mapping[$protocol])) {
@ -149,7 +151,8 @@ class Service extends AbstractModel
'json' => Parser\JsonRpcParser::class,
'query' => Parser\QueryParser::class,
'rest-json' => Parser\RestJsonParser::class,
'rest-xml' => Parser\RestXmlParser::class
'rest-xml' => Parser\RestXmlParser::class,
'smithy-rpc-v2-cbor' => Parser\RpcV2CborParser::class
];
$proto = $api->getProtocol();

View file

@ -8,6 +8,7 @@ namespace Aws\Api;
enum SupportedProtocols: string
{
case JSON = 'json';
case CBOR = 'smithy-rpc-v2-cbor';
case REST_JSON = 'rest-json';
case REST_XML = 'rest-xml';
case QUERY = 'query';

View file

@ -28,16 +28,16 @@ class TimestampShape extends Shape
$value = $value->getTimestamp();
} elseif (is_string($value)) {
$value = strtotime($value);
} elseif (!is_int($value)) {
} elseif (!is_int($value) && !is_float($value)) {
throw new \InvalidArgumentException('Unable to handle the provided'
. ' timestamp type: ' . gettype($value));
}
switch ($format) {
case 'iso8601':
return gmdate('Y-m-d\TH:i:s\Z', $value);
return gmdate('Y-m-d\TH:i:s\Z', (int) $value);
case 'rfc822':
return gmdate('D, d M Y H:i:s \G\M\T', $value);
return gmdate('D, d M Y H:i:s \G\M\T', (int) $value);
case 'unixTimestamp':
return $value;
default:

View file

@ -131,6 +131,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise disassociateFleetAsync(array $args = [])
* @method \Aws\Result disassociateSoftwareFromImageBuilder(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociateSoftwareFromImageBuilderAsync(array $args = [])
* @method \Aws\Result drainSessionInstance(array $args = [])
* @method \GuzzleHttp\Promise\Promise drainSessionInstanceAsync(array $args = [])
* @method \Aws\Result enableUser(array $args = [])
* @method \GuzzleHttp\Promise\Promise enableUserAsync(array $args = [])
* @method \Aws\Result expireSession(array $args = [])

View file

@ -283,6 +283,7 @@ class AwsClient implements AwsClientInterface
$args['with_resolved']($config);
}
$this->addUserAgentMiddleware($config);
$this->addEventStreamHttpFlagMiddleware();
}
public function getHandlerList()
@ -543,7 +544,7 @@ class AwsClient implements AwsClientInterface
{
$list = $this->getHandlerList();
$list->appendBuild(
Middleware::mapRequest(function (RequestInterface $r) {
Middleware::mapRequest(static function (RequestInterface $r) {
return $r->withHeader(
'x-amzn-query-mode',
"true"
@ -649,6 +650,34 @@ class AwsClient implements AwsClientInterface
);
}
/**
* Enables streaming the response by using the stream flag.
*
* @return void
*/
private function addEventStreamHttpFlagMiddleware(): void
{
$api = $this->getApi();
$this->getHandlerList()
-> appendInit(
static function (callable $handler) use ($api) {
return static function (CommandInterface $command, $request = null) use ($handler, $api) {
$operation = $api->getOperation($command->getName());
$output = $operation->getOutput();
foreach ($output->getMembers() as $memberProps) {
if (!empty($memberProps['eventstream'])) {
$command['@http']['stream'] = true;
break;
}
}
return $handler($command, $request);
};
},
'event-streaming-flag-middleware'
);
}
/**
* Retrieves client context param definition from service model,
* creates mapping of client context param names with client-provided
@ -737,29 +766,6 @@ class AwsClient implements AwsClientInterface
return $this->endpointProvider instanceof EndpointProviderV2;
}
public static function emitDeprecationWarning() {
trigger_error(
"This method is deprecated. It will be removed in an upcoming release."
, E_USER_DEPRECATED
);
$phpVersion = PHP_VERSION_ID;
if ($phpVersion < 70205) {
$phpVersionString = phpversion();
@trigger_error(
"This installation of the SDK is using PHP version"
. " {$phpVersionString}, which will be deprecated on August"
. " 15th, 2023. Please upgrade your PHP version to a minimum of"
. " 7.2.5 before then to continue receiving updates to the AWS"
. " SDK for PHP. To disable this warning, set"
. " suppress_php_deprecation_warning to true on the client constructor"
. " or set the environment variable AWS_SUPPRESS_PHP_DEPRECATION_WARNING"
. " to true.",
E_USER_DEPRECATED
);
}
}
/**
* Returns a service model and doc model with any necessary changes

View file

@ -75,7 +75,7 @@ trait AwsClientTrait
$name = $this->aliases[ucfirst($name)];
}
$params = isset($args[0]) ? $args[0] : [];
$params = $args['args'] ?? $args[0] ?? [];
if (!empty($isAsync)) {
return $this->executeAsync(

View file

@ -7,14 +7,24 @@ use Aws\AwsClient;
* This client is used to interact with the **AWS Billing and Cost Management Dashboards** service.
* @method \Aws\Result createDashboard(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDashboardAsync(array $args = [])
* @method \Aws\Result createScheduledReport(array $args = [])
* @method \GuzzleHttp\Promise\Promise createScheduledReportAsync(array $args = [])
* @method \Aws\Result deleteDashboard(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDashboardAsync(array $args = [])
* @method \Aws\Result deleteScheduledReport(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteScheduledReportAsync(array $args = [])
* @method \Aws\Result executeScheduledReport(array $args = [])
* @method \GuzzleHttp\Promise\Promise executeScheduledReportAsync(array $args = [])
* @method \Aws\Result getDashboard(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDashboardAsync(array $args = [])
* @method \Aws\Result getResourcePolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise getResourcePolicyAsync(array $args = [])
* @method \Aws\Result getScheduledReport(array $args = [])
* @method \GuzzleHttp\Promise\Promise getScheduledReportAsync(array $args = [])
* @method \Aws\Result listDashboards(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDashboardsAsync(array $args = [])
* @method \Aws\Result listScheduledReports(array $args = [])
* @method \GuzzleHttp\Promise\Promise listScheduledReportsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
@ -23,5 +33,7 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateDashboard(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateDashboardAsync(array $args = [])
* @method \Aws\Result updateScheduledReport(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateScheduledReportAsync(array $args = [])
*/
class BCMDashboardsClient extends AwsClient {}

View file

@ -101,6 +101,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getBackupVaultNotificationsAsync(array $args = [])
* @method \Aws\Result getLegalHold(array $args = [])
* @method \GuzzleHttp\Promise\Promise getLegalHoldAsync(array $args = [])
* @method \Aws\Result getPITRMalwareScanResults(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPITRMalwareScanResultsAsync(array $args = [])
* @method \Aws\Result getRecoveryPointIndexDetails(array $args = [])
* @method \GuzzleHttp\Promise\Promise getRecoveryPointIndexDetailsAsync(array $args = [])
* @method \Aws\Result getRecoveryPointRestoreMetadata(array $args = [])

View file

@ -13,6 +13,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createConsumableResourceAsync(array $args = [])
* @method \Aws\Result createJobQueue(array $args = [])
* @method \GuzzleHttp\Promise\Promise createJobQueueAsync(array $args = [])
* @method \Aws\Result createQuotaShare(array $args = [])
* @method \GuzzleHttp\Promise\Promise createQuotaShareAsync(array $args = [])
* @method \Aws\Result createSchedulingPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise createSchedulingPolicyAsync(array $args = [])
* @method \Aws\Result createServiceEnvironment(array $args = [])
@ -23,6 +25,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteConsumableResourceAsync(array $args = [])
* @method \Aws\Result deleteJobQueue(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteJobQueueAsync(array $args = [])
* @method \Aws\Result deleteQuotaShare(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteQuotaShareAsync(array $args = [])
* @method \Aws\Result deleteSchedulingPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteSchedulingPolicyAsync(array $args = [])
* @method \Aws\Result deleteServiceEnvironment(array $args = [])
@ -39,6 +43,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise describeJobQueuesAsync(array $args = [])
* @method \Aws\Result describeJobs(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeJobsAsync(array $args = [])
* @method \Aws\Result describeQuotaShare(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeQuotaShareAsync(array $args = [])
* @method \Aws\Result describeSchedulingPolicies(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeSchedulingPoliciesAsync(array $args = [])
* @method \Aws\Result describeServiceEnvironments(array $args = [])
@ -53,6 +59,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listJobsAsync(array $args = [])
* @method \Aws\Result listJobsByConsumableResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listJobsByConsumableResourceAsync(array $args = [])
* @method \Aws\Result listQuotaShares(array $args = [])
* @method \GuzzleHttp\Promise\Promise listQuotaSharesAsync(array $args = [])
* @method \Aws\Result listSchedulingPolicies(array $args = [])
* @method \GuzzleHttp\Promise\Promise listSchedulingPoliciesAsync(array $args = [])
* @method \Aws\Result listServiceJobs(array $args = [])
@ -79,9 +87,13 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateConsumableResourceAsync(array $args = [])
* @method \Aws\Result updateJobQueue(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateJobQueueAsync(array $args = [])
* @method \Aws\Result updateQuotaShare(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateQuotaShareAsync(array $args = [])
* @method \Aws\Result updateSchedulingPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateSchedulingPolicyAsync(array $args = [])
* @method \Aws\Result updateServiceEnvironment(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateServiceEnvironmentAsync(array $args = [])
* @method \Aws\Result updateServiceJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateServiceJobAsync(array $args = [])
*/
class BatchClient extends AwsClient {}

View file

@ -5,10 +5,14 @@ use Aws\AwsClient;
/**
* This client is used to interact with the **Amazon Bedrock** service.
* @method \Aws\Result batchDeleteAdvancedPromptOptimizationJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchDeleteAdvancedPromptOptimizationJobAsync(array $args = [])
* @method \Aws\Result batchDeleteEvaluationJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchDeleteEvaluationJobAsync(array $args = [])
* @method \Aws\Result cancelAutomatedReasoningPolicyBuildWorkflow(array $args = [])
* @method \GuzzleHttp\Promise\Promise cancelAutomatedReasoningPolicyBuildWorkflowAsync(array $args = [])
* @method \Aws\Result createAdvancedPromptOptimizationJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise createAdvancedPromptOptimizationJobAsync(array $args = [])
* @method \Aws\Result createAutomatedReasoningPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise createAutomatedReasoningPolicyAsync(array $args = [])
* @method \Aws\Result createAutomatedReasoningPolicyTestCase(array $args = [])
@ -71,10 +75,14 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deletePromptRouterAsync(array $args = [])
* @method \Aws\Result deleteProvisionedModelThroughput(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteProvisionedModelThroughputAsync(array $args = [])
* @method \Aws\Result deleteResourcePolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteResourcePolicyAsync(array $args = [])
* @method \Aws\Result deregisterMarketplaceModelEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise deregisterMarketplaceModelEndpointAsync(array $args = [])
* @method \Aws\Result exportAutomatedReasoningPolicyVersion(array $args = [])
* @method \GuzzleHttp\Promise\Promise exportAutomatedReasoningPolicyVersionAsync(array $args = [])
* @method \Aws\Result getAdvancedPromptOptimizationJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAdvancedPromptOptimizationJobAsync(array $args = [])
* @method \Aws\Result getAutomatedReasoningPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAutomatedReasoningPolicyAsync(array $args = [])
* @method \Aws\Result getAutomatedReasoningPolicyAnnotations(array $args = [])
@ -121,8 +129,12 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getPromptRouterAsync(array $args = [])
* @method \Aws\Result getProvisionedModelThroughput(array $args = [])
* @method \GuzzleHttp\Promise\Promise getProvisionedModelThroughputAsync(array $args = [])
* @method \Aws\Result getResourcePolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise getResourcePolicyAsync(array $args = [])
* @method \Aws\Result getUseCaseForModelAccess(array $args = [])
* @method \GuzzleHttp\Promise\Promise getUseCaseForModelAccessAsync(array $args = [])
* @method \Aws\Result listAdvancedPromptOptimizationJobs(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAdvancedPromptOptimizationJobsAsync(array $args = [])
* @method \Aws\Result listAutomatedReasoningPolicies(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAutomatedReasoningPoliciesAsync(array $args = [])
* @method \Aws\Result listAutomatedReasoningPolicyBuildWorkflows(array $args = [])
@ -169,6 +181,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise putEnforcedGuardrailConfigurationAsync(array $args = [])
* @method \Aws\Result putModelInvocationLoggingConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise putModelInvocationLoggingConfigurationAsync(array $args = [])
* @method \Aws\Result putResourcePolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise putResourcePolicyAsync(array $args = [])
* @method \Aws\Result putUseCaseForModelAccess(array $args = [])
* @method \GuzzleHttp\Promise\Promise putUseCaseForModelAccessAsync(array $args = [])
* @method \Aws\Result registerMarketplaceModelEndpoint(array $args = [])
@ -177,6 +191,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise startAutomatedReasoningPolicyBuildWorkflowAsync(array $args = [])
* @method \Aws\Result startAutomatedReasoningPolicyTestWorkflow(array $args = [])
* @method \GuzzleHttp\Promise\Promise startAutomatedReasoningPolicyTestWorkflowAsync(array $args = [])
* @method \Aws\Result stopAdvancedPromptOptimizationJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopAdvancedPromptOptimizationJobAsync(array $args = [])
* @method \Aws\Result stopEvaluationJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopEvaluationJobAsync(array $args = [])
* @method \Aws\Result stopModelCustomizationJob(array $args = [])

View file

@ -13,16 +13,36 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise batchUpdateMemoryRecordsAsync(array $args = [])
* @method \Aws\Result completeResourceTokenAuth(array $args = [])
* @method \GuzzleHttp\Promise\Promise completeResourceTokenAuthAsync(array $args = [])
* @method \Aws\Result createABTest(array $args = [])
* @method \GuzzleHttp\Promise\Promise createABTestAsync(array $args = [])
* @method \Aws\Result createEvent(array $args = [])
* @method \GuzzleHttp\Promise\Promise createEventAsync(array $args = [])
* @method \Aws\Result createPaymentInstrument(array $args = [])
* @method \GuzzleHttp\Promise\Promise createPaymentInstrumentAsync(array $args = [])
* @method \Aws\Result createPaymentSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise createPaymentSessionAsync(array $args = [])
* @method \Aws\Result deleteABTest(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteABTestAsync(array $args = [])
* @method \Aws\Result deleteBatchEvaluation(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteBatchEvaluationAsync(array $args = [])
* @method \Aws\Result deleteEvent(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteEventAsync(array $args = [])
* @method \Aws\Result deleteMemoryRecord(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteMemoryRecordAsync(array $args = [])
* @method \Aws\Result deletePaymentInstrument(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePaymentInstrumentAsync(array $args = [])
* @method \Aws\Result deletePaymentSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePaymentSessionAsync(array $args = [])
* @method \Aws\Result deleteRecommendation(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteRecommendationAsync(array $args = [])
* @method \Aws\Result evaluate(array $args = [])
* @method \GuzzleHttp\Promise\Promise evaluateAsync(array $args = [])
* @method \Aws\Result getABTest(array $args = [])
* @method \GuzzleHttp\Promise\Promise getABTestAsync(array $args = [])
* @method \Aws\Result getAgentCard(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAgentCardAsync(array $args = [])
* @method \Aws\Result getBatchEvaluation(array $args = [])
* @method \GuzzleHttp\Promise\Promise getBatchEvaluationAsync(array $args = [])
* @method \Aws\Result getBrowserSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise getBrowserSessionAsync(array $args = [])
* @method \Aws\Result getCodeInterpreterSession(array $args = [])
@ -31,10 +51,20 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getEventAsync(array $args = [])
* @method \Aws\Result getMemoryRecord(array $args = [])
* @method \GuzzleHttp\Promise\Promise getMemoryRecordAsync(array $args = [])
* @method \Aws\Result getPaymentInstrument(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPaymentInstrumentAsync(array $args = [])
* @method \Aws\Result getPaymentInstrumentBalance(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPaymentInstrumentBalanceAsync(array $args = [])
* @method \Aws\Result getPaymentSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPaymentSessionAsync(array $args = [])
* @method \Aws\Result getRecommendation(array $args = [])
* @method \GuzzleHttp\Promise\Promise getRecommendationAsync(array $args = [])
* @method \Aws\Result getResourceApiKey(array $args = [])
* @method \GuzzleHttp\Promise\Promise getResourceApiKeyAsync(array $args = [])
* @method \Aws\Result getResourceOauth2Token(array $args = [])
* @method \GuzzleHttp\Promise\Promise getResourceOauth2TokenAsync(array $args = [])
* @method \Aws\Result getResourcePaymentToken(array $args = [])
* @method \GuzzleHttp\Promise\Promise getResourcePaymentTokenAsync(array $args = [])
* @method \Aws\Result getWorkloadAccessToken(array $args = [])
* @method \GuzzleHttp\Promise\Promise getWorkloadAccessTokenAsync(array $args = [])
* @method \Aws\Result getWorkloadAccessTokenForJWT(array $args = [])
@ -43,10 +73,20 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getWorkloadAccessTokenForUserIdAsync(array $args = [])
* @method \Aws\Result invokeAgentRuntime(array $args = [])
* @method \GuzzleHttp\Promise\Promise invokeAgentRuntimeAsync(array $args = [])
* @method \Aws\Result invokeAgentRuntimeCommand(array $args = [])
* @method \GuzzleHttp\Promise\Promise invokeAgentRuntimeCommandAsync(array $args = [])
* @method \Aws\Result invokeBrowser(array $args = [])
* @method \GuzzleHttp\Promise\Promise invokeBrowserAsync(array $args = [])
* @method \Aws\Result invokeCodeInterpreter(array $args = [])
* @method \GuzzleHttp\Promise\Promise invokeCodeInterpreterAsync(array $args = [])
* @method \Aws\Result invokeHarness(array $args = [])
* @method \GuzzleHttp\Promise\Promise invokeHarnessAsync(array $args = [])
* @method \Aws\Result listABTests(array $args = [])
* @method \GuzzleHttp\Promise\Promise listABTestsAsync(array $args = [])
* @method \Aws\Result listActors(array $args = [])
* @method \GuzzleHttp\Promise\Promise listActorsAsync(array $args = [])
* @method \Aws\Result listBatchEvaluations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listBatchEvaluationsAsync(array $args = [])
* @method \Aws\Result listBrowserSessions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listBrowserSessionsAsync(array $args = [])
* @method \Aws\Result listCodeInterpreterSessions(array $args = [])
@ -57,22 +97,42 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listMemoryExtractionJobsAsync(array $args = [])
* @method \Aws\Result listMemoryRecords(array $args = [])
* @method \GuzzleHttp\Promise\Promise listMemoryRecordsAsync(array $args = [])
* @method \Aws\Result listPaymentInstruments(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPaymentInstrumentsAsync(array $args = [])
* @method \Aws\Result listPaymentSessions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPaymentSessionsAsync(array $args = [])
* @method \Aws\Result listRecommendations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listRecommendationsAsync(array $args = [])
* @method \Aws\Result listSessions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listSessionsAsync(array $args = [])
* @method \Aws\Result processPayment(array $args = [])
* @method \GuzzleHttp\Promise\Promise processPaymentAsync(array $args = [])
* @method \Aws\Result retrieveMemoryRecords(array $args = [])
* @method \GuzzleHttp\Promise\Promise retrieveMemoryRecordsAsync(array $args = [])
* @method \Aws\Result saveBrowserSessionProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise saveBrowserSessionProfileAsync(array $args = [])
* @method \Aws\Result searchRegistryRecords(array $args = [])
* @method \GuzzleHttp\Promise\Promise searchRegistryRecordsAsync(array $args = [])
* @method \Aws\Result startBatchEvaluation(array $args = [])
* @method \GuzzleHttp\Promise\Promise startBatchEvaluationAsync(array $args = [])
* @method \Aws\Result startBrowserSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise startBrowserSessionAsync(array $args = [])
* @method \Aws\Result startCodeInterpreterSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise startCodeInterpreterSessionAsync(array $args = [])
* @method \Aws\Result startMemoryExtractionJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise startMemoryExtractionJobAsync(array $args = [])
* @method \Aws\Result startRecommendation(array $args = [])
* @method \GuzzleHttp\Promise\Promise startRecommendationAsync(array $args = [])
* @method \Aws\Result stopBatchEvaluation(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopBatchEvaluationAsync(array $args = [])
* @method \Aws\Result stopBrowserSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopBrowserSessionAsync(array $args = [])
* @method \Aws\Result stopCodeInterpreterSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopCodeInterpreterSessionAsync(array $args = [])
* @method \Aws\Result stopRuntimeSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopRuntimeSessionAsync(array $args = [])
* @method \Aws\Result updateABTest(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateABTestAsync(array $args = [])
* @method \Aws\Result updateBrowserStream(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateBrowserStreamAsync(array $args = [])
*/

View file

@ -5,6 +5,8 @@ use Aws\AwsClient;
/**
* This client is used to interact with the **Amazon Bedrock Agent Core Control Plane Fronting Layer** service.
* @method \Aws\Result addDatasetExamples(array $args = [])
* @method \GuzzleHttp\Promise\Promise addDatasetExamplesAsync(array $args = [])
* @method \Aws\Result createAgentRuntime(array $args = [])
* @method \GuzzleHttp\Promise\Promise createAgentRuntimeAsync(array $args = [])
* @method \Aws\Result createAgentRuntimeEndpoint(array $args = [])
@ -13,24 +15,46 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createApiKeyCredentialProviderAsync(array $args = [])
* @method \Aws\Result createBrowser(array $args = [])
* @method \GuzzleHttp\Promise\Promise createBrowserAsync(array $args = [])
* @method \Aws\Result createBrowserProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise createBrowserProfileAsync(array $args = [])
* @method \Aws\Result createCodeInterpreter(array $args = [])
* @method \GuzzleHttp\Promise\Promise createCodeInterpreterAsync(array $args = [])
* @method \Aws\Result createConfigurationBundle(array $args = [])
* @method \GuzzleHttp\Promise\Promise createConfigurationBundleAsync(array $args = [])
* @method \Aws\Result createDataset(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDatasetAsync(array $args = [])
* @method \Aws\Result createDatasetVersion(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDatasetVersionAsync(array $args = [])
* @method \Aws\Result createEvaluator(array $args = [])
* @method \GuzzleHttp\Promise\Promise createEvaluatorAsync(array $args = [])
* @method \Aws\Result createGateway(array $args = [])
* @method \GuzzleHttp\Promise\Promise createGatewayAsync(array $args = [])
* @method \Aws\Result createGatewayRule(array $args = [])
* @method \GuzzleHttp\Promise\Promise createGatewayRuleAsync(array $args = [])
* @method \Aws\Result createGatewayTarget(array $args = [])
* @method \GuzzleHttp\Promise\Promise createGatewayTargetAsync(array $args = [])
* @method \Aws\Result createHarness(array $args = [])
* @method \GuzzleHttp\Promise\Promise createHarnessAsync(array $args = [])
* @method \Aws\Result createMemory(array $args = [])
* @method \GuzzleHttp\Promise\Promise createMemoryAsync(array $args = [])
* @method \Aws\Result createOauth2CredentialProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise createOauth2CredentialProviderAsync(array $args = [])
* @method \Aws\Result createOnlineEvaluationConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise createOnlineEvaluationConfigAsync(array $args = [])
* @method \Aws\Result createPaymentConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise createPaymentConnectorAsync(array $args = [])
* @method \Aws\Result createPaymentCredentialProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise createPaymentCredentialProviderAsync(array $args = [])
* @method \Aws\Result createPaymentManager(array $args = [])
* @method \GuzzleHttp\Promise\Promise createPaymentManagerAsync(array $args = [])
* @method \Aws\Result createPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise createPolicyAsync(array $args = [])
* @method \Aws\Result createPolicyEngine(array $args = [])
* @method \GuzzleHttp\Promise\Promise createPolicyEngineAsync(array $args = [])
* @method \Aws\Result createRegistry(array $args = [])
* @method \GuzzleHttp\Promise\Promise createRegistryAsync(array $args = [])
* @method \Aws\Result createRegistryRecord(array $args = [])
* @method \GuzzleHttp\Promise\Promise createRegistryRecordAsync(array $args = [])
* @method \Aws\Result createWorkloadIdentity(array $args = [])
* @method \GuzzleHttp\Promise\Promise createWorkloadIdentityAsync(array $args = [])
* @method \Aws\Result deleteAgentRuntime(array $args = [])
@ -41,24 +65,46 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteApiKeyCredentialProviderAsync(array $args = [])
* @method \Aws\Result deleteBrowser(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteBrowserAsync(array $args = [])
* @method \Aws\Result deleteBrowserProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteBrowserProfileAsync(array $args = [])
* @method \Aws\Result deleteCodeInterpreter(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteCodeInterpreterAsync(array $args = [])
* @method \Aws\Result deleteConfigurationBundle(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteConfigurationBundleAsync(array $args = [])
* @method \Aws\Result deleteDataset(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDatasetAsync(array $args = [])
* @method \Aws\Result deleteDatasetExamples(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDatasetExamplesAsync(array $args = [])
* @method \Aws\Result deleteEvaluator(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteEvaluatorAsync(array $args = [])
* @method \Aws\Result deleteGateway(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteGatewayAsync(array $args = [])
* @method \Aws\Result deleteGatewayRule(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteGatewayRuleAsync(array $args = [])
* @method \Aws\Result deleteGatewayTarget(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteGatewayTargetAsync(array $args = [])
* @method \Aws\Result deleteHarness(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteHarnessAsync(array $args = [])
* @method \Aws\Result deleteMemory(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteMemoryAsync(array $args = [])
* @method \Aws\Result deleteOauth2CredentialProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteOauth2CredentialProviderAsync(array $args = [])
* @method \Aws\Result deleteOnlineEvaluationConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteOnlineEvaluationConfigAsync(array $args = [])
* @method \Aws\Result deletePaymentConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePaymentConnectorAsync(array $args = [])
* @method \Aws\Result deletePaymentCredentialProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePaymentCredentialProviderAsync(array $args = [])
* @method \Aws\Result deletePaymentManager(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePaymentManagerAsync(array $args = [])
* @method \Aws\Result deletePolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePolicyAsync(array $args = [])
* @method \Aws\Result deletePolicyEngine(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePolicyEngineAsync(array $args = [])
* @method \Aws\Result deleteRegistry(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteRegistryAsync(array $args = [])
* @method \Aws\Result deleteRegistryRecord(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteRegistryRecordAsync(array $args = [])
* @method \Aws\Result deleteResourcePolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteResourcePolicyAsync(array $args = [])
* @method \Aws\Result deleteWorkloadIdentity(array $args = [])
@ -71,26 +117,54 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getApiKeyCredentialProviderAsync(array $args = [])
* @method \Aws\Result getBrowser(array $args = [])
* @method \GuzzleHttp\Promise\Promise getBrowserAsync(array $args = [])
* @method \Aws\Result getBrowserProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise getBrowserProfileAsync(array $args = [])
* @method \Aws\Result getCodeInterpreter(array $args = [])
* @method \GuzzleHttp\Promise\Promise getCodeInterpreterAsync(array $args = [])
* @method \Aws\Result getConfigurationBundle(array $args = [])
* @method \GuzzleHttp\Promise\Promise getConfigurationBundleAsync(array $args = [])
* @method \Aws\Result getConfigurationBundleVersion(array $args = [])
* @method \GuzzleHttp\Promise\Promise getConfigurationBundleVersionAsync(array $args = [])
* @method \Aws\Result getDataset(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDatasetAsync(array $args = [])
* @method \Aws\Result getEvaluator(array $args = [])
* @method \GuzzleHttp\Promise\Promise getEvaluatorAsync(array $args = [])
* @method \Aws\Result getGateway(array $args = [])
* @method \GuzzleHttp\Promise\Promise getGatewayAsync(array $args = [])
* @method \Aws\Result getGatewayRule(array $args = [])
* @method \GuzzleHttp\Promise\Promise getGatewayRuleAsync(array $args = [])
* @method \Aws\Result getGatewayTarget(array $args = [])
* @method \GuzzleHttp\Promise\Promise getGatewayTargetAsync(array $args = [])
* @method \Aws\Result getHarness(array $args = [])
* @method \GuzzleHttp\Promise\Promise getHarnessAsync(array $args = [])
* @method \Aws\Result getMemory(array $args = [])
* @method \GuzzleHttp\Promise\Promise getMemoryAsync(array $args = [])
* @method \Aws\Result getOauth2CredentialProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise getOauth2CredentialProviderAsync(array $args = [])
* @method \Aws\Result getOnlineEvaluationConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise getOnlineEvaluationConfigAsync(array $args = [])
* @method \Aws\Result getPaymentConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPaymentConnectorAsync(array $args = [])
* @method \Aws\Result getPaymentCredentialProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPaymentCredentialProviderAsync(array $args = [])
* @method \Aws\Result getPaymentManager(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPaymentManagerAsync(array $args = [])
* @method \Aws\Result getPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPolicyAsync(array $args = [])
* @method \Aws\Result getPolicyEngine(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPolicyEngineAsync(array $args = [])
* @method \Aws\Result getPolicyEngineSummary(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPolicyEngineSummaryAsync(array $args = [])
* @method \Aws\Result getPolicyGeneration(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPolicyGenerationAsync(array $args = [])
* @method \Aws\Result getPolicyGenerationSummary(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPolicyGenerationSummaryAsync(array $args = [])
* @method \Aws\Result getPolicySummary(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPolicySummaryAsync(array $args = [])
* @method \Aws\Result getRegistry(array $args = [])
* @method \GuzzleHttp\Promise\Promise getRegistryAsync(array $args = [])
* @method \Aws\Result getRegistryRecord(array $args = [])
* @method \GuzzleHttp\Promise\Promise getRegistryRecordAsync(array $args = [])
* @method \Aws\Result getResourcePolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise getResourcePolicyAsync(array $args = [])
* @method \Aws\Result getTokenVault(array $args = [])
@ -105,30 +179,62 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listAgentRuntimesAsync(array $args = [])
* @method \Aws\Result listApiKeyCredentialProviders(array $args = [])
* @method \GuzzleHttp\Promise\Promise listApiKeyCredentialProvidersAsync(array $args = [])
* @method \Aws\Result listBrowserProfiles(array $args = [])
* @method \GuzzleHttp\Promise\Promise listBrowserProfilesAsync(array $args = [])
* @method \Aws\Result listBrowsers(array $args = [])
* @method \GuzzleHttp\Promise\Promise listBrowsersAsync(array $args = [])
* @method \Aws\Result listCodeInterpreters(array $args = [])
* @method \GuzzleHttp\Promise\Promise listCodeInterpretersAsync(array $args = [])
* @method \Aws\Result listConfigurationBundleVersions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listConfigurationBundleVersionsAsync(array $args = [])
* @method \Aws\Result listConfigurationBundles(array $args = [])
* @method \GuzzleHttp\Promise\Promise listConfigurationBundlesAsync(array $args = [])
* @method \Aws\Result listDatasetExamples(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDatasetExamplesAsync(array $args = [])
* @method \Aws\Result listDatasetVersions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDatasetVersionsAsync(array $args = [])
* @method \Aws\Result listDatasets(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDatasetsAsync(array $args = [])
* @method \Aws\Result listEvaluators(array $args = [])
* @method \GuzzleHttp\Promise\Promise listEvaluatorsAsync(array $args = [])
* @method \Aws\Result listGatewayRules(array $args = [])
* @method \GuzzleHttp\Promise\Promise listGatewayRulesAsync(array $args = [])
* @method \Aws\Result listGatewayTargets(array $args = [])
* @method \GuzzleHttp\Promise\Promise listGatewayTargetsAsync(array $args = [])
* @method \Aws\Result listGateways(array $args = [])
* @method \GuzzleHttp\Promise\Promise listGatewaysAsync(array $args = [])
* @method \Aws\Result listHarnesses(array $args = [])
* @method \GuzzleHttp\Promise\Promise listHarnessesAsync(array $args = [])
* @method \Aws\Result listMemories(array $args = [])
* @method \GuzzleHttp\Promise\Promise listMemoriesAsync(array $args = [])
* @method \Aws\Result listOauth2CredentialProviders(array $args = [])
* @method \GuzzleHttp\Promise\Promise listOauth2CredentialProvidersAsync(array $args = [])
* @method \Aws\Result listOnlineEvaluationConfigs(array $args = [])
* @method \GuzzleHttp\Promise\Promise listOnlineEvaluationConfigsAsync(array $args = [])
* @method \Aws\Result listPaymentConnectors(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPaymentConnectorsAsync(array $args = [])
* @method \Aws\Result listPaymentCredentialProviders(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPaymentCredentialProvidersAsync(array $args = [])
* @method \Aws\Result listPaymentManagers(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPaymentManagersAsync(array $args = [])
* @method \Aws\Result listPolicies(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPoliciesAsync(array $args = [])
* @method \Aws\Result listPolicyEngineSummaries(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPolicyEngineSummariesAsync(array $args = [])
* @method \Aws\Result listPolicyEngines(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPolicyEnginesAsync(array $args = [])
* @method \Aws\Result listPolicyGenerationAssets(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPolicyGenerationAssetsAsync(array $args = [])
* @method \Aws\Result listPolicyGenerationSummaries(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPolicyGenerationSummariesAsync(array $args = [])
* @method \Aws\Result listPolicyGenerations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPolicyGenerationsAsync(array $args = [])
* @method \Aws\Result listPolicySummaries(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPolicySummariesAsync(array $args = [])
* @method \Aws\Result listRegistries(array $args = [])
* @method \GuzzleHttp\Promise\Promise listRegistriesAsync(array $args = [])
* @method \Aws\Result listRegistryRecords(array $args = [])
* @method \GuzzleHttp\Promise\Promise listRegistryRecordsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result listWorkloadIdentities(array $args = [])
@ -139,6 +245,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise setTokenVaultCMKAsync(array $args = [])
* @method \Aws\Result startPolicyGeneration(array $args = [])
* @method \GuzzleHttp\Promise\Promise startPolicyGenerationAsync(array $args = [])
* @method \Aws\Result submitRegistryRecordForApproval(array $args = [])
* @method \GuzzleHttp\Promise\Promise submitRegistryRecordForApprovalAsync(array $args = [])
* @method \Aws\Result synchronizeGatewayTargets(array $args = [])
* @method \GuzzleHttp\Promise\Promise synchronizeGatewayTargetsAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
@ -151,22 +259,44 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateAgentRuntimeEndpointAsync(array $args = [])
* @method \Aws\Result updateApiKeyCredentialProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateApiKeyCredentialProviderAsync(array $args = [])
* @method \Aws\Result updateConfigurationBundle(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateConfigurationBundleAsync(array $args = [])
* @method \Aws\Result updateDataset(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateDatasetAsync(array $args = [])
* @method \Aws\Result updateDatasetExamples(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateDatasetExamplesAsync(array $args = [])
* @method \Aws\Result updateEvaluator(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateEvaluatorAsync(array $args = [])
* @method \Aws\Result updateGateway(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateGatewayAsync(array $args = [])
* @method \Aws\Result updateGatewayRule(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateGatewayRuleAsync(array $args = [])
* @method \Aws\Result updateGatewayTarget(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateGatewayTargetAsync(array $args = [])
* @method \Aws\Result updateHarness(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateHarnessAsync(array $args = [])
* @method \Aws\Result updateMemory(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateMemoryAsync(array $args = [])
* @method \Aws\Result updateOauth2CredentialProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateOauth2CredentialProviderAsync(array $args = [])
* @method \Aws\Result updateOnlineEvaluationConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateOnlineEvaluationConfigAsync(array $args = [])
* @method \Aws\Result updatePaymentConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise updatePaymentConnectorAsync(array $args = [])
* @method \Aws\Result updatePaymentCredentialProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise updatePaymentCredentialProviderAsync(array $args = [])
* @method \Aws\Result updatePaymentManager(array $args = [])
* @method \GuzzleHttp\Promise\Promise updatePaymentManagerAsync(array $args = [])
* @method \Aws\Result updatePolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise updatePolicyAsync(array $args = [])
* @method \Aws\Result updatePolicyEngine(array $args = [])
* @method \GuzzleHttp\Promise\Promise updatePolicyEngineAsync(array $args = [])
* @method \Aws\Result updateRegistry(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateRegistryAsync(array $args = [])
* @method \Aws\Result updateRegistryRecord(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateRegistryRecordAsync(array $args = [])
* @method \Aws\Result updateRegistryRecordStatus(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateRegistryRecordStatusAsync(array $args = [])
* @method \Aws\Result updateWorkloadIdentity(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateWorkloadIdentityAsync(array $args = [])
*/

View file

@ -11,22 +11,40 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createBlueprintAsync(array $args = [])
* @method \Aws\Result createBlueprintVersion(array $args = [])
* @method \GuzzleHttp\Promise\Promise createBlueprintVersionAsync(array $args = [])
* @method \Aws\Result createDataAutomationLibrary(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDataAutomationLibraryAsync(array $args = [])
* @method \Aws\Result createDataAutomationProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDataAutomationProjectAsync(array $args = [])
* @method \Aws\Result deleteBlueprint(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteBlueprintAsync(array $args = [])
* @method \Aws\Result deleteDataAutomationLibrary(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDataAutomationLibraryAsync(array $args = [])
* @method \Aws\Result deleteDataAutomationProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDataAutomationProjectAsync(array $args = [])
* @method \Aws\Result getBlueprint(array $args = [])
* @method \GuzzleHttp\Promise\Promise getBlueprintAsync(array $args = [])
* @method \Aws\Result getBlueprintOptimizationStatus(array $args = [])
* @method \GuzzleHttp\Promise\Promise getBlueprintOptimizationStatusAsync(array $args = [])
* @method \Aws\Result getDataAutomationLibrary(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDataAutomationLibraryAsync(array $args = [])
* @method \Aws\Result getDataAutomationLibraryEntity(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDataAutomationLibraryEntityAsync(array $args = [])
* @method \Aws\Result getDataAutomationLibraryIngestionJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDataAutomationLibraryIngestionJobAsync(array $args = [])
* @method \Aws\Result getDataAutomationProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDataAutomationProjectAsync(array $args = [])
* @method \Aws\Result invokeBlueprintOptimizationAsync(array $args = [])
* @method \GuzzleHttp\Promise\Promise invokeBlueprintOptimizationAsyncAsync(array $args = [])
* @method \Aws\Result invokeDataAutomationLibraryIngestionJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise invokeDataAutomationLibraryIngestionJobAsync(array $args = [])
* @method \Aws\Result listBlueprints(array $args = [])
* @method \GuzzleHttp\Promise\Promise listBlueprintsAsync(array $args = [])
* @method \Aws\Result listDataAutomationLibraries(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDataAutomationLibrariesAsync(array $args = [])
* @method \Aws\Result listDataAutomationLibraryEntities(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDataAutomationLibraryEntitiesAsync(array $args = [])
* @method \Aws\Result listDataAutomationLibraryIngestionJobs(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDataAutomationLibraryIngestionJobsAsync(array $args = [])
* @method \Aws\Result listDataAutomationProjects(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDataAutomationProjectsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
@ -37,6 +55,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateBlueprint(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateBlueprintAsync(array $args = [])
* @method \Aws\Result updateDataAutomationLibrary(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateDataAutomationLibraryAsync(array $args = [])
* @method \Aws\Result updateDataAutomationProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateDataAutomationProjectAsync(array $args = [])
*/

View file

@ -0,0 +1,664 @@
<?php
namespace Aws\Cbor;
use Aws\Cbor\Exception\CborException;
/**
* Decodes Concise Binary Object Representation encoded strings
* into PHP values according to RFC 8949
*
* https://www.rfc-editor.org/rfc/rfc8949.html
*
* Supports Major types 0-7 including:
* - Type 0: Unsigned integers
* - Type 1: Negative integers
* - Type 2: Byte strings
* - Type 3: Text strings (UTF-8)
* - Type 4: Arrays
* - Type 5: Maps
* - Type 6: Tagged values (timestamps)
* - Type 7: Simple values (null, bool, float)
*
* @internal
*/
final class CborDecoder
{
private int $offset;
private int $length;
/**
* Decode CBOR binary data to PHP value
*
* @param string $data The CBOR-encoded binary data to decode
*
* @return mixed The decoded PHP value (can be any type: int, string, array, bool, null, float)
* @throws CborException If data is empty or malformed CBOR
*/
public function decode(string $data): mixed
{
if ($data === '') {
throw new CborException("No data to decode");
}
$this->offset = 0;
$this->length = strlen($data);
return $this->decodeValue($data);
}
/**
* Decode multiple CBOR values from sequential binary data
*
* @param string $data The CBOR-encoded binary data containing multiple values
*
* @return array Array of decoded PHP values in the order they appear in the data
* @throws CborException If data is malformed CBOR
*/
public function decodeAll(string $data): array
{
$this->length = strlen($data);
$this->offset = 0;
$values = [];
while ($this->offset < $this->length) {
$values[] = $this->decodeValue($data);
}
return $values;
}
/**
* Decodes a single CBOR value at the current offset
*
* @param string $data Reference to the CBOR data being decoded
*
* @return mixed The decoded value
* @throws CborException If unexpected end of data or invalid CBOR format
*/
private function decodeValue(string &$data): mixed
{
$offset = $this->offset;
$length = $this->length;
if ($offset >= $length) {
throw new CborException("Unexpected end of data");
}
$byte = ord($data[$offset++]);
$majorType = $byte >> 5;
$info = $byte & 0x1F;
switch ($majorType) {
case 0: // Unsigned integer
if ($info < 24) {
$this->offset = $offset;
return $info;
}
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 1;
return ord($data[$offset]);
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 2;
return (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 4;
return unpack('N', $data, $offset)[1];
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 8;
return unpack('J', $data, $offset)[1];
default:
throw new CborException("Invalid additional info for integer: $info");
}
case 1: // Negative integer
if ($info < 24) {
$this->offset = $offset;
return -1 - $info;
}
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 1;
return -1 - ord($data[$offset]);
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 2;
return -1 - ((ord($data[$offset]) << 8) | ord($data[$offset + 1]));
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 4;
return -1 - unpack('N', $data, $offset)[1];
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 8;
$unsigned = unpack('J', $data, $offset)[1];
return ($unsigned === 9223372036854775807) ? PHP_INT_MIN : -1 - $unsigned;
default:
throw new CborException("Invalid additional info for integer: $info");
}
case 2: // Byte string
if ($info < 24) {
$len = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$len = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('J', $data, $offset)[1];
$offset += 8;
break;
case 31:
$this->offset = $offset;
return $this->decodeIndefiniteString($data, 0x40);
default:
throw new CborException("Invalid additional info for byte string: $info");
}
}
if ($offset + $len > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + $len;
return substr($data, $offset, $len);
case 3: // Text string
if ($info < 24) {
$len = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$len = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('J', $data, $offset)[1];
$offset += 8;
break;
case 31:
$this->offset = $offset;
return $this->decodeIndefiniteString($data, 0x60);
default:
throw new CborException("Invalid additional info for text string: $info");
}
}
if ($offset + $len > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + $len;
return substr($data, $offset, $len);
case 4: // Array
if ($info < 24) {
$count = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$count = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$count = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$count = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$count = unpack('J', $data, $offset)[1];
$offset += 8;
break;
case 31:
$this->offset = $offset;
return $this->decodeIndefiniteArray($data);
default:
throw new CborException("Invalid additional info for array: $info");
}
}
$this->offset = $offset;
$arr = [];
for ($i = 0; $i < $count; $i++) {
$arr[] = $this->decodeValue($data);
}
return $arr;
case 5: // Map
if ($info < 24) {
$count = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$count = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$count = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$count = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$count = unpack('J', $data, $offset)[1];
$offset += 8;
break;
case 31:
$this->offset = $offset;
return $this->decodeIndefiniteMap($data);
default:
throw new CborException("Invalid additional info for map: $info");
}
}
$this->offset = $offset;
$map = [];
for ($i = 0; $i < $count; $i++) {
$key = $this->decodeValue($data);
$map[$key] = $this->decodeValue($data);
}
return $map;
case 6: // Tag
switch ($info) {
case 24:
$offset++;
break;
case 25:
$offset += 2;
break;
case 26:
$offset += 4;
break;
case 27:
$offset += 8;
break;
}
$this->offset = $offset;
return $this->decodeValue($data);
case 7: // Simple/float
switch ($info) {
case 20:
$this->offset = $offset;
return false;
case 21:
$this->offset = $offset;
return true;
case 22:
case 23:
$this->offset = $offset;
return null;
case 25: // Half-precision float
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 2;
$half = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$sign = ($half >> 15) & 0x01;
$exp = ($half >> 10) & 0x1F;
$mant = $half & 0x3FF;
if ($exp === 0) {
return $mant === 0
? ($sign ? -0.0 : 0.0)
: ($sign ? -1 : 1) * pow(2, -14) * ($mant / 1024);
}
if ($exp === 31) {
return $mant === 0 ? ($sign ? -INF : INF) : NAN;
}
return (float) (($sign ? -1 : 1) * pow(2, $exp - 15) * (1 + $mant / 1024));
case 26: // Single-precision float
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 4;
return unpack('G', $data, $offset)[1];
case 27: // Double-precision float
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$this->offset = $offset + 8;
return unpack('E', $data, $offset)[1];
case 31:
throw new CborException("Unexpected break");
default:
throw new CborException("Unknown simple value: $info");
}
default:
throw new CborException("Unknown major type: $majorType");
}
}
/**
* Decode indefinite-length string (byte or text)
*
* @param string $data Reference to the CBOR data being decoded
* @param int $expectedMajor Expected major type (0x40 for byte string, 0x60 for text string)
*
* @return string The concatenated string from all chunks
* @throws CborException If invalid chunk format or unexpected end of data
*/
private function decodeIndefiniteString(string &$data, int $expectedMajor): string
{
$chunks = [];
while (true) {
$offset = $this->offset;
$length = $this->length;
if ($offset >= $length) {
throw new CborException("Unexpected end of data");
}
$byte = ord($data[$offset++]);
if ($byte === 0xFF) {
$this->offset = $offset;
return implode('', $chunks);
}
if (($byte & 0xE0) !== $expectedMajor) {
throw new CborException("Invalid chunk in indefinite string");
}
$info = $byte & 0x1F;
if ($info === 31) {
throw new CborException("Nested indefinite string");
}
if ($info < 24) {
$len = $info;
} else {
switch ($info) {
case 24:
if ($offset >= $length) {
throw new CborException("Not enough data");
}
$len = ord($data[$offset++]);
break;
case 25:
if ($offset + 2 > $length) {
throw new CborException("Not enough data");
}
$len = (ord($data[$offset]) << 8) | ord($data[$offset + 1]);
$offset += 2;
break;
case 26:
if ($offset + 4 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('N', $data, $offset)[1];
$offset += 4;
break;
case 27:
if ($offset + 8 > $length) {
throw new CborException("Not enough data");
}
$len = unpack('J', $data, $offset)[1];
$offset += 8;
break;
default:
throw new CborException("Invalid chunk length info: $info");
}
}
if ($offset + $len > $length) {
throw new CborException("Not enough data for chunk");
}
$chunks[] = substr($data, $offset, $len);
$this->offset = $offset + $len;
}
}
/**
* Decode indefinite-length array
*
* @param string $data Reference to the CBOR data being decoded
*
* @return array The decoded array elements
* @throws CborException If unexpected end of data
*/
private function decodeIndefiniteArray(string &$data): array
{
$result = [];
while (true) {
if ($this->offset >= $this->length) {
throw new CborException("Unexpected end of data");
}
if (ord($data[$this->offset]) === 0xFF) {
$this->offset++;
return $result;
}
$result[] = $this->decodeValue($data);
}
}
/**
* Decode indefinite-length map
*
* @param string $data Reference to the CBOR data being decoded
*
* @return array The decoded map as associative array
* @throws CborException If unexpected end of data or odd number of items
*/
private function decodeIndefiniteMap(string &$data): array
{
$result = [];
while (true) {
if ($this->offset >= $this->length) {
throw new CborException("Unexpected end of data");
}
if (ord($data[$this->offset]) === 0xFF) {
$this->offset++;
return $result;
}
$key = $this->decodeValue($data);
$result[$key] = $this->decodeValue($data);
}
}
}

View file

@ -0,0 +1,357 @@
<?php
namespace Aws\Cbor;
use Aws\Cbor\Exception\CborException;
use DateTimeInterface;
/**
* Encodes PHP values to Concise Binary Object Representation according to RFC 8949
* https://www.rfc-editor.org/rfc/rfc8949.html
*
* Supports Major types 0-7 including:
* - Type 0: Unsigned integers
* - Type 1: Negative integers
* - Type 2: Byte strings (via ['__cbor_bytes' => $data] wrappers)
* - Type 3: Text strings (UTF-8)
* - Type 4: Arrays
* - Type 5: Maps
* - Type 6: Tagged values (timestamps)
* - Type 7: Simple values (null, bool, float)
*
* @internal
*/
final class CborEncoder
{
/**
* Pre-encoded integers 0-23 (single byte) and common larger values
* CBOR major type 0 (unsigned integer)
*/
private const INT_CACHE = [
0 => "\x00", 1 => "\x01", 2 => "\x02", 3 => "\x03",
4 => "\x04", 5 => "\x05", 6 => "\x06", 7 => "\x07",
8 => "\x08", 9 => "\x09", 10 => "\x0A", 11 => "\x0B",
12 => "\x0C", 13 => "\x0D", 14 => "\x0E", 15 => "\x0F",
16 => "\x10", 17 => "\x11", 18 => "\x12", 19 => "\x13",
20 => "\x14", 21 => "\x15", 22 => "\x16", 23 => "\x17",
24 => "\x18\x18", 25 => "\x18\x19", 26 => "\x18\x1A",
32 => "\x18\x20", 50 => "\x18\x32", 64 => "\x18\x40",
100 => "\x18\x64", 128 => "\x18\x80", 200 => "\x18\xC8",
255 => "\x18\xFF", 256 => "\x19\x01\x00", 500 => "\x19\x01\xF4",
1000 => "\x19\x03\xE8", 1023 => "\x19\x03\xFF",
];
/**
* Pre-encoded negative integers -1 to -24 and common larger values
* CBOR major type 1 (negative integer)
*/
private const NEG_CACHE = [
-1 => "\x20", -2 => "\x21", -3 => "\x22", -4 => "\x23",
-5 => "\x24", -10 => "\x29", -20 => "\x33", -24 => "\x37",
-25 => "\x38\x18", -50 => "\x38\x31", -100 => "\x38\x63",
];
/**
* Encode a PHP value to CBOR binary string
*
* @param mixed $value The value to encode
*
* @return string
*/
public function encode(mixed $value): string
{
return $this->encodeValue($value);
}
/**
* Recursively encode a value to CBOR
*
* @param mixed $value Value to encode
* @return string Encoded CBOR bytes
*/
private function encodeValue(mixed $value): string
{
switch (gettype($value)) {
case 'string':
$len = strlen($value);
if ($len < 24) {
return chr(0x60 | $len) . $value;
}
if ($len < 0x100) {
return "\x78" . chr($len) . $value;
}
return $this->encodeTextString($value);
case 'array':
// Encode a byte string (major type 2)
if (isset($value['__cbor_bytes'])) {
$bytes = $value['__cbor_bytes'];
$len = strlen($bytes);
if ($len < 24) {
return chr(0x40 | $len) . $bytes;
}
if ($len < 0x100) {
return "\x58" . chr($len) . $bytes;
}
if ($len < 0x10000) {
return "\x59" . pack('n', $len) . $bytes;
}
return "\x5A" . pack('N', $len) . $bytes;
}
if (array_is_list($value)) {
return $this->encodeArray($value);
}
return $this->encodeMap($value);
case 'integer':
if (isset(self::INT_CACHE[$value])) {
return self::INT_CACHE[$value];
}
if (isset(self::NEG_CACHE[$value])) {
return self::NEG_CACHE[$value];
}
// Fast path for positive integers
// Major type 0: unsigned integer
if ($value >= 0) {
if ($value < 24) {
return chr($value);
}
if ($value < 0x100) {
return "\x18" . chr($value);
}
if ($value < 0x10000) {
return "\x19" . pack('n', $value);
}
if ($value < 0x100000000) {
return "\x1A" . pack('N', $value);
}
return "\x1B" . pack('J', $value);
}
return $this->encodeInteger($value);
case 'double':
// Encode a float (major type 7, float 64)
return "\xFB" . pack('E', $value);
case 'boolean':
// Encode a boolean (major type 7, simple)
return $value ? "\xF5" : "\xF4";
case 'NULL':
// Encode null (major type 7, simple)
return "\xF6";
case 'object':
// Encode timestamp (major type 6, tag 1)
if ($value instanceof DateTimeInterface) {
$timestamp = $value->getTimestamp();
$micro = (int) $value->format('u');
if ($micro === 0) {
if ($timestamp >= 0 && $timestamp < 0x100000000) {
return "\xC1\x1A" . pack('N', $timestamp);
}
return "\xC1" . $this->encodeInteger($timestamp);
}
return "\xC1\xFB" . pack('E', $timestamp + $micro / 1e6);
}
throw new CborException("Cannot encode object of type: " . get_class($value));
default:
throw new CborException("Cannot encode value of type: " . gettype($value));
}
}
/**
* Encode an integer (major type 0 or 1)
*
* @param int $value
* @return string
*/
private function encodeInteger(int $value): string
{
if (isset(self::INT_CACHE[$value])) {
return self::INT_CACHE[$value];
}
if (isset(self::NEG_CACHE[$value])) {
return self::NEG_CACHE[$value];
}
if ($value >= 0) {
// Major type 0: unsigned integer
if ($value < 24) {
return chr($value);
}
if ($value < 0x100) {
return "\x18" . chr($value);
}
if ($value < 0x10000) {
return "\x19" . pack('n', $value);
}
if ($value < 0x100000000) {
return "\x1A" . pack('N', $value);
}
return "\x1B" . pack('J', $value);
}
// Major type 1: negative integer (-1 - n)
$value = -1 - $value;
if ($value < 24) {
return chr(0x20 | $value);
}
if ($value < 0x100) {
return "\x38" . chr($value);
}
if ($value < 0x10000) {
return "\x39" . pack('n', $value);
}
if ($value < 0x100000000) {
return "\x3A" . pack('N', $value);
}
return "\x3B" . pack('J', $value);
}
/**
* Encode a text string (major type 3)
*
* @param string $value
* @return string
*/
private function encodeTextString(string $value): string
{
$len = strlen($value);
if ($len < 24) {
return chr(0x60 | $len) . $value;
}
if ($len < 0x100) {
return "\x78" . chr($len) . $value;
}
if ($len < 0x10000) {
return "\x79" . pack('n', $len) . $value;
}
if ($len < 0x100000000) {
return "\x7A" . pack('N', $len) . $value;
}
return "\x7B" . pack('J', $len) . $value;
}
/**
* Encode an array (major type 4)
*
* @param array $value
* @return string
*/
private function encodeArray(array $value): string
{
$count = count($value);
if ($count < 24) {
$result = chr(0x80 | $count);
} elseif ($count < 0x100) {
$result = "\x98" . chr($count);
} elseif ($count < 0x10000) {
$result = "\x99" . pack('n', $count);
} elseif ($count < 0x100000000) {
$result = "\x9A" . pack('N', $count);
} else {
$result = "\x9B" . pack('J', $count);
}
foreach ($value as $item) {
$result .= $this->encodeValue($item);
}
return $result;
}
/**
* Encode a map (major type 5)
*
* @param array $value
* @return string
*/
private function encodeMap(array $value): string
{
$count = count($value);
if ($count < 24) {
$result = chr(0xA0 | $count);
} elseif ($count < 0x100) {
$result = "\xB8" . chr($count);
} elseif ($count < 0x10000) {
$result = "\xB9" . pack('n', $count);
} elseif ($count < 0x100000000) {
$result = "\xBA" . pack('N', $count);
} else {
$result = "\xBB" . pack('J', $count);
}
foreach ($value as $k => $v) {
if (is_int($k)) {
$result .= $this->encodeInteger($k);
} else {
$len = strlen($k);
if ($len < 24) {
$result .= chr(0x60 | $len) . $k;
} elseif ($len < 0x100) {
$result .= "\x78" . chr($len) . $k;
} else {
$result .= "\x79" . pack('n', $len) . $k;
}
}
$result .= $this->encodeValue($v);
}
return $result;
}
/**
* Create an empty map (major type 5 with 0 elements)
*
* @return string
*/
public function encodeEmptyMap(): string
{
return "\xA0";
}
/**
* Create an empty indefinite map (major type 5 indefinite length)
*
* @return string
*/
public function encodeEmptyIndefiniteMap(): string
{
return "\xBF\xFF";
}
}

View file

@ -0,0 +1,6 @@
<?php
namespace Aws\Cbor\Exception;
use RuntimeException;
class CborException extends RuntimeException {}

View file

@ -27,10 +27,13 @@ use Aws\Endpoint\UseFipsEndpoint\ConfigurationProvider as UseFipsConfigProvider;
use Aws\EndpointDiscovery\ConfigurationInterface;
use Aws\EndpointDiscovery\ConfigurationProvider;
use Aws\EndpointV2\EndpointDefinitionProvider;
use Aws\EndpointV2\EndpointProviderV2;
use Aws\Exception\AwsException;
use Aws\Exception\InvalidRegionException;
use Aws\Retry\ConfigurationInterface as RetryConfigInterface;
use Aws\Retry\ConfigurationProvider as RetryConfigProvider;
use Aws\Retry\V3\OptIn as NewRetriesOptIn;
use Aws\Retry\V3\RetryMiddleware as RetryV3Middleware;
use Aws\Signature\SignatureProvider;
use Aws\Token\Token;
use Aws\Token\TokenInterface;
@ -547,28 +550,42 @@ class ClientResolver
public static function _apply_retries($value, array &$args, HandlerList $list)
{
// A value of 0 for the config option disables retries
if ($value) {
$config = RetryConfigProvider::unwrap($value);
if ($config->getMode() === 'legacy') {
// # of retries is 1 less than # of attempts
$decider = RetryMiddleware::createDefaultDecider(
$config->getMaxAttempts() - 1
);
$list->appendSign(
Middleware::retry($decider, null, $args['stats']['retries']),
'retry'
);
} else {
$list->appendSign(
RetryMiddlewareV2::wrap(
$config,
['collect_stats' => $args['stats']['retries']]
),
'retry'
);
}
if (!$value) {
return;
}
$config = RetryConfigProvider::unwrap($value);
if ($config->getMode() === 'legacy') {
// # of retries is 1 less than # of attempts
$decider = RetryMiddleware::createDefaultDecider(
$config->getMaxAttempts() - 1
);
$list->appendSign(
Middleware::retry($decider, null, $args['stats']['retries']),
'retry'
);
return;
}
if (NewRetriesOptIn::isEnabled()) {
$list->appendSign(
RetryV3Middleware::wrap($config, [
'collect_stats' => $args['stats']['retries'],
'service' => $args['service'],
]),
'retry'
);
return;
}
$list->appendSign(
RetryMiddlewareV2::wrap(
$config,
['collect_stats' => $args['stats']['retries']]
),
'retry'
);
}
public static function _apply_defaults($value, array &$args, HandlerList $list)
@ -791,7 +808,7 @@ class ClientResolver
public static function _apply_endpoint_provider($value, array &$args)
{
if (!isset($args['endpoint'])) {
if ($value instanceof \Aws\EndpointV2\EndpointProviderV2) {
if ($value instanceof EndpointProviderV2) {
$options = self::getEndpointProviderOptions($args);
$value = PartitionEndpointProvider::defaultProvider($options)
->getPartition($args['region'], $args['service']);
@ -1112,14 +1129,13 @@ class ClientResolver
if (self::isValidService($serviceName)
&& self::isValidApiVersion($serviceName, $apiVersion)
) {
$ruleset = EndpointDefinitionProvider::getEndpointRuleset(
$partitions = EndpointDefinitionProvider::getPartitions();
$parsed = EndpointDefinitionProvider::getParsedRuleset(
$service->getServiceName(),
$service->getApiVersion()
);
return new \Aws\EndpointV2\EndpointProviderV2(
$ruleset,
EndpointDefinitionProvider::getPartitions()
$service->getApiVersion(),
$partitions
);
return new EndpointProviderV2($parsed, $partitions);
}
$options = self::getEndpointProviderOptions($args);
return PartitionEndpointProvider::defaultProvider($options)
@ -1167,7 +1183,7 @@ class ClientResolver
}
// Assign user's preferred auth scheme list
$args['auth_scheme_preference'] = $value;
$args['config']['auth_scheme_preference'] = $value;
}
public static function _default_signature_version(array &$args)
@ -1247,12 +1263,6 @@ class ClientResolver
$args['suppress_php_deprecation_warning'] =
\Aws\boolean_value($_ENV["AWS_SUPPRESS_PHP_DEPRECATION_WARNING"]);
}
if ($args['suppress_php_deprecation_warning'] === false
&& PHP_VERSION_ID < 80100
) {
self::emitDeprecationWarning();
}
}
public static function _default_endpoint(array &$args)
@ -1440,21 +1450,4 @@ EOT;
__DIR__ . "/data/{$service}/$apiVersion"
);
}
private static function emitDeprecationWarning()
{
$phpVersionString = phpversion();
trigger_error(
"This installation of the SDK is using PHP version"
. " {$phpVersionString}, which will be deprecated on January"
. " 13th, 2025.\nPlease upgrade your PHP version to a minimum of"
. " 8.1.x to continue receiving updates for the AWS"
. " SDK for PHP.\nTo disable this warning, set"
. " suppress_php_deprecation_warning to true on the client constructor"
. " or set the environment variable AWS_SUPPRESS_PHP_DEPRECATION_WARNING"
. " to true.\nMore information can be found at: "
. "https://aws.amazon.com/blogs/developer/announcing-the-end-of-support-for-php-runtimes-8-0-x-and-below-in-the-aws-sdk-for-php/\n",
E_USER_DEPRECATED
);
}
}

View file

@ -81,8 +81,10 @@ class Signer
$signatureHash = [];
if ($policy) {
$policy = preg_replace('/\s/s', '', $policy);
self::validatePolicy($policy);
$signatureHash['Policy'] = $this->encode($policy);
} elseif ($resource && $expires) {
self::validateResourceUrl($resource);
$expires = (int) $expires; // Handle epoch passed as string
$policy = $this->createCannedPolicy($resource, $expires);
$signatureHash['Expires'] = $expires;
@ -136,4 +138,35 @@ class Signer
{
return strtr(base64_encode($policy), '+=/', '-_~');
}
/**
* Validates a customer provided json document.
*
* @param string $jsonPolicy
*
* @return void
*/
private static function validatePolicy(string $jsonPolicy): void
{
$policy = json_decode($jsonPolicy, true);
foreach ($policy['Statement'] ?? [] as $statement) {
if (isset($statement['Resource'])) {
self::validateResourceUrl($statement['Resource']);
}
}
}
/**
* @param string $url
*
* @return void
*/
private static function validateResourceUrl(string $url): void
{
if (preg_match('/["\\\\\x00-\x1F]/', $url)) {
throw new \InvalidArgumentException(
'URL contains invalid characters: ", \\, or control characters'
);
}
}
}

View file

@ -101,7 +101,7 @@ class UrlSigner
$parts = parse_url($url);
$pathParts = pathinfo($parts['path']);
$resource = ltrim(
$pathParts['dirname'] . '/' . $pathParts['basename'],
str_replace('\\', '/', $pathParts['dirname']) . '/' . $pathParts['basename'],
'/'
);

View file

@ -78,7 +78,7 @@ class CloudSearchDomainClient extends AwsClient
$query = $r->getUri()->getQuery();
$req = $r->withMethod('POST')
->withBody(Psr7\Utils::streamFor($query))
->withHeader('Content-Length', strlen($query))
->withHeader('Content-Length', (string) strlen($query))
->withHeader('Content-Type', 'application/x-www-form-urlencoded')
->withUri($r->getUri()->withQuery(''));
return $req;

View file

@ -6,6 +6,8 @@ use Aws\AwsClient;
/**
* This client is used to interact with the **Amazon CloudWatch** service.
*
* @method \Aws\Result deleteAlarmMuteRule(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAlarmMuteRuleAsync(array $args = [])
* @method \Aws\Result deleteAlarms(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAlarmsAsync(array $args = [])
* @method \Aws\Result deleteAnomalyDetector(array $args = [])
@ -36,6 +38,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise enableAlarmActionsAsync(array $args = [])
* @method \Aws\Result enableInsightRules(array $args = [])
* @method \GuzzleHttp\Promise\Promise enableInsightRulesAsync(array $args = [])
* @method \Aws\Result getAlarmMuteRule(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAlarmMuteRuleAsync(array $args = [])
* @method \Aws\Result getDashboard(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDashboardAsync(array $args = [])
* @method \Aws\Result getInsightRuleReport(array $args = [])
@ -48,6 +52,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getMetricStreamAsync(array $args = [])
* @method \Aws\Result getMetricWidgetImage(array $args = [])
* @method \GuzzleHttp\Promise\Promise getMetricWidgetImageAsync(array $args = [])
* @method \Aws\Result getOTelEnrichment(array $args = [])
* @method \GuzzleHttp\Promise\Promise getOTelEnrichmentAsync(array $args = [])
* @method \Aws\Result listAlarmMuteRules(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAlarmMuteRulesAsync(array $args = [])
* @method \Aws\Result listDashboards(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDashboardsAsync(array $args = [])
* @method \Aws\Result listManagedInsightRules(array $args = [])
@ -58,6 +66,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listMetricsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result putAlarmMuteRule(array $args = [])
* @method \GuzzleHttp\Promise\Promise putAlarmMuteRuleAsync(array $args = [])
* @method \Aws\Result putAnomalyDetector(array $args = [])
* @method \GuzzleHttp\Promise\Promise putAnomalyDetectorAsync(array $args = [])
* @method \Aws\Result putCompositeAlarm(array $args = [])
@ -78,8 +88,12 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise setAlarmStateAsync(array $args = [])
* @method \Aws\Result startMetricStreams(array $args = [])
* @method \GuzzleHttp\Promise\Promise startMetricStreamsAsync(array $args = [])
* @method \Aws\Result startOTelEnrichment(array $args = [])
* @method \GuzzleHttp\Promise\Promise startOTelEnrichmentAsync(array $args = [])
* @method \Aws\Result stopMetricStreams(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopMetricStreamsAsync(array $args = [])
* @method \Aws\Result stopOTelEnrichment(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopOTelEnrichmentAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])

View file

@ -1,85 +0,0 @@
<?php
namespace Aws\CloudWatchEvidently;
use Aws\AwsClient;
/**
* This client is used to interact with the **Amazon CloudWatch Evidently** service.
* @method \Aws\Result batchEvaluateFeature(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchEvaluateFeatureAsync(array $args = [])
* @method \Aws\Result createExperiment(array $args = [])
* @method \GuzzleHttp\Promise\Promise createExperimentAsync(array $args = [])
* @method \Aws\Result createFeature(array $args = [])
* @method \GuzzleHttp\Promise\Promise createFeatureAsync(array $args = [])
* @method \Aws\Result createLaunch(array $args = [])
* @method \GuzzleHttp\Promise\Promise createLaunchAsync(array $args = [])
* @method \Aws\Result createProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise createProjectAsync(array $args = [])
* @method \Aws\Result createSegment(array $args = [])
* @method \GuzzleHttp\Promise\Promise createSegmentAsync(array $args = [])
* @method \Aws\Result deleteExperiment(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteExperimentAsync(array $args = [])
* @method \Aws\Result deleteFeature(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteFeatureAsync(array $args = [])
* @method \Aws\Result deleteLaunch(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteLaunchAsync(array $args = [])
* @method \Aws\Result deleteProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteProjectAsync(array $args = [])
* @method \Aws\Result deleteSegment(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteSegmentAsync(array $args = [])
* @method \Aws\Result evaluateFeature(array $args = [])
* @method \GuzzleHttp\Promise\Promise evaluateFeatureAsync(array $args = [])
* @method \Aws\Result getExperiment(array $args = [])
* @method \GuzzleHttp\Promise\Promise getExperimentAsync(array $args = [])
* @method \Aws\Result getExperimentResults(array $args = [])
* @method \GuzzleHttp\Promise\Promise getExperimentResultsAsync(array $args = [])
* @method \Aws\Result getFeature(array $args = [])
* @method \GuzzleHttp\Promise\Promise getFeatureAsync(array $args = [])
* @method \Aws\Result getLaunch(array $args = [])
* @method \GuzzleHttp\Promise\Promise getLaunchAsync(array $args = [])
* @method \Aws\Result getProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise getProjectAsync(array $args = [])
* @method \Aws\Result getSegment(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSegmentAsync(array $args = [])
* @method \Aws\Result listExperiments(array $args = [])
* @method \GuzzleHttp\Promise\Promise listExperimentsAsync(array $args = [])
* @method \Aws\Result listFeatures(array $args = [])
* @method \GuzzleHttp\Promise\Promise listFeaturesAsync(array $args = [])
* @method \Aws\Result listLaunches(array $args = [])
* @method \GuzzleHttp\Promise\Promise listLaunchesAsync(array $args = [])
* @method \Aws\Result listProjects(array $args = [])
* @method \GuzzleHttp\Promise\Promise listProjectsAsync(array $args = [])
* @method \Aws\Result listSegmentReferences(array $args = [])
* @method \GuzzleHttp\Promise\Promise listSegmentReferencesAsync(array $args = [])
* @method \Aws\Result listSegments(array $args = [])
* @method \GuzzleHttp\Promise\Promise listSegmentsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result putProjectEvents(array $args = [])
* @method \GuzzleHttp\Promise\Promise putProjectEventsAsync(array $args = [])
* @method \Aws\Result startExperiment(array $args = [])
* @method \GuzzleHttp\Promise\Promise startExperimentAsync(array $args = [])
* @method \Aws\Result startLaunch(array $args = [])
* @method \GuzzleHttp\Promise\Promise startLaunchAsync(array $args = [])
* @method \Aws\Result stopExperiment(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopExperimentAsync(array $args = [])
* @method \Aws\Result stopLaunch(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopLaunchAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result testSegmentPattern(array $args = [])
* @method \GuzzleHttp\Promise\Promise testSegmentPatternAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateExperiment(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateExperimentAsync(array $args = [])
* @method \Aws\Result updateFeature(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateFeatureAsync(array $args = [])
* @method \Aws\Result updateLaunch(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateLaunchAsync(array $args = [])
* @method \Aws\Result updateProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateProjectAsync(array $args = [])
* @method \Aws\Result updateProjectDataDelivery(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateProjectDataDeliveryAsync(array $args = [])
*/
class CloudWatchEvidentlyClient extends AwsClient {}

View file

@ -1,9 +0,0 @@
<?php
namespace Aws\CloudWatchEvidently\Exception;
use Aws\Exception\AwsException;
/**
* Represents an error interacting with the **Amazon CloudWatch Evidently** service.
*/
class CloudWatchEvidentlyException extends AwsException {}

View file

@ -28,6 +28,8 @@ use Generator;
* @method \GuzzleHttp\Promise\Promise createLogGroupAsync(array $args = [])
* @method \Aws\Result createLogStream(array $args = [])
* @method \GuzzleHttp\Promise\Promise createLogStreamAsync(array $args = [])
* @method \Aws\Result createLookupTable(array $args = [])
* @method \GuzzleHttp\Promise\Promise createLookupTableAsync(array $args = [])
* @method \Aws\Result createScheduledQuery(array $args = [])
* @method \GuzzleHttp\Promise\Promise createScheduledQueryAsync(array $args = [])
* @method \Aws\Result deleteAccountPolicy(array $args = [])
@ -54,6 +56,8 @@ use Generator;
* @method \GuzzleHttp\Promise\Promise deleteLogGroupAsync(array $args = [])
* @method \Aws\Result deleteLogStream(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteLogStreamAsync(array $args = [])
* @method \Aws\Result deleteLookupTable(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteLookupTableAsync(array $args = [])
* @method \Aws\Result deleteMetricFilter(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteMetricFilterAsync(array $args = [])
* @method \Aws\Result deleteQueryDefinition(array $args = [])
@ -94,6 +98,8 @@ use Generator;
* @method \GuzzleHttp\Promise\Promise describeLogGroupsAsync(array $args = [])
* @method \Aws\Result describeLogStreams(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeLogStreamsAsync(array $args = [])
* @method \Aws\Result describeLookupTables(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeLookupTablesAsync(array $args = [])
* @method \Aws\Result describeMetricFilters(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeMetricFiltersAsync(array $args = [])
* @method \Aws\Result describeQueries(array $args = [])
@ -134,6 +140,8 @@ use Generator;
* @method \GuzzleHttp\Promise\Promise getLogObjectAsync(array $args = [])
* @method \Aws\Result getLogRecord(array $args = [])
* @method \GuzzleHttp\Promise\Promise getLogRecordAsync(array $args = [])
* @method \Aws\Result getLookupTable(array $args = [])
* @method \GuzzleHttp\Promise\Promise getLookupTableAsync(array $args = [])
* @method \Aws\Result getQueryResults(array $args = [])
* @method \GuzzleHttp\Promise\Promise getQueryResultsAsync(array $args = [])
* @method \Aws\Result getScheduledQuery(array $args = [])
@ -164,6 +172,8 @@ use Generator;
* @method \GuzzleHttp\Promise\Promise listTagsLogGroupAsync(array $args = [])
* @method \Aws\Result putAccountPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise putAccountPolicyAsync(array $args = [])
* @method \Aws\Result putBearerTokenAuthentication(array $args = [])
* @method \GuzzleHttp\Promise\Promise putBearerTokenAuthenticationAsync(array $args = [])
* @method \Aws\Result putDataProtectionPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise putDataProtectionPolicyAsync(array $args = [])
* @method \Aws\Result putDeliveryDestination(array $args = [])
@ -220,41 +230,12 @@ use Generator;
* @method \GuzzleHttp\Promise\Promise updateDeliveryConfigurationAsync(array $args = [])
* @method \Aws\Result updateLogAnomalyDetector(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateLogAnomalyDetectorAsync(array $args = [])
* @method \Aws\Result updateLookupTable(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateLookupTableAsync(array $args = [])
* @method \Aws\Result updateScheduledQuery(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateScheduledQueryAsync(array $args = [])
*/
class CloudWatchLogsClient extends AwsClient {
static $streamingCommands = [
'StartLiveTail' => true
];
public function __construct(array $args)
{
parent::__construct($args);
$this->addStreamingFlagMiddleware();
}
private function addStreamingFlagMiddleware()
{
$this->getHandlerList()
-> appendInit(
$this->getStreamingFlagMiddleware(),
'streaming-flag-middleware'
);
}
private function getStreamingFlagMiddleware(): callable
{
return function (callable $handler) {
return function (CommandInterface $command, $request = null) use ($handler) {
if (!empty(self::$streamingCommands[$command->getName()])) {
$command['@http']['stream'] = true;
}
return $handler($command, $request);
};
};
}
/**
* Helper method for 'startLiveTail' operation that checks for results.

View file

@ -8,6 +8,8 @@ use Aws\AwsClient;
*
* @method \Aws\Result addCustomAttributes(array $args = [])
* @method \GuzzleHttp\Promise\Promise addCustomAttributesAsync(array $args = [])
* @method \Aws\Result addUserPoolClientSecret(array $args = [])
* @method \GuzzleHttp\Promise\Promise addUserPoolClientSecretAsync(array $args = [])
* @method \Aws\Result adminAddUserToGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise adminAddUserToGroupAsync(array $args = [])
* @method \Aws\Result adminConfirmSignUp(array $args = [])
@ -90,6 +92,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createUserPoolClientAsync(array $args = [])
* @method \Aws\Result createUserPoolDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise createUserPoolDomainAsync(array $args = [])
* @method \Aws\Result createUserPoolReplica(array $args = [])
* @method \GuzzleHttp\Promise\Promise createUserPoolReplicaAsync(array $args = [])
* @method \Aws\Result deleteGroup(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteGroupAsync(array $args = [])
* @method \Aws\Result deleteIdentityProvider(array $args = [])
@ -108,8 +112,12 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteUserPoolAsync(array $args = [])
* @method \Aws\Result deleteUserPoolClient(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteUserPoolClientAsync(array $args = [])
* @method \Aws\Result deleteUserPoolClientSecret(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteUserPoolClientSecretAsync(array $args = [])
* @method \Aws\Result deleteUserPoolDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteUserPoolDomainAsync(array $args = [])
* @method \Aws\Result deleteUserPoolReplica(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteUserPoolReplicaAsync(array $args = [])
* @method \Aws\Result deleteWebAuthnCredential(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteWebAuthnCredentialAsync(array $args = [])
* @method \Aws\Result describeIdentityProvider(array $args = [])
@ -178,8 +186,12 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listTermsAsync(array $args = [])
* @method \Aws\Result listUserImportJobs(array $args = [])
* @method \GuzzleHttp\Promise\Promise listUserImportJobsAsync(array $args = [])
* @method \Aws\Result listUserPoolClientSecrets(array $args = [])
* @method \GuzzleHttp\Promise\Promise listUserPoolClientSecretsAsync(array $args = [])
* @method \Aws\Result listUserPoolClients(array $args = [])
* @method \GuzzleHttp\Promise\Promise listUserPoolClientsAsync(array $args = [])
* @method \Aws\Result listUserPoolReplicas(array $args = [])
* @method \GuzzleHttp\Promise\Promise listUserPoolReplicasAsync(array $args = [])
* @method \Aws\Result listUserPools(array $args = [])
* @method \GuzzleHttp\Promise\Promise listUserPoolsAsync(array $args = [])
* @method \Aws\Result listUsers(array $args = [])
@ -240,6 +252,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateUserPoolClientAsync(array $args = [])
* @method \Aws\Result updateUserPoolDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateUserPoolDomainAsync(array $args = [])
* @method \Aws\Result updateUserPoolReplica(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateUserPoolReplicaAsync(array $args = [])
* @method \Aws\Result verifySoftwareToken(array $args = [])
* @method \GuzzleHttp\Promise\Promise verifySoftwareTokenAsync(array $args = [])
* @method \Aws\Result verifyUserAttribute(array $args = [])

View file

@ -31,6 +31,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise associateLexBotAsync(array $args = [])
* @method \Aws\Result associatePhoneNumberContactFlow(array $args = [])
* @method \GuzzleHttp\Promise\Promise associatePhoneNumberContactFlowAsync(array $args = [])
* @method \Aws\Result associateQueueEmailAddresses(array $args = [])
* @method \GuzzleHttp\Promise\Promise associateQueueEmailAddressesAsync(array $args = [])
* @method \Aws\Result associateQueueQuickConnects(array $args = [])
* @method \GuzzleHttp\Promise\Promise associateQueueQuickConnectsAsync(array $args = [])
* @method \Aws\Result associateRoutingProfileQueues(array $args = [])
@ -97,6 +99,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createInstanceAsync(array $args = [])
* @method \Aws\Result createIntegrationAssociation(array $args = [])
* @method \GuzzleHttp\Promise\Promise createIntegrationAssociationAsync(array $args = [])
* @method \Aws\Result createNotification(array $args = [])
* @method \GuzzleHttp\Promise\Promise createNotificationAsync(array $args = [])
* @method \Aws\Result createParticipant(array $args = [])
* @method \GuzzleHttp\Promise\Promise createParticipantAsync(array $args = [])
* @method \Aws\Result createPersistentContactAssociation(array $args = [])
@ -171,6 +175,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteInstanceAsync(array $args = [])
* @method \Aws\Result deleteIntegrationAssociation(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteIntegrationAssociationAsync(array $args = [])
* @method \Aws\Result deleteNotification(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteNotificationAsync(array $args = [])
* @method \Aws\Result deletePredefinedAttribute(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePredefinedAttributeAsync(array $args = [])
* @method \Aws\Result deletePrompt(array $args = [])
@ -213,6 +219,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteWorkspacePageAsync(array $args = [])
* @method \Aws\Result describeAgentStatus(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeAgentStatusAsync(array $args = [])
* @method \Aws\Result describeAttachedFilesConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeAttachedFilesConfigurationAsync(array $args = [])
* @method \Aws\Result describeAuthenticationProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeAuthenticationProfileAsync(array $args = [])
* @method \Aws\Result describeContact(array $args = [])
@ -243,6 +251,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise describeInstanceAttributeAsync(array $args = [])
* @method \Aws\Result describeInstanceStorageConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeInstanceStorageConfigAsync(array $args = [])
* @method \Aws\Result describeNotification(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeNotificationAsync(array $args = [])
* @method \Aws\Result describePhoneNumber(array $args = [])
* @method \GuzzleHttp\Promise\Promise describePhoneNumberAsync(array $args = [])
* @method \Aws\Result describePredefinedAttribute(array $args = [])
@ -295,6 +305,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise disassociateLexBotAsync(array $args = [])
* @method \Aws\Result disassociatePhoneNumberContactFlow(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociatePhoneNumberContactFlowAsync(array $args = [])
* @method \Aws\Result disassociateQueueEmailAddresses(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociateQueueEmailAddressesAsync(array $args = [])
* @method \Aws\Result disassociateQueueQuickConnects(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociateQueueQuickConnectsAsync(array $args = [])
* @method \Aws\Result disassociateRoutingProfileQueues(array $args = [])
@ -355,6 +367,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listApprovedOriginsAsync(array $args = [])
* @method \Aws\Result listAssociatedContacts(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAssociatedContactsAsync(array $args = [])
* @method \Aws\Result listAttachedFilesConfigurations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAttachedFilesConfigurationsAsync(array $args = [])
* @method \Aws\Result listAuthenticationProfiles(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAuthenticationProfilesAsync(array $args = [])
* @method \Aws\Result listBots(array $args = [])
@ -409,6 +423,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listLambdaFunctionsAsync(array $args = [])
* @method \Aws\Result listLexBots(array $args = [])
* @method \GuzzleHttp\Promise\Promise listLexBotsAsync(array $args = [])
* @method \Aws\Result listNotifications(array $args = [])
* @method \GuzzleHttp\Promise\Promise listNotificationsAsync(array $args = [])
* @method \Aws\Result listPhoneNumbers(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPhoneNumbersAsync(array $args = [])
* @method \Aws\Result listPhoneNumbersV2(array $args = [])
@ -417,6 +433,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listPredefinedAttributesAsync(array $args = [])
* @method \Aws\Result listPrompts(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPromptsAsync(array $args = [])
* @method \Aws\Result listQueueEmailAddresses(array $args = [])
* @method \GuzzleHttp\Promise\Promise listQueueEmailAddressesAsync(array $args = [])
* @method \Aws\Result listQueueQuickConnects(array $args = [])
* @method \GuzzleHttp\Promise\Promise listQueueQuickConnectsAsync(array $args = [])
* @method \Aws\Result listQueues(array $args = [])
@ -461,6 +479,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listUseCasesAsync(array $args = [])
* @method \Aws\Result listUserHierarchyGroups(array $args = [])
* @method \GuzzleHttp\Promise\Promise listUserHierarchyGroupsAsync(array $args = [])
* @method \Aws\Result listUserNotifications(array $args = [])
* @method \GuzzleHttp\Promise\Promise listUserNotificationsAsync(array $args = [])
* @method \Aws\Result listUserProficiencies(array $args = [])
* @method \GuzzleHttp\Promise\Promise listUserProficienciesAsync(array $args = [])
* @method \Aws\Result listUsers(array $args = [])
@ -511,6 +531,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise searchHoursOfOperationOverridesAsync(array $args = [])
* @method \Aws\Result searchHoursOfOperations(array $args = [])
* @method \GuzzleHttp\Promise\Promise searchHoursOfOperationsAsync(array $args = [])
* @method \Aws\Result searchNotifications(array $args = [])
* @method \GuzzleHttp\Promise\Promise searchNotificationsAsync(array $args = [])
* @method \Aws\Result searchPredefinedAttributes(array $args = [])
* @method \GuzzleHttp\Promise\Promise searchPredefinedAttributesAsync(array $args = [])
* @method \Aws\Result searchPrompts(array $args = [])
@ -597,6 +619,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateAgentStatus(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateAgentStatusAsync(array $args = [])
* @method \Aws\Result updateAttachedFilesConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateAttachedFilesConfigurationAsync(array $args = [])
* @method \Aws\Result updateAuthenticationProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateAuthenticationProfileAsync(array $args = [])
* @method \Aws\Result updateContact(array $args = [])
@ -639,6 +663,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateInstanceAttributeAsync(array $args = [])
* @method \Aws\Result updateInstanceStorageConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateInstanceStorageConfigAsync(array $args = [])
* @method \Aws\Result updateNotificationContent(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateNotificationContentAsync(array $args = [])
* @method \Aws\Result updateParticipantAuthentication(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateParticipantAuthenticationAsync(array $args = [])
* @method \Aws\Result updateParticipantRoleConfig(array $args = [])
@ -687,6 +713,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateTestCaseAsync(array $args = [])
* @method \Aws\Result updateTrafficDistribution(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateTrafficDistributionAsync(array $args = [])
* @method \Aws\Result updateUserConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateUserConfigAsync(array $args = [])
* @method \Aws\Result updateUserHierarchy(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateUserHierarchyAsync(array $args = [])
* @method \Aws\Result updateUserHierarchyGroupName(array $args = [])
@ -695,6 +723,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateUserHierarchyStructureAsync(array $args = [])
* @method \Aws\Result updateUserIdentityInfo(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateUserIdentityInfoAsync(array $args = [])
* @method \Aws\Result updateUserNotificationStatus(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateUserNotificationStatusAsync(array $args = [])
* @method \Aws\Result updateUserPhoneConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateUserPhoneConfigAsync(array $args = [])
* @method \Aws\Result updateUserProficiencies(array $args = [])

View file

@ -15,6 +15,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteCampaignCommunicationLimitsAsync(array $args = [])
* @method \Aws\Result deleteCampaignCommunicationTime(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteCampaignCommunicationTimeAsync(array $args = [])
* @method \Aws\Result deleteCampaignEntryLimits(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteCampaignEntryLimitsAsync(array $args = [])
* @method \Aws\Result deleteConnectInstanceConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteConnectInstanceConfigAsync(array $args = [])
* @method \Aws\Result deleteConnectInstanceIntegration(array $args = [])
@ -67,6 +69,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateCampaignCommunicationLimitsAsync(array $args = [])
* @method \Aws\Result updateCampaignCommunicationTime(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateCampaignCommunicationTimeAsync(array $args = [])
* @method \Aws\Result updateCampaignEntryLimits(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateCampaignEntryLimitsAsync(array $args = [])
* @method \Aws\Result updateCampaignFlowAssociation(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateCampaignFlowAssociationAsync(array $args = [])
* @method \Aws\Result updateCampaignName(array $args = [])

View file

@ -87,6 +87,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateFieldAsync(array $args = [])
* @method \Aws\Result updateLayout(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateLayoutAsync(array $args = [])
* @method \Aws\Result updateRelatedItem(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateRelatedItemAsync(array $args = [])
* @method \Aws\Result updateTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateTemplateAsync(array $args = [])
*/

View file

@ -0,0 +1,39 @@
<?php
namespace Aws\ConnectHealth;
use Aws\AwsClient;
/**
* This client is used to interact with the **Connect Health** service.
* @method \Aws\Result activateSubscription(array $args = [])
* @method \GuzzleHttp\Promise\Promise activateSubscriptionAsync(array $args = [])
* @method \Aws\Result createDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDomainAsync(array $args = [])
* @method \Aws\Result createSubscription(array $args = [])
* @method \GuzzleHttp\Promise\Promise createSubscriptionAsync(array $args = [])
* @method \Aws\Result deactivateSubscription(array $args = [])
* @method \GuzzleHttp\Promise\Promise deactivateSubscriptionAsync(array $args = [])
* @method \Aws\Result deleteDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDomainAsync(array $args = [])
* @method \Aws\Result getDomain(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDomainAsync(array $args = [])
* @method \Aws\Result getMedicalScribeListeningSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise getMedicalScribeListeningSessionAsync(array $args = [])
* @method \Aws\Result getPatientInsightsJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPatientInsightsJobAsync(array $args = [])
* @method \Aws\Result getSubscription(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSubscriptionAsync(array $args = [])
* @method \Aws\Result listDomains(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDomainsAsync(array $args = [])
* @method \Aws\Result listSubscriptions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listSubscriptionsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result startPatientInsightsJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise startPatientInsightsJobAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
*/
class ConnectHealthClient extends AwsClient {}

View file

@ -0,0 +1,9 @@
<?php
namespace Aws\ConnectHealth\Exception;
use Aws\Exception\AwsException;
/**
* Represents an error interacting with the **Connect Health** service.
*/
class ConnectHealthException extends AwsException {}

View file

@ -265,7 +265,7 @@ class InstanceProfileProvider
$userAgent .= ' ' . \Aws\default_user_agent();
$request = $request->withHeader('User-Agent', $userAgent);
foreach ($headers as $key => $value) {
$request = $request->withHeader($key, $value);
$request = $request->withHeader($key, (string) $value);
}
return $fn($request, ['timeout' => $this->timeout])

View file

@ -11,6 +11,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise batchGetCalculatedAttributeForProfileAsync(array $args = [])
* @method \Aws\Result batchGetProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetProfileAsync(array $args = [])
* @method \Aws\Result batchPutProfileObject(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchPutProfileObjectAsync(array $args = [])
* @method \Aws\Result createCalculatedAttributeDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise createCalculatedAttributeDefinitionAsync(array $args = [])
* @method \Aws\Result createDomain(array $args = [])
@ -27,6 +29,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createProfileAsync(array $args = [])
* @method \Aws\Result createRecommender(array $args = [])
* @method \GuzzleHttp\Promise\Promise createRecommenderAsync(array $args = [])
* @method \Aws\Result createRecommenderFilter(array $args = [])
* @method \GuzzleHttp\Promise\Promise createRecommenderFilterAsync(array $args = [])
* @method \Aws\Result createRecommenderSchema(array $args = [])
* @method \GuzzleHttp\Promise\Promise createRecommenderSchemaAsync(array $args = [])
* @method \Aws\Result createSegmentDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise createSegmentDefinitionAsync(array $args = [])
* @method \Aws\Result createSegmentEstimate(array $args = [])
@ -59,6 +65,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteProfileObjectTypeAsync(array $args = [])
* @method \Aws\Result deleteRecommender(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteRecommenderAsync(array $args = [])
* @method \Aws\Result deleteRecommenderFilter(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteRecommenderFilterAsync(array $args = [])
* @method \Aws\Result deleteRecommenderSchema(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteRecommenderSchemaAsync(array $args = [])
* @method \Aws\Result deleteSegmentDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteSegmentDefinitionAsync(array $args = [])
* @method \Aws\Result deleteWorkflow(array $args = [])
@ -99,6 +109,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getProfileRecommendationsAsync(array $args = [])
* @method \Aws\Result getRecommender(array $args = [])
* @method \GuzzleHttp\Promise\Promise getRecommenderAsync(array $args = [])
* @method \Aws\Result getRecommenderFilter(array $args = [])
* @method \GuzzleHttp\Promise\Promise getRecommenderFilterAsync(array $args = [])
* @method \Aws\Result getRecommenderSchema(array $args = [])
* @method \GuzzleHttp\Promise\Promise getRecommenderSchemaAsync(array $args = [])
* @method \Aws\Result getSegmentDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSegmentDefinitionAsync(array $args = [])
* @method \Aws\Result getSegmentEstimate(array $args = [])
@ -151,8 +165,12 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listProfileObjectTypesAsync(array $args = [])
* @method \Aws\Result listProfileObjects(array $args = [])
* @method \GuzzleHttp\Promise\Promise listProfileObjectsAsync(array $args = [])
* @method \Aws\Result listRecommenderFilters(array $args = [])
* @method \GuzzleHttp\Promise\Promise listRecommenderFiltersAsync(array $args = [])
* @method \Aws\Result listRecommenderRecipes(array $args = [])
* @method \GuzzleHttp\Promise\Promise listRecommenderRecipesAsync(array $args = [])
* @method \Aws\Result listRecommenderSchemas(array $args = [])
* @method \GuzzleHttp\Promise\Promise listRecommenderSchemasAsync(array $args = [])
* @method \Aws\Result listRecommenders(array $args = [])
* @method \GuzzleHttp\Promise\Promise listRecommendersAsync(array $args = [])
* @method \Aws\Result listRuleBasedMatches(array $args = [])

View file

@ -7,18 +7,26 @@ use Aws\AwsClient;
* This client is used to interact with the **Amazon Aurora DSQL** service.
* @method \Aws\Result createCluster(array $args = [])
* @method \GuzzleHttp\Promise\Promise createClusterAsync(array $args = [])
* @method \Aws\Result createStream(array $args = [])
* @method \GuzzleHttp\Promise\Promise createStreamAsync(array $args = [])
* @method \Aws\Result deleteCluster(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteClusterAsync(array $args = [])
* @method \Aws\Result deleteClusterPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteClusterPolicyAsync(array $args = [])
* @method \Aws\Result deleteStream(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteStreamAsync(array $args = [])
* @method \Aws\Result getCluster(array $args = [])
* @method \GuzzleHttp\Promise\Promise getClusterAsync(array $args = [])
* @method \Aws\Result getClusterPolicy(array $args = [])
* @method \GuzzleHttp\Promise\Promise getClusterPolicyAsync(array $args = [])
* @method \Aws\Result getStream(array $args = [])
* @method \GuzzleHttp\Promise\Promise getStreamAsync(array $args = [])
* @method \Aws\Result getVpcEndpointServiceName(array $args = [])
* @method \GuzzleHttp\Promise\Promise getVpcEndpointServiceNameAsync(array $args = [])
* @method \Aws\Result listClusters(array $args = [])
* @method \GuzzleHttp\Promise\Promise listClustersAsync(array $args = [])
* @method \Aws\Result listStreams(array $args = [])
* @method \GuzzleHttp\Promise\Promise listStreamsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result putClusterPolicy(array $args = [])

View file

@ -65,6 +65,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createGroupProfileAsync(array $args = [])
* @method \Aws\Result createListingChangeSet(array $args = [])
* @method \GuzzleHttp\Promise\Promise createListingChangeSetAsync(array $args = [])
* @method \Aws\Result createNotebook(array $args = [])
* @method \GuzzleHttp\Promise\Promise createNotebookAsync(array $args = [])
* @method \Aws\Result createProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise createProjectAsync(array $args = [])
* @method \Aws\Result createProjectMembership(array $args = [])
@ -119,6 +121,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteGlossaryTermAsync(array $args = [])
* @method \Aws\Result deleteListing(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteListingAsync(array $args = [])
* @method \Aws\Result deleteNotebook(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteNotebookAsync(array $args = [])
* @method \Aws\Result deleteProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteProjectAsync(array $args = [])
* @method \Aws\Result deleteProjectMembership(array $args = [])
@ -193,6 +197,12 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getListingAsync(array $args = [])
* @method \Aws\Result getMetadataGenerationRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise getMetadataGenerationRunAsync(array $args = [])
* @method \Aws\Result getNotebook(array $args = [])
* @method \GuzzleHttp\Promise\Promise getNotebookAsync(array $args = [])
* @method \Aws\Result getNotebookExport(array $args = [])
* @method \GuzzleHttp\Promise\Promise getNotebookExportAsync(array $args = [])
* @method \Aws\Result getNotebookRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise getNotebookRunAsync(array $args = [])
* @method \Aws\Result getProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise getProjectAsync(array $args = [])
* @method \Aws\Result getProjectProfile(array $args = [])
@ -253,6 +263,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listLineageNodeHistoryAsync(array $args = [])
* @method \Aws\Result listMetadataGenerationRuns(array $args = [])
* @method \GuzzleHttp\Promise\Promise listMetadataGenerationRunsAsync(array $args = [])
* @method \Aws\Result listNotebookRuns(array $args = [])
* @method \GuzzleHttp\Promise\Promise listNotebookRunsAsync(array $args = [])
* @method \Aws\Result listNotebooks(array $args = [])
* @method \GuzzleHttp\Promise\Promise listNotebooksAsync(array $args = [])
* @method \Aws\Result listNotifications(array $args = [])
* @method \GuzzleHttp\Promise\Promise listNotificationsAsync(array $args = [])
* @method \Aws\Result listPolicyGrants(array $args = [])
@ -285,6 +299,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise putDataExportConfigurationAsync(array $args = [])
* @method \Aws\Result putEnvironmentBlueprintConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise putEnvironmentBlueprintConfigurationAsync(array $args = [])
* @method \Aws\Result queryGraph(array $args = [])
* @method \GuzzleHttp\Promise\Promise queryGraphAsync(array $args = [])
* @method \Aws\Result rejectPredictions(array $args = [])
* @method \GuzzleHttp\Promise\Promise rejectPredictionsAsync(array $args = [])
* @method \Aws\Result rejectSubscriptionRequest(array $args = [])
@ -309,6 +325,14 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise startDataSourceRunAsync(array $args = [])
* @method \Aws\Result startMetadataGenerationRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise startMetadataGenerationRunAsync(array $args = [])
* @method \Aws\Result startNotebookExport(array $args = [])
* @method \GuzzleHttp\Promise\Promise startNotebookExportAsync(array $args = [])
* @method \Aws\Result startNotebookImport(array $args = [])
* @method \GuzzleHttp\Promise\Promise startNotebookImportAsync(array $args = [])
* @method \Aws\Result startNotebookRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise startNotebookRunAsync(array $args = [])
* @method \Aws\Result stopNotebookRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopNotebookRunAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
@ -339,6 +363,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateGlossaryTermAsync(array $args = [])
* @method \Aws\Result updateGroupProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateGroupProfileAsync(array $args = [])
* @method \Aws\Result updateNotebook(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateNotebookAsync(array $args = [])
* @method \Aws\Result updateProject(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateProjectAsync(array $args = [])
* @method \Aws\Result updateProjectProfile(array $args = [])

View file

@ -23,8 +23,24 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise assumeQueueRoleForUserAsync(array $args = [])
* @method \Aws\Result assumeQueueRoleForWorker(array $args = [])
* @method \GuzzleHttp\Promise\Promise assumeQueueRoleForWorkerAsync(array $args = [])
* @method \Aws\Result batchGetJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetJobAsync(array $args = [])
* @method \Aws\Result batchGetJobEntity(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetJobEntityAsync(array $args = [])
* @method \Aws\Result batchGetSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetSessionAsync(array $args = [])
* @method \Aws\Result batchGetSessionAction(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetSessionActionAsync(array $args = [])
* @method \Aws\Result batchGetStep(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetStepAsync(array $args = [])
* @method \Aws\Result batchGetTask(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetTaskAsync(array $args = [])
* @method \Aws\Result batchGetWorker(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchGetWorkerAsync(array $args = [])
* @method \Aws\Result batchUpdateJob(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchUpdateJobAsync(array $args = [])
* @method \Aws\Result batchUpdateTask(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchUpdateTaskAsync(array $args = [])
* @method \Aws\Result copyJobTemplate(array $args = [])
* @method \GuzzleHttp\Promise\Promise copyJobTemplateAsync(array $args = [])
* @method \Aws\Result createBudget(array $args = [])
@ -77,6 +93,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteQueueLimitAssociationAsync(array $args = [])
* @method \Aws\Result deleteStorageProfile(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteStorageProfileAsync(array $args = [])
* @method \Aws\Result deleteVolume(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteVolumeAsync(array $args = [])
* @method \Aws\Result deleteWorker(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteWorkerAsync(array $args = [])
* @method \Aws\Result disassociateMemberFromFarm(array $args = [])
@ -101,6 +119,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getLimitAsync(array $args = [])
* @method \Aws\Result getMonitor(array $args = [])
* @method \GuzzleHttp\Promise\Promise getMonitorAsync(array $args = [])
* @method \Aws\Result getMonitorSettings(array $args = [])
* @method \GuzzleHttp\Promise\Promise getMonitorSettingsAsync(array $args = [])
* @method \Aws\Result getQueue(array $args = [])
* @method \GuzzleHttp\Promise\Promise getQueueAsync(array $args = [])
* @method \Aws\Result getQueueEnvironment(array $args = [])
@ -123,6 +143,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getStorageProfileForQueueAsync(array $args = [])
* @method \Aws\Result getTask(array $args = [])
* @method \GuzzleHttp\Promise\Promise getTaskAsync(array $args = [])
* @method \Aws\Result getVolume(array $args = [])
* @method \GuzzleHttp\Promise\Promise getVolumeAsync(array $args = [])
* @method \Aws\Result getWorker(array $args = [])
* @method \GuzzleHttp\Promise\Promise getWorkerAsync(array $args = [])
* @method \Aws\Result listAvailableMeteredProducts(array $args = [])
@ -181,6 +203,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result listTasks(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTasksAsync(array $args = [])
* @method \Aws\Result listVolumes(array $args = [])
* @method \GuzzleHttp\Promise\Promise listVolumesAsync(array $args = [])
* @method \Aws\Result listWorkers(array $args = [])
* @method \GuzzleHttp\Promise\Promise listWorkersAsync(array $args = [])
* @method \Aws\Result putMeteredProduct(array $args = [])
@ -211,6 +235,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateLimitAsync(array $args = [])
* @method \Aws\Result updateMonitor(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateMonitorAsync(array $args = [])
* @method \Aws\Result updateMonitorSettings(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateMonitorSettingsAsync(array $args = [])
* @method \Aws\Result updateQueue(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateQueueAsync(array $args = [])
* @method \Aws\Result updateQueueEnvironment(array $args = [])

View file

@ -0,0 +1,123 @@
<?php
namespace Aws\DevOpsAgent;
use Aws\AwsClient;
/**
* This client is used to interact with the **AWS DevOps Agent Service** service.
* @method \Aws\Result associateService(array $args = [])
* @method \GuzzleHttp\Promise\Promise associateServiceAsync(array $args = [])
* @method \Aws\Result createAgentSpace(array $args = [])
* @method \GuzzleHttp\Promise\Promise createAgentSpaceAsync(array $args = [])
* @method \Aws\Result createAsset(array $args = [])
* @method \GuzzleHttp\Promise\Promise createAssetAsync(array $args = [])
* @method \Aws\Result createAssetFile(array $args = [])
* @method \GuzzleHttp\Promise\Promise createAssetFileAsync(array $args = [])
* @method \Aws\Result createBacklogTask(array $args = [])
* @method \GuzzleHttp\Promise\Promise createBacklogTaskAsync(array $args = [])
* @method \Aws\Result createChat(array $args = [])
* @method \GuzzleHttp\Promise\Promise createChatAsync(array $args = [])
* @method \Aws\Result createPrivateConnection(array $args = [])
* @method \GuzzleHttp\Promise\Promise createPrivateConnectionAsync(array $args = [])
* @method \Aws\Result deleteAgentSpace(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAgentSpaceAsync(array $args = [])
* @method \Aws\Result deleteAsset(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAssetAsync(array $args = [])
* @method \Aws\Result deleteAssetFile(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAssetFileAsync(array $args = [])
* @method \Aws\Result deletePrivateConnection(array $args = [])
* @method \GuzzleHttp\Promise\Promise deletePrivateConnectionAsync(array $args = [])
* @method \Aws\Result deregisterService(array $args = [])
* @method \GuzzleHttp\Promise\Promise deregisterServiceAsync(array $args = [])
* @method \Aws\Result describePrivateConnection(array $args = [])
* @method \GuzzleHttp\Promise\Promise describePrivateConnectionAsync(array $args = [])
* @method \Aws\Result disableOperatorApp(array $args = [])
* @method \GuzzleHttp\Promise\Promise disableOperatorAppAsync(array $args = [])
* @method \Aws\Result disassociateService(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociateServiceAsync(array $args = [])
* @method \Aws\Result enableOperatorApp(array $args = [])
* @method \GuzzleHttp\Promise\Promise enableOperatorAppAsync(array $args = [])
* @method \Aws\Result getAccountUsage(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAccountUsageAsync(array $args = [])
* @method \Aws\Result getAgentSpace(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAgentSpaceAsync(array $args = [])
* @method \Aws\Result getAsset(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAssetAsync(array $args = [])
* @method \Aws\Result getAssetContent(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAssetContentAsync(array $args = [])
* @method \Aws\Result getAssetFile(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAssetFileAsync(array $args = [])
* @method \Aws\Result getAssociation(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAssociationAsync(array $args = [])
* @method \Aws\Result getBacklogTask(array $args = [])
* @method \GuzzleHttp\Promise\Promise getBacklogTaskAsync(array $args = [])
* @method \Aws\Result getOperatorApp(array $args = [])
* @method \GuzzleHttp\Promise\Promise getOperatorAppAsync(array $args = [])
* @method \Aws\Result getRecommendation(array $args = [])
* @method \GuzzleHttp\Promise\Promise getRecommendationAsync(array $args = [])
* @method \Aws\Result getService(array $args = [])
* @method \GuzzleHttp\Promise\Promise getServiceAsync(array $args = [])
* @method \Aws\Result listAgentSpaces(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAgentSpacesAsync(array $args = [])
* @method \Aws\Result listAssetFiles(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAssetFilesAsync(array $args = [])
* @method \Aws\Result listAssetTypes(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAssetTypesAsync(array $args = [])
* @method \Aws\Result listAssetVersions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAssetVersionsAsync(array $args = [])
* @method \Aws\Result listAssets(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAssetsAsync(array $args = [])
* @method \Aws\Result listAssociations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAssociationsAsync(array $args = [])
* @method \Aws\Result listBacklogTasks(array $args = [])
* @method \GuzzleHttp\Promise\Promise listBacklogTasksAsync(array $args = [])
* @method \Aws\Result listChats(array $args = [])
* @method \GuzzleHttp\Promise\Promise listChatsAsync(array $args = [])
* @method \Aws\Result listExecutions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listExecutionsAsync(array $args = [])
* @method \Aws\Result listGoals(array $args = [])
* @method \GuzzleHttp\Promise\Promise listGoalsAsync(array $args = [])
* @method \Aws\Result listJournalRecords(array $args = [])
* @method \GuzzleHttp\Promise\Promise listJournalRecordsAsync(array $args = [])
* @method \Aws\Result listPendingMessages(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPendingMessagesAsync(array $args = [])
* @method \Aws\Result listPrivateConnections(array $args = [])
* @method \GuzzleHttp\Promise\Promise listPrivateConnectionsAsync(array $args = [])
* @method \Aws\Result listRecommendations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listRecommendationsAsync(array $args = [])
* @method \Aws\Result listServices(array $args = [])
* @method \GuzzleHttp\Promise\Promise listServicesAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result listWebhooks(array $args = [])
* @method \GuzzleHttp\Promise\Promise listWebhooksAsync(array $args = [])
* @method \Aws\Result registerService(array $args = [])
* @method \GuzzleHttp\Promise\Promise registerServiceAsync(array $args = [])
* @method \Aws\Result sendMessage(array $args = [])
* @method \GuzzleHttp\Promise\Promise sendMessageAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateAgentSpace(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateAgentSpaceAsync(array $args = [])
* @method \Aws\Result updateAsset(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateAssetAsync(array $args = [])
* @method \Aws\Result updateAssetFile(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateAssetFileAsync(array $args = [])
* @method \Aws\Result updateAssociation(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateAssociationAsync(array $args = [])
* @method \Aws\Result updateBacklogTask(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateBacklogTaskAsync(array $args = [])
* @method \Aws\Result updateGoal(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateGoalAsync(array $args = [])
* @method \Aws\Result updateOperatorAppIdpConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateOperatorAppIdpConfigAsync(array $args = [])
* @method \Aws\Result updatePrivateConnectionCertificate(array $args = [])
* @method \GuzzleHttp\Promise\Promise updatePrivateConnectionCertificateAsync(array $args = [])
* @method \Aws\Result updateRecommendation(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateRecommendationAsync(array $args = [])
* @method \Aws\Result validateAwsAssociations(array $args = [])
* @method \GuzzleHttp\Promise\Promise validateAwsAssociationsAsync(array $args = [])
*/
class DevOpsAgentClient extends AwsClient {}

View file

@ -0,0 +1,9 @@
<?php
namespace Aws\DevOpsAgent\Exception;
use Aws\Exception\AwsException;
/**
* Represents an error interacting with the **AWS DevOps Agent Service** service.
*/
class DevOpsAgentException extends AwsException {}

View file

@ -7,8 +7,14 @@ use Aws\ClientResolver;
use Aws\Exception\AwsException;
use Aws\HandlerList;
use Aws\Middleware;
use Aws\Retry\Configuration as RetryConfiguration;
use Aws\Retry\ConfigurationInterface as RetryConfigurationInterface;
use Aws\Retry\ConfigurationProvider as RetryConfigurationProvider;
use Aws\Retry\V3\OptIn as NewRetriesOptIn;
use Aws\Retry\V3\RetryMiddleware as RetryV3Middleware;
use Aws\RetryMiddleware;
use Aws\RetryMiddlewareV2;
use GuzzleHttp\Promise\Create;
/**
* This client is used to interact with the **Amazon DynamoDB** service.
@ -130,16 +136,50 @@ use Aws\RetryMiddlewareV2;
*/
class DynamoDbClient extends AwsClient
{
/** @internal Default attempts for the AWS_NEW_RETRIES_2026 path. */
private const DYNAMODB_MAX_ATTEMPTS = 4;
/** @internal Base backoff in ms for the AWS_NEW_RETRIES_2026 path. */
private const DEFAULT_BASE_DELAY_MS = 25;
/**
* @internal Legacy-mode fallback when an array config does not specify
* max_attempts. Only consulted on the AWS_NEW_RETRIES_2026 path.
*/
public const DEFAULT_LEGACY_MAX_ATTEMPTS = 10;
public static function getArguments()
{
$args = parent::getArguments();
$args['retries']['default'] = 10;
$args['retries']['default'] = NewRetriesOptIn::isEnabled()
? [__CLASS__, '_defaultRetries']
: self::DEFAULT_LEGACY_MAX_ATTEMPTS;
$args['retries']['fn'] = [__CLASS__, '_applyRetryConfig'];
$args['api_provider']['fn'] = [__CLASS__, '_applyApiProvider'];
return $args;
}
/**
* @internal Default retry-config provider for the AWS_NEW_RETRIES_2026
* path. Falls through to env/INI before applying the DynamoDB
* default of {@see self::DYNAMODB_MAX_ATTEMPTS} attempts in
* the specs standard mode.
*/
public static function _defaultRetries()
{
return RetryConfigurationProvider::chain(
RetryConfigurationProvider::env(),
RetryConfigurationProvider::ini(),
function () {
return Create::promiseFor(
new RetryConfiguration(
RetryConfigurationProvider::getDefaultMode(),
self::DYNAMODB_MAX_ATTEMPTS
)
);
}
);
}
/**
* Convenience method for instantiating and registering the DynamoDB
* Session handler with this DynamoDB client object.
@ -159,40 +199,103 @@ class DynamoDbClient extends AwsClient
/** @internal */
public static function _applyRetryConfig($value, array &$args, HandlerList $list)
{
if ($value) {
$config = \Aws\Retry\ConfigurationProvider::unwrap($value);
if ($config->getMode() === 'legacy') {
$list->appendSign(
Middleware::retry(
RetryMiddleware::createDefaultDecider(
$config->getMaxAttempts() - 1,
['error_codes' => ['TransactionInProgressException']]
),
function ($retries) {
return $retries
? RetryMiddleware::exponentialDelay($retries) / 2
: 0;
},
isset($args['stats']['retries'])
? (bool)$args['stats']['retries']
: false
),
'retry'
);
} else {
$list->appendSign(
RetryMiddlewareV2::wrap(
$config,
[
'collect_stats' => $args['stats']['retries'],
'transient_error_codes' => ['TransactionInProgressException']
]
),
'retry'
);
}
if (!$value) {
return;
}
$config = RetryConfigurationProvider::unwrap($value);
if ($config->getMode() === 'legacy') {
self::appendLegacyModeRetries($value, $config, $args, $list);
return;
}
if (NewRetriesOptIn::isEnabled()) {
self::appendStandardModeRetriesNew($config, $args, $list);
return;
}
self::appendStandardModeRetries($config, $args, $list);
}
private static function appendLegacyModeRetries(
$value,
RetryConfigurationInterface $config,
array &$args,
HandlerList $list
): void
{
$maxRetries = self::resolveLegacyModeMaxRetries($value, $config);
$list->appendSign(
Middleware::retry(
RetryMiddleware::createDefaultDecider(
$maxRetries,
['error_codes' => ['TransactionInProgressException']]
),
function ($retries) {
return $retries
? RetryMiddleware::exponentialDelay($retries) / 2
: 0;
},
isset($args['stats']['retries']) ? (bool) $args['stats']['retries'] : false
),
'retry'
);
}
private static function resolveLegacyModeMaxRetries(
$value,
RetryConfigurationInterface $config
): int
{
if (
NewRetriesOptIn::isEnabled()
&& is_array($value)
&& !isset($value['max_attempts'])
) {
return self::DEFAULT_LEGACY_MAX_ATTEMPTS;
}
return $config->getMaxAttempts() - 1;
}
private static function appendStandardModeRetries(
RetryConfigurationInterface $config,
array &$args,
HandlerList $list
): void
{
$list->appendSign(
RetryMiddlewareV2::wrap(
$config,
[
'collect_stats' => $args['stats']['retries'],
'transient_error_codes' => ['TransactionInProgressException'],
]
),
'retry'
);
}
private static function appendStandardModeRetriesNew(
RetryConfigurationInterface $config,
array &$args,
HandlerList $list
): void
{
$list->appendSign(
RetryV3Middleware::wrap(
$config,
[
'collect_stats' => $args['stats']['retries'],
'service' => $args['service'],
'base_delay' => self::DEFAULT_BASE_DELAY_MS,
'transient_error_codes' => ['TransactionInProgressException'],
]
),
'retry'
);
}
/** @internal */

View file

@ -17,22 +17,34 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getDashboardForJobRunAsync(array $args = [])
* @method \Aws\Result getJobRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise getJobRunAsync(array $args = [])
* @method \Aws\Result getResourceDashboard(array $args = [])
* @method \GuzzleHttp\Promise\Promise getResourceDashboardAsync(array $args = [])
* @method \Aws\Result getSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSessionAsync(array $args = [])
* @method \Aws\Result getSessionEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSessionEndpointAsync(array $args = [])
* @method \Aws\Result listApplications(array $args = [])
* @method \GuzzleHttp\Promise\Promise listApplicationsAsync(array $args = [])
* @method \Aws\Result listJobRunAttempts(array $args = [])
* @method \GuzzleHttp\Promise\Promise listJobRunAttemptsAsync(array $args = [])
* @method \Aws\Result listJobRuns(array $args = [])
* @method \GuzzleHttp\Promise\Promise listJobRunsAsync(array $args = [])
* @method \Aws\Result listSessions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listSessionsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result startApplication(array $args = [])
* @method \GuzzleHttp\Promise\Promise startApplicationAsync(array $args = [])
* @method \Aws\Result startJobRun(array $args = [])
* @method \GuzzleHttp\Promise\Promise startJobRunAsync(array $args = [])
* @method \Aws\Result startSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise startSessionAsync(array $args = [])
* @method \Aws\Result stopApplication(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopApplicationAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result terminateSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise terminateSessionAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateApplication(array $args = [])

View file

@ -438,6 +438,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise acceptAddressTransferAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result acceptCapacityReservationBillingOwnership(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise acceptCapacityReservationBillingOwnershipAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result acceptTransitGatewayClientVpnAttachment(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise acceptTransitGatewayClientVpnAttachmentAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result acceptTransitGatewayMulticastDomainAssociations(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise acceptTransitGatewayMulticastDomainAssociationsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result acceptTransitGatewayPeeringAttachment(array $args = []) (supported in versions 2016-11-15)
@ -510,6 +512,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise createCapacityReservationAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createCapacityReservationBySplitting(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createCapacityReservationBySplittingAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createCapacityReservationCancellationQuote(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createCapacityReservationCancellationQuoteAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createCapacityReservationFleet(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createCapacityReservationFleetAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createCarrierGateway(array $args = []) (supported in versions 2016-11-15)
@ -596,6 +600,10 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise createRouteServerEndpointAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createRouteServerPeer(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createRouteServerPeerAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createSecondaryNetwork(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createSecondaryNetworkAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createSecondarySubnet(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createSecondarySubnetAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createSnapshots(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise createSnapshotsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result createStoreImageTask(array $args = []) (supported in versions 2016-11-15)
@ -732,6 +740,10 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise deleteRouteServerEndpointAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteRouteServerPeer(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteRouteServerPeerAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteSecondaryNetwork(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteSecondaryNetworkAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteSecondarySubnet(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteSecondarySubnetAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteSubnetCidrReservation(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteSubnetCidrReservationAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteTrafficMirrorFilter(array $args = []) (supported in versions 2016-11-15)
@ -744,6 +756,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise deleteTrafficMirrorTargetAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteTransitGateway(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteTransitGatewayAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteTransitGatewayClientVpnAttachment(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteTransitGatewayClientVpnAttachmentAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteTransitGatewayConnect(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise deleteTransitGatewayConnectAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result deleteTransitGatewayConnectPeer(array $args = []) (supported in versions 2016-11-15)
@ -824,6 +838,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise describeCapacityManagerDataExportsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeCapacityReservationBillingRequests(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeCapacityReservationBillingRequestsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeCapacityReservationCancellationQuotes(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeCapacityReservationCancellationQuotesAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeCapacityReservationFleets(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeCapacityReservationFleetsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeCapacityReservationTopology(array $args = []) (supported in versions 2016-11-15)
@ -900,6 +916,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise describeIpamExternalResourceVerificationTokensAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeIpamPolicies(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeIpamPoliciesAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeIpamPoolAllocations(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeIpamPoolAllocationsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeIpamPools(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeIpamPoolsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeIpamPrefixListResolverTargets(array $args = []) (supported in versions 2016-11-15)
@ -964,6 +982,12 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise describeRouteServerPeersAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeRouteServers(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeRouteServersAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeSecondaryInterfaces(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeSecondaryInterfacesAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeSecondaryNetworks(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeSecondaryNetworksAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeSecondarySubnets(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeSecondarySubnetsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeSecurityGroupRules(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise describeSecurityGroupRulesAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result describeSecurityGroupVpcAssociations(array $args = []) (supported in versions 2016-11-15)
@ -1170,6 +1194,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise getCapacityManagerMetricDataAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result getCapacityManagerMetricDimensions(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise getCapacityManagerMetricDimensionsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result getCapacityManagerMonitoredTagKeys(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise getCapacityManagerMonitoredTagKeysAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result getCapacityReservationUsage(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise getCapacityReservationUsageAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result getCoipPoolUsage(array $args = []) (supported in versions 2016-11-15)
@ -1230,6 +1256,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise getManagedPrefixListAssociationsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result getManagedPrefixListEntries(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise getManagedPrefixListEntriesAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result getManagedResourceVisibility(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise getManagedResourceVisibilityAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result getNetworkInsightsAccessScopeAnalysisFindings(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise getNetworkInsightsAccessScopeAnalysisFindingsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result getNetworkInsightsAccessScopeContent(array $args = []) (supported in versions 2016-11-15)
@ -1334,6 +1362,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise modifyIpamPolicyAllocationRulesAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyIpamPool(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise modifyIpamPoolAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyIpamPoolAllocation(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise modifyIpamPoolAllocationAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyIpamPrefixListResolver(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise modifyIpamPrefixListResolverAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyIpamPrefixListResolverTarget(array $args = []) (supported in versions 2016-11-15)
@ -1350,6 +1380,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise modifyLocalGatewayRouteAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyManagedPrefixList(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise modifyManagedPrefixListAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyManagedResourceVisibility(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise modifyManagedResourceVisibilityAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyPrivateDnsNameOptions(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise modifyPrivateDnsNameOptionsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result modifyPublicIpDnsNameOptions(array $args = []) (supported in versions 2016-11-15)
@ -1438,6 +1470,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise registerTransitGatewayMulticastGroupSourcesAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result rejectCapacityReservationBillingOwnership(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise rejectCapacityReservationBillingOwnershipAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result rejectTransitGatewayClientVpnAttachment(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise rejectTransitGatewayClientVpnAttachmentAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result rejectTransitGatewayMulticastDomainAssociations(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise rejectTransitGatewayMulticastDomainAssociationsAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result rejectTransitGatewayPeeringAttachment(array $args = []) (supported in versions 2016-11-15)
@ -1498,6 +1532,8 @@ use Aws\PresignUrlMiddleware;
* @method \GuzzleHttp\Promise\Promise unassignPrivateNatGatewayAddressAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result unlockSnapshot(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise unlockSnapshotAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result updateCapacityManagerMonitoredTagKeys(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise updateCapacityManagerMonitoredTagKeysAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result updateCapacityManagerOrganizationsAccess(array $args = []) (supported in versions 2016-11-15)
* @method \GuzzleHttp\Promise\Promise updateCapacityManagerOrganizationsAccessAsync(array $args = []) (supported in versions 2016-11-15)
* @method \Aws\Result updateInterruptibleCapacityReservationAllocation(array $args = []) (supported in versions 2016-11-15)

View file

@ -6,10 +6,14 @@ use Aws\AwsClient;
/**
* This client is used to interact with **Amazon ECS**.
*
* @method \Aws\Result continueServiceDeployment(array $args = [])
* @method \GuzzleHttp\Promise\Promise continueServiceDeploymentAsync(array $args = [])
* @method \Aws\Result createCapacityProvider(array $args = [])
* @method \GuzzleHttp\Promise\Promise createCapacityProviderAsync(array $args = [])
* @method \Aws\Result createCluster(array $args = [])
* @method \GuzzleHttp\Promise\Promise createClusterAsync(array $args = [])
* @method \Aws\Result createDaemon(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDaemonAsync(array $args = [])
* @method \Aws\Result createExpressGatewayService(array $args = [])
* @method \GuzzleHttp\Promise\Promise createExpressGatewayServiceAsync(array $args = [])
* @method \Aws\Result createService(array $args = [])
@ -24,6 +28,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteCapacityProviderAsync(array $args = [])
* @method \Aws\Result deleteCluster(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteClusterAsync(array $args = [])
* @method \Aws\Result deleteDaemon(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDaemonAsync(array $args = [])
* @method \Aws\Result deleteDaemonTaskDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDaemonTaskDefinitionAsync(array $args = [])
* @method \Aws\Result deleteExpressGatewayService(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteExpressGatewayServiceAsync(array $args = [])
* @method \Aws\Result deleteService(array $args = [])
@ -42,6 +50,14 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise describeClustersAsync(array $args = [])
* @method \Aws\Result describeContainerInstances(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeContainerInstancesAsync(array $args = [])
* @method \Aws\Result describeDaemon(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeDaemonAsync(array $args = [])
* @method \Aws\Result describeDaemonDeployments(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeDaemonDeploymentsAsync(array $args = [])
* @method \Aws\Result describeDaemonRevisions(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeDaemonRevisionsAsync(array $args = [])
* @method \Aws\Result describeDaemonTaskDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeDaemonTaskDefinitionAsync(array $args = [])
* @method \Aws\Result describeExpressGatewayService(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeExpressGatewayServiceAsync(array $args = [])
* @method \Aws\Result describeServiceDeployments(array $args = [])
@ -70,6 +86,12 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listClustersAsync(array $args = [])
* @method \Aws\Result listContainerInstances(array $args = [])
* @method \GuzzleHttp\Promise\Promise listContainerInstancesAsync(array $args = [])
* @method \Aws\Result listDaemonDeployments(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDaemonDeploymentsAsync(array $args = [])
* @method \Aws\Result listDaemonTaskDefinitions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDaemonTaskDefinitionsAsync(array $args = [])
* @method \Aws\Result listDaemons(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDaemonsAsync(array $args = [])
* @method \Aws\Result listServiceDeployments(array $args = [])
* @method \GuzzleHttp\Promise\Promise listServiceDeploymentsAsync(array $args = [])
* @method \Aws\Result listServices(array $args = [])
@ -94,6 +116,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise putClusterCapacityProvidersAsync(array $args = [])
* @method \Aws\Result registerContainerInstance(array $args = [])
* @method \GuzzleHttp\Promise\Promise registerContainerInstanceAsync(array $args = [])
* @method \Aws\Result registerDaemonTaskDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise registerDaemonTaskDefinitionAsync(array $args = [])
* @method \Aws\Result registerTaskDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise registerTaskDefinitionAsync(array $args = [])
* @method \Aws\Result runTask(array $args = [])
@ -124,6 +148,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateContainerAgentAsync(array $args = [])
* @method \Aws\Result updateContainerInstancesState(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateContainerInstancesStateAsync(array $args = [])
* @method \Aws\Result updateDaemon(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateDaemonAsync(array $args = [])
* @method \Aws\Result updateExpressGatewayService(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateExpressGatewayServiceAsync(array $args = [])
* @method \Aws\Result updateService(array $args = [])

View file

@ -0,0 +1,41 @@
<?php
namespace Aws\ElementalInference;
use Aws\AwsClient;
/**
* This client is used to interact with the **AWS Elemental Inference** service.
* @method \Aws\Result associateFeed(array $args = [])
* @method \GuzzleHttp\Promise\Promise associateFeedAsync(array $args = [])
* @method \Aws\Result createDictionary(array $args = [])
* @method \GuzzleHttp\Promise\Promise createDictionaryAsync(array $args = [])
* @method \Aws\Result createFeed(array $args = [])
* @method \GuzzleHttp\Promise\Promise createFeedAsync(array $args = [])
* @method \Aws\Result deleteDictionary(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteDictionaryAsync(array $args = [])
* @method \Aws\Result deleteFeed(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteFeedAsync(array $args = [])
* @method \Aws\Result disassociateFeed(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociateFeedAsync(array $args = [])
* @method \Aws\Result exportDictionaryEntries(array $args = [])
* @method \GuzzleHttp\Promise\Promise exportDictionaryEntriesAsync(array $args = [])
* @method \Aws\Result getDictionary(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDictionaryAsync(array $args = [])
* @method \Aws\Result getFeed(array $args = [])
* @method \GuzzleHttp\Promise\Promise getFeedAsync(array $args = [])
* @method \Aws\Result listDictionaries(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDictionariesAsync(array $args = [])
* @method \Aws\Result listFeeds(array $args = [])
* @method \GuzzleHttp\Promise\Promise listFeedsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateDictionary(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateDictionaryAsync(array $args = [])
* @method \Aws\Result updateFeed(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateFeedAsync(array $args = [])
*/
class ElementalInferenceClient extends AwsClient {}

View file

@ -0,0 +1,9 @@
<?php
namespace Aws\ElementalInference\Exception;
use Aws\Exception\AwsException;
/**
* Represents an error interacting with the **AWS Elemental Inference** service.
*/
class ElementalInferenceException extends AwsException {}

View file

@ -58,6 +58,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getOnClusterAppUIPresignedURLAsync(array $args = [])
* @method \Aws\Result getPersistentAppUIPresignedURL(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPersistentAppUIPresignedURLAsync(array $args = [])
* @method \Aws\Result getSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSessionAsync(array $args = [])
* @method \Aws\Result getSessionEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSessionEndpointAsync(array $args = [])
* @method \Aws\Result getStudioSessionMapping(array $args = [])
* @method \GuzzleHttp\Promise\Promise getStudioSessionMappingAsync(array $args = [])
* @method \Aws\Result listBootstrapActions(array $args = [])
@ -76,6 +80,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listReleaseLabelsAsync(array $args = [])
* @method \Aws\Result listSecurityConfigurations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listSecurityConfigurationsAsync(array $args = [])
* @method \Aws\Result listSessions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listSessionsAsync(array $args = [])
* @method \Aws\Result listSteps(array $args = [])
* @method \GuzzleHttp\Promise\Promise listStepsAsync(array $args = [])
* @method \Aws\Result listStudioSessionMappings(array $args = [])
@ -118,10 +124,14 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise setVisibleToAllUsersAsync(array $args = [])
* @method \Aws\Result startNotebookExecution(array $args = [])
* @method \GuzzleHttp\Promise\Promise startNotebookExecutionAsync(array $args = [])
* @method \Aws\Result startSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise startSessionAsync(array $args = [])
* @method \Aws\Result stopNotebookExecution(array $args = [])
* @method \GuzzleHttp\Promise\Promise stopNotebookExecutionAsync(array $args = [])
* @method \Aws\Result terminateJobFlows(array $args = [])
* @method \GuzzleHttp\Promise\Promise terminateJobFlowsAsync(array $args = [])
* @method \Aws\Result terminateSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise terminateSessionAsync(array $args = [])
* @method \Aws\Result updateStudio(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateStudioAsync(array $args = [])
* @method \Aws\Result updateStudioSessionMapping(array $args = [])

View file

@ -21,7 +21,7 @@ class EndpointDiscoveryMiddleware
private static $discoveryCooldown = 60;
private $args;
private $client;
private \WeakReference $client;
private $config;
private $discoveryTimes = [];
private $nextHandler;
@ -32,7 +32,7 @@ class EndpointDiscoveryMiddleware
$args,
$config
) {
return function (callable $handler) use (
return static function (callable $handler) use (
$client,
$args,
$config
@ -53,7 +53,7 @@ class EndpointDiscoveryMiddleware
$config
) {
$this->nextHandler = $handler;
$this->client = $client;
$this->client = \WeakReference::create($client);
$this->args = $args;
$this->service = $client->getApi();
$this->config = $config;
@ -91,7 +91,7 @@ class EndpointDiscoveryMiddleware
$identifiers = $this->getIdentifiers($op);
$cacheKey = $this->getCacheKey(
$this->client->getCredentials()->wait(),
$this->client->get()->getCredentials()->wait(),
$cmd,
$identifiers
);
@ -178,7 +178,7 @@ class EndpointDiscoveryMiddleware
) {
$discCmd = $this->getDiscoveryCommand($cmd, $identifiers);
$this->discoveryTimes[$cacheKey] = time();
$result = $this->client->execute($discCmd);
$result = $this->client->get()->execute($discCmd);
if (isset($result['Endpoints'])) {
$endpointData = [];
@ -237,7 +237,7 @@ class EndpointDiscoveryMiddleware
$params['Identifiers'][$identifier] = $cmd[$identifier];
}
}
$command = $this->client->getCommand($endpointOperation, $params);
$command = $this->client->get()->getCommand($endpointOperation, $params);
$command->getHandlerList()->appendBuild(
Middleware::mapRequest(function (RequestInterface $r) {
return $r->withHeader(

View file

@ -0,0 +1,79 @@
<?php
namespace Aws\EndpointV2\Bdd;
use Aws\EndpointV2\Ruleset\RulesetEndpoint;
use Aws\Exception\UnresolvedEndpointException;
/**
* Walks an endpoint BDD to produce a {@see RulesetEndpoint} or throw an
* {@see UnresolvedEndpointException}.
*
* The traversal follows the smithy rules-engine reference algorithm:
* each reference is either a node pointer (optionally complemented with a
* negative sign), one of the two terminals (`1` / `-1`), or a result pointer
* (offset by {@see self::RESULT_OFFSET}). Nodes are laid out as contiguous
* triples `[conditionIndex, highRef, lowRef]` inside the ruleset's flat
* node array.
*
* @internal
*/
final class BddEvaluator
{
private const TERMINAL_TRUE = 1;
private const TERMINAL_FALSE = -1;
private const RESULT_OFFSET = 100_000_000;
public function __construct(
private readonly BddRuleset $ruleset,
private readonly BddResultResolver $resultResolver
) {
}
/**
* Resolves an endpoint from the BDD for the given input parameters.
*
* @throws UnresolvedEndpointException when resolution reaches the no-match
* terminal or an error result rule.
*/
public function evaluate(array $inputParameters): RulesetEndpoint
{
$this->ruleset->applyParameterDefaults($inputParameters);
$nodes = $this->ruleset->getNodes();
$conditions = $this->ruleset->getConditions();
$library = $this->ruleset->standardLibrary;
$ref = $this->ruleset->getRoot();
while (true) {
if ($ref >= self::RESULT_OFFSET) {
return $this->resultResolver->resolve(
$ref - self::RESULT_OFFSET,
$inputParameters
);
}
if ($ref === self::TERMINAL_TRUE || $ref === self::TERMINAL_FALSE) {
// Throws exception
$this->resultResolver->resolveNoMatch($inputParameters);
}
$isComplement = $ref < 0;
$base = ($isComplement ? -$ref : $ref) * 3 - 3;
$condIndex = $nodes[$base];
$value = $library->callFunction(
$conditions[$condIndex],
$inputParameters
);
$condResult = ($value !== null && $value !== false);
// Complement edges invert the high/low selection without
// duplicating nodes in the BDD.
$ref = ($condResult xor $isComplement)
? $nodes[$base + 1]
: $nodes[$base + 2];
}
}
}

View file

@ -0,0 +1,68 @@
<?php
namespace Aws\EndpointV2\Bdd;
use Aws\Exception\UnresolvedEndpointException;
/**
* Decodes the base64 `nodes` string shipped in the endpointBdd trait into a
* flat array of signed 32-bit integers. Every node occupies three slots:
* `[conditionIndex, highRef, lowRef]`.
*
* The flat representation is intentional indexing into an int array is
* cheaper per step than materialising a node object for every traversal,
* and the evaluator hot loop references thousands of slots for a large BDD.
*
* @internal
*/
final class BddNodeDecoder
{
private const BYTES_PER_NODE = 12;
private const INT_32_MAX = 2147483647;
private const INT_32_OFFSET = 4294967296;
/**
* Decodes `$encoded` and verifies that the byte count matches
* `$expectedNodeCount`. Returns a flat int array of length
* `3 * $expectedNodeCount`.
*
* @throws UnresolvedEndpointException when the payload is not valid base64
* or its length does not match the declared node count.
*/
public static function decode(string $encoded, int $expectedNodeCount): array
{
$bytes = base64_decode($encoded, true);
if ($bytes === false) {
throw new UnresolvedEndpointException(
'Endpoint BDD nodes are not valid base64.'
);
}
$expectedBytes = $expectedNodeCount * self::BYTES_PER_NODE;
if (strlen($bytes) !== $expectedBytes) {
throw new UnresolvedEndpointException(sprintf(
'Endpoint BDD node payload is %d bytes but %d were expected'
. ' for %d nodes.',
strlen($bytes),
$expectedBytes,
$expectedNodeCount
));
}
if ($expectedNodeCount === 0) {
return [];
}
// unpack() with 'N' returns unsigned 32-bit big-endian ints. We fold
// values above INT_32_MAX back into signed space to match the trait.
$unsigned = unpack('N*', $bytes);
$flat = [];
foreach ($unsigned as $value) {
$flat[] = $value > self::INT_32_MAX
? $value - self::INT_32_OFFSET
: $value;
}
return $flat;
}
}

View file

@ -0,0 +1,140 @@
<?php
namespace Aws\EndpointV2\Bdd;
use Aws\EndpointV2\Ruleset\RulesetEndpoint;
use Aws\EndpointV2\Ruleset\RulesetStandardLibrary;
use Aws\Exception\UnresolvedEndpointException;
/**
* Turns a BDD result reference into a {@see RulesetEndpoint} or throws the
* appropriate {@see UnresolvedEndpointException}. The behavior matches the
* tree evaluator's endpoint and error rules so downstream middleware cannot
* tell which evaluator produced the result.
*
* Result index `0` is reserved for the implicit no-match rule defined by the
* trait. The BDD may also reach a no-match via either terminal reference,
* which is handled by {@see resolveNoMatch()}.
*
* @internal
*/
final class BddResultResolver
{
public function __construct(
private readonly BddRuleset $ruleset
) {
}
/**
* @throws UnresolvedEndpointException
*/
public function resolve(int $resultIndex, array $inputParameters): RulesetEndpoint
{
if ($resultIndex === 0) {
$this->resolveNoMatch($inputParameters);
}
// The serialized `results` array omits the implicit no-match rule,
// so defined result index N lives at array offset N - 1.
$results = $this->ruleset->getResults();
$result = $results[$resultIndex - 1] ?? null;
if ($result === null) {
throw new UnresolvedEndpointException(sprintf(
'Endpoint BDD referenced unknown result index %d.',
$resultIndex
));
}
if (isset($result['error'])) {
$this->throwError($result, $inputParameters);
}
if (!isset($result['endpoint'])) {
throw new UnresolvedEndpointException(
'Endpoint BDD result is missing an `endpoint` or `error` block.'
);
}
return $this->buildEndpoint($result['endpoint'], $inputParameters);
}
/**
* @throws UnresolvedEndpointException
*/
public function resolveNoMatch(array $inputParameters): never
{
throw new UnresolvedEndpointException(
'Unable to resolve an endpoint using the provider arguments: '
. json_encode($inputParameters)
);
}
/**
* @throws UnresolvedEndpointException
*/
private function throwError(array $result, array $inputParameters): never
{
$message = $this->ruleset->standardLibrary->resolveValue(
$result['error'],
$inputParameters
);
throw new UnresolvedEndpointException((string) $message);
}
private function buildEndpoint(array $endpoint, array $inputParameters): RulesetEndpoint
{
$library = $this->ruleset->standardLibrary;
$url = $library->resolveValue($endpoint['url'], $inputParameters);
$properties = isset($endpoint['properties'])
? $this->resolveProperties($endpoint['properties'], $inputParameters, $library)
: null;
$headers = isset($endpoint['headers'])
? $this->resolveHeaders($endpoint['headers'], $inputParameters, $library)
: null;
return new RulesetEndpoint($url, $properties, $headers);
}
private function resolveProperties(
$properties,
array $inputParameters,
RulesetStandardLibrary $library
) {
if (is_array($properties)) {
$resolved = [];
foreach ($properties as $key => $value) {
$resolved[$key] = $this->resolveProperties(
$value,
$inputParameters,
$library
);
}
return $resolved;
}
// Inline some of the isTemplate check here to avoid unnecessary resolution attempts on simple strings
if (is_string($properties) && str_contains($properties, '{') && $library->isTemplate($properties)) {
return $library->resolveTemplateString($properties, $inputParameters);
}
return $properties;
}
private function resolveHeaders(
array $headers,
array $inputParameters,
RulesetStandardLibrary $library
): array {
$resolved = [];
foreach ($headers as $name => $values) {
$resolvedValues = [];
foreach ($values as $value) {
$resolvedValues[] = $library->resolveValue($value, $inputParameters);
}
$resolved[$name] = $resolvedValues;
}
return $resolved;
}
}

View file

@ -0,0 +1,127 @@
<?php
namespace Aws\EndpointV2\Bdd;
use Aws\EndpointV2\Ruleset\RulesetParameter;
use Aws\EndpointV2\Ruleset\RulesetStandardLibrary;
use Aws\Exception\UnresolvedEndpointException;
/**
* Parsed form of the `smithy.rules#endpointBdd` trait. Reuses
* {@see RulesetParameter} so parameter coercion and validation behave
* identically to the tree-based ruleset.
*
* Instances are immutable after construction. A single instance is shared
* across all requests for a given service/client pair.
*
* @internal
*/
final class BddRuleset
{
private const REQUIRED_FIELDS = [
'conditions', 'results', 'nodes', 'root', 'nodeCount'
];
/** @var array<string, RulesetParameter> */
private array $parameters;
/** @var array<int, array> */
private array $conditions;
/** @var array<int, array> */
private array $results;
/** @var int[] Flat triples: [condIdx, hi, lo, condIdx, hi, lo, ...] */
private array $nodes;
private int $root;
public readonly RulesetStandardLibrary $standardLibrary;
public function __construct(array $definition, array $partitions)
{
foreach (self::REQUIRED_FIELDS as $key) {
if (!array_key_exists($key, $definition)) {
throw new UnresolvedEndpointException(
"Endpoint BDD definition is missing `{$key}`."
);
}
}
$this->parameters = $this->buildParameters($definition['parameters'] ?? []);
$this->conditions = $definition['conditions'];
$this->results = $definition['results'];
$this->root = (int) $definition['root'];
$nodeCount = $definition['nodeCount'];
$this->nodes = BddNodeDecoder::decode(
(string) $definition['nodes'],
$nodeCount
);
$this->standardLibrary = new RulesetStandardLibrary($partitions);
}
/**
* @return array<string, RulesetParameter>
*/
public function getParameters(): array
{
return $this->parameters;
}
/**
* @return array<int, array>
*/
public function getConditions(): array
{
return $this->conditions;
}
/**
* @return array<int, array>
*/
public function getResults(): array
{
return $this->results;
}
/**
* @return int[]
*/
public function getNodes(): array
{
return $this->nodes;
}
public function getRoot(): int
{
return $this->root;
}
/**
* Applies parameter defaults and type checks. Mirrors the tree ruleset so
* services migrating from one shape to the other see identical input
* validation behavior.
*/
public function applyParameterDefaults(array &$inputParameters): void
{
foreach ($this->parameters as $name => $param) {
$value = $inputParameters[$name] ?? null;
if (is_null($value) && !is_null($param->getDefault())) {
$inputParameters[$name] = $param->getDefault();
} elseif (!is_null($value)) {
$param->validateInputParam($value);
}
}
}
private function buildParameters(array $parameters): array
{
$built = [];
foreach ($parameters as $name => $definition) {
$built[$name] = new RulesetParameter($name, $definition);
}
return $built;
}
}

View file

@ -2,17 +2,63 @@
namespace Aws\EndpointV2;
use Aws\EndpointV2\Bdd\BddRuleset;
use Aws\EndpointV2\Ruleset\Ruleset;
/**
* Provides Endpoint-related artifacts used for endpoint resolution
* and testing.
*/
class EndpointDefinitionProvider
{
/**
* Returns a parsed ruleset for the service either a {@see BddRuleset}
* if a compiled BDD is shipped, or a {@see Ruleset} otherwise. Selection
* is driven by which file is packaged, so callers get a typed object
* rather than having to inspect the raw array.
*
* @param $service
* @param $apiVersion
* @param array $partitions
* @param null $baseDir
*
* @return Ruleset|BddRuleset
*/
public static function getParsedRuleset(
$service,
$apiVersion,
array $partitions,
$baseDir = null
): BddRuleset|Ruleset
{
$bdd = self::getEndpointBdd($service, $apiVersion, $baseDir, false);
if ($bdd !== null) {
return new BddRuleset($bdd, $partitions);
}
return new Ruleset(
self::getEndpointRuleset($service, $apiVersion, $baseDir),
$partitions
);
}
public static function getEndpointRuleset($service, $apiVersion, $baseDir = null)
{
return self::getData($service, $apiVersion, 'ruleset', $baseDir);
}
/**
* Returns the parsed endpoint BDD for a service, or null when
* `$throwIfMissing` is false and no BDD file is packaged.
*/
public static function getEndpointBdd(
$service,
$apiVersion,
$baseDir = null,
$throwIfMissing = true
) {
return self::getData($service, $apiVersion, 'bdd', $baseDir, $throwIfMissing);
}
public static function getEndpointTests($service, $apiVersion, $baseDir = null)
{
return self::getData($service, $apiVersion, 'tests', $baseDir);
@ -30,11 +76,14 @@ class EndpointDefinitionProvider
}
}
private static function getData($service, $apiVersion, $type, $baseDir)
private static function getData($service, $apiVersion, $type, $baseDir, $throwIfMissing = true)
{
$basePath = $baseDir ? $baseDir : __DIR__ . '/../data';
$basePath = $baseDir ?: __DIR__ . '/../data';
$serviceDir = $basePath . "/{$service}";
if (!is_dir($serviceDir)) {
if (!$throwIfMissing) {
return null;
}
throw new \InvalidArgumentException(
'Invalid service name.'
);
@ -46,21 +95,39 @@ class EndpointDefinitionProvider
$rulesetPath = $serviceDir . '/' . $apiVersion;
if (!is_dir($rulesetPath)) {
if (!$throwIfMissing) {
return null;
}
throw new \InvalidArgumentException(
'Invalid api version.'
);
}
$fileName = $type === 'tests' ? '/endpoint-tests-1' : '/endpoint-rule-set-1';
$fileName = self::getFileName($type);
if (file_exists($rulesetPath . $fileName . '.json.php')) {
return require($rulesetPath . $fileName . '.json.php');
} elseif (file_exists($rulesetPath . $fileName . '.json')) {
return json_decode(file_get_contents($rulesetPath . $fileName . '.json'), true);
} else {
throw new \InvalidArgumentException(
'Specified ' . $type . ' endpoint file for ' . $service . ' with api version ' . $apiVersion . ' does not exist.'
);
}
if (!$throwIfMissing) {
return null;
}
throw new \InvalidArgumentException(
'Specified ' . $type . ' endpoint file for ' . $service
. ' with api version ' . $apiVersion . ' does not exist.'
);
}
private static function getFileName($type): string
{
return match ($type) {
'tests' => '/endpoint-tests-1',
'bdd' => '/endpoint-bdd',
default => '/endpoint-rule-set-1',
};
}
private static function getLatest($service)
@ -68,4 +135,4 @@ class EndpointDefinitionProvider
$manifest = \Aws\manifest();
return $manifest[$service]['versions']['latest'];
}
}
}

View file

@ -2,32 +2,75 @@
namespace Aws\EndpointV2;
use Aws\EndpointV2\Bdd\BddEvaluator;
use Aws\EndpointV2\Bdd\BddResultResolver;
use Aws\EndpointV2\Bdd\BddRuleset;
use Aws\EndpointV2\Ruleset\Ruleset;
use Aws\EndpointV2\Ruleset\RulesetEndpoint;
use Aws\Exception\UnresolvedEndpointException;
use Aws\LruArrayCache;
/**
* Given a service's Ruleset and client-provided input parameters, provides
* Given a service's ruleset and client-provided input parameters, provides
* either an object reflecting the properties of a resolved endpoint,
* or throws an error.
*
* Supports both the classic decision tree ruleset (`endpointRuleSet` trait)
* and the binary decision diagram ruleset (`endpointBdd` trait). A raw
* definition array is always interpreted as a tree ruleset; to use a BDD,
* construct a {@see BddRuleset} and hand it in directly.
*/
class EndpointProviderV2
{
/** @var Ruleset */
/** @var Ruleset|null */
private $ruleset;
/** @var BddRuleset|null */
private $bddRuleset;
/** @var BddEvaluator|null */
private $bddEvaluator;
/** @var LruArrayCache */
private $cache;
public function __construct(array $ruleset, array $partitions)
/**
* @param array|Ruleset|BddRuleset $ruleset A parsed ruleset instance, or
* a raw tree ruleset array from the service model.
* @param array $partitions AWS partitions data. Ignored when $ruleset is
* already a parsed instance, since the instance carries its own
* partition data.
*/
public function __construct($ruleset, array $partitions)
{
$this->ruleset = new Ruleset($ruleset, $partitions);
if ($ruleset instanceof BddRuleset) {
$this->bddRuleset = $ruleset;
$this->bddEvaluator = new BddEvaluator(
$ruleset,
new BddResultResolver($ruleset)
);
} elseif ($ruleset instanceof Ruleset) {
$this->ruleset = $ruleset;
} elseif (is_array($ruleset)) {
$this->ruleset = new Ruleset($ruleset, $partitions);
} else {
throw new \InvalidArgumentException(
'EndpointProviderV2 expects an array, Ruleset, or BddRuleset'
. ' but received ' . (is_object($ruleset)
? get_class($ruleset)
: gettype($ruleset))
);
}
$this->cache = new LruArrayCache(100);
}
/**
* @return Ruleset
* Returns the parsed tree ruleset for services using the legacy
* `endpointRuleSet` trait. Returns null when the provider was built from
* an `endpointBdd` trait.
*
* @return Ruleset|null
*/
public function getRuleset()
{
@ -35,8 +78,17 @@ class EndpointProviderV2
}
/**
* Given a Ruleset and input parameters, determines the correct endpoint
* or an error to be thrown for a given request.
* Returns the parsed BDD ruleset for services using the `endpointBdd`
* trait, or null when the provider was built from a tree ruleset.
*/
public function getBddRuleset(): ?BddRuleset
{
return $this->bddRuleset;
}
/**
* Given input parameters, determines the correct endpoint or an error
* to be thrown for a given request.
*
* @return RulesetEndpoint
* @throws UnresolvedEndpointException
@ -50,13 +102,19 @@ class EndpointProviderV2
return $match;
}
$endpoint = $this->ruleset->evaluate($inputParameters);
$endpoint = $this->bddEvaluator !== null
? $this->bddEvaluator->evaluate($inputParameters)
: $this->ruleset->evaluate($inputParameters);
// This condition just applies to endpoint resolution
// through the decision tree evaluation process.
if ($endpoint === false) {
throw new UnresolvedEndpointException(
'Unable to resolve an endpoint using the provider arguments: '
. json_encode($inputParameters)
);
}
$this->cache->set($hashedParams, $endpoint);
return $endpoint;
@ -66,4 +124,16 @@ class EndpointProviderV2
{
return md5(serialize($inputParameters));
}
/**
* @return array
*/
public function getActiveParameters(): array
{
if ($this->bddRuleset !== null) {
return $this->bddRuleset->getParameters();
}
return $this->ruleset->getParameters();
}
}

View file

@ -126,7 +126,7 @@ class EndpointV2Middleware
*/
private function resolveArgs(array $commandArgs, Operation $operation): array
{
$rulesetParams = $this->endpointProvider->getRuleset()->getParameters();
$rulesetParams = $this->endpointProvider->getActiveParameters();
if (isset($rulesetParams[self::ACCOUNT_ID_PARAM])
&& isset($rulesetParams[self::ACCOUNT_ID_ENDPOINT_MODE_PARAM])) {

View file

@ -22,11 +22,9 @@ class RulesetStandardLibrary
. 1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]
. {1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]
. |1{0,1}[0-9]){0,1}[0-9])/';
const TEMPLATE_ESCAPE_RE = '/{\{\s*(.*?)\s*\}\}/';
const TEMPLATE_SEARCH_RE = '/\{[a-zA-Z#]+\}/';
const TEMPLATE_PARSE_RE = '#\{((?>[^\{\}]+)|(?R))*\}#x';
const TEMPLATE_SEARCH_RE = '/\{\{.*?\}\}|\{[a-zA-Z0-9_#]+\}/';
const TEMPLATE_PARSE_RE = '/\{\{\s*([^{}]*?)\s*\}\}|\{([a-zA-Z0-9_]+(?:#[a-zA-Z0-9_]+)*)\}/';
const HOST_LABEL_RE = '/^(?!-)[a-zA-Z\d-]{1,63}(?<!-)$/';
private $partitions;
public function __construct($partitions)
@ -181,11 +179,11 @@ class RulesetStandardLibrary
$urlInfo = [];
$urlInfo['scheme'] = $parsed['scheme'];
$urlInfo['authority'] = isset($parsed['host']) ? $parsed['host'] : '';
$urlInfo['authority'] = $parsed['host'] ?? '';
if (isset($parsed['port'])) {
$urlInfo['authority'] = $urlInfo['authority'] . ":" . $parsed['port'];
}
$urlInfo['path'] = isset($parsed['path']) ? $parsed['path'] : '';
$urlInfo['path'] = $parsed['path'] ?? '';
$urlInfo['normalizedPath'] = !empty($parsed['path'])
? rtrim($urlInfo['path'] ?: '', '/' . "/") . '/'
: '/';
@ -229,34 +227,37 @@ class RulesetStandardLibrary
*/
public function parseArn($arnString)
{
if (is_null($arnString)
|| substr( $arnString, 0, 3 ) !== "arn"
if (!is_string($arnString)
|| strncmp($arnString, 'arn', 3) !== 0
) {
return null;
}
$arn = [];
$parts = explode(':', $arnString, 6);
if (sizeof($parts) < 6) {
if (count($parts) < 6) {
return null;
}
$arn['partition'] = isset($parts[1]) ? $parts[1] : null;
$arn['service'] = isset($parts[2]) ? $parts[2] : null;
$arn['region'] = isset($parts[3]) ? $parts[3] : null;
$arn['accountId'] = isset($parts[4]) ? $parts[4] : null;
$arn['resourceId'] = isset($parts[5]) ? $parts[5] : null;
$partition = $parts[1];
$service = $parts[2];
$region = $parts[3];
$accountId = $parts[4];
$resource = $parts[5];
if (empty($arn['partition'])
|| empty($arn['service'])
|| empty($arn['resourceId'])
if ($partition === ''
|| $service === ''
|| $resource === ''
) {
return null;
}
$resource = $arn['resourceId'];
$arn['resourceId'] = preg_split("/[:\/]/", $resource);
return $arn;
return [
'partition' => $partition,
'service' => $service,
'region' => $region,
'accountId' => $accountId,
'resourceId' => preg_split("/[:\/]/", $resource),
];
}
/**
@ -284,6 +285,61 @@ class RulesetStandardLibrary
return $partitions['partitions'][0]['outputs'];
}
/**
* Returns the first non-null argument, or null if every argument is null.
* Mirrors the standard library `coalesce` function and accepts any number
* of already-resolved values.
*
* @return mixed
*/
public function coalesce(...$values)
{
foreach ($values as $value) {
if (!is_null($value)) {
return $value;
}
}
return null;
}
/**
* Splits a string on a delimiter up to an optional limit, returning an
* array of string parts. Mirrors the smithy `split` function: a `null` or
* `0` limit means "no limit", a positive limit caps the number of parts,
* and any other input (non-string, empty delimiter, negative limit)
* returns null so downstream conditions treat it as "no value".
*
* @return array|null
*/
public function split($input, $delimiter, $limit = null)
{
if (!is_string($input) || !is_string($delimiter) || $delimiter === '') {
return null;
}
if (is_null($limit) || $limit === 0) {
return explode($delimiter, $input);
}
if (!is_int($limit) || $limit < 0) {
return null;
}
return explode($delimiter, $input, $limit);
}
/**
* Functional if-then-else. Returns `$then` when `$condition` is truthy,
* otherwise `$else`. Arguments are resolved eagerly by the caller, which
* matches the rules engine semantics for function arguments.
*
* @return mixed
*/
public function ite($condition, $then, $else)
{
return filter_var($condition, FILTER_VALIDATE_BOOLEAN) ? $then : $else;
}
/**
* Evaluates whether a value is a valid bucket name for virtual host
* style bucket URLs.
@ -313,25 +369,86 @@ class RulesetStandardLibrary
public function callFunction($funcCondition, &$inputParameters)
{
$funcArgs = [];
$argv = $funcCondition['argv'];
$assign = $funcCondition['assign'] ?? null;
$fn = $funcCondition['fn'];
switch ($fn) {
case 'aws.parseArn':
$result = $this->parseArn(
$this->resolveValue($argv[0], $inputParameters)
);
break;
forEach($funcCondition['argv'] as $arg) {
$funcArgs[] = $this->resolveValue($arg, $inputParameters);
case 'getAttr':
$result = $this->getAttr(
$this->resolveValue($argv[0], $inputParameters),
$argv[1]
);
break;
case 'stringEquals':
$result = $this->stringEquals(
$this->resolveValue($argv[0], $inputParameters),
$this->resolveValue($argv[1], $inputParameters)
);
break;
case 'booleanEquals':
$result = $this->booleanEquals(
$this->resolveValue($argv[0], $inputParameters),
$this->resolveValue($argv[1], $inputParameters)
);
break;
case 'isSet':
$arg = $argv[0];
$result = isset($arg['ref'])
? isset($inputParameters[$arg['ref']])
: $this->is_set($this->resolveValue($arg, $inputParameters));
break;
case 'not':
$result = $this->not(
$this->resolveValue($argv[0], $inputParameters)
);
break;
case 'substring':
$result = $this->substring(
$this->resolveValue($argv[0], $inputParameters),
$this->resolveValue($argv[1], $inputParameters),
$this->resolveValue($argv[2], $inputParameters),
isset($argv[3])
? $this->resolveValue($argv[3], $inputParameters)
: false
);
break;
default:
$funcArgs = [];
foreach ($argv as $arg) {
$funcArgs[] = $this->resolveValue($arg, $inputParameters);
}
$funcName = str_replace('aws.', '', $fn);
if ($funcName === 'isSet') {
$funcName = 'is_set';
}
if (!method_exists($this, $funcName)) {
throw new UnresolvedEndpointException(
"Unknown endpoint function `{$fn}`."
);
}
$result = call_user_func_array(
[$this, $funcName],
$funcArgs
);
}
$funcName = str_replace('aws.', '', $funcCondition['fn']);
if ($funcName === 'isSet') {
$funcName = 'is_set';
}
$result = call_user_func_array(
[RulesetStandardLibrary::class, $funcName],
$funcArgs
);
if (isset($funcCondition['assign'])) {
$assign = $funcCondition['assign'];
if (isset($inputParameters[$assign])){
if ($assign !== null) {
if (isset($inputParameters[$assign])) {
throw new UnresolvedEndpointException(
"Assignment `{$assign}` already exists in input parameters" .
" or has already been assigned by an endpoint rule and cannot be overwritten."
@ -346,13 +463,20 @@ class RulesetStandardLibrary
{
//Given a value, check if it's a function, reference or template.
//returns resolved value
if ($this->isFunc($value)) {
return $this->callFunction($value, $inputParameters);
} elseif ($this->isRef($value)) {
return isset($inputParameters[$value['ref']]) ? $inputParameters[$value['ref']] : null;
} elseif ($this->isTemplate($value)) {
if (is_array($value)) {
if (isset($value['fn'])) {
return $this->callFunction($value, $inputParameters);
}
if (isset($value['ref'])) {
return $inputParameters[$value['ref']] ?? null;
}
} elseif (is_string($value)
&& str_contains($value, '{')
&& $this->isTemplate($value)
) {
return $this->resolveTemplateString($value, $inputParameters);
}
return $value;
}
@ -368,7 +492,9 @@ class RulesetStandardLibrary
public function isTemplate($arg)
{
return is_string($arg) && !empty(preg_match(self::TEMPLATE_SEARCH_RE, $arg));
return is_string($arg)
&& str_contains($arg, '{')
&& preg_match(self::TEMPLATE_SEARCH_RE, $arg) === 1;
}
public function resolveTemplateString($value, $inputParameters)
@ -376,14 +502,14 @@ class RulesetStandardLibrary
return preg_replace_callback(
self::TEMPLATE_PARSE_RE,
function ($match) use ($inputParameters) {
if (preg_match(self::TEMPLATE_ESCAPE_RE, $match[0])) {
return $match[1];
if (str_starts_with($match[0], '{{')) {
return '{' . $match[1] . '}';
}
$notFoundMessage = 'Resolved value was null. Please check rules and ' .
'input parameters and try again.';
$parts = explode("#", $match[1]);
$parts = explode("#", $match[2]);
if (count($parts) > 1) {
$resolvedValue = $inputParameters;
foreach($parts as $part) {

View file

@ -7,20 +7,32 @@ use Aws\AwsClient;
* This client is used to interact with the **Amazon Elastic VMware Service** service.
* @method \Aws\Result associateEipToVlan(array $args = [])
* @method \GuzzleHttp\Promise\Promise associateEipToVlanAsync(array $args = [])
* @method \Aws\Result createEntitlement(array $args = [])
* @method \GuzzleHttp\Promise\Promise createEntitlementAsync(array $args = [])
* @method \Aws\Result createEnvironment(array $args = [])
* @method \GuzzleHttp\Promise\Promise createEnvironmentAsync(array $args = [])
* @method \Aws\Result createEnvironmentConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise createEnvironmentConnectorAsync(array $args = [])
* @method \Aws\Result createEnvironmentHost(array $args = [])
* @method \GuzzleHttp\Promise\Promise createEnvironmentHostAsync(array $args = [])
* @method \Aws\Result deleteEntitlement(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteEntitlementAsync(array $args = [])
* @method \Aws\Result deleteEnvironment(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteEnvironmentAsync(array $args = [])
* @method \Aws\Result deleteEnvironmentConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteEnvironmentConnectorAsync(array $args = [])
* @method \Aws\Result deleteEnvironmentHost(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteEnvironmentHostAsync(array $args = [])
* @method \Aws\Result disassociateEipFromVlan(array $args = [])
* @method \GuzzleHttp\Promise\Promise disassociateEipFromVlanAsync(array $args = [])
* @method \Aws\Result getDepotUrl(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDepotUrlAsync(array $args = [])
* @method \Aws\Result getEnvironment(array $args = [])
* @method \GuzzleHttp\Promise\Promise getEnvironmentAsync(array $args = [])
* @method \Aws\Result getVersions(array $args = [])
* @method \GuzzleHttp\Promise\Promise getVersionsAsync(array $args = [])
* @method \Aws\Result listEnvironmentConnectors(array $args = [])
* @method \GuzzleHttp\Promise\Promise listEnvironmentConnectorsAsync(array $args = [])
* @method \Aws\Result listEnvironmentHosts(array $args = [])
* @method \GuzzleHttp\Promise\Promise listEnvironmentHostsAsync(array $args = [])
* @method \Aws\Result listEnvironmentVlans(array $args = [])
@ -29,9 +41,13 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise listEnvironmentsAsync(array $args = [])
* @method \Aws\Result listTagsForResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise listTagsForResourceAsync(array $args = [])
* @method \Aws\Result listVmEntitlements(array $args = [])
* @method \GuzzleHttp\Promise\Promise listVmEntitlementsAsync(array $args = [])
* @method \Aws\Result tagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateEnvironmentConnector(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateEnvironmentConnectorAsync(array $args = [])
*/
class EvsClient extends AwsClient {}

View file

@ -88,6 +88,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise describeContainerFleetAsync(array $args = [])
* @method \Aws\Result describeContainerGroupDefinition(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeContainerGroupDefinitionAsync(array $args = [])
* @method \Aws\Result describeContainerGroupPortMappings(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeContainerGroupPortMappingsAsync(array $args = [])
* @method \Aws\Result describeEC2InstanceLimits(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeEC2InstanceLimitsAsync(array $args = [])
* @method \Aws\Result describeFleetAttributes(array $args = [])
@ -150,6 +152,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getGameSessionLogUrlAsync(array $args = [])
* @method \Aws\Result getInstanceAccess(array $args = [])
* @method \GuzzleHttp\Promise\Promise getInstanceAccessAsync(array $args = [])
* @method \Aws\Result getPlayerConnectionDetails(array $args = [])
* @method \GuzzleHttp\Promise\Promise getPlayerConnectionDetailsAsync(array $args = [])
* @method \Aws\Result listAliases(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAliasesAsync(array $args = [])
* @method \Aws\Result listBuilds(array $args = [])

View file

@ -121,8 +121,8 @@ class GlacierClient extends AwsClient
*/
private function getChecksumsMiddleware()
{
return function (callable $handler) {
return function (
return static function (callable $handler) {
return static function (
CommandInterface $command,
?RequestInterface $request = null
) use ($handler) {
@ -192,14 +192,15 @@ class GlacierClient extends AwsClient
*/
private function getApiVersionMiddleware()
{
return function (callable $handler) {
return function (
$apiVersion = $this->getApi()->getMetadata('apiVersion');
return static function (callable $handler) use ($apiVersion) {
return static function (
CommandInterface $command,
?RequestInterface $request = null
) use ($handler) {
) use ($handler, $apiVersion) {
return $handler($command, $request->withHeader(
'x-amz-glacier-version',
$this->getApi()->getMetadata('apiVersion')
$apiVersion
));
};
};

View file

@ -123,6 +123,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteColumnStatisticsTaskSettingsAsync(array $args = [])
* @method \Aws\Result deleteConnection(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteConnectionAsync(array $args = [])
* @method \Aws\Result deleteConnectionType(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteConnectionTypeAsync(array $args = [])
* @method \Aws\Result deleteCrawler(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteCrawlerAsync(array $args = [])
* @method \Aws\Result deleteCustomEntityType(array $args = [])
@ -221,6 +223,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getCrawlersAsync(array $args = [])
* @method \Aws\Result getCustomEntityType(array $args = [])
* @method \GuzzleHttp\Promise\Promise getCustomEntityTypeAsync(array $args = [])
* @method \Aws\Result getDashboardUrl(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDashboardUrlAsync(array $args = [])
* @method \Aws\Result getDataCatalogEncryptionSettings(array $args = [])
* @method \GuzzleHttp\Promise\Promise getDataCatalogEncryptionSettingsAsync(array $args = [])
* @method \Aws\Result getDataQualityModel(array $args = [])
@ -303,6 +307,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getSecurityConfigurationsAsync(array $args = [])
* @method \Aws\Result getSession(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSessionAsync(array $args = [])
* @method \Aws\Result getSessionEndpoint(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSessionEndpointAsync(array $args = [])
* @method \Aws\Result getStatement(array $args = [])
* @method \GuzzleHttp\Promise\Promise getStatementAsync(array $args = [])
* @method \Aws\Result getTable(array $args = [])
@ -411,6 +417,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise putWorkflowRunPropertiesAsync(array $args = [])
* @method \Aws\Result querySchemaVersionMetadata(array $args = [])
* @method \GuzzleHttp\Promise\Promise querySchemaVersionMetadataAsync(array $args = [])
* @method \Aws\Result registerConnectionType(array $args = [])
* @method \GuzzleHttp\Promise\Promise registerConnectionTypeAsync(array $args = [])
* @method \Aws\Result registerSchemaVersion(array $args = [])
* @method \GuzzleHttp\Promise\Promise registerSchemaVersionAsync(array $args = [])
* @method \Aws\Result removeSchemaVersionMetadata(array $args = [])

View file

@ -27,6 +27,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteMissionProfileAsync(array $args = [])
* @method \Aws\Result describeContact(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeContactAsync(array $args = [])
* @method \Aws\Result describeContactVersion(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeContactVersionAsync(array $args = [])
* @method \Aws\Result describeEphemeris(array $args = [])
* @method \GuzzleHttp\Promise\Promise describeEphemerisAsync(array $args = [])
* @method \Aws\Result getAgentConfiguration(array $args = [])
@ -43,14 +45,20 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getMissionProfileAsync(array $args = [])
* @method \Aws\Result getSatellite(array $args = [])
* @method \GuzzleHttp\Promise\Promise getSatelliteAsync(array $args = [])
* @method \Aws\Result listAntennas(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAntennasAsync(array $args = [])
* @method \Aws\Result listConfigs(array $args = [])
* @method \GuzzleHttp\Promise\Promise listConfigsAsync(array $args = [])
* @method \Aws\Result listContactVersions(array $args = [])
* @method \GuzzleHttp\Promise\Promise listContactVersionsAsync(array $args = [])
* @method \Aws\Result listContacts(array $args = [])
* @method \GuzzleHttp\Promise\Promise listContactsAsync(array $args = [])
* @method \Aws\Result listDataflowEndpointGroups(array $args = [])
* @method \GuzzleHttp\Promise\Promise listDataflowEndpointGroupsAsync(array $args = [])
* @method \Aws\Result listEphemerides(array $args = [])
* @method \GuzzleHttp\Promise\Promise listEphemeridesAsync(array $args = [])
* @method \Aws\Result listGroundStationReservations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listGroundStationReservationsAsync(array $args = [])
* @method \Aws\Result listGroundStations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listGroundStationsAsync(array $args = [])
* @method \Aws\Result listMissionProfiles(array $args = [])
@ -71,6 +79,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise updateAgentStatusAsync(array $args = [])
* @method \Aws\Result updateConfig(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateConfigAsync(array $args = [])
* @method \Aws\Result updateContact(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateContactAsync(array $args = [])
* @method \Aws\Result updateEphemeris(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateEphemerisAsync(array $args = [])
* @method \Aws\Result updateMissionProfile(array $args = [])

View file

@ -11,6 +11,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise batchGetStreamKeyAsync(array $args = [])
* @method \Aws\Result batchStartViewerSessionRevocation(array $args = [])
* @method \GuzzleHttp\Promise\Promise batchStartViewerSessionRevocationAsync(array $args = [])
* @method \Aws\Result createAdConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise createAdConfigurationAsync(array $args = [])
* @method \Aws\Result createChannel(array $args = [])
* @method \GuzzleHttp\Promise\Promise createChannelAsync(array $args = [])
* @method \Aws\Result createPlaybackRestrictionPolicy(array $args = [])
@ -19,6 +21,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise createRecordingConfigurationAsync(array $args = [])
* @method \Aws\Result createStreamKey(array $args = [])
* @method \GuzzleHttp\Promise\Promise createStreamKeyAsync(array $args = [])
* @method \Aws\Result deleteAdConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteAdConfigurationAsync(array $args = [])
* @method \Aws\Result deleteChannel(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteChannelAsync(array $args = [])
* @method \Aws\Result deletePlaybackKeyPair(array $args = [])
@ -29,6 +33,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise deleteRecordingConfigurationAsync(array $args = [])
* @method \Aws\Result deleteStreamKey(array $args = [])
* @method \GuzzleHttp\Promise\Promise deleteStreamKeyAsync(array $args = [])
* @method \Aws\Result getAdConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise getAdConfigurationAsync(array $args = [])
* @method \Aws\Result getChannel(array $args = [])
* @method \GuzzleHttp\Promise\Promise getChannelAsync(array $args = [])
* @method \Aws\Result getPlaybackKeyPair(array $args = [])
@ -45,6 +51,10 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise getStreamSessionAsync(array $args = [])
* @method \Aws\Result importPlaybackKeyPair(array $args = [])
* @method \GuzzleHttp\Promise\Promise importPlaybackKeyPairAsync(array $args = [])
* @method \Aws\Result insertAdBreak(array $args = [])
* @method \GuzzleHttp\Promise\Promise insertAdBreakAsync(array $args = [])
* @method \Aws\Result listAdConfigurations(array $args = [])
* @method \GuzzleHttp\Promise\Promise listAdConfigurationsAsync(array $args = [])
* @method \Aws\Result listChannels(array $args = [])
* @method \GuzzleHttp\Promise\Promise listChannelsAsync(array $args = [])
* @method \Aws\Result listPlaybackKeyPairs(array $args = [])
@ -71,6 +81,8 @@ use Aws\AwsClient;
* @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = [])
* @method \Aws\Result untagResource(array $args = [])
* @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = [])
* @method \Aws\Result updateAdConfiguration(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateAdConfigurationAsync(array $args = [])
* @method \Aws\Result updateChannel(array $args = [])
* @method \GuzzleHttp\Promise\Promise updateChannelAsync(array $args = [])
* @method \Aws\Result updatePlaybackRestrictionPolicy(array $args = [])

Some files were not shown because too many files have changed in this diff Show more