| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 | <?phpnamespace Aws;use Aws\Api\ListShape;use Aws\Api\MapShape;use Aws\Api\Service;use Aws\Api\Shape;use Aws\Api\StructureShape;use Closure;/** * Inspects command input values and casts them to their modeled type. * This covers query compatible services which have migrated from query * to JSON wire protocols. * * @internal */class QueryCompatibleInputMiddleware{    /** @var callable */    private $nextHandler;    /** @var Service */    private $service;    /** @var CommandInterface */    private $command;    /**     * Create a middleware wrapper function.     *     * @param Service $service     * @return Closure     */    public static function wrap(Service $service) : Closure    {        return static function (callable $handler) use ($service) {            return new self($handler, $service);        };    }    public function __construct(callable $nextHandler, Service $service)    {        $this->service = $service;        $this->nextHandler = $nextHandler;    }    public function __invoke(CommandInterface $cmd)    {        $this->command = $cmd;        $nextHandler = $this->nextHandler;        $op = $this->service->getOperation($cmd->getName());        $inputMembers = $op->getInput()->getMembers();        $input = $cmd->toArray();        foreach ($input as $param => $value) {            if (isset($inputMembers[$param])) {                $shape = $inputMembers[$param];                $this->processInput($value, $shape, [$param]);            }        }        return $nextHandler($this->command);    }    /**     * Recurses a given input shape. if a given scalar input does not match its     * modeled type, it is cast to its modeled type.     *     * @param $input     * @param $shape     * @param array $path     *     * @return void     */    private function processInput($input, $shape, array $path) : void    {        switch ($shape->getType()) {            case 'structure':                $this->processStructure($input, $shape, $path);                break;            case 'list':                $this->processList($input, $shape, $path);                break;            case 'map':                $this->processMap($input, $shape, $path);                break;            default:                $this->processScalar($input, $shape, $path);        }    }    /**     * @param array $input     * @param StructureShape $shape     * @param array $path     *     * @return void     */    private function processStructure(        array $input,        StructureShape $shape,        array $path    ) : void    {        foreach ($input as $param => $value) {            if ($shape->hasMember($param)) {                $memberPath = array_merge($path, [$param]);                $this->processInput($value, $shape->getMember($param), $memberPath);            }        }    }    /**     * @param array $input     * @param ListShape $shape     * @param array $path     *     * @return void     */    private function processList(        array $input,        ListShape $shape,        array $path    ) : void    {        foreach ($input as $param => $value) {            $memberPath = array_merge($path, [$param]);            $this->processInput($value, $shape->getMember(), $memberPath);        }    }    /**     * @param array $input     * @param MapShape $shape     * @param array $path     *     * @return void     */    private function processMap(array $input, MapShape $shape, array $path) : void    {        foreach ($input as $param => $value) {            $memberPath = array_merge($path, [$param]);            $this->processInput($value, $shape->getValue(), $memberPath);        }    }    /**     * @param $input     * @param Shape $shape     * @param array $path     *     * @return void     */    private function processScalar($input, Shape $shape, array $path) : void    {        $expectedType = $shape->getType();        if (!$this->isModeledType($input, $expectedType)) {            trigger_error(                "The provided type for `". implode(' -> ', $path) ."` value was `"                . (gettype($input) ===  'double' ? 'float' : gettype($input)) . "`."                . " The modeled type is `{$expectedType}`.",                E_USER_WARNING            );            $value = $this->castValue($input, $expectedType);            $this->changeValueAtPath($path, $value);        }    }    /**     * Modifies command in place     *     * @param array $path     * @param $newValue     *     * @return void     */    private function changeValueAtPath(array $path, $newValue) : void    {        $commandRef = &$this->command;        foreach ($path as $segment) {            if (!isset($commandRef[$segment])) {                return;            }            $commandRef = &$commandRef[$segment];        }        $commandRef = $newValue;    }    /**     * @param $value     * @param $type     *     * @return bool     */    private function isModeledType($value, $type) : bool    {        switch ($type) {            case 'string':                return is_string($value);            case 'integer':            case 'long':                return is_int($value);            case 'float':                return is_float($value);            default:                return true;        }    }    /**     * @param $value     * @param $type     *     * @return float|int|mixed|string     */    private function castValue($value, $type)    {        switch ($type) {            case 'integer':                return (int) $value;            case 'long' :                return $value + 0;            case 'float':                return (float) $value;            case 'string':                return (string) $value;            default:                return $value;        }    }}
 |