<?php
namespace Aws\DynamoDb;

use Aws\Api\Parser\Crc32ValidatingParser;
use Aws\AwsClient;
use Aws\ClientResolver;
use Aws\Exception\AwsException;
use Aws\HandlerList;
use Aws\Middleware;
use Aws\RetryMiddleware;
use Aws\RetryMiddlewareV2;

/**
 * This client is used to interact with the **Amazon DynamoDB** service.
 *
 * @method \Aws\Result batchGetItem(array $args = [])
 * @method \GuzzleHttp\Promise\Promise batchGetItemAsync(array $args = [])
 * @method \Aws\Result batchWriteItem(array $args = [])
 * @method \GuzzleHttp\Promise\Promise batchWriteItemAsync(array $args = [])
 * @method \Aws\Result createTable(array $args = [])
 * @method \GuzzleHttp\Promise\Promise createTableAsync(array $args = [])
 * @method \Aws\Result deleteItem(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteItemAsync(array $args = [])
 * @method \Aws\Result deleteTable(array $args = [])
 * @method \GuzzleHttp\Promise\Promise deleteTableAsync(array $args = [])
 * @method \Aws\Result describeTable(array $args = [])
 * @method \GuzzleHttp\Promise\Promise describeTableAsync(array $args = [])
 * @method \Aws\Result getItem(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getItemAsync(array $args = [])
 * @method \Aws\Result listTables(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listTablesAsync(array $args = [])
 * @method \Aws\Result putItem(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putItemAsync(array $args = [])
 * @method \Aws\Result query(array $args = [])
 * @method \GuzzleHttp\Promise\Promise queryAsync(array $args = [])
 * @method \Aws\Result scan(array $args = [])
 * @method \GuzzleHttp\Promise\Promise scanAsync(array $args = [])
 * @method \Aws\Result updateItem(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateItemAsync(array $args = [])
 * @method \Aws\Result updateTable(array $args = [])
 * @method \GuzzleHttp\Promise\Promise updateTableAsync(array $args = [])
 * @method \Aws\Result batchExecuteStatement(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise batchExecuteStatementAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result createBackup(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise createBackupAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result createGlobalTable(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise createGlobalTableAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result deleteBackup(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise deleteBackupAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeBackup(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeBackupAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeContinuousBackups(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeContinuousBackupsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeContributorInsights(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeContributorInsightsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeEndpoints(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeEndpointsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeExport(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeExportAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeGlobalTable(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeGlobalTableAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeGlobalTableSettings(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeGlobalTableSettingsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeImport(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeImportAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeKinesisStreamingDestination(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeKinesisStreamingDestinationAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeLimits(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeLimitsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeTableReplicaAutoScaling(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeTableReplicaAutoScalingAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result describeTimeToLive(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise describeTimeToLiveAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result disableKinesisStreamingDestination(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise disableKinesisStreamingDestinationAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result enableKinesisStreamingDestination(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise enableKinesisStreamingDestinationAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result executeStatement(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise executeStatementAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result executeTransaction(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise executeTransactionAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result exportTableToPointInTime(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise exportTableToPointInTimeAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result importTable(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise importTableAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result listBackups(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise listBackupsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result listContributorInsights(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise listContributorInsightsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result listExports(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise listExportsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result listGlobalTables(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise listGlobalTablesAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result listImports(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise listImportsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result listTagsOfResource(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise listTagsOfResourceAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result restoreTableFromBackup(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise restoreTableFromBackupAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result restoreTableToPointInTime(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise restoreTableToPointInTimeAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result tagResource(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise tagResourceAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result transactGetItems(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise transactGetItemsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result transactWriteItems(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise transactWriteItemsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result untagResource(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise untagResourceAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result updateContinuousBackups(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise updateContinuousBackupsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result updateContributorInsights(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise updateContributorInsightsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result updateGlobalTable(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise updateGlobalTableAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result updateGlobalTableSettings(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise updateGlobalTableSettingsAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result updateTableReplicaAutoScaling(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise updateTableReplicaAutoScalingAsync(array $args = []) (supported in versions 2012-08-10)
 * @method \Aws\Result updateTimeToLive(array $args = []) (supported in versions 2012-08-10)
 * @method \GuzzleHttp\Promise\Promise updateTimeToLiveAsync(array $args = []) (supported in versions 2012-08-10)
 */
class DynamoDbClient extends AwsClient
{
    public static function getArguments()
    {
        $args = parent::getArguments();
        $args['retries']['default'] = 10;
        $args['retries']['fn'] = [__CLASS__, '_applyRetryConfig'];
        $args['api_provider']['fn'] = [__CLASS__, '_applyApiProvider'];

        return $args;
    }

    /**
     * Convenience method for instantiating and registering the DynamoDB
     * Session handler with this DynamoDB client object.
     *
     * @param array $config Array of options for the session handler factory
     *
     * @return SessionHandler
     */
    public function registerSessionHandler(array $config = [])
    {
        $handler = SessionHandler::fromClient($this, $config);
        $handler->register();

        return $handler;
    }

    /** @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'
                );
            }
        }
    }

    /** @internal */
    public static function _applyApiProvider($value, array &$args, HandlerList $list)
    {
        ClientResolver::_apply_api_provider($value, $args);
        $args['parser'] = new Crc32ValidatingParser($args['parser']);
    }
}


<?php
namespace Aws\DynamoDb;

use Aws\DynamoDb\Exception\DynamoDbException;

/**
 * The locking connection adds locking logic to the read operation.
 */
class LockingSessionConnection extends StandardSessionConnection
{
    public function __construct(DynamoDbClient $client, array $config = [])
    {
        parent::__construct($client, $config);
    }

    /**
     * {@inheritdoc}
     * Retries the request until the lock can be acquired
     */
    public function read($id)
    {
        // Create the params for the UpdateItem operation so that a lock can be
        // set and item returned (via ReturnValues) in a one, atomic operation.
        $params = [
            'TableName'        => $this->getTableName(),
            'Key'              => $this->formatKey($id),
            'Expected'         => ['lock' => ['Exists' => false]],
            'AttributeUpdates' => ['lock' => ['Value' => ['N' => '1']]],
            'ReturnValues'     => 'ALL_NEW',
        ];

        // Acquire the lock and fetch the item data.
        $timeout  = time() + $this->getMaxLockWaitTime();
        while (true) {
            try {
                $item = [];
                $result = $this->client->updateItem($params);
                if (isset($result['Attributes'])) {
                    foreach ($result['Attributes'] as $key => $value) {
                        $item[$key] = current($value);
                    }
                }
                return $item;
            } catch (DynamoDbException $e) {
                if ($e->getAwsErrorCode() === 'ConditionalCheckFailedException'
                    && time() < $timeout
                ) {
                    usleep(rand(
                        $this->getMinLockRetryMicrotime(),
                        $this->getMaxLockRetryMicrotime()
                    ));
                } else {
                    break;
                }
            }
        }
    }
}


<?php
namespace Aws\DynamoDb;

use Psr\Http\Message\StreamInterface;

/**
 * Marshals and unmarshals JSON documents and PHP arrays into DynamoDB items.
 */
class Marshaler
{
    /** @var array Default options to merge into provided options. */
    private static $defaultOptions = [
        'ignore_invalid'  => false,
        'nullify_invalid' => false,
        'wrap_numbers'    => false,
    ];

    /** @var array Marshaler options. */
    private $options;

    /**
     * Instantiates a DynamoDB Marshaler.
     *
     * The following options are valid.
     *
     * - ignore_invalid: (bool) Set to `true` if invalid values should be
     *   ignored (i.e., not included) during marshaling.
     * - nullify_invalid: (bool) Set to `true` if invalid values should be set
     *   to null.
     * - wrap_numbers: (bool) Set to `true` to wrap numbers with `NumberValue`
     *   objects during unmarshaling to preserve the precision.
     *
     * @param array $options Marshaler options
     */
    public function __construct(array $options = [])
    {
        $this->options = $options + self::$defaultOptions;
    }

    /**
     * Creates a special object to represent a DynamoDB binary (B) value.
     *
     * This helps disambiguate binary values from string (S) values.
     *
     * @param mixed $value A binary value compatible with Guzzle streams.
     *
     * @return BinaryValue
     * @see GuzzleHttp\Stream\Stream::factory
     */
    public function binary($value)
    {
        return new BinaryValue($value);
    }

    /**
     * Creates a special object to represent a DynamoDB number (N) value.
     *
     * This helps maintain the precision of large integer/float in PHP.
     *
     * @param string|int|float $value A number value.
     *
     * @return NumberValue
     */
    public function number($value)
    {
        return new NumberValue($value);
    }

    /**
     * Creates a special object to represent a DynamoDB set (SS/NS/BS) value.
     *
     * This helps disambiguate set values from list (L) values.
     *
     * @param array $values The values of the set.
     *
     * @return SetValue
     *
     */
    public function set(array $values)
    {
        return new SetValue($values);
    }

    /**
     * Marshal a JSON document from a string to a DynamoDB item.
     *
     * The result is an array formatted in the proper parameter structure
     * required by the DynamoDB API for items.
     *
     * @param string $json A valid JSON document.
     *
     * @return array Item formatted for DynamoDB.
     * @throws \InvalidArgumentException if the JSON is invalid.
     */
    public function marshalJson($json)
    {
        $data = json_decode($json);
        if (!($data instanceof \stdClass)) {
            throw new \InvalidArgumentException(
                'The JSON document must be valid and be an object at its root.'
            );
        }

        return current($this->marshalValue($data));
    }

    /**
     * Marshal a native PHP array of data to a DynamoDB item.
     *
     * The result is an array formatted in the proper parameter structure
     * required by the DynamoDB API for items.
     *
     * @param array|\stdClass $item An associative array of data.
     *
     * @return array Item formatted for DynamoDB.
     */
    public function marshalItem($item)
    {
        return current($this->marshalValue($item));
    }

    /**
     * Marshal a native PHP value into a DynamoDB attribute value.
     *
     * The result is an associative array that is formatted in the proper
     * `[TYPE => VALUE]` parameter structure required by the DynamoDB API.
     *
     * @param mixed $value A scalar, array, or `stdClass` value.
     *
     * @return array Attribute formatted for DynamoDB.
     * @throws \UnexpectedValueException if the value cannot be marshaled.
     */
    public function marshalValue($value)
    {
        $type = gettype($value);

        // Handle string values.
        if ($type === 'string') {
            return ['S' => $value];
        }

        // Handle number values.
        if ($type === 'integer'
            || $type === 'double'
            || $value instanceof NumberValue
        ) {
            return ['N' => (string) $value];
        }

        // Handle boolean values.
        if ($type === 'boolean') {
            return ['BOOL' => $value];
        }

        // Handle null values.
        if ($type === 'NULL') {
            return ['NULL' => true];
        }

        // Handle set values.
        if ($value instanceof SetValue) {
            if (count($value) === 0) {
                return $this->handleInvalid('empty sets are invalid');
            }
            $previousType = null;
            $data = [];
            foreach ($value as $v) {
                $marshaled = $this->marshalValue($v);
                $setType = key($marshaled);
                if (!$previousType) {
                    $previousType = $setType;
                } elseif ($setType !== $previousType) {
                    return $this->handleInvalid('sets must be uniform in type');
                }
                $data[] = current($marshaled);
            }

            return [$previousType . 'S' => array_values(array_unique($data))];
        }

        // Handle list and map values.
        $dbType = 'L';
        if ($value instanceof \stdClass) {
            $type = 'array';
            $dbType = 'M';
        }
        if ($type === 'array' || $value instanceof \Traversable) {
            $data = [];
            $index = 0;
            foreach ($value as $k => $v) {
                if ($v = $this->marshalValue($v)) {
                    $data[$k] = $v;
                    if ($dbType === 'L' && (!is_int($k) || $k != $index++)) {
                        $dbType = 'M';
                    }
                }
            }
            return [$dbType => $data];
        }

        // Handle binary values.
        if (is_resource($value) || $value instanceof StreamInterface) {
            $value = $this->binary($value);
        }
        if ($value instanceof BinaryValue) {
            return ['B' => (string) $value];
        }

        // Handle invalid values.
        return $this->handleInvalid('encountered unexpected value');
    }

    /**
     * Unmarshal a document (item) from a DynamoDB operation result into a JSON
     * document string.
     *
     * @param array $data            Item/document from a DynamoDB result.
     * @param int   $jsonEncodeFlags Flags to use with `json_encode()`.
     *
     * @return string
     */
    public function unmarshalJson(array $data, $jsonEncodeFlags = 0)
    {
        return json_encode(
            $this->unmarshalValue(['M' => $data], true),
            $jsonEncodeFlags
        );
    }

    /**
     * Unmarshal an item from a DynamoDB operation result into a native PHP
     * array. If you set $mapAsObject to true, then a stdClass value will be
     * returned instead.
     *
     * @param array $data Item from a DynamoDB result.
     * @param bool  $mapAsObject Whether maps should be represented as stdClass.
     *
     * @return array|\stdClass
     */
    public function unmarshalItem(array $data, $mapAsObject = false)
    {
        return $this->unmarshalValue(['M' => $data], $mapAsObject);
    }

    /**
     * Unmarshal a value from a DynamoDB operation result into a native PHP
     * value. Will return a scalar, array, or (if you set $mapAsObject to true)
     * stdClass value.
     *
     * @param array $value       Value from a DynamoDB result.
     * @param bool  $mapAsObject Whether maps should be represented as stdClass.
     *
     * @return mixed
     * @throws \UnexpectedValueException
     */
    public function unmarshalValue(array $value, $mapAsObject = false)
    {
        $type = key($value);
        $value = $value[$type];
        switch ($type) {
            case 'S':
            case 'BOOL':
                return $value;
            case 'NULL':
                return null;
            case 'N':
                if ($this->options['wrap_numbers']) {
                    return new NumberValue($value);
                }

                // Use type coercion to unmarshal numbers to int/float.
                return $value + 0;
            case 'M':
                if ($mapAsObject) {
                    $data = new \stdClass;
                    foreach ($value as $k => $v) {
                        $data->$k = $this->unmarshalValue($v, $mapAsObject);
                    }
                    return $data;
                }
                // NOBREAK: Unmarshal M the same way as L, for arrays.
            case 'L':
                foreach ($value as $k => $v) {
                    $value[$k] = $this->unmarshalValue($v, $mapAsObject);
                }
                return $value;
            case 'B':
                return new BinaryValue($value);
            case 'SS':
            case 'NS':
            case 'BS':
                foreach ($value as $k => $v) {
                    $value[$k] = $this->unmarshalValue([$type[0] => $v]);
                }
                return new SetValue($value);
        }

        throw new \UnexpectedValueException("Unexpected type: {$type}.");
    }

    /**
     * Handle invalid value based on marshaler configuration.
     *
     * @param string $message Error message
     *
     * @return array|null
     */
    private function handleInvalid($message)
    {
        if ($this->options['ignore_invalid']) {
            return null;
        }

        if ($this->options['nullify_invalid']) {
            return ['NULL' => true];
        }

        throw new \UnexpectedValueException("Marshaling error: {$message}.");
    }
}


<?php
namespace Aws\DynamoDb;

/**
 * Special object to represent a DynamoDB Number (N) value.
 */
class NumberValue implements \JsonSerializable
{
    /** @var string Number value. */
    private $value;

    /**
     * @param string|int|float $value A number value.
     */
    public function __construct($value)
    {
        $this->value = (string) $value;
    }

    #[\ReturnTypeWillChange]
    public function jsonSerialize()
    {
        return $this->value;
    }

    public function __toString()
    {
        return $this->value;
    }
}


<?php
namespace Aws\DynamoDb;

trait SessionConnectionConfigTrait
{
    /** @var string Name of table to store the sessions */
    protected $tableName = 'sessions';

    /** @var string Name of hash key in table. Default: "id" */
    protected $hashKey = 'id';

    /** @var string Name of the data attribute in table. Default: "data" */
    protected $dataAttribute = 'data';

    /** @var string Type of the data attribute in table. Default: "string" */
    protected $dataAttributeType = 'string';

    /** @var integer Lifetime of inactive sessions expiration */
    protected $sessionLifetime;

    /** @var string Name of the session life time attribute in table. Default: "expires" */
    protected $sessionLifetimeAttribute = 'expires';

    /** @var string Whether or not to use consistent reads */
    protected $consistentRead = true;

    /** @var string Batch options used for garbage collection */
    protected $batchConfig = [];

    /** @var boolean Whether or not to use session locking */
    protected $locking = false;

    /** @var integer Max time (s) to wait for lock acquisition */
    protected $maxLockWaitTime = 10;

    /** @var integer Min time (Âµs) to wait between lock attempts */
    protected $minLockRetryMicrotime = 10000;

    /** @var integer Max time (Âµs) to wait between lock attempts */
    protected $maxLockRetryMicrotime = 50000;

    /**
     * It initialize the Config class and
     * it sets values in case of valid configurations.
     *
     * It transforms parameters underscore separated in camelcase "this_is_a_test" => ThisIsATest
     * and it uses it in order to set the values.
     *
     * @param array $config
     */
    public function initConfig( array $config = [] )
    {
        if (!empty($config))
        {
            foreach ($config as $key => $value)
            {
                $method = 'set' . str_replace('_', '', ucwords($key, '_'));
                if(method_exists($this,$method))
                {
                    call_user_func_array(array($this, $method), array($value));
                }
            }
        }

        // It applies the default PHP session lifetime, if no session lifetime config is provided
        if(!isset($config['session_lifetime']))
        {
            $this->setSessionLifetime((int) ini_get('session.gc_maxlifetime'));
        }
    }

    /**
     * @return string
     */
    public function getTableName()
    {
        return $this->tableName;
    }

    /**
     * @param string $tableName
     */
    public function setTableName($tableName)
    {
        $this->tableName = $tableName;
    }

    /**
     * @return string
     */
    public function getHashKey()
    {
        return $this->hashKey;
    }

    /**
     * @param string $hashKey
     */
    public function setHashKey($hashKey)
    {
        $this->hashKey = $hashKey;
    }

    /**
     * @return string
     */
    public function getDataAttribute()
    {
        return $this->dataAttribute;
    }

    /**
     * @param string $dataAttribute
     */
    public function setDataAttribute($dataAttribute)
    {
        $this->dataAttribute = $dataAttribute;
    }

    /**
     * @return string
     */
    public function getDataAttributeType()
    {
        return $this->dataAttributeType;
    }

    /**
     * @param string $dataAttributeType
     */
    public function setDataAttributeType($dataAttributeType)
    {
        $this->dataAttributeType = $dataAttributeType;
    }

    /**
     * @return number
     */
    public function getSessionLifetime()
    {
        return $this->sessionLifetime;
    }

    /**
     * @param number $sessionLifetime
     */
    public function setSessionLifetime($sessionLifetime)
    {
        $this->sessionLifetime = $sessionLifetime;
    }

    /**
     * @return string
     */
    public function getSessionLifetimeAttribute()
    {
        return $this->sessionLifetimeAttribute;
    }

    /**
     * @param string $sessionLifetimeAttribute
     */
    public function setSessionLifetimeAttribute($sessionLifetimeAttribute)
    {
        $this->sessionLifetimeAttribute = $sessionLifetimeAttribute;
    }

    /**
     * @return boolean
     */
    public function isConsistentRead()
    {
        return $this->consistentRead;
    }

    /**
     * @param boolean $consistentRead
     */
    public function setConsistentRead($consistentRead)
    {
        $this->consistentRead = $consistentRead;
    }

    /**
     * @return mixed
     */
    public function getBatchConfig()
    {
        return $this->batchConfig;
    }

    /**
     * @param mixed $batchConfig
     */
    public function setBatchConfig($batchConfig)
    {
        $this->batchConfig = $batchConfig;
    }
    /**
     * @return boolean
     */
    public function isLocking()
    {
        return $this->locking;
    }

    /**
     * @param boolean $locking
     */
    public function setLocking($locking)
    {
        $this->locking = $locking;
    }

    /**
     * @return number
     */
    public function getMaxLockWaitTime()
    {
        return $this->maxLockWaitTime;
    }

    /**
     * @param number $maxLockWaitTime
     */
    public function setMaxLockWaitTime($maxLockWaitTime)
    {
        $this->maxLockWaitTime = $maxLockWaitTime;
    }

    /**
     * @return number
     */
    public function getMinLockRetryMicrotime()
    {
        return $this->minLockRetryMicrotime;
    }

    /**
     * @param number $minLockRetryMicrotime
     */
    public function setMinLockRetryMicrotime($minLockRetryMicrotime)
    {
        $this->minLockRetryMicrotime = $minLockRetryMicrotime;
    }

    /**
     * @return number
     */
    public function getMaxLockRetryMicrotime()
    {
        return $this->maxLockRetryMicrotime;
    }

    /**
     * @param number $maxLockRetryMicrotime
     */
    public function setMaxLockRetryMicrotime($maxLockRetryMicrotime)
    {
        $this->maxLockRetryMicrotime = $maxLockRetryMicrotime;
    }
}


<?php
namespace Aws\DynamoDb;

/**
 * The session connection provides the underlying logic for interacting with
 * Amazon DynamoDB and performs all of the reading and writing operations.
 */
interface SessionConnectionInterface
{
    /**
     * Reads session data from DynamoDB
     *
     * @param string $id Session ID
     *
     * @return array
     */
    public function read($id);

    /**
     * Writes session data to DynamoDB
     *
     * @param string $id        Session ID
     * @param string $data      Serialized session data
     * @param bool   $isChanged Whether or not the data has changed
     *
     * @return bool
     */
    public function write($id, $data, $isChanged);

    /**
     * Deletes session record from DynamoDB
     *
     * @param string $id Session ID
     *
     * @return bool
     */
    public function delete($id);

    /**
     * Performs garbage collection on the sessions stored in the DynamoDB
     *
     * @return bool
     */
    public function deleteExpired();
}


<?php
namespace Aws\DynamoDb;

/**
 * Provides an interface for using Amazon DynamoDB as a session store by hooking
 * into PHP's session handler hooks. Once registered, You may use the native
 * `$_SESSION` superglobal and session functions, and the sessions will be
 * stored automatically in DynamoDB. DynamoDB is a great session storage
 * solution due to its speed, scalability, and fault tolerance.
 *
 * For maximum performance, we recommend that you keep the size of your sessions
 * small. Locking is disabled by default, since it can drive up latencies and
 * costs under high traffic. Only turn it on if you need it.
 *
 * By far, the most expensive operation is garbage collection. Therefore, we
 * encourage you to carefully consider your session garbage collection strategy.
 * Note: the DynamoDB Session Handler does not allow garbage collection to be
 * triggered randomly. You must run garbage collection manually or through other
 * automated means using a cron job or similar scheduling technique.
 */
class SessionHandler implements \SessionHandlerInterface
{
    /** @var SessionConnectionInterface Session save logic.*/
    private $connection;

    /** @var string Session save path. */
    private $savePath;

    /** @var string Session name. */
    private $sessionName;

    /** @var string The last known session ID */
    private $openSessionId = '';

    /** @var string Stores serialized data for tracking changes. */
    private $dataRead = '';

    /** @var bool Keeps track of whether the session has been written. */
    private $sessionWritten = false;

    /**
     * Creates a new DynamoDB Session Handler.
     *
     * The configuration array accepts the following array keys and values:
     * - table_name:                    Name of table to store the sessions.
     * - hash_key:                      Name of hash key in table. Default: "id".
     * - data_attribute:                Name of the data attribute in table. Default: "data".
     * - session_lifetime:              Lifetime of inactive sessions expiration.
     * - session_lifetime_attribute:    Name of the session life time attribute in table. Default: "expires".
     * - consistent_read:               Whether or not to use consistent reads.
     * - batch_config:                  Batch options used for garbage collection.
     * - locking:                       Whether or not to use session locking.
     * - max_lock_wait_time:            Max time (s) to wait for lock acquisition.
     * - min_lock_retry_microtime:      Min time (Âµs) to wait between lock attempts.
     * - max_lock_retry_microtime:      Max time (Âµs) to wait between lock attempts.
     *
     * You can find the full list of parameters and defaults within the trait
     * Aws\DynamoDb\SessionConnectionConfigTrait
     *
     * @param DynamoDbClient $client Client for doing DynamoDB operations
     * @param array          $config Configuration for the Session Handler
     *
     * @return SessionHandler
     */
    public static function fromClient(DynamoDbClient $client, array $config = [])
    {
        $config += ['locking' => false];
        if ($config['locking']) {
            $connection = new LockingSessionConnection($client, $config);
        } else {
            $connection = new StandardSessionConnection($client, $config);
        }

        return new static($connection);
    }

    /**
     * @param SessionConnectionInterface $connection
     */
    public function __construct(SessionConnectionInterface $connection)
    {
        $this->connection = $connection;
    }

    /**
     * Register the DynamoDB session handler.
     *
     * @return bool Whether or not the handler was registered.
     * @codeCoverageIgnore
     */
    public function register()
    {
         return session_set_save_handler($this, true);
    }

    /**
     * Open a session for writing. Triggered by session_start().
     *
     * @param string $savePath    Session save path.
     * @param string $sessionName Session name.
     *
     * @return bool Whether or not the operation succeeded.
     */
     #[\ReturnTypeWillChange]
    public function open($savePath, $sessionName)
    {
        $this->savePath = $savePath;
        $this->sessionName = $sessionName;

        return true;
    }

    /**
     * Close a session from writing.
     *
     * @return bool Success
     */
     #[\ReturnTypeWillChange]
    public function close()
    {
        $id = session_id();
        // Make sure the session is unlocked and the expiration time is updated,
        // even if the write did not occur
        if ($this->openSessionId !== $id || !$this->sessionWritten) {
            $result = $this->connection->write($this->formatId($id), '', false);
            $this->sessionWritten = (bool) $result;
        }

        return $this->sessionWritten;
    }

    /**
     * Read a session stored in DynamoDB.
     *
     * @param string $id Session ID.
     *
     * @return string Session data.
     */
     #[\ReturnTypeWillChange]
    public function read($id)
    {
        $this->openSessionId = $id;
        // PHP expects an empty string to be returned from this method if no
        // data is retrieved
        $this->dataRead = '';

        // Get session data using the selected locking strategy
        $item = $this->connection->read($this->formatId($id));

        $dataAttribute = $this->connection->getDataAttribute();
        $sessionLifetimeAttribute = $this->connection->getSessionLifetimeAttribute();

        // Return the data if it is not expired. If it is expired, remove it
        if (isset($item[$sessionLifetimeAttribute]) && isset($item[$dataAttribute])) {
            $this->dataRead = $item[$dataAttribute];
            if ($item[$sessionLifetimeAttribute] <= time()) {
                $this->dataRead = '';
                $this->destroy($id);
            }
        }

        return $this->dataRead;
    }

    /**
     * Write a session to DynamoDB.
     *
     * @param string $id   Session ID.
     * @param string $data Serialized session data to write.
     *
     * @return bool Whether or not the operation succeeded.
     */
     #[\ReturnTypeWillChange]
    public function write($id, $data)
    {
        $changed = $id !== $this->openSessionId
            || $data !== $this->dataRead;
        $this->openSessionId = $id;

        // Write the session data using the selected locking strategy
        $this->sessionWritten = $this->connection
            ->write($this->formatId($id), $data, $changed);

        return $this->sessionWritten;
    }

    /**
     * Delete a session stored in DynamoDB.
     *
     * @param string $id Session ID.
     *
     * @return bool Whether or not the operation succeeded.
     */
     #[\ReturnTypeWillChange]
    public function destroy($id)
    {
        $this->openSessionId = $id;
        // Delete the session data using the selected locking strategy
        $this->sessionWritten
            = $this->connection->delete($this->formatId($id));

        return $this->sessionWritten;
    }

    /**
     * Satisfies the session handler interface, but does nothing. To do garbage
     * collection, you must manually call the garbageCollect() method.
     *
     * @param int $maxLifetime Ignored.
     *
     * @return bool Whether or not the operation succeeded.
     * @codeCoverageIgnore
     */
     #[\ReturnTypeWillChange]
    public function gc($maxLifetime)
    {
        // Garbage collection for a DynamoDB table must be triggered manually.
        return true;
    }

    /**
     * Triggers garbage collection on expired sessions.
     * @codeCoverageIgnore
     */
    public function garbageCollect()
    {
        $this->connection->deleteExpired();
    }

    /**
     * Prepend the session ID with the session name.
     *
     * @param string $id The session ID.
     *
     * @return string Prepared session ID.
     */
    private function formatId($id)
    {
        return trim($this->sessionName . '_' . $id, '_');
    }
}


<?php
namespace Aws\DynamoDb;

/**
 * Special object to represent a DynamoDB set (SS/NS/BS) value.
 */
class SetValue implements \JsonSerializable, \Countable, \IteratorAggregate
{
    /** @var array Values in the set as provided. */
    private $values;

    /**
     * @param array  $values Values in the set.
     */
    public function __construct(array $values)
    {
        $this->values = $values;
    }

    /**
     * Get the values formatted for PHP and JSON.
     *
     * @return array
     */
    public function toArray()
    {
        return $this->values;
    }

    /**
     * @return int
     */
    #[\ReturnTypeWillChange]
    public function count()
    {
        return count($this->values);
    }

    #[\ReturnTypeWillChange]
    public function getIterator()
    {
        return new \ArrayIterator($this->values);
    }

    #[\ReturnTypeWillChange]
    public function jsonSerialize()
    {
        return $this->toArray();
    }
}


<?php
namespace Aws\DynamoDb;

use Aws\DynamoDb\Exception\DynamoDbException;

/**
 * The standard connection performs the read and write operations to DynamoDB.
 */
class StandardSessionConnection implements SessionConnectionInterface
{
    use SessionConnectionConfigTrait;

    /** @var DynamoDbClient The DynamoDB client */
    protected $client;

    /**
     * @param DynamoDbClient    $client DynamoDB client
     * @param array             $config Session handler config
     */
    public function __construct(DynamoDbClient $client, array $config = [])
    {
        $this->client = $client;
        $this->initConfig($config);
    }

    public function read($id)
    {
        $item = [];
        try {
            // Execute a GetItem command to retrieve the item.
            $result = $this->client->getItem([
                 'TableName'      => $this->getTableName(),
                 'Key'            => $this->formatKey($id),
                 'ConsistentRead' => $this->isConsistentRead(),
             ]);

            // Get the item values
            $result = isset($result['Item']) ? $result['Item'] : [];
            foreach ($result as $key => $value) {
                $item[$key] = current($value);
            }
        } catch (DynamoDbException $e) {
            // Could not retrieve item, so return nothing.
        }

        return $item;
    }

    public function write($id, $data, $isChanged)
    {
        // Prepare the attributes
        $expires = time() + $this->getSessionLifetime();
        $attributes = [
            $this->getSessionLifetimeAttribute() => ['Value' => ['N' => (string) $expires]],
            'lock' => ['Action' => 'DELETE'],
        ];
        if ($isChanged) {
            if ($data != '') {
                $type = $this->getDataAttributeType();
                if ($type == 'binary') {
                    $attributes[$this->getDataAttribute()] = ['Value' => ['B' => $data]];
                } else {
                    $attributes[$this->getDataAttribute()] = ['Value' => ['S' => $data]];
                }

            } else {
                $attributes[$this->getDataAttribute()] = ['Action' => 'DELETE'];
            }
        }

        // Perform the UpdateItem command
        try {
            return (bool) $this->client->updateItem([
                'TableName'        => $this->getTableName(),
                'Key'              => $this->formatKey($id),
                'AttributeUpdates' => $attributes,
            ]);
        } catch (DynamoDbException $e) {
            return $this->triggerError("Error writing session $id: {$e->getMessage()}");
        }
    }

    public function delete($id)
    {
        try {
            return (bool) $this->client->deleteItem([
                'TableName' => $this->getTableName(),
                'Key'       => $this->formatKey($id),
            ]);
        } catch (DynamoDbException $e) {
            return $this->triggerError("Error deleting session $id: {$e->getMessage()}");
        }
    }

    public function deleteExpired()
    {
        // Create a Scan iterator for finding expired session items
        $scan = $this->client->getPaginator('Scan', [
            'TableName' => $this->getTableName(),
            'AttributesToGet' => [$this->getHashKey()],
            'ScanFilter' => [
                $this->getSessionLifetimeAttribute() => [
                    'ComparisonOperator' => 'LT',
                    'AttributeValueList' => [['N' => (string) time()]],
                ],
                'lock' => [
                    'ComparisonOperator' => 'NULL',
                ]
            ],
        ]);

        // Create a WriteRequestBatch for deleting the expired items
        $batch = new WriteRequestBatch($this->client, $this->getBatchConfig());

        // Perform Scan and BatchWriteItem (delete) operations as needed
        foreach ($scan->search('Items') as $item) {
            $batch->delete(
                [$this->getHashKey() => $item[$this->getHashKey()]],
                $this->getTableName()
            );
        }

        // Delete any remaining items that were not auto-flushed
        $batch->flush();
    }

    /**
     * @param string $key
     *
     * @return array
     */
    protected function formatKey($key)
    {
        return [$this->getHashKey() => ['S' => $key]];
    }

    /**
     * @param string $error
     *
     * @return bool
     */
    protected function triggerError($error)
    {
        trigger_error($error, E_USER_WARNING);

        return false;
    }
}


<?php
namespace Aws\DynamoDb;

use Aws\CommandInterface;
use Aws\CommandPool;
use Aws\Exception\AwsException;
use Aws\ResultInterface;

/**
 * The WriteRequestBatch is an object that is capable of efficiently sending
 * DynamoDB BatchWriteItem requests from queued up put and delete item requests.
 * requests. The batch attempts to send the requests with the fewest requests
 * to DynamoDB as possible and also re-queues any unprocessed items to ensure
 * that all items are sent.
 */
class WriteRequestBatch
{
    /** @var DynamoDbClient DynamoDB client used to perform write operations. */
    private $client;

    /** @var array Configuration options for the batch. */
    private $config;

    /** @var array Queue of pending put/delete requests in the batch. */
    private $queue;

    /**
     * Creates a WriteRequestBatch object that is capable of efficiently sending
     * DynamoDB BatchWriteItem requests from queued up Put and Delete requests.
     *
     * @param DynamoDbClient $client DynamoDB client used to send batches.
     * @param array          $config Batch configuration options.
     *     - table: (string) DynamoDB table used by the batch, this can be
     *       overridden for each individual put() or delete() call.
     *     - batch_size: (int) The size of each batch (default: 25). The batch
     *       size must be between 2 and 25. If you are sending batches of large
     *       items, you may consider lowering the batch size, otherwise, you
     *       should use 25.
     *     - pool_size: (int) This number dictates how many BatchWriteItem
     *       requests you would like to do in parallel. For example, if the
     *       "batch_size" is 25, and "pool_size" is 3, then you would send 3
     *       BatchWriteItem requests at a time, each with 25 items. Please keep
     *       your throughput in mind when choosing the "pool_size" option.
     *     - autoflush: (bool) This option allows the batch to automatically
     *       flush once there are enough items (i.e., "batch_size" * "pool_size")
     *       in the queue. This defaults to true, so you must set this to false
     *       to stop autoflush.
     *     - before: (callable) Executed before every BatchWriteItem operation.
     *       It should accept an \Aws\CommandInterface object as its argument.
     *     - error: Executed if an error was encountered executing a,
     *       BatchWriteItem operation, otherwise errors are ignored. It should
     *       accept an \Aws\Exception\AwsException as its argument.
     *
     * @throws \InvalidArgumentException if the batch size is not between 2 and 25.
     */
    public function __construct(DynamoDbClient $client, array $config = [])
    {
        // Apply defaults
        $config += [
            'table'      => null,
            'batch_size' => 25,
            'pool_size'  => 1,
            'autoflush'  => true,
            'before'     => null,
            'error'      => null
        ];

        // Ensure the batch size is valid
        if ($config['batch_size'] > 25 || $config['batch_size'] < 2) {
            throw new \InvalidArgumentException('"batch_size" must be between 2 and 25.');
        }

        // Ensure the callbacks are valid
        if ($config['before'] && !is_callable($config['before'])) {
            throw new \InvalidArgumentException('"before" must be callable.');
        }
        if ($config['error'] && !is_callable($config['error'])) {
            throw new \InvalidArgumentException('"error" must be callable.');
        }

        // If autoflush is enabled, set the threshold
        if ($config['autoflush']) {
            $config['threshold'] = $config['batch_size'] * $config['pool_size'];
        }

        $this->client = $client;
        $this->config = $config;
        $this->queue = [];
    }

    /**
     * Adds a put item request to the batch.
     *
     * @param array       $item  Data for an item to put. Format:
     *     [
     *         'attribute1' => ['type' => 'value'],
     *         'attribute2' => ['type' => 'value'],
     *         ...
     *     ]
     * @param string|null $table The name of the table. This must be specified
     *                           unless the "table" option was provided in the
     *                           config of the WriteRequestBatch.
     *
     * @return $this
     */
    public function put(array $item, $table = null)
    {
        $this->queue[] = [
            'table' => $this->determineTable($table),
            'data'  => ['PutRequest' => ['Item' => $item]],
        ];

        $this->autoFlush();

        return $this;
    }

    /**
     * Adds a delete item request to the batch.
     *
     * @param array       $key   Key of an item to delete. Format:
     *     [
     *         'key1' => ['type' => 'value'],
     *         ...
     *     ]
     * @param string|null $table The name of the table. This must be specified
     *                           unless the "table" option was provided in the
     *                           config of the WriteRequestBatch.
     *
     * @return $this
     */
    public function delete(array $key, $table = null)
    {
        $this->queue[] = [
            'table' => $this->determineTable($table),
            'data'  => ['DeleteRequest' => ['Key' => $key]],
        ];

        $this->autoFlush();

        return $this;
    }

    /**
     * Flushes the batch by combining all the queued put and delete requests
     * into BatchWriteItem commands and executing them. Unprocessed items are
     * automatically re-queued.
     *
     * @param bool $untilEmpty If true, flushing will continue until the queue
     *                         is completely empty. This will make sure that
     *                         unprocessed items are all eventually sent.
     *
     * @return $this
     */
    public function flush($untilEmpty = true)
    {
        // Send BatchWriteItem requests until the queue is empty
        $keepFlushing = true;
        while ($this->queue && $keepFlushing) {
            $commands = $this->prepareCommands();
            $pool = new CommandPool($this->client, $commands, [
                'before' => $this->config['before'],
                'concurrency' => $this->config['pool_size'],
                'fulfilled'   => function (ResultInterface $result) {
                    // Re-queue any unprocessed items
                    if ($result->hasKey('UnprocessedItems')) {
                        $this->retryUnprocessed($result['UnprocessedItems']);
                    }
                },
                'rejected' => function ($reason) {
                    if ($reason instanceof AwsException) {
                        $code = $reason->getAwsErrorCode();
                        if ($code === 'ProvisionedThroughputExceededException') {
                            $this->retryUnprocessed($reason->getCommand()['RequestItems']);
                        } elseif (is_callable($this->config['error'])) {
                            $this->config['error']($reason);
                        }
                    }
                }
            ]);
            $pool->promise()->wait();
            $keepFlushing = (bool) $untilEmpty;
        }

        return $this;
    }

    /**
     * Creates BatchWriteItem commands from the items in the queue.
     *
     * @return CommandInterface[]
     */
    private function prepareCommands()
    {
        // Chunk the queue into batches
        $batches = array_chunk($this->queue, $this->config['batch_size']);
        $this->queue = [];

        // Create BatchWriteItem commands for each batch
        $commands = [];
        foreach ($batches as $batch) {
            $requests = [];
            foreach ($batch as $item) {
                if (!isset($requests[$item['table']])) {
                    $requests[$item['table']] = [];
                }
                $requests[$item['table']][] = $item['data'];
            }
            $commands[] = $this->client->getCommand(
                'BatchWriteItem',
                ['RequestItems' => $requests]
            );
        }

        return $commands;
    }

    /**
     * Re-queues unprocessed results with the correct data.
     *
     * @param array $unprocessed Unprocessed items from a result.
     */
    private function retryUnprocessed(array $unprocessed)
    {
        foreach ($unprocessed as $table => $requests) {
            foreach ($requests as $request) {
                $this->queue[] = [
                    'table' => $table,
                    'data'  => $request,
                ];
            }
        }
    }

    /**
     * If autoflush is enabled and the threshold is met, flush the batch
     */
    private function autoFlush()
    {
        if ($this->config['autoflush']
            && count($this->queue) >= $this->config['threshold']
        ) {
            // Flush only once. Unprocessed items are handled in a later flush.
            $this->flush(false);
        }
    }

    /**
     * Determine the table name by looking at what was provided and what the
     * WriteRequestBatch was originally configured with.
     *
     * @param string|null $table The table name.
     *
     * @return string
     * @throws \RuntimeException if there was no table specified.
     */
    private function determineTable($table)
    {
        $table = $table ?: $this->config['table'];
        if (!$table) {
            throw new \RuntimeException('There was no table specified.');
        }

        return $table;
    }
}


<?php
namespace Aws\DynamoDb\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the Amazon DynamoDB service.
 */
class DynamoDbException extends AwsException {}


<?php
namespace Aws\DynamoDbStreams;

use Aws\AwsClient;
use Aws\DynamoDb\DynamoDbClient;

/**
 * This client is used to interact with the **Amazon DynamoDb Streams** service.
 *
 * @method \Aws\Result describeStream(array $args = [])
 * @method \GuzzleHttp\Promise\Promise describeStreamAsync(array $args = [])
 * @method \Aws\Result getRecords(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getRecordsAsync(array $args = [])
 * @method \Aws\Result getShardIterator(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getShardIteratorAsync(array $args = [])
 * @method \Aws\Result listStreams(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listStreamsAsync(array $args = [])
 */
class DynamoDbStreamsClient extends AwsClient
{
    public static function getArguments()
    {
        $args = parent::getArguments();
        $args['retries']['default'] = 11;
        $args['retries']['fn'] = [DynamoDbClient::class, '_applyRetryConfig'];

        return $args;
    }
}


<?php
namespace Aws\DynamoDbStreams\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the Amazon DynamoDB Streams service.
 */
class DynamoDbStreamsException extends AwsException {}


<?php
namespace Aws\EBS;

use Aws\AwsClient;

/**
 * This client is used to interact with the **Amazon Elastic Block Store** service.
 * @method \Aws\Result completeSnapshot(array $args = [])
 * @method \GuzzleHttp\Promise\Promise completeSnapshotAsync(array $args = [])
 * @method \Aws\Result getSnapshotBlock(array $args = [])
 * @method \GuzzleHttp\Promise\Promise getSnapshotBlockAsync(array $args = [])
 * @method \Aws\Result listChangedBlocks(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listChangedBlocksAsync(array $args = [])
 * @method \Aws\Result listSnapshotBlocks(array $args = [])
 * @method \GuzzleHttp\Promise\Promise listSnapshotBlocksAsync(array $args = [])
 * @method \Aws\Result putSnapshotBlock(array $args = [])
 * @method \GuzzleHttp\Promise\Promise putSnapshotBlockAsync(array $args = [])
 * @method \Aws\Result startSnapshot(array $args = [])
 * @method \GuzzleHttp\Promise\Promise startSnapshotAsync(array $args = [])
 */
class EBSClient extends AwsClient {}


<?php
namespace Aws\EBS\Exception;

use Aws\Exception\AwsException;

/**
 * Represents an error interacting with the **Amazon Elastic Block Store** service.
 */
class EBSException extends AwsException {}