├── .php-cs-fixer.dist.php ├── LICENSE ├── README.md ├── UPGRADE.md ├── composer.json └── src ├── Adapter ├── AdapterDefinitionFactory.php └── Builder │ ├── AbstractAdapterDefinitionBuilder.php │ ├── AdapterDefinitionBuilderInterface.php │ ├── AsyncAwsAdapterDefinitionBuilder.php │ ├── AwsAdapterDefinitionBuilder.php │ ├── AzureAdapterDefinitionBuilder.php │ ├── BunnyCDNAdapterDefinitionBuilder.php │ ├── FtpAdapterDefinitionBuilder.php │ ├── GcloudAdapterDefinitionBuilder.php │ ├── GridFSAdapterDefinitionBuilder.php │ ├── LocalAdapterDefinitionBuilder.php │ ├── MemoryAdapterDefinitionBuilder.php │ ├── SftpAdapterDefinitionBuilder.php │ └── WebDAVAdapterDefinitionBuilder.php ├── DependencyInjection ├── Compiler │ ├── GcloudFactoryPass.php │ └── LazyFactoryPass.php ├── Configuration.php └── FlysystemExtension.php ├── Exception └── MissingPackageException.php ├── FlysystemBundle.php └── Lazy └── LazyFactory.php /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | setRules([ 5 | '@Symfony' => true, 6 | 'phpdoc_annotation_without_dot' => false, 7 | ]) 8 | ->setFinder( 9 | PhpCsFixer\Finder::create() 10 | ->in(__DIR__) 11 | ) 12 | ; 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Titouan Galopin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flysystem-bundle 2 | 3 | [![Packagist Version](https://img.shields.io/packagist/v/league/flysystem-bundle.svg?style=flat-square)](https://packagist.org/packages/league/flysystem-bundle) 4 | [![Software license](https://img.shields.io/github/license/thephpleague/flysystem-bundle.svg?style=flat-square)](https://github.com/thephpleague/flysystem-bundle/blob/master/LICENSE) 5 | 6 | flysystem-bundle is a Symfony bundle integrating the [Flysystem](https://flysystem.thephpleague.com) 7 | library into Symfony applications. 8 | 9 | It provides an efficient abstraction for the filesystem in order to change the storage backend depending 10 | on the execution environment (local files in development, cloud storage in production and memory in tests). 11 | 12 | > Note: you are reading the documentation for flysystem-bundle 3.0, which relies on Flysystem 3. 13 | > If you use Flysystem 1.x, use [flysystem-bundle 1.x](https://github.com/thephpleague/flysystem-bundle/tree/1.x). 14 | > If you use Flysystem 2.x, use [flysystem-bundle 2.x](https://github.com/thephpleague/flysystem-bundle/tree/2.x). 15 | > Read the [Upgrade guide](https://github.com/thephpleague/flysystem-bundle/blob/master/UPGRADE.md) to learn how to upgrade. 16 | 17 | ## Installation 18 | 19 | flysystem-bundle 3.x requires PHP 8.0+ and Symfony 5.4+. 20 | 21 | > If you need support for a lower PHP/Symfony version, consider using 22 | > [flysystem-bundle 2.x](https://github.com/thephpleague/flysystem-bundle/tree/2.x) which support Flysystem 3.x 23 | > and older PHP/Symfony versions. 24 | 25 | You can install the bundle using Symfony Flex: 26 | 27 | ``` 28 | composer require league/flysystem-bundle 29 | ``` 30 | 31 | ## Basic usage 32 | 33 | The default configuration file created by Symfony Flex provides enough configuration to 34 | use Flysystem in your application as soon as you install the bundle: 35 | 36 | ```yaml 37 | # config/packages/flysystem.yaml 38 | 39 | flysystem: 40 | storages: 41 | default.storage: 42 | adapter: 'local' 43 | options: 44 | directory: '%kernel.project_dir%/var/storage/default' 45 | ``` 46 | 47 | This configuration defines a single storage service (`default.storage`) based on the local adapter 48 | and configured to use the `%kernel.project_dir%/var/storage/default` directory. 49 | 50 | For each storage defined under `flysystem.storages`, an associated service is created using the 51 | name you provide (in this case, a service `default.storage` will be created). The bundle also 52 | creates a named alias for each of these services. 53 | 54 | This means you have two ways of using the defined storages: 55 | 56 | * either using autowiring, by typehinting against the `FilesystemOperator` and using the 57 | variable name matching one of your storages: 58 | 59 | ```php 60 | use League\Flysystem\FilesystemOperator; 61 | 62 | class MyService 63 | { 64 | private FilesystemOperator $storage; 65 | 66 | // The variable name $defaultStorage matters: it needs to be the camelized version 67 | // of the name of your storage. 68 | public function __construct(FilesystemOperator $defaultStorage) 69 | { 70 | $this->storage = $defaultStorage; 71 | } 72 | 73 | // ... 74 | } 75 | ``` 76 | 77 | The same goes for controllers: 78 | 79 | ```php 80 | use League\Flysystem\FilesystemOperator; 81 | 82 | class MyController 83 | { 84 | // The variable name $defaultStorage matters: it needs to be the camelized version 85 | // of the name of your storage. 86 | public function index(FilesystemOperator $defaultStorage) 87 | { 88 | // ... 89 | } 90 | } 91 | ``` 92 | 93 | * or using manual injection, by injecting the service named `default.storage` inside 94 | your services. 95 | 96 | Once you have a FilesystemOperator, you can call methods from the 97 | [Filesystem API](https://flysystem.thephpleague.com/v2/docs/usage/filesystem-api/) 98 | to interact with your storage. 99 | 100 | ## Full documentation 101 | 102 | 1. [Getting started](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/1-getting-started.md) 103 | 2. Cloud storage providers: 104 | [AsyncAws S3](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/2-cloud-storage-providers.md#asyncaws-s3), 105 | [AWS SDK S3](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/2-cloud-storage-providers.md#aws-sdk-s3), 106 | [Azure](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/2-cloud-storage-providers.md#azure), 107 | [Google Cloud Storage](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/2-cloud-storage-providers.md#google-cloud-storage), 108 | [DigitalOcean Spaces](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/2-cloud-storage-providers.md#digitalocean-spaces), 109 | [Scaleway Object Storage](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/2-cloud-storage-providers.md#scaleway-object-storage) 110 | 3. [Interacting with FTP and SFTP servers](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/3-interacting-with-ftp-and-sftp-servers.md) 111 | 4. [Using a lazy adapter to switch storage backend using an environment variable](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/4-using-lazy-adapter-to-switch-at-runtime.md) 112 | 5. [Creating a custom adapter](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/5-creating-a-custom-adapter.md) 113 | 6. [MongoDB GridFS](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/6-gridfs.md) 114 | 7. [WebDAV](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/7-webdav.md) 115 | 8. [BunnyCDN](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/8-bunnycdn.md) 116 | 117 | * [Security issue disclosure procedure](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/A-security-disclosure-procedure.md) 118 | * [Configuration reference](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/B-configuration-reference.md) 119 | 120 | ## Security Issues 121 | 122 | If you discover a security vulnerability within the bundle, please follow 123 | [our disclosure procedure](https://github.com/thephpleague/flysystem-bundle/blob/master/docs/A-security-disclosure-procedure.md). 124 | 125 | ## Backward Compatibility promise 126 | 127 | This library follows the same Backward Compatibility promise as the Symfony framework: 128 | [https://symfony.com/doc/current/contributing/code/bc.html](https://symfony.com/doc/current/contributing/code/bc.html) 129 | 130 | > *Note*: many classes in this bundle are either marked `@final` or `@internal`. 131 | > `@internal` classes are excluded from any Backward Compatibility promise (you should not use them in your code) 132 | > whereas `@final` classes can be used but should not be extended (use composition instead). 133 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | # Upgrading from 2.0 to 3.0 2 | 3 | flysystem-bundle 3.0 dropped support for End-Of-Life versions of PHP, Symfony and Flysystem. 4 | 5 | No backward incompatible code change have been directly introduced by this bundle version. 6 | You should read the [Flysystem 3.x changelog](https://github.com/thephpleague/flysystem/blob/3.x/CHANGELOG.md) 7 | for any indirect change that may affect you. 8 | 9 | The list of changes is the following: 10 | 11 | * Dropped support for PHP 7.x ; 12 | * Dropped support for Symfony 4.2 to 5.3 ; 13 | * Dropped support of Flysystem 2.x ; 14 | * Added support for Azure Blob Storage (`league/flysystem-azure-blob-storage ^3.1`) 15 | 16 | # Upgrading from 1.0 to 2.0 17 | 18 | flysystem-bundle 2.0 introduces backward incompatible changes, meaning you will need to update 19 | the code of your projects to upgrade. 20 | 21 | flysystem-bundle 2.0 relies on Flysystem 2.0, which introduced most of the backward incompatible 22 | changes. You should read 23 | [Flysystem 2.0 upgrade guide](https://flysystem.thephpleague.com/v2/docs/advanced/upgrade-to-2.0.0/). 24 | 25 | In addition to the library updates, the bundle also changed a bit: 26 | 27 | * Add official support for PHP 8.x ; 28 | * Migration to AsyncAWS 1.0 ; 29 | * Drop support for PHP 7.1 ; 30 | * Drop support for Azure, Dropbox, Rackspace and WebDAV adapters (following the main library) ; 31 | * Drop support for null, cache, zip and replicate adapters (following the main library) ; 32 | * Drop support for plugins ; 33 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "league/flysystem-bundle", 3 | "type": "symfony-bundle", 4 | "description": "Symfony bundle integrating Flysystem into Symfony applications", 5 | "keywords": [ 6 | "symfony", 7 | "flysystem", 8 | "filesystem", 9 | "bundle" 10 | ], 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Titouan Galopin", 15 | "email": "galopintitouan@gmail.com" 16 | } 17 | ], 18 | "autoload": { 19 | "psr-4": { 20 | "League\\FlysystemBundle\\": "src" 21 | } 22 | }, 23 | "autoload-dev": { 24 | "psr-4": { 25 | "Tests\\League\\FlysystemBundle\\": "tests" 26 | } 27 | }, 28 | "require": { 29 | "php": ">=8.0", 30 | "league/flysystem": "^3.0", 31 | "symfony/config": "^5.4 || ^6.0 || ^7.0", 32 | "symfony/deprecation-contracts": "^2.1 || ^3", 33 | "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", 34 | "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", 35 | "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0" 36 | }, 37 | "require-dev": { 38 | "doctrine/mongodb-odm": "^2.0", 39 | "league/flysystem-async-aws-s3": "^3.1", 40 | "league/flysystem-aws-s3-v3": "^3.1", 41 | "league/flysystem-azure-blob-storage": "^3.1", 42 | "league/flysystem-ftp": "^3.1", 43 | "league/flysystem-google-cloud-storage": "^3.1", 44 | "league/flysystem-gridfs": "^3.28", 45 | "league/flysystem-memory": "^3.1", 46 | "league/flysystem-read-only": "^3.15", 47 | "league/flysystem-sftp-v3": "^3.1", 48 | "league/flysystem-webdav": "^3.29", 49 | "platformcommunity/flysystem-bunnycdn": "^3.3", 50 | "symfony/dotenv": "^5.4 || ^6.0 || ^7.0", 51 | "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0", 52 | "symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0", 53 | "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0", 54 | "symfony/yaml": "^5.4 || ^6.0 || ^7.0" 55 | }, 56 | "config": { 57 | "preferred-install": { 58 | "*": "dist" 59 | }, 60 | "sort-packages": true 61 | }, 62 | "minimum-stability": "beta" 63 | } 64 | -------------------------------------------------------------------------------- /src/Adapter/AdapterDefinitionFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter; 13 | 14 | use League\FlysystemBundle\Adapter\Builder\AdapterDefinitionBuilderInterface; 15 | use Symfony\Component\DependencyInjection\Definition; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * 20 | * @internal 21 | */ 22 | final class AdapterDefinitionFactory 23 | { 24 | /** 25 | * @var AdapterDefinitionBuilderInterface[] 26 | */ 27 | private array $builders; 28 | 29 | public function __construct() 30 | { 31 | $this->builders = [ 32 | new Builder\AsyncAwsAdapterDefinitionBuilder(), 33 | new Builder\AwsAdapterDefinitionBuilder(), 34 | new Builder\AzureAdapterDefinitionBuilder(), 35 | new Builder\FtpAdapterDefinitionBuilder(), 36 | new Builder\GcloudAdapterDefinitionBuilder(), 37 | new Builder\GridFSAdapterDefinitionBuilder(), 38 | new Builder\LocalAdapterDefinitionBuilder(), 39 | new Builder\MemoryAdapterDefinitionBuilder(), 40 | new Builder\SftpAdapterDefinitionBuilder(), 41 | new Builder\WebDAVAdapterDefinitionBuilder(), 42 | new Builder\BunnyCDNAdapterDefinitionBuilder(), 43 | ]; 44 | } 45 | 46 | public function createDefinition(string $name, array $options, ?string $defaultVisibilityForDirectories = null): ?Definition 47 | { 48 | foreach ($this->builders as $builder) { 49 | if ($builder->getName() === $name) { 50 | return $builder->createDefinition($options, $defaultVisibilityForDirectories); 51 | } 52 | } 53 | 54 | return null; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Adapter/Builder/AbstractAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\UnixVisibility\PortableVisibilityConverter; 15 | use League\FlysystemBundle\Exception\MissingPackageException; 16 | use Symfony\Component\DependencyInjection\Definition; 17 | use Symfony\Component\OptionsResolver\OptionsResolver; 18 | 19 | /** 20 | * @author Titouan Galopin 21 | * 22 | * @internal 23 | */ 24 | abstract class AbstractAdapterDefinitionBuilder implements AdapterDefinitionBuilderInterface 25 | { 26 | final public function createDefinition(array $options, ?string $defaultVisibilityForDirectories): Definition 27 | { 28 | $this->ensureRequiredPackagesAvailable(); 29 | 30 | $resolver = new OptionsResolver(); 31 | $this->configureOptions($resolver); 32 | 33 | $definition = new Definition(); 34 | $definition->setPublic(false); 35 | $this->configureDefinition($definition, $resolver->resolve($options), $defaultVisibilityForDirectories); 36 | 37 | return $definition; 38 | } 39 | 40 | abstract protected function getRequiredPackages(): array; 41 | 42 | abstract protected function configureOptions(OptionsResolver $resolver); 43 | 44 | abstract protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories); 45 | 46 | protected function configureUnixOptions(OptionsResolver $resolver): void 47 | { 48 | $resolver->setDefault('permissions', function (OptionsResolver $subResolver) { 49 | $subResolver->setDefault('file', function (OptionsResolver $permsResolver) { 50 | $permsResolver->setDefault('public', 0644); 51 | $permsResolver->setAllowedTypes('public', 'scalar'); 52 | 53 | $permsResolver->setDefault('private', 0600); 54 | $permsResolver->setAllowedTypes('private', 'scalar'); 55 | }); 56 | 57 | $subResolver->setDefault('dir', function (OptionsResolver $permsResolver) { 58 | $permsResolver->setDefault('public', 0755); 59 | $permsResolver->setAllowedTypes('public', 'scalar'); 60 | 61 | $permsResolver->setDefault('private', 0700); 62 | $permsResolver->setAllowedTypes('private', 'scalar'); 63 | }); 64 | }); 65 | } 66 | 67 | protected function createUnixDefinition(array $permissions, string $defaultVisibilityForDirectories): Definition 68 | { 69 | return (new Definition(PortableVisibilityConverter::class)) 70 | ->setFactory([PortableVisibilityConverter::class, 'fromArray']) 71 | ->addArgument([ 72 | 'file' => [ 73 | 'public' => (int) $permissions['file']['public'], 74 | 'private' => (int) $permissions['file']['private'], 75 | ], 76 | 'dir' => [ 77 | 'public' => (int) $permissions['dir']['public'], 78 | 'private' => (int) $permissions['dir']['private'], 79 | ], 80 | ]) 81 | ->addArgument($defaultVisibilityForDirectories) 82 | ->setShared(false) 83 | ; 84 | } 85 | 86 | private function ensureRequiredPackagesAvailable(): void 87 | { 88 | $missingPackages = []; 89 | foreach ($this->getRequiredPackages() as $requiredClass => $packageName) { 90 | if (!class_exists($requiredClass)) { 91 | $missingPackages[] = $packageName; 92 | } 93 | } 94 | 95 | if (!$missingPackages) { 96 | return; 97 | } 98 | 99 | throw new MissingPackageException(sprintf("Missing package%s, to use the \"%s\" adapter, run:\n\ncomposer require %s", \count($missingPackages) > 1 ? 's' : '', $this->getName(), implode(' ', $missingPackages))); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Adapter/Builder/AdapterDefinitionBuilderInterface.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use Symfony\Component\DependencyInjection\Definition; 15 | 16 | /** 17 | * @author Titouan Galopin 18 | * 19 | * @internal 20 | */ 21 | interface AdapterDefinitionBuilderInterface 22 | { 23 | public function getName(): string; 24 | 25 | /** 26 | * Create the definition for this builder's adapter given an array of options. 27 | */ 28 | public function createDefinition(array $options, ?string $defaultVisibilityForDirectories): Definition; 29 | } 30 | -------------------------------------------------------------------------------- /src/Adapter/Builder/AsyncAwsAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\AsyncAwsS3\AsyncAwsS3Adapter; 15 | use League\Flysystem\AsyncAwsS3\PortableVisibilityConverter; 16 | use League\Flysystem\Visibility; 17 | use Symfony\Component\DependencyInjection\Definition; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | /** 22 | * @author Titouan Galopin 23 | * @author Tobias Nyholm 24 | * 25 | * @internal 26 | */ 27 | final class AsyncAwsAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 28 | { 29 | public function getName(): string 30 | { 31 | return 'asyncaws'; 32 | } 33 | 34 | protected function getRequiredPackages(): array 35 | { 36 | return [ 37 | AsyncAwsS3Adapter::class => 'league/flysystem-async-aws-s3', 38 | ]; 39 | } 40 | 41 | protected function configureOptions(OptionsResolver $resolver): void 42 | { 43 | $resolver->setRequired('client'); 44 | $resolver->setAllowedTypes('client', 'string'); 45 | 46 | $resolver->setRequired('bucket'); 47 | $resolver->setAllowedTypes('bucket', 'string'); 48 | 49 | $resolver->setDefault('prefix', ''); 50 | $resolver->setAllowedTypes('prefix', 'string'); 51 | } 52 | 53 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 54 | { 55 | $definition->setClass(AsyncAwsS3Adapter::class); 56 | $definition->setArgument(0, new Reference($options['client'])); 57 | $definition->setArgument(1, $options['bucket']); 58 | $definition->setArgument(2, $options['prefix']); 59 | $definition->setArgument(3, 60 | (new Definition(PortableVisibilityConverter::class)) 61 | ->setArgument(0, $defaultVisibilityForDirectories ?? Visibility::PUBLIC) 62 | ->setShared(false) 63 | ); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Adapter/Builder/AwsAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\AwsS3V3\AwsS3V3Adapter; 15 | use League\Flysystem\AwsS3V3\PortableVisibilityConverter; 16 | use League\Flysystem\Visibility; 17 | use Symfony\Component\DependencyInjection\Definition; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | /** 22 | * @author Titouan Galopin 23 | * 24 | * @internal 25 | */ 26 | final class AwsAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 27 | { 28 | public function getName(): string 29 | { 30 | return 'aws'; 31 | } 32 | 33 | protected function getRequiredPackages(): array 34 | { 35 | return [ 36 | AwsS3V3Adapter::class => 'league/flysystem-aws-s3-v3', 37 | ]; 38 | } 39 | 40 | protected function configureOptions(OptionsResolver $resolver): void 41 | { 42 | $resolver->setRequired('client'); 43 | $resolver->setAllowedTypes('client', 'string'); 44 | 45 | $resolver->setRequired('bucket'); 46 | $resolver->setAllowedTypes('bucket', 'string'); 47 | 48 | $resolver->setDefault('prefix', ''); 49 | $resolver->setAllowedTypes('prefix', 'string'); 50 | 51 | $resolver->setDefault('options', []); 52 | $resolver->setAllowedTypes('options', 'array'); 53 | 54 | $resolver->setDefault('streamReads', true); 55 | $resolver->setAllowedTypes('streamReads', 'bool'); 56 | } 57 | 58 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 59 | { 60 | $definition->setClass(AwsS3V3Adapter::class); 61 | $definition->setArgument(0, new Reference($options['client'])); 62 | $definition->setArgument(1, $options['bucket']); 63 | $definition->setArgument(2, $options['prefix']); 64 | $definition->setArgument(3, 65 | (new Definition(PortableVisibilityConverter::class)) 66 | ->setArgument(0, $defaultVisibilityForDirectories ?? Visibility::PUBLIC) 67 | ->setShared(false) 68 | ); 69 | $definition->setArgument(4, null); 70 | $definition->setArgument(5, $options['options']); 71 | $definition->setArgument(6, $options['streamReads']); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Adapter/Builder/AzureAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\AzureBlobStorage\AzureBlobStorageAdapter; 15 | use Symfony\Component\DependencyInjection\Definition; 16 | use Symfony\Component\DependencyInjection\Reference; 17 | use Symfony\Component\OptionsResolver\OptionsResolver; 18 | 19 | /** 20 | * @author Titouan Galopin 21 | * 22 | * @internal 23 | */ 24 | final class AzureAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 25 | { 26 | public function getName(): string 27 | { 28 | return 'azure'; 29 | } 30 | 31 | protected function getRequiredPackages(): array 32 | { 33 | return [ 34 | AzureBlobStorageAdapter::class => 'league/flysystem-azure-blob-storage', 35 | ]; 36 | } 37 | 38 | protected function configureOptions(OptionsResolver $resolver): void 39 | { 40 | $resolver->setRequired('client'); 41 | $resolver->setAllowedTypes('client', 'string'); 42 | 43 | $resolver->setRequired('container'); 44 | $resolver->setAllowedTypes('container', 'string'); 45 | 46 | $resolver->setDefault('prefix', ''); 47 | $resolver->setAllowedTypes('prefix', 'string'); 48 | } 49 | 50 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 51 | { 52 | $definition->setClass(AzureBlobStorageAdapter::class); 53 | $definition->setArgument(0, new Reference($options['client'])); 54 | $definition->setArgument(1, $options['container']); 55 | $definition->setArgument(2, $options['prefix']); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Adapter/Builder/BunnyCDNAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use PlatformCommunity\Flysystem\BunnyCDN\BunnyCDNAdapter; 15 | use Symfony\Component\DependencyInjection\Definition; 16 | use Symfony\Component\DependencyInjection\Reference; 17 | use Symfony\Component\OptionsResolver\OptionsResolver; 18 | 19 | /** 20 | * @internal 21 | */ 22 | final class BunnyCDNAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 23 | { 24 | public function getName(): string 25 | { 26 | return 'bunnycdn'; 27 | } 28 | 29 | protected function getRequiredPackages(): array 30 | { 31 | return [ 32 | BunnyCDNAdapter::class => 'platformcommunity/flysystem-bunnycdn', 33 | ]; 34 | } 35 | 36 | protected function configureOptions(OptionsResolver $resolver): void 37 | { 38 | $resolver->setRequired('client'); 39 | $resolver->setAllowedTypes('client', 'string'); 40 | 41 | $resolver->setDefault('pull_zone', ''); 42 | $resolver->setAllowedTypes('pull_zone', 'string'); 43 | } 44 | 45 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 46 | { 47 | $definition->setClass(BunnyCDNAdapter::class); 48 | $definition->setArguments([ 49 | new Reference($options['client']), 50 | $options['pull_zone'], 51 | ]); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Adapter/Builder/FtpAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\Ftp\FtpAdapter; 15 | use League\Flysystem\Ftp\FtpConnectionOptions; 16 | use League\Flysystem\Visibility; 17 | use Symfony\Component\DependencyInjection\Definition; 18 | use Symfony\Component\DependencyInjection\Reference; 19 | use Symfony\Component\OptionsResolver\OptionsResolver; 20 | 21 | /** 22 | * @author Titouan Galopin 23 | * 24 | * @internal 25 | */ 26 | final class FtpAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 27 | { 28 | public function getName(): string 29 | { 30 | return 'ftp'; 31 | } 32 | 33 | protected function getRequiredPackages(): array 34 | { 35 | return [ 36 | FtpAdapter::class => 'league/flysystem-ftp', 37 | ]; 38 | } 39 | 40 | protected function configureOptions(OptionsResolver $resolver): void 41 | { 42 | $resolver->setRequired('host'); 43 | $resolver->setAllowedTypes('host', 'string'); 44 | 45 | $resolver->setRequired('username'); 46 | $resolver->setAllowedTypes('username', 'string'); 47 | 48 | $resolver->setRequired('password'); 49 | $resolver->setAllowedTypes('password', 'string'); 50 | 51 | $resolver->setDefault('port', 21); 52 | $resolver->setAllowedTypes('port', 'scalar'); 53 | 54 | $resolver->setDefault('root', ''); 55 | $resolver->setAllowedTypes('root', 'string'); 56 | 57 | $resolver->setDefault('passive', true); 58 | $resolver->setAllowedTypes('passive', 'scalar'); 59 | 60 | $resolver->setDefault('ssl', false); 61 | $resolver->setAllowedTypes('ssl', 'scalar'); 62 | 63 | $resolver->setDefault('timeout', 90); 64 | $resolver->setAllowedTypes('timeout', 'scalar'); 65 | 66 | $resolver->setDefault('ignore_passive_address', null); 67 | $resolver->setAllowedTypes('ignore_passive_address', ['null', 'bool', 'scalar']); 68 | 69 | $resolver->setDefault('utf8', false); 70 | $resolver->setAllowedTypes('utf8', 'scalar'); 71 | 72 | $resolver->setDefault('transfer_mode', null); 73 | $resolver->setAllowedTypes('transfer_mode', ['null', 'scalar']); 74 | $resolver->setAllowedValues('transfer_mode', [null, FTP_ASCII, FTP_BINARY]); 75 | 76 | $resolver->setDefault('system_type', null); 77 | $resolver->setAllowedTypes('system_type', ['null', 'string']); 78 | $resolver->setAllowedValues('system_type', [null, 'windows', 'unix']); 79 | 80 | $resolver->setDefault('timestamps_on_unix_listings_enabled', false); 81 | $resolver->setAllowedTypes('timestamps_on_unix_listings_enabled', 'bool'); 82 | 83 | $resolver->setDefault('recurse_manually', true); 84 | $resolver->setAllowedTypes('recurse_manually', 'bool'); 85 | 86 | $resolver->setDefault('connectivityChecker', null); 87 | $resolver->setAllowedTypes('connectivityChecker', ['string', 'null']); 88 | 89 | $this->configureUnixOptions($resolver); 90 | } 91 | 92 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 93 | { 94 | $options['transferMode'] = $options['transfer_mode']; 95 | $options['systemType'] = $options['system_type']; 96 | $options['timestampsOnUnixListingsEnabled'] = $options['timestamps_on_unix_listings_enabled']; 97 | $options['ignorePassiveAddress'] = $options['ignore_passive_address']; 98 | $options['recurseManually'] = $options['recurse_manually']; 99 | 100 | $connectivityChecker = null; 101 | if (null !== $options['connectivityChecker']) { 102 | $connectivityChecker = new Reference($options['connectivityChecker']); 103 | } 104 | 105 | unset( 106 | $options['transfer_mode'], 107 | $options['system_type'], 108 | $options['timestamps_on_unix_listings_enabled'], 109 | $options['ignore_passive_address'], 110 | $options['recurse_manually'], 111 | $options['connectivityChecker'] 112 | ); 113 | 114 | $definition->setClass(FtpAdapter::class); 115 | $definition->setArgument(0, 116 | (new Definition(FtpConnectionOptions::class)) 117 | ->setFactory([FtpConnectionOptions::class, 'fromArray']) 118 | ->addArgument($options) 119 | ->setShared(false) 120 | ); 121 | $definition->setArgument(1, null); 122 | $definition->setArgument(2, $connectivityChecker); 123 | $definition->setArgument(3, $this->createUnixDefinition($options['permissions'], $defaultVisibilityForDirectories ?? Visibility::PRIVATE)); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Adapter/Builder/GcloudAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\GoogleCloudStorage\GoogleCloudStorageAdapter; 15 | use Symfony\Component\DependencyInjection\Definition; 16 | use Symfony\Component\DependencyInjection\Reference; 17 | use Symfony\Component\OptionsResolver\OptionsResolver; 18 | 19 | /** 20 | * @author Titouan Galopin 21 | * 22 | * @internal 23 | */ 24 | final class GcloudAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 25 | { 26 | public function getName(): string 27 | { 28 | return 'gcloud'; 29 | } 30 | 31 | protected function getRequiredPackages(): array 32 | { 33 | return [ 34 | GoogleCloudStorageAdapter::class => 'league/flysystem-google-cloud-storage', 35 | ]; 36 | } 37 | 38 | protected function configureOptions(OptionsResolver $resolver): void 39 | { 40 | $resolver->setRequired('client'); 41 | $resolver->setAllowedTypes('client', 'string'); 42 | 43 | $resolver->setRequired('bucket'); 44 | $resolver->setAllowedTypes('bucket', 'string'); 45 | 46 | $resolver->setDefault('prefix', ''); 47 | $resolver->setAllowedTypes('prefix', 'string'); 48 | 49 | $resolver->setDefault('visibility_handler', null); 50 | $resolver->setAllowedTypes('visibility_handler', ['string', 'null']); 51 | } 52 | 53 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 54 | { 55 | $bucketDefinition = new Definition(); 56 | $bucketDefinition->setFactory([new Reference($options['client']), 'bucket']); 57 | $bucketDefinition->setArgument(0, $options['bucket']); 58 | 59 | $visibilityHandlerReference = null; 60 | if (null !== $options['visibility_handler']) { 61 | $visibilityHandlerReference = new Reference($options['visibility_handler']); 62 | } 63 | 64 | $definition->setClass(GoogleCloudStorageAdapter::class); 65 | $definition->setArgument(0, $bucketDefinition); 66 | $definition->setArgument(1, $options['prefix']); 67 | $definition->setArgument(2, $visibilityHandlerReference); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Adapter/Builder/GridFSAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use Doctrine\ODM\MongoDB\DocumentManager; 15 | use League\Flysystem\GridFS\GridFSAdapter; 16 | use MongoDB\Client; 17 | use MongoDB\GridFS\Bucket; 18 | use Symfony\Component\DependencyInjection\Definition; 19 | use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; 20 | use Symfony\Component\DependencyInjection\Reference; 21 | use Symfony\Component\OptionsResolver\OptionsResolver; 22 | 23 | /** 24 | * @author Jérôme Tamarelle 25 | * 26 | * @internal 27 | */ 28 | final class GridFSAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 29 | { 30 | public function getName(): string 31 | { 32 | return 'gridfs'; 33 | } 34 | 35 | protected function getRequiredPackages(): array 36 | { 37 | return [ 38 | GridFSAdapter::class => 'league/flysystem-gridfs', 39 | ]; 40 | } 41 | 42 | protected function configureOptions(OptionsResolver $resolver): void 43 | { 44 | $resolver->define('bucket')->default(null)->allowedTypes('string', 'null'); 45 | $resolver->define('prefix')->default('')->allowedTypes('string'); 46 | $resolver->define('database')->default(null)->allowedTypes('string', 'null'); 47 | $resolver->define('doctrine_connection')->allowedTypes('string'); 48 | $resolver->define('mongodb_uri')->allowedTypes('string'); 49 | $resolver->define('mongodb_uri_options')->default([])->allowedTypes('array'); 50 | $resolver->define('mongodb_driver_options')->default([])->allowedTypes('array'); 51 | } 52 | 53 | /** 54 | * @param array{bucket:string|null, prefix:string, database:string|null, doctrine_connection?:string, mongodb_uri?:string, mongodb_uri_options:array, mongodb_driver_options:array} $options 55 | */ 56 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 57 | { 58 | if (isset($options['doctrine_connection'])) { 59 | if (isset($options['mongodb_uri'])) { 60 | throw new InvalidArgumentException('In GridFS configuration, "doctrine_connection" and "mongodb_uri" options cannot be set together.'); 61 | } 62 | $bucket = new Definition(Bucket::class); 63 | $bucket->setFactory([self::class, 'initializeBucketFromDocumentManager']); 64 | $bucket->setArguments([ 65 | new Reference(sprintf('doctrine_mongodb.odm.%s_document_manager', $options['doctrine_connection'])), 66 | $options['database'], 67 | $options['bucket'], 68 | ]); 69 | } elseif (isset($options['mongodb_uri'])) { 70 | $bucket = new Definition(Bucket::class); 71 | $bucket->setFactory([self::class, 'initializeBucketFromConfig']); 72 | $bucket->setArguments([ 73 | $options['mongodb_uri'], 74 | $options['mongodb_uri_options'], 75 | $options['mongodb_driver_options'], 76 | $options['database'] ?? throw new InvalidArgumentException('MongoDB "database" name is required for Flysystem GridFS configuration'), 77 | $options['bucket'], 78 | ]); 79 | } elseif ($options['bucket']) { 80 | $bucket = new Reference($options['bucket']); 81 | } else { 82 | throw new InvalidArgumentException('Flysystem GridFS configuration requires a "bucket" service name, a "mongodb_uri" or a "doctrine_connection" name'); 83 | } 84 | 85 | $definition->setClass(GridFSAdapter::class); 86 | $definition->setArgument(0, $bucket); 87 | $definition->setArgument(1, $options['prefix']); 88 | } 89 | 90 | public static function initializeBucketFromDocumentManager(DocumentManager $documentManager, ?string $dbName, ?string $bucketName): Bucket 91 | { 92 | return $documentManager 93 | ->getClient() 94 | ->selectDatabase($dbName ?? $documentManager->getConfiguration()->getDefaultDB()) 95 | ->selectGridFSBucket(['bucketName' => $bucketName ?? 'fs', 'disableMD5' => true]); 96 | } 97 | 98 | public static function initializeBucketFromConfig(string $uri, array $uriOptions, array $driverOptions, ?string $dbName, ?string $bucketName): Bucket 99 | { 100 | return (new Client($uri, $uriOptions, $driverOptions)) 101 | ->selectDatabase($dbName) 102 | ->selectGridFSBucket(['bucketName' => $bucketName ?? 'fs', 'disableMD5' => true]); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Adapter/Builder/LocalAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\Local\LocalFilesystemAdapter; 15 | use League\Flysystem\Visibility; 16 | use Symfony\Component\DependencyInjection\Definition; 17 | use Symfony\Component\OptionsResolver\OptionsResolver; 18 | 19 | /** 20 | * @author Titouan Galopin 21 | * 22 | * @internal 23 | */ 24 | final class LocalAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 25 | { 26 | public function getName(): string 27 | { 28 | return 'local'; 29 | } 30 | 31 | protected function getRequiredPackages(): array 32 | { 33 | return []; 34 | } 35 | 36 | protected function configureOptions(OptionsResolver $resolver): void 37 | { 38 | $resolver->setRequired('directory'); 39 | $resolver->setAllowedTypes('directory', 'string'); 40 | 41 | $this->configureUnixOptions($resolver); 42 | 43 | $resolver->setDefault('lock', 0); 44 | $resolver->setAllowedTypes('lock', 'scalar'); 45 | 46 | $resolver->setDefault('skip_links', false); 47 | $resolver->setAllowedTypes('skip_links', 'scalar'); 48 | 49 | $resolver->setDefault('lazy_root_creation', false); 50 | $resolver->setAllowedTypes('lazy_root_creation', 'scalar'); 51 | } 52 | 53 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 54 | { 55 | $definition->setClass(LocalFilesystemAdapter::class); 56 | $definition->setArgument(0, $options['directory']); 57 | $definition->setArgument(1, $this->createUnixDefinition($options['permissions'], $defaultVisibilityForDirectories ?? Visibility::PRIVATE)); 58 | $definition->setArgument(2, $options['lock']); 59 | $definition->setArgument(3, $options['skip_links'] ? LocalFilesystemAdapter::SKIP_LINKS : LocalFilesystemAdapter::DISALLOW_LINKS); 60 | $definition->setArgument(4, null); 61 | $definition->setArgument(5, $options['lazy_root_creation']); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Adapter/Builder/MemoryAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\InMemory\InMemoryFilesystemAdapter; 15 | use Symfony\Component\DependencyInjection\Definition; 16 | use Symfony\Component\OptionsResolver\OptionsResolver; 17 | 18 | /** 19 | * @author Titouan Galopin 20 | * 21 | * @internal 22 | */ 23 | final class MemoryAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 24 | { 25 | public function getName(): string 26 | { 27 | return 'memory'; 28 | } 29 | 30 | protected function getRequiredPackages(): array 31 | { 32 | return [ 33 | InMemoryFilesystemAdapter::class => 'league/flysystem-memory', 34 | ]; 35 | } 36 | 37 | protected function configureOptions(OptionsResolver $resolver) 38 | { 39 | } 40 | 41 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 42 | { 43 | $definition->setClass(InMemoryFilesystemAdapter::class); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Adapter/Builder/SftpAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\PhpseclibV2\SftpAdapter as SftpAdapterLegacy; 15 | use League\Flysystem\PhpseclibV2\SftpConnectionProvider as SftpConnectionProviderLegacy; 16 | use League\Flysystem\PhpseclibV3\SftpAdapter; 17 | use League\Flysystem\PhpseclibV3\SftpConnectionProvider; 18 | use League\Flysystem\Visibility; 19 | use Symfony\Component\DependencyInjection\Definition; 20 | use Symfony\Component\DependencyInjection\Reference; 21 | use Symfony\Component\OptionsResolver\OptionsResolver; 22 | 23 | /** 24 | * @author Titouan Galopin 25 | * 26 | * @internal 27 | */ 28 | final class SftpAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 29 | { 30 | public function getName(): string 31 | { 32 | return 'sftp'; 33 | } 34 | 35 | protected function getRequiredPackages(): array 36 | { 37 | $adapterFqcn = SftpAdapter::class; 38 | $packageRequire = 'league/flysystem-sftp-v3'; 39 | 40 | // Prevent BC 41 | if (class_exists(SftpAdapterLegacy::class)) { 42 | trigger_deprecation('league/flysystem-bundle', '2.2', '"league/flysystem-sftp" is deprecated, use "league/flysystem-sftp-v3" instead.'); 43 | 44 | $adapterFqcn = SftpAdapterLegacy::class; 45 | $packageRequire = 'league/flysystem-sftp'; 46 | } 47 | 48 | return [ 49 | $adapterFqcn => $packageRequire, 50 | ]; 51 | } 52 | 53 | protected function configureOptions(OptionsResolver $resolver): void 54 | { 55 | $resolver->setRequired('host'); 56 | $resolver->setAllowedTypes('host', 'string'); 57 | 58 | $resolver->setRequired('username'); 59 | $resolver->setAllowedTypes('username', 'string'); 60 | 61 | $resolver->setDefault('password', null); 62 | $resolver->setAllowedTypes('password', ['string', 'null']); 63 | 64 | $resolver->setDefault('port', 22); 65 | $resolver->setAllowedTypes('port', 'scalar'); 66 | 67 | $resolver->setDefault('root', ''); 68 | $resolver->setAllowedTypes('root', 'string'); 69 | 70 | $resolver->setDefault('privateKey', null); 71 | $resolver->setAllowedTypes('privateKey', ['string', 'null']); 72 | 73 | $resolver->setDefault('passphrase', null); 74 | $resolver->setAllowedTypes('passphrase', ['string', 'null']); 75 | 76 | $resolver->setDefault('hostFingerprint', null); 77 | $resolver->setAllowedTypes('hostFingerprint', ['string', 'null']); 78 | 79 | $resolver->setDefault('timeout', 90); 80 | $resolver->setAllowedTypes('timeout', 'scalar'); 81 | 82 | $resolver->setDefault('directoryPerm', 0744); 83 | $resolver->setAllowedTypes('directoryPerm', 'scalar'); 84 | 85 | $resolver->setDefault('permPrivate', 0700); 86 | $resolver->setAllowedTypes('permPrivate', 'scalar'); 87 | 88 | $resolver->setDefault('permPublic', 0744); 89 | $resolver->setAllowedTypes('permPublic', 'scalar'); 90 | 91 | $resolver->setDefault('connectivityChecker', null); 92 | $resolver->setAllowedTypes('connectivityChecker', ['string', 'null']); 93 | 94 | $resolver->setDefault('preferredAlgorithms', []); 95 | $resolver->setAllowedTypes('preferredAlgorithms', 'array'); 96 | 97 | $this->configureUnixOptions($resolver); 98 | } 99 | 100 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 101 | { 102 | // Prevent BC 103 | $adapterFqcn = SftpAdapter::class; 104 | $connectionFqcn = SftpConnectionProvider::class; 105 | if (class_exists(SftpAdapterLegacy::class)) { 106 | $adapterFqcn = SftpAdapterLegacy::class; 107 | $connectionFqcn = SftpConnectionProviderLegacy::class; 108 | } 109 | 110 | if (null !== $options['connectivityChecker']) { 111 | $options['connectivityChecker'] = new Reference($options['connectivityChecker']); 112 | } 113 | 114 | $definition->setClass($adapterFqcn); 115 | $definition->setArgument(0, 116 | (new Definition($connectionFqcn)) 117 | ->setFactory([$connectionFqcn, 'fromArray']) 118 | ->addArgument($options) 119 | ->setShared(false) 120 | ); 121 | $definition->setArgument(1, $options['root']); 122 | $definition->setArgument(2, $this->createUnixDefinition($options['permissions'], $defaultVisibilityForDirectories ?? Visibility::PRIVATE)); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Adapter/Builder/WebDAVAdapterDefinitionBuilder.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Adapter\Builder; 13 | 14 | use League\Flysystem\WebDAV\WebDAVAdapter; 15 | use Symfony\Component\DependencyInjection\Definition; 16 | use Symfony\Component\DependencyInjection\Reference; 17 | use Symfony\Component\OptionsResolver\OptionsResolver; 18 | 19 | /** 20 | * @author Kévin Dunglas 21 | * 22 | * @internal 23 | */ 24 | final class WebDAVAdapterDefinitionBuilder extends AbstractAdapterDefinitionBuilder 25 | { 26 | public function getName(): string 27 | { 28 | return 'webdav'; 29 | } 30 | 31 | protected function getRequiredPackages(): array 32 | { 33 | return [ 34 | WebDAVAdapter::class => 'league/flysystem-webdav', 35 | ]; 36 | } 37 | 38 | protected function configureOptions(OptionsResolver $resolver): void 39 | { 40 | $resolver->setRequired('client'); 41 | $resolver->setAllowedTypes('client', 'string'); 42 | 43 | $resolver->setDefault('prefix', ''); 44 | $resolver->setAllowedTypes('prefix', 'string'); 45 | 46 | $resolver->setDefault('visibility_handling', WebDAVAdapter::ON_VISIBILITY_THROW_ERROR); 47 | $resolver->setAllowedTypes('visibility_handling', ['string']); 48 | 49 | $resolver->setDefault('manual_copy', false); 50 | $resolver->setAllowedTypes('manual_copy', 'bool'); 51 | 52 | $resolver->setDefault('manual_move', false); 53 | $resolver->setAllowedTypes('manual_move', 'bool'); 54 | } 55 | 56 | protected function configureDefinition(Definition $definition, array $options, ?string $defaultVisibilityForDirectories): void 57 | { 58 | $definition->setClass(WebDAVAdapter::class); 59 | $definition->setArguments([ 60 | new Reference($options['client']), 61 | $options['prefix'], 62 | $options['visibility_handling'], 63 | $options['manual_copy'], 64 | $options['manual_move'], 65 | ]); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/GcloudFactoryPass.php: -------------------------------------------------------------------------------- 1 | register(PortableVisibilityHandler::class, PortableVisibilityHandler::class); 26 | $container->setAlias('flysystem.adapter.gcloud.visibility.portable', PortableVisibilityHandler::class); 27 | 28 | $container->register(UniformBucketLevelAccessVisibility::class, UniformBucketLevelAccessVisibility::class); 29 | $container->setAlias('flysystem.adapter.gcloud.visibility.uniform', UniformBucketLevelAccessVisibility::class); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/LazyFactoryPass.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\DependencyInjection\Compiler; 13 | 14 | use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; 15 | use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; 16 | use Symfony\Component\DependencyInjection\ContainerBuilder; 17 | use Symfony\Component\DependencyInjection\Reference; 18 | 19 | /** 20 | * @author Titouan Galopin 21 | * 22 | * @internal 23 | */ 24 | final class LazyFactoryPass implements CompilerPassInterface 25 | { 26 | /** 27 | * {@inheritdoc} 28 | */ 29 | public function process(ContainerBuilder $container): void 30 | { 31 | $factories = []; 32 | foreach ($container->findTaggedServiceIds('flysystem.storage') as $serviceId => $tags) { 33 | foreach ($tags as $tag) { 34 | if (isset($tag['storage'])) { 35 | $factories[$tag['storage']] = new Reference($serviceId); 36 | } 37 | } 38 | } 39 | 40 | $lazyFactory = $container->getDefinition('flysystem.adapter.lazy.factory'); 41 | $lazyFactory->setArgument(0, ServiceLocatorTagPass::register($container, $factories)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\DependencyInjection; 13 | 14 | use Symfony\Component\Config\Definition\Builder\TreeBuilder; 15 | use Symfony\Component\Config\Definition\ConfigurationInterface; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * 20 | * @internal 21 | */ 22 | final class Configuration implements ConfigurationInterface 23 | { 24 | public function getConfigTreeBuilder(): TreeBuilder 25 | { 26 | $treeBuilder = new TreeBuilder('flysystem'); 27 | $rootNode = $treeBuilder->getRootNode(); 28 | 29 | $rootNode 30 | ->fixXmlConfig('storage') 31 | ->children() 32 | ->arrayNode('storages') 33 | ->useAttributeAsKey('name') 34 | ->arrayPrototype() 35 | ->performNoDeepMerging() 36 | ->children() 37 | ->scalarNode('adapter')->isRequired()->end() 38 | ->arrayNode('options') 39 | ->variablePrototype() 40 | ->end() 41 | ->defaultValue([]) 42 | ->end() 43 | ->scalarNode('visibility')->defaultNull()->end() 44 | ->scalarNode('directory_visibility')->defaultNull()->end() 45 | ->booleanNode('case_sensitive')->defaultTrue()->end() 46 | ->booleanNode('disable_asserts')->defaultFalse()->end() 47 | ->arrayNode('public_url') 48 | ->beforeNormalization()->castToArray()->end() 49 | ->defaultValue([]) 50 | ->scalarPrototype()->end() 51 | ->end() 52 | ->scalarNode('path_normalizer')->defaultNull()->end() 53 | ->scalarNode('public_url_generator')->defaultNull()->end() 54 | ->scalarNode('temporary_url_generator')->defaultNull()->end() 55 | ->booleanNode('read_only')->defaultFalse()->end() 56 | ->end() 57 | ->end() 58 | ->defaultValue([]) 59 | ->end() 60 | ->end() 61 | ; 62 | 63 | return $treeBuilder; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/DependencyInjection/FlysystemExtension.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\DependencyInjection; 13 | 14 | use League\Flysystem\Filesystem; 15 | use League\Flysystem\FilesystemOperator; 16 | use League\Flysystem\FilesystemReader; 17 | use League\Flysystem\FilesystemWriter; 18 | use League\Flysystem\ReadOnly\ReadOnlyFilesystemAdapter; 19 | use League\FlysystemBundle\Adapter\AdapterDefinitionFactory; 20 | use League\FlysystemBundle\Exception\MissingPackageException; 21 | use League\FlysystemBundle\Lazy\LazyFactory; 22 | use Symfony\Component\DependencyInjection\ContainerBuilder; 23 | use Symfony\Component\DependencyInjection\Definition; 24 | use Symfony\Component\DependencyInjection\Extension\Extension; 25 | use Symfony\Component\DependencyInjection\Reference; 26 | use Symfony\Component\OptionsResolver\OptionsResolver; 27 | 28 | /** 29 | * @author Titouan Galopin 30 | */ 31 | final class FlysystemExtension extends Extension 32 | { 33 | public function load(array $configs, ContainerBuilder $container): void 34 | { 35 | $configuration = new Configuration(); 36 | $config = $this->processConfiguration($configuration, $configs); 37 | 38 | $container 39 | ->setDefinition('flysystem.adapter.lazy.factory', new Definition(LazyFactory::class)) 40 | ->setPublic(false) 41 | ; 42 | 43 | $this->createStoragesDefinitions($config, $container); 44 | } 45 | 46 | private function createStoragesDefinitions(array $config, ContainerBuilder $container): void 47 | { 48 | $definitionFactory = new AdapterDefinitionFactory(); 49 | 50 | foreach ($config['storages'] as $storageName => $storageConfig) { 51 | // If the storage is a lazy one, it's resolved at runtime 52 | if ('lazy' === $storageConfig['adapter']) { 53 | $container->setDefinition($storageName, $this->createLazyStorageDefinition($storageName, $storageConfig['options'])); 54 | 55 | // Register named autowiring alias 56 | $container->registerAliasForArgument($storageName, FilesystemOperator::class, $storageName)->setPublic(false); 57 | $container->registerAliasForArgument($storageName, FilesystemReader::class, $storageName)->setPublic(false); 58 | $container->registerAliasForArgument($storageName, FilesystemWriter::class, $storageName)->setPublic(false); 59 | 60 | continue; 61 | } 62 | 63 | // Create adapter definition 64 | if ($adapter = $definitionFactory->createDefinition($storageConfig['adapter'], $storageConfig['options'], $storageConfig['directory_visibility'] ?? null)) { 65 | // Native adapter 66 | $container->setDefinition($id = 'flysystem.adapter.'.$storageName, $adapter)->setPublic(false); 67 | } else { 68 | // Custom adapter 69 | $container->setAlias($id = 'flysystem.adapter.'.$storageName, $storageConfig['adapter'])->setPublic(false); 70 | } 71 | 72 | // Create ReadOnly adapter 73 | if ($storageConfig['read_only']) { 74 | if (!class_exists(ReadOnlyFilesystemAdapter::class)) { 75 | throw new MissingPackageException("Missing package, to use the readonly option, run:\n\ncomposer require league/flysystem-read-only"); 76 | } 77 | 78 | $originalAdapterId = $id; 79 | $container->setDefinition( 80 | $id = $id.'.read_only', 81 | $this->createReadOnlyAdapterDefinition(new Reference($originalAdapterId)) 82 | ); 83 | } 84 | 85 | // Create storage definition 86 | $container->setDefinition( 87 | $storageName, 88 | $this->createStorageDefinition($storageName, new Reference($id), $storageConfig) 89 | ); 90 | 91 | // Register named autowiring alias 92 | $container->registerAliasForArgument($storageName, FilesystemOperator::class, $storageName)->setPublic(false); 93 | $container->registerAliasForArgument($storageName, FilesystemReader::class, $storageName)->setPublic(false); 94 | $container->registerAliasForArgument($storageName, FilesystemWriter::class, $storageName)->setPublic(false); 95 | } 96 | } 97 | 98 | private function createLazyStorageDefinition(string $storageName, array $options): Definition 99 | { 100 | $resolver = new OptionsResolver(); 101 | $resolver->setRequired('source'); 102 | $resolver->setAllowedTypes('source', 'string'); 103 | 104 | $definition = new Definition(FilesystemOperator::class); 105 | $definition->setPublic(false); 106 | $definition->setFactory([new Reference('flysystem.adapter.lazy.factory'), 'createStorage']); 107 | $definition->setArgument(0, $resolver->resolve($options)['source']); 108 | $definition->setArgument(1, $storageName); 109 | $definition->addTag('flysystem.storage', ['storage' => $storageName]); 110 | 111 | return $definition; 112 | } 113 | 114 | private function createStorageDefinition(string $storageName, Reference $adapter, array $config): Definition 115 | { 116 | $publicUrl = null; 117 | if ($config['public_url']) { 118 | $publicUrl = 1 === count($config['public_url']) ? $config['public_url'][0] : $config['public_url']; 119 | } 120 | 121 | $definition = new Definition(Filesystem::class); 122 | $definition->setPublic(false); 123 | $definition->setArgument(0, $adapter); 124 | $definition->setArgument(1, [ 125 | 'visibility' => $config['visibility'], 126 | 'directory_visibility' => $config['directory_visibility'], 127 | 'case_sensitive' => $config['case_sensitive'], 128 | 'disable_asserts' => $config['disable_asserts'], 129 | 'public_url' => $publicUrl, 130 | ]); 131 | $definition->setArgument(2, $config['path_normalizer'] ? new Reference($config['path_normalizer']) : null); 132 | $definition->setArgument(3, $config['public_url_generator'] ? new Reference($config['public_url_generator']) : null); 133 | $definition->setArgument(4, $config['temporary_url_generator'] ? new Reference($config['temporary_url_generator']) : null); 134 | $definition->addTag('flysystem.storage', ['storage' => $storageName]); 135 | 136 | return $definition; 137 | } 138 | 139 | private function createReadOnlyAdapterDefinition(Reference $adapter): Definition 140 | { 141 | $definition = new Definition(ReadOnlyFilesystemAdapter::class); 142 | $definition->setPublic(false); 143 | $definition->setArgument(0, $adapter); 144 | 145 | return $definition; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/Exception/MissingPackageException.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Exception; 13 | 14 | /** 15 | * @author Titouan Galopin 16 | */ 17 | final class MissingPackageException extends \RuntimeException 18 | { 19 | public function __construct(string $message = '', \Throwable $previous = null) 20 | { 21 | parent::__construct($message, 0, $previous); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/FlysystemBundle.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle; 13 | 14 | use League\FlysystemBundle\DependencyInjection\Compiler\GcloudFactoryPass; 15 | use League\FlysystemBundle\DependencyInjection\Compiler\LazyFactoryPass; 16 | use Symfony\Component\DependencyInjection\ContainerBuilder; 17 | use Symfony\Component\HttpKernel\Bundle\Bundle; 18 | 19 | /** 20 | * @author Titouan Galopin 21 | */ 22 | final class FlysystemBundle extends Bundle 23 | { 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | public function build(ContainerBuilder $container): void 28 | { 29 | parent::build($container); 30 | 31 | $container->addCompilerPass(new LazyFactoryPass()); 32 | $container->addCompilerPass(new GcloudFactoryPass()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Lazy/LazyFactory.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace League\FlysystemBundle\Lazy; 13 | 14 | use League\Flysystem\FilesystemOperator; 15 | use Psr\Container\ContainerInterface; 16 | 17 | /** 18 | * @author Titouan Galopin 19 | * 20 | * @internal 21 | */ 22 | final class LazyFactory 23 | { 24 | private ContainerInterface $storages; 25 | 26 | public function __construct(ContainerInterface $storages) 27 | { 28 | $this->storages = $storages; 29 | } 30 | 31 | public function createStorage(string $source, string $storageName): FilesystemOperator 32 | { 33 | if ($source === $storageName) { 34 | throw new \InvalidArgumentException('The "lazy" adapter source is referring to itself as "'.$source.'", which would lead to infinite recursion.'); 35 | } 36 | 37 | if (!$this->storages->has($source)) { 38 | throw new \InvalidArgumentException('You have requested a non-existent source storage "'.$source.'" in lazy storage "'.$storageName.'".'); 39 | } 40 | 41 | return $this->storages->get($source); 42 | } 43 | } 44 | --------------------------------------------------------------------------------