From ca452c8c0c184c82c742d08ac205960c8c67956b Mon Sep 17 00:00:00 2001 From: Ian Jenkins Date: Wed, 30 Jun 2021 19:39:36 +0100 Subject: [PATCH] [Flystem] Add FlysystemV2 adapter for version 2 support of flysystem. Done as a new adapter to allows users to use the existing adapter for flysystem version 1. --- .github/workflows/ci.yml | 1 + Makefile | 1 + spec/Gaufrette/Adapter/FlysystemV2Spec.php | 98 ++++++++++++ src/Gaufrette/Adapter/FlysystemV2.php | 173 +++++++++++++++++++++ 4 files changed, 273 insertions(+) create mode 100644 spec/Gaufrette/Adapter/FlysystemV2Spec.php create mode 100644 src/Gaufrette/Adapter/FlysystemV2.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 920dd53f..5ab11e2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,7 @@ jobs: - { php: '7.4', packages: 'google/apiclient:^1.1.3', phpspec: 'spec/Gaufrette/Adapter/GoogleCloudStorageSpec.php' } - { php: '7.4', packages: 'doctrine/dbal:^2.3', phpspec: 'spec/Gaufrette/Adapter/DoctrineDbalSpec.php' } - { php: '7.4', packages: 'league/flysystem:^1.0', phpspec: 'spec/Gaufrette/Adapter/FlysystemSpec.php' } + - { php: '7.4', packages: 'league/flysystem:^2.0', phpspec: 'spec/Gaufrette/Adapter/FlysystemV2Spec.php' } - { php: '7.4', packages: 'microsoft/azure-storage-blob:^1.0', phpspec: 'spec/Gaufrette/Adapter/AzureBlobStore' } - { php: '7.4', packages: 'mongodb/mongodb:^1.1', phpspec: 'spec/Gaufrette/Adapter/GridFSSpec.php' } - { php: '7.4', packages: 'phpseclib/phpseclib:^2.0', phpspec: 'spec/Gaufrette/Adapter/PhpseclibSftpSpec.php' } diff --git a/Makefile b/Makefile index cea370e7..0d7cd935 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,7 @@ remove-phpspec: rm spec/Gaufrette/Adapter/GoogleCloudStorageSpec.php rm spec/Gaufrette/Adapter/DoctrineDbalSpec.php rm spec/Gaufrette/Adapter/FlysystemSpec.php + rm spec/Gaufrette/Adapter/FlysystemV2Spec.php rm -r spec/Gaufrette/Adapter/AzureBlobStorage rm spec/Gaufrette/Adapter/GridFSSpec.php rm spec/Gaufrette/Adapter/PhpseclibSftpSpec.php diff --git a/spec/Gaufrette/Adapter/FlysystemV2Spec.php b/spec/Gaufrette/Adapter/FlysystemV2Spec.php new file mode 100644 index 00000000..1140dc32 --- /dev/null +++ b/spec/Gaufrette/Adapter/FlysystemV2Spec.php @@ -0,0 +1,98 @@ +beConstructedWith($adapter, $config); + } + + function it_is_adapter() + { + $this->shouldImplement('Gaufrette\Adapter'); + } + + function it_is_list_keys_aware() + { + $this->shouldImplement('Gaufrette\Adapter\ListKeysAware'); + } + + function it_reads_file(\League\Flysystem\FilesystemAdapter $adapter) + { + $adapter->read('filename')->willReturn('Hello.'); + $this->read('filename')->shouldReturn('Hello.'); + } + + function it_writes_file(\League\Flysystem\FilesystemAdapter $adapter, \League\Flysystem\Config $config) + { + $this->shouldNotThrow('League\Flysystem\UnableToWriteFile')->duringWrite('filename', 'Hello.', $config); + $adapter->fileSize('filename')->willReturn(new FileAttributes('filename', 100)); + $adapter->write('filename', 'Hello.', $config)->shouldBeCalled(); + + $this->write('filename', 'Hello.')->shouldReturn(100); + } + + function it_checks_if_file_exists(\League\Flysystem\FilesystemAdapter $adapter) + { + $adapter->fileExists('filename')->willReturn(true); + + $this->exists('filename')->shouldReturn(true); + } + + function it_fetches_keys(\League\Flysystem\FilesystemAdapter $adapter) + { + $adapter->listContents('', true)->willReturn( + yield new DirectoryAttributes('folder', null, 1457104978) + ); + + $this->keys()->shouldReturn(['folder']); + } + + function it_lists_keys(\League\Flysystem\FilesystemAdapter $adapter) + { + $adapter->listContents('', true)->willReturn([ + new DirectoryAttributes('folder', null, 1457104978), + new FileAttributes('file', 22, null, 1457104978), + ] + ); + + $this->listKeys()->shouldReturn([ + 'keys' => ['file'], + 'dirs' => ['folder'], + ]); + } + + function it_fetches_mtime(\League\Flysystem\FilesystemAdapter $adapter) + { + $adapter->lastModified('filename')->willReturn(new FileAttributes('filename', null, null, 1457104978)); + + $this->mtime('filename')->shouldReturn(1457104978); + } + + function it_deletes_file(\League\Flysystem\FilesystemAdapter $adapter) + { + $this->shouldNotThrow('League\Flysystem\UnableToDeleteFile')->duringDelete('filename'); + $adapter->delete('filename')->shouldBeCalled(); + + $this->delete('filename')->shouldReturn(true); + } + + function it_renames_file(\League\Flysystem\FilesystemAdapter $adapter, \League\Flysystem\Config $config) + { + $this->shouldNotThrow('League\Flysystem\UnableToMoveFile')->duringRename('oldfilename', 'newfilename'); + $adapter->move('oldfilename', 'newfilename', $config)->shouldBeCalled(); + + $this->rename('oldfilename', 'newfilename')->shouldReturn(true); + } + + function it_does_not_support_is_directory(\League\Flysystem\FilesystemAdapter $adapter) + { + $this->shouldThrow('Gaufrette\Exception\UnsupportedAdapterMethodException')->duringisDirectory('folder'); + } +} diff --git a/src/Gaufrette/Adapter/FlysystemV2.php b/src/Gaufrette/Adapter/FlysystemV2.php new file mode 100644 index 00000000..162add1e --- /dev/null +++ b/src/Gaufrette/Adapter/FlysystemV2.php @@ -0,0 +1,173 @@ +adapter = $adapter; + $this->config = $this->ensureConfig($config); + } + + /** + * {@inheritdoc} + */ + public function read($key) + { + return $this->adapter->read($key); + } + + /** + * {@inheritdoc} + */ + public function write($key, $content) + { + try { + $this->adapter->write($key, $content, $this->config); + + return $this->adapter->fileSize($key)->fileSize(); + } catch (UnableToWriteFile $exception) { + return false; + } + } + + /** + * {@inheritdoc} + */ + public function exists($key) + { + return $this->adapter->fileExists($key); + } + + /** + * {@inheritdoc} + */ + public function keys() + { + return array_map(function (StorageAttributes $content) { + return $content->path(); + }, $this->adapter->listContents('', true)); + } + + /** + * {@inheritdoc} + */ + public function listKeys($prefix = '') + { + $dirs = []; + $keys = []; + + foreach ($this->adapter->listContents('', true) as $content) { + if (empty($prefix) || 0 === strpos($content->path(), $prefix)) { + if ($content->isDir()) { + $dirs[] = $content->path(); + } else { + $keys[] = $content->path(); + } + } + } + + return [ + 'keys' => $keys, + 'dirs' => $dirs, + ]; + } + + /** + * {@inheritdoc} + */ + public function mtime($key) + { + return $this->adapter->lastModified($key)->lastModified(); + } + + /** + * {@inheritdoc} + */ + public function delete($key) + { + try { + $this->adapter->delete($key); + } catch (UnableToDeleteFile $exception) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function rename($sourceKey, $targetKey) + { + try { + $this->adapter->move($sourceKey, $targetKey, $this->config); + } catch (UnableToMoveFile $exception) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function isDirectory($key) + { + throw new UnsupportedAdapterMethodException('isDirectory is not supported by this adapter.'); + } + + /** + * Ensure a Config instance. + * + * @param null|array|Config $config + * + * @return Config config instance + * + * @throw LogicException + */ + private function ensureConfig($config) + { + if ($config === null) { + return new Config(); + } + + if ($config instanceof Config) { + return $config; + } + + if (is_array($config)) { + return new Config($config); + } + + throw new \LogicException('A config should either be an array or a League\Flysystem\Config object.'); + } +}