commit 9cfd06866ea6dd44a66e718949a024c644cd4fc6 Author: frostealth Date: Fri Apr 8 15:50:37 2016 +0600 initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4a21708 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# editorconfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_new_line = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0d4cc5d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +.gitattributes export-ignore +.gitignore export-ignore +.editorconfig export-ignore +grumphp.yml export-ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ef3443 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +thumbs.db +*.class +*.pyc +*.pyo +*.swp +.project +.settings +.idea +composer.phar +composer.lock +vendor diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..40b1bcb --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Ivan Kudinov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2986533 --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# Yii2 AWS S3 + +An Amazon S3 component for Yii2. + +[![License](https://poser.pugx.org/frostealth/yii2-aws-s3/license)](https://github.com/frostealth/yii2-aws-s3/blob/2.x/LICENSE.md) +[![Latest Stable Version](https://poser.pugx.org/frostealth/yii2-aws-s3/v/stable)](https://packagist.org/packages/frostealth/yii2-aws-s3) +[![Total Downloads](https://poser.pugx.org/frostealth/yii2-aws-s3/downloads)](https://packagist.org/packages/frostealth/yii2-aws-s3) +[![Latest Unstable Version](https://poser.pugx.org/frostealth/yii2-aws-s3/v/unstable)](https://packagist.org/packages/frostealth/yii2-aws-s3) + +## Installation + +1. Run the [Composer](http://getcomposer.org/download/) command to install the latest stable version: + + ```bash + composer require frostealth/yii2-aws-s3 ~2.0@stable + ``` + +2. Add the component to `config/main.php` + + ```php + 'components' => [ + // ... + 's3' => [ + 'class' => 'frostealth\yii2\aws\s3\Service', + 'credentials' => [ // Aws\Credentials\CredentialsInterface|array|callable + 'key' => 'my-key', + 'secret' => 'my-secret', + ], + 'region' => 'my-region', + 'defaultBucket' => 'my-bucket', + 'defaultAcl' => 'public-read', + ], + // ... + ], + ``` + +## Basic usage + +### Usage the command factory and additional params + +```php +/** @var \frostealth\yii2\aws\s3\Service $s3 */ +$s3 = Yii::$app->get('s3'); + +/** @var \Aws\ResultInterface $result */ +$result = $s3->commands()->get('filename.ext')->saveAs('/path/to/local/file.ext')->execute(); + +$result = $s3->commands()->put('filename.ext', 'body')->setContentType('text/plain')->execute(); + +$result = $s3->commands()->delete('filename.ext')->execute(); + +$result = $s3->commands()->upload('filename.ext', '/path/to/local/file.ext')->setAcl('private')->execute(); + +$result = $s3->commands()->restore('filename.ext', $days = 7)->execute(); + +$isExisting = $s3->commands()->exist('filename.ext')->execute(); + +$url = $s3->commands()->getUrl('filename.ext')->execute(); + +$signedUrl = $s3->commands()->getPresignedUrl('filename.ext', '+2 days')->execute(); +``` + +### Short syntax + +```php +/** @var \frostealth\yii2\aws\s3\Service $s3 */ +$s3 = Yii::$app->get('s3'); + +/** @var \Aws\ResultInterface $result */ +$result = $s3->get('filename.ext'); + +$result = $s3->put('filename.ext', 'body'); + +$result = $s3->delete('filename.ext'); + +$result = $s3->upload('filename.ext', '/path/to/local/file.ext'); + +$result = $s3->restore('filename.ext', $days = 7); + +$isExisting = $s3->exist('filename.ext'); + +$url = $s3->getUrl('filename.ext'); + +$signedUrl = $s3->getPresignedUrl('filename.ext', '+2 days'); +``` + +### Asynchronous execution + +```php +/** @var \frostealth\yii2\aws\s3\Service $s3 */ +$s3 = Yii::$app->get('s3'); + +/** @var \GuzzleHttp\Promise\PromiseInterface $promise */ +$promise = $s3->commands()->get('filename.ext')->async()->execute(); + +$promise = $s3->commands()->put('filename.ext', 'body')->async()->execute(); + +$promise = $s3->commands()->delete('filename.ext')->async()->execute(); + +$promise = $s3->commands()->upload('filename.ext', 'source')->async()->execute(); +``` + +## License + +Yii2 AWS S3 is licensed under the MIT License. +See the [LICENSE.md](LICENSE.md) file for more information. \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b65fa0e --- /dev/null +++ b/composer.json @@ -0,0 +1,36 @@ +{ + "name": "frostealth/yii2-aws-s3", + "type": "yii2-extension", + "description": "An Amazon S3 component for Yii2", + "keywords": ["yii2", "aws", "s3", "aws-s3", "yii2-aws", "yii2-s3"], + "homepage": "https://github.com/frostealth/yii2-aws-s3", + "license": "MIT", + "authors": [ + { + "name": "Ivan Kudinov", + "email": "i.kudinov@frostealth.ru", + "homepage": "http://frostealth.ru" + }, + { + "name": "Constantine Chuprik", + "email": "constantinchuprik@gmail.com" + } + ], + "support": { + "issues": "https://github.com/frostealth/yii2-aws-s3/issues?state=open" + }, + "require": { + "php": ">=7.0.0", + "aws/aws-sdk-php": "~3.17", + "yiisoft/yii2": "~2.0" + }, + "require-dev": { + "phpro/grumphp": "~0.8", + "squizlabs/php_codesniffer": "~2.3" + }, + "autoload": { + "psr-4": { + "frostealth\\yii2\\aws\\s3\\": "src/" + } + } +} diff --git a/grumphp.yml b/grumphp.yml new file mode 100644 index 0000000..baf1ca1 --- /dev/null +++ b/grumphp.yml @@ -0,0 +1,12 @@ +parameters: + git_dir: . + bin_dir: ./vendor/bin + tasks: + git_blacklist: + keywords: + - "die(" + - "die;" + - "exit(" + - "exit;" + - "print_r(" + - "var_dump(" diff --git a/src/Service.php b/src/Service.php new file mode 100644 index 0000000..4493a6c --- /dev/null +++ b/src/Service.php @@ -0,0 +1,273 @@ + '2006-03-01']; + + /** @var array */ + private $components = []; + + /** @var array */ + private $definitions = [ + 'client' => ['class' => 'Aws\S3\S3Client'], + 'resolver' => ['class' => 'frostealth\yii2\aws\s3\base\HandlerResolver'], + 'bus' => ['class' => 'frostealth\yii2\aws\s3\base\Bus'], + 'builder' => ['class' => 'frostealth\yii2\aws\s3\base\CommandBuilder'], + 'factory' => ['class' => 'frostealth\yii2\aws\s3\base\CommandFactory'], + ]; + + /** + * Initializes the object. + * This method is invoked at the end of the constructor after the object is initialized with the + * given configuration. + */ + public function init() + { + if (empty($this->clientConfig['credentials'])) { + throw new InvalidConfigException('Credentials is not set.'); + } + + if (empty($this->clientConfig['region'])) { + throw new InvalidConfigException('Region is not set.'); + } + + if (empty($this->defaultBucket)) { + throw new InvalidConfigException('Default bucket name is not set.'); + } + + foreach ($this->definitions as $name => $definition) { + $this->components[$name] = $this->components[$name] ?? $definition; + } + } + + /** + * Executes a command. + * + * @param \frostealth\yii2\aws\s3\interfaces\commands\Command $command + * + * @return mixed + */ + public function execute(Command $command) + { + return $this->getComponent('bus')->execute($command); + } + + /** + * Creates a command with default params. + * + * @param string $commandClass + * + * @return \frostealth\yii2\aws\s3\interfaces\commands\Command + */ + public function create(string $commandClass): Command + { + return $this->getComponent('builder')->build($commandClass); + } + + /** + * Returns command factory. + * + * @return \frostealth\yii2\aws\s3\base\CommandFactory + */ + public function commands(): CommandFactory + { + return $this->getComponent('factory'); + } + + /** + * Returns handler resolver. + * + * @return \frostealth\yii2\aws\s3\interfaces\HandlerResolver + */ + public function getResolver(): HandlerResolver + { + return $this->getComponent('resolver'); + } + + /** + * @param string $name + * @param array $params + * + * @return mixed + */ + public function __call($name, $params) + { + if (method_exists($this->commands(), $name)) { + $result = call_user_func_array([$this->commands(), $name], $params); + + return $result instanceof Command ? $this->execute($result) : $result; + } + + return parent::__call($name, $params); + } + + /** + * @param \Aws\Credentials\CredentialsInterface|array|callable $credentials + */ + public function setCredentials($credentials) + { + $this->clientConfig['credentials'] = $credentials; + } + + /** + * @param string $region + */ + public function setRegion(string $region) + { + $this->clientConfig['region'] = $region; + } + + /** + * @param array|bool $debug + */ + public function setDebug($debug) + { + $this->clientConfig['debug'] = $debug; + } + + /** + * @param string|array|object $resolver + */ + public function setResolver($resolver) + { + $this->setComponent('resolver', $resolver); + } + + /** + * @param string|array|object $bus + */ + public function setBus($bus) + { + $this->setComponent('bus', $bus); + } + + /** + * @param string|array|object $builder + */ + public function setBuilder($builder) + { + $this->setComponent('builder', $builder); + } + + /** + * @param string|array|object $factory + */ + public function setFactory($factory) + { + $this->setComponent('factory', $factory); + } + + /** + * @param string $name + * + * @return object + */ + protected function getComponent(string $name) + { + if (!is_object($this->components[$name])) { + $this->components[$name] = $this->createComponent($name); + } + + return $this->components[$name]; + } + + /** + * @param string $name + * @param array|object|string $definition + */ + protected function setComponent(string $name, $definition) + { + if (!is_object($definition)) { + $definition = !is_array($definition) ? ['class' => $definition] : $definition; + $definition = ArrayHelper::merge($this->definitions[$name], $definition); + } + + $this->components[$name] = $definition; + } + + /** + * @param string $name + * + * @return object + * @throws \yii\base\InvalidConfigException + */ + protected function createComponent(string $name) + { + $definition = $this->components[$name]; + $params = $this->getComponentParams($name); + + return \Yii::createObject($definition, $params); + } + + /** + * @param string $name + * + * @return array + */ + protected function getComponentParams(string $name): array + { + switch ($name) { + case 'client': + $params = [$this->clientConfig]; + break; + case 'resolver': + $params = [$this->getComponent('client')]; + break; + case 'bus': + $params = [$this->getComponent('resolver')]; + break; + case 'builder': + $params = [$this->getComponent('bus'), $this->defaultBucket, $this->defaultAcl]; + break; + case 'factory': + $params = [$this->getComponent('builder')]; + break; + default: + $params = []; + } + + return $params; + } +} diff --git a/src/base/Bus.php b/src/base/Bus.php new file mode 100644 index 0000000..91d7626 --- /dev/null +++ b/src/base/Bus.php @@ -0,0 +1,38 @@ +resolver = $inflector; + } + + /** + * @param \frostealth\yii2\aws\s3\interfaces\commands\Command $command + * + * @return mixed + */ + public function execute(interfaces\commands\Command $command) + { + $handler = $this->resolver->resolve($command); + + return call_user_func([$handler, 'handle'], $command); + } +} diff --git a/src/base/CommandBuilder.php b/src/base/CommandBuilder.php new file mode 100644 index 0000000..1aff7ea --- /dev/null +++ b/src/base/CommandBuilder.php @@ -0,0 +1,68 @@ +bus = $bus; + $this->bucket = $bucket; + $this->acl = $acl; + } + + /** + * @param string $className + * + * @return \frostealth\yii2\aws\s3\interfaces\commands\Command + * @throws \yii\base\InvalidConfigException + */ + public function build(string $className): interfaces\commands\Command + { + $params = is_subclass_of($className, interfaces\commands\ExecutableCommand::class) ? [$this->bus] : []; + + /** @var interfaces\commands\Command $command */ + $command = \Yii::createObject($className, $params); + + $this->prepareCommand($command); + + return $command; + } + + /** + * @param \frostealth\yii2\aws\s3\interfaces\commands\Command $command + */ + protected function prepareCommand(interfaces\commands\Command $command) + { + if ($command instanceof interfaces\commands\HasBucket) { + $command->setBucket($this->bucket); + } + + if ($command instanceof interfaces\commands\HasAcl) { + $command->setAcl($this->acl); + } + } +} diff --git a/src/base/CommandFactory.php b/src/base/CommandFactory.php new file mode 100644 index 0000000..40abfc4 --- /dev/null +++ b/src/base/CommandFactory.php @@ -0,0 +1,160 @@ +builder = $builder; + } + + /** + * @param string $filename + * + * @return \frostealth\yii2\aws\s3\commands\GetCommand + */ + public function get(string $filename): GetCommand + { + /** @var GetCommand $command */ + $command = $this->builder->build(GetCommand::class); + $command->setFilename($filename); + + return $command; + } + + /** + * @param string $filename + * @param mixed $body + * + * @return \frostealth\yii2\aws\s3\commands\PutCommand + */ + public function put(string $filename, $body): PutCommand + { + /** @var PutCommand $command */ + $command = $this->builder->build(PutCommand::class); + $command->setFilename($filename)->setBody($body); + + return $command; + } + + /** + * @param string $filename + * + * @return \frostealth\yii2\aws\s3\commands\DeleteCommand + */ + public function delete(string $filename): DeleteCommand + { + /** @var DeleteCommand $command */ + $command = $this->builder->build(DeleteCommand::class); + $command->setFilename($filename); + + return $command; + } + + /** + * @param string $filename + * @param mixed $source + * + * @return \frostealth\yii2\aws\s3\commands\UploadCommand + */ + public function upload(string $filename, $source): UploadCommand + { + /** @var UploadCommand $command */ + $command = $this->builder->build(UploadCommand::class); + $command->setFilename($filename)->setSource($source); + + return $command; + } + + /** + * @param string $filename + * @param int $days lifetime of the active copy in days + * + * @return \frostealth\yii2\aws\s3\commands\RestoreCommand + */ + public function restore(string $filename, int $days): RestoreCommand + { + /** @var RestoreCommand $command */ + $command = $this->builder->build(RestoreCommand::class); + $command->setFilename($filename)->setDays($days); + + return $command; + } + + /** + * @param string $filename + * + * @return \frostealth\yii2\aws\s3\commands\ExistCommand + */ + public function exist(string $filename): ExistCommand + { + /** @var ExistCommand $command */ + $command = $this->builder->build(ExistCommand::class); + $command->setFilename($filename); + + return $command; + } + + /** + * @param string $filename + * + * @return \frostealth\yii2\aws\s3\commands\GetUrlCommand + */ + public function getUrl(string $filename): GetUrlCommand + { + /** @var GetUrlCommand $command */ + $command = $this->builder->build(GetUrlCommand::class); + $command->setFilename($filename); + + return $command; + } + + /** + * @param string $filename + * @param mixed $expires + * + * @return \frostealth\yii2\aws\s3\commands\GetPresignedUrlCommand + */ + public function getPresignedUrl(string $filename, $expires): GetPresignedUrlCommand + { + /** @var GetPresignedUrlCommand $command */ + $command = $this->builder->build(GetPresignedUrlCommand::class); + $command->setFilename($filename)->setExpires($expires); + + return $command; + } + + /** + * @param string $commandClass + * + * @return \frostealth\yii2\aws\s3\interfaces\commands\Command + */ + final protected function createCommand(string $commandClass) + { + return $this->builder->build($commandClass); + } +} diff --git a/src/base/HandlerResolver.php b/src/base/HandlerResolver.php new file mode 100644 index 0000000..3129ec0 --- /dev/null +++ b/src/base/HandlerResolver.php @@ -0,0 +1,98 @@ +s3Client = $s3Client; + parent::__construct($config); + } + + /** + * @param \frostealth\yii2\aws\s3\interfaces\commands\Command $command + * + * @return \frostealth\yii2\aws\s3\interfaces\handlers\Handler + * @throws \yii\base\Exception + */ + public function resolve(interfaces\commands\Command $command): interfaces\handlers\Handler + { + $commandClass = get_class($command); + + if (isset($this->handlers[$commandClass])) { + $handler = $this->handlers[$commandClass]; + + return is_object($handler) ? $handler : $this->createHandler($handler); + } + + if ($command instanceof interfaces\commands\PlainCommand) { + return $this->createHandler(PlainCommandHandler::class); + } + + $handlerClass = $commandClass . 'Handler'; + if (class_exists($handlerClass)) { + return $this->createHandler($handlerClass); + } + + $handlerClass = str_replace('\\commands\\', '\\handlers\\', $handlerClass); + if (class_exists($handlerClass)) { + return $this->createHandler($handlerClass); + } + + throw new Exception("Could not terminate the handler of a command of type \"{$commandClass}\""); + } + + /** + * @param string $commandClass + * @param mixed $handler + */ + public function bindHandler(string $commandClass, $handler) + { + $this->handlers[$commandClass] = $handler; + } + + /** + * @param array $handlers + */ + public function setHandlers(array $handlers) + { + foreach ($handlers as $commandClass => $handler) { + $this->bindHandler($commandClass, $handler); + } + } + + /** + * @param string|array $type + * + * @return \frostealth\yii2\aws\s3\interfaces\handlers\Handler + * @throws \yii\base\InvalidConfigException + */ + protected function createHandler($type): interfaces\handlers\Handler + { + return \Yii::createObject($type, [$this->s3Client]); + } +} diff --git a/src/base/commands/ExecutableCommand.php b/src/base/commands/ExecutableCommand.php new file mode 100644 index 0000000..f8c7431 --- /dev/null +++ b/src/base/commands/ExecutableCommand.php @@ -0,0 +1,35 @@ +bus = $bus; + } + + /** + * @return mixed + */ + public function execute() + { + return $this->bus->execute($this); + } +} diff --git a/src/base/commands/traits/Async.php b/src/base/commands/traits/Async.php new file mode 100644 index 0000000..af46bce --- /dev/null +++ b/src/base/commands/traits/Async.php @@ -0,0 +1,34 @@ +isAsync = $async; + + return $this; + } + + /** + * @return bool + */ + final public function isAsync(): bool + { + return $this->isAsync; + } +} diff --git a/src/base/commands/traits/Options.php b/src/base/commands/traits/Options.php new file mode 100644 index 0000000..4dca597 --- /dev/null +++ b/src/base/commands/traits/Options.php @@ -0,0 +1,47 @@ +options = $value; + + return $this; + } + + /** + * @return array + */ + final public function getOptions(): array + { + return $this->options; + } + + /** + * @param string $name + * @param mixed $value + * + * @return $this + */ + final public function setOption(string $name, $value) + { + $this->options[$name] = $value; + + return $this; + } +} diff --git a/src/base/handlers/Handler.php b/src/base/handlers/Handler.php new file mode 100644 index 0000000..672af47 --- /dev/null +++ b/src/base/handlers/Handler.php @@ -0,0 +1,27 @@ +s3Client = $s3Client; + } +} diff --git a/src/commands/DeleteCommand.php b/src/commands/DeleteCommand.php new file mode 100644 index 0000000..7c09dd5 --- /dev/null +++ b/src/commands/DeleteCommand.php @@ -0,0 +1,104 @@ +args['Bucket'] ?? ''; + } + + /** + * @param string $bucket + * + * @return $this + */ + public function setBucket(string $bucket) + { + $this->args['Bucket'] = $bucket; + + return $this; + } + + /** + * @return string + */ + public function getFilename(): string + { + return $this->args['Key'] ?? ''; + } + + /** + * @param string $filename + * + * @return $this + */ + public function setFilename(string $filename) + { + $this->args['Key'] = $filename; + + return $this; + } + + /** + * @return string + */ + public function getVersionId(): string + { + return $this->args['VersionId'] ?? ''; + } + + /** + * @param string $versionId + * + * @return $this + */ + public function setVersionId(string $versionId) + { + $this->args['VersionId'] = $versionId; + + return $this; + } + + /** + * @return string + */ + public function getName(): string + { + return 'DeleteObject'; + } + + /** + * @return array + */ + public function toArgs(): array + { + return array_replace($this->options, $this->args); + } +} diff --git a/src/commands/ExistCommand.php b/src/commands/ExistCommand.php new file mode 100644 index 0000000..3a43da7 --- /dev/null +++ b/src/commands/ExistCommand.php @@ -0,0 +1,65 @@ +bucket; + } + + /** + * @param string $bucket + * + * @return $this + */ + public function setBucket(string $bucket) + { + $this->bucket = $bucket; + + return $this; + } + + /** + * @return string + */ + public function getFilename(): string + { + return $this->filename; + } + + /** + * @param string $filename + * + * @return $this + */ + public function setFilename(string $filename) + { + $this->filename = $filename; + + return $this; + } +} diff --git a/src/commands/GetCommand.php b/src/commands/GetCommand.php new file mode 100644 index 0000000..1881c68 --- /dev/null +++ b/src/commands/GetCommand.php @@ -0,0 +1,108 @@ +args['Bucket'] ?? ''; + } + + /** + * @param string $bucket + * + * @return $this + */ + public function setBucket(string $bucket) + { + $this->args['Bucket'] = $bucket; + + return $this; + } + + /** + * @return string + */ + public function getFilename(): string + { + return $this->args['Key'] ?? ''; + } + + /** + * @param string $filename + * + * @return $this + */ + public function setFilename(string $filename) + { + $this->args['Key'] = $filename; + + return $this; + } + + /** + * @param string $value + * + * @return $this + */ + public function saveAs(string $value) + { + $this->args['SaveAs'] = $value; + + return $this; + } + + /** + * @param string $ifMatch + * + * @return $this + */ + public function ifMatch(string $ifMatch) + { + $this->args['IfMatch'] = $ifMatch; + + return $this; + } + + /** + * @return string + */ + public function getName(): string + { + return 'GetObject'; + } + + /** + * @return array + */ + public function toArgs(): array + { + return array_replace($this->options, $this->args); + } +} diff --git a/src/commands/GetPresignedUrlCommand.php b/src/commands/GetPresignedUrlCommand.php new file mode 100644 index 0000000..5c88895 --- /dev/null +++ b/src/commands/GetPresignedUrlCommand.php @@ -0,0 +1,90 @@ +args['Bucket'] ?? ''; + } + + /** + * @param string $bucket + * + * @return $this + */ + public function setBucket(string $bucket) + { + $this->args['Bucket'] = $bucket; + + return $this; + } + + /** + * @return string + */ + public function getFilename(): string + { + return $this->args['Key'] ?? ''; + } + + /** + * @param string $filename + * + * @return $this + */ + public function setFilename(string $filename) + { + $this->args['Key'] = $filename; + + return $this; + } + + /** + * @return mixed + */ + public function getExpires() + { + return $this->expires; + } + + /** + * @param int|string|\DateTime $expires + * + * @return $this + */ + public function setExpires($expires) + { + $this->expires = $expires; + + return $this; + } + + /** + * @return array + */ + public function getArgs(): array + { + return $this->args; + } +} diff --git a/src/commands/GetUrlCommand.php b/src/commands/GetUrlCommand.php new file mode 100644 index 0000000..a01e271 --- /dev/null +++ b/src/commands/GetUrlCommand.php @@ -0,0 +1,62 @@ +bucket; + } + + /** + * @param string $bucket + * + * @return $this + */ + public function setBucket(string $bucket) + { + $this->bucket = $bucket; + + return $this; + } + + /** + * @return string + */ + public function getFilename(): string + { + return $this->filename; + } + + /** + * @param string $filename + * + * @return $this + */ + public function setFilename(string $filename) + { + $this->filename = $filename; + + return $this; + } +} diff --git a/src/commands/PutCommand.php b/src/commands/PutCommand.php new file mode 100644 index 0000000..ce93e40 --- /dev/null +++ b/src/commands/PutCommand.php @@ -0,0 +1,185 @@ +args['Bucket'] ?? ''; + } + + /** + * @param string $bucket + * + * @return $this + */ + public function setBucket(string $bucket) + { + $this->args['Bucket'] = $bucket; + + return $this; + } + + /** + * @return string + */ + public function getFilename(): string + { + return $this->args['Key'] ?? ''; + } + + /** + * @param string $filename + * + * @return $this + */ + public function setFilename(string $filename) + { + $this->args['Key'] = $filename; + + return $this; + } + + /** + * @return string + */ + public function getAcl(): string + { + return $this->args['ACL'] ?? ''; + } + + /** + * @param string $acl + * + * @return $this + */ + public function setAcl(string $acl) + { + $this->args['ACL'] = $acl; + + return $this; + } + + /** + * @return mixed + */ + public function getBody() + { + return $this->args['Body'] ?? null; + } + + /** + * @param mixed $body + * + * @return $this + */ + public function setBody($body) + { + $this->args['Body'] = $body; + + return $this; + } + + /** + * @return array + */ + public function getMetadata(): array + { + return $this->args['Metadata'] ?? []; + } + + /** + * @param array $metadata + * + * @return $this + */ + public function setMetadata(array $metadata) + { + $this->args['Metadata'] = $metadata; + + return $this; + } + + /** + * @return string + */ + public function getContentType(): string + { + return $this->args['ContentType'] ?? ''; + } + + /** + * @param string $contentType + * + * @return $this + */ + public function setContentType(string $contentType) + { + $this->args['ContentType'] = $contentType; + + return $this; + } + + /** + * @return mixed + */ + public function getExpires() + { + return $this->args['Expires'] ?? null; + } + + /** + * @param mixed $expires + * + * @return $this + */ + public function setExpires($expires) + { + $this->args['Expires'] = $expires; + + return $this; + } + + /** + * @return string + */ + public function getName(): string + { + return 'PutObject'; + } + + /** + * @return array + */ + public function toArgs(): array + { + return array_replace($this->options, $this->args); + } +} diff --git a/src/commands/RestoreCommand.php b/src/commands/RestoreCommand.php new file mode 100644 index 0000000..ba15c0a --- /dev/null +++ b/src/commands/RestoreCommand.php @@ -0,0 +1,122 @@ +args['Bucket'] ?? ''; + } + + /** + * @param string $bucket + * + * @return $this + */ + public function setBucket(string $bucket) + { + $this->args['Bucket'] = $bucket; + + return $this; + } + + /** + * @return string + */ + public function getFilename(): string + { + return $this->args['Key'] ?? ''; + } + + /** + * @param string $filename + * + * @return $this + */ + public function setFilename(string $filename) + { + $this->args['Key'] = $filename; + + return $this; + } + + /** + * @return int lifetime of the active copy in days + */ + public function getDays(): int + { + return $this->args['Days'] ?? 0; + } + + /** + * @param int $days lifetime of the active copy in days + * + * @return $this + */ + public function setDays(int $days) + { + $this->args['Days'] = $days; + + return $this; + } + + /** + * @return string + */ + public function getVersionId(): string + { + return $this->args['VersionId'] ?? ''; + } + + /** + * @param string $versionId + * + * @return $this + */ + public function setVersionId(string $versionId) + { + $this->args['VersionId'] = $versionId; + + return $this; + } + + /** + * @return string + */ + public function getName(): string + { + return 'RestoreObject'; + } + + /** + * @return array + */ + public function toArgs(): array + { + return $this->args; + } +} diff --git a/src/commands/UploadCommand.php b/src/commands/UploadCommand.php new file mode 100644 index 0000000..6909e0d --- /dev/null +++ b/src/commands/UploadCommand.php @@ -0,0 +1,198 @@ +bucket; + } + + /** + * @param string $bucket + * + * @return $this + */ + public function setBucket(string $bucket) + { + $this->bucket = $bucket; + + return $this; + } + + /** + * @return string + */ + public function getAcl(): string + { + return (string)$this->acl; + } + + /** + * @param string $acl + * + * @return $this + */ + public function setAcl(string $acl) + { + $this->acl = $acl; + + return $this; + } + + /** + * @return mixed + */ + public function getSource() + { + return $this->source; + } + + /** + * @param mixed $source + * + * @return $this + */ + public function setSource($source) + { + $this->source = $source; + + return $this; + } + + /** + * @return string + */ + public function getFilename(): string + { + return (string)$this->filename; + } + + /** + * @param string $filename + * + * @return $this + */ + public function setFilename(string $filename) + { + $this->filename = $filename; + + return $this; + } + + /** + * @return int + */ + public function getPartSize(): int + { + return $this->options['part_size'] ?? 0; + } + + /** + * @param int $partSize + * + * @return $this + */ + public function setPartSize(int $partSize) + { + $this->options['part_size'] = $partSize; + + return $this; + } + + /** + * @return int + */ + public function getConcurrency(): int + { + return $this->options['concurrency'] ?? 0; + } + + /** + * @param int $concurrency + * + * @return $this + */ + public function setConcurrency(int $concurrency) + { + $this->options['concurrency'] = $concurrency; + + return $this; + } + + /** + * @return int + */ + public function getMupThreshold(): int + { + return $this->options['mup_threshold'] ?? 0; + } + + /** + * @param int $mupThreshold + * + * @return $this + */ + public function setMupThreshold(int $mupThreshold) + { + $this->options['mup_threshold'] = $mupThreshold; + + return $this; + } + + /** + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * @param callable $beforeUpload + * + * @return $this + */ + public function beforeUpload(callable $beforeUpload) + { + $this->options['before_upload'] = $beforeUpload; + + return $this; + } +} diff --git a/src/handlers/ExistCommandHandler.php b/src/handlers/ExistCommandHandler.php new file mode 100644 index 0000000..3adafbe --- /dev/null +++ b/src/handlers/ExistCommandHandler.php @@ -0,0 +1,28 @@ +s3Client->doesObjectExist( + $command->getBucket(), + $command->getFilename(), + $command->getOptions() + ); + } +} diff --git a/src/handlers/GetPresignedUrlCommandHandler.php b/src/handlers/GetPresignedUrlCommandHandler.php new file mode 100644 index 0000000..c33b582 --- /dev/null +++ b/src/handlers/GetPresignedUrlCommandHandler.php @@ -0,0 +1,27 @@ +s3Client->getCommand('GetObject', $command->getArgs()); + $request = $this->s3Client->createPresignedRequest($awsCommand, $command->getExpires()); + + return (string)$request->getUri(); + } +} diff --git a/src/handlers/GetUrlCommandHandler.php b/src/handlers/GetUrlCommandHandler.php new file mode 100644 index 0000000..0fd93f6 --- /dev/null +++ b/src/handlers/GetUrlCommandHandler.php @@ -0,0 +1,24 @@ +s3Client->getObjectUrl($command->getBucket(), $command->getFilename()); + } +} diff --git a/src/handlers/PlainCommandHandler.php b/src/handlers/PlainCommandHandler.php new file mode 100644 index 0000000..f1a4f06 --- /dev/null +++ b/src/handlers/PlainCommandHandler.php @@ -0,0 +1,53 @@ +toAwsCommand($command); + + /** @var \GuzzleHttp\Promise\PromiseInterface $promise */ + $promise = $this->s3Client->executeAsync($awsCommand); + + return $this->commandIsAsync($command) ? $promise : $promise->wait(); + } + + /** + * @param \frostealth\yii2\aws\s3\interfaces\commands\PlainCommand $command + * + * @return bool + */ + protected function commandIsAsync(PlainCommand $command): bool + { + return $command instanceof Asynchronous && $command->isAsync(); + } + + /** + * @param \frostealth\yii2\aws\s3\interfaces\commands\PlainCommand $command + * + * @return \Aws\CommandInterface + */ + protected function toAwsCommand(PlainCommand $command): AwsCommand + { + $args = array_filter($command->toArgs()); + + return $this->s3Client->getCommand($command->getName(), $args); + } +} diff --git a/src/handlers/UploadCommandHandler.php b/src/handlers/UploadCommandHandler.php new file mode 100644 index 0000000..1439c43 --- /dev/null +++ b/src/handlers/UploadCommandHandler.php @@ -0,0 +1,53 @@ +toStream($command->getSource()); + $options = array_filter($command->getOptions()); + + $promise = $this->s3Client->uploadAsync( + $command->getBucket(), + $command->getFilename(), + $source, + $command->getAcl(), + $options + ); + + return $command->isAsync() ? $promise : $promise->wait(); + } + + /** + * Create a new stream based on the input type. + * + * @param resource|string|StreamInterface $source path to a local file, resource or stream + * + * @return StreamInterface + */ + protected function toStream($source): StreamInterface + { + if (is_string($source)) { + $source = Psr7\try_fopen($source, 'r+'); + } + + return Psr7\stream_for($source); + } +} diff --git a/src/interfaces/Bus.php b/src/interfaces/Bus.php new file mode 100644 index 0000000..bbb6ae7 --- /dev/null +++ b/src/interfaces/Bus.php @@ -0,0 +1,20 @@ +