-
Notifications
You must be signed in to change notification settings - Fork 73
[DX] Extend adapter builder #186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 3.x
Are you sure you want to change the base?
Conversation
After a second thought, we could open it this way and think about the last 2 points later. |
This PR implements a solution to the issue discussed in thephpleague#181, enabling external bundles to register their custom storage adapters with Flysystem Bundle without requiring them to be directly added to the main bundle's codebase. I had to remove the `@internal` annotation from `AdapterDefinitionBuilderInterface` and `AbstractAdapterDefinitionBuilder` to allow them to be referenced by other bundles. ## Usage Example External bundles can now register their adapters as follows: ``` php class AzureBlobStorageAdapterBundle extends Bundle { /** * {@inheritdoc} */ public function build(ContainerBuilder $container): void { parent::build($container); /** @var FlysystemExtension $extension */ $extension = $container->getExtension('flysystem'); $extension->addAdapterDefinitionBuilder(new AzureOssAdapterDefinitionBuilder()); } } ```
4e314a1
to
856dbe6
Compare
ceb7865
to
409f306
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added some random comments. I think it's a good idea to review this.
->end(); | ||
} | ||
public function createAdapter(ContainerBuilder $container, string $storageName, array $options, ?string $defaultVisibilityForDirectories): string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method should receive a ContainerConfigurator
. The PHP-DSL for service declaration is very convenient. It's the same as service.php
config files.
Example of how it's used in the MicroKernelTrait
: https://github.com/symfony/symfony/blob/e48850bdb8161e1aa1338432ea469932857b2df5/src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php#L194
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's probably very convenient, but we need to dynamically register adapter services based on configuration. Since the conditions aren't always known in advance, the ContainerBuilder gives us direct access to service registration methods, which seems more straightforward for this conditional, programmatic service creation.
Once registered, you can use the `debug:config flysystem` command to see your custom adapter | ||
and all its available options in the configuration tree. | ||
|
||
### Testing your custom builder |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding this section. Tests are too often skipped because people don't know how to write them.
public function getRequiredPackages(): array | ||
{ | ||
// Return required packages for your adapter | ||
// Format: ['ClassName' => 'vendor/package-name'] | ||
return []; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The required package is not only about the package name, but also the version. Does this syntax allow adding the version constraint after the package name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this syntax doesn't support version constraints. It could be interesting, but the use cases are very limited (if not non-existent).
Currently, this mechanism is very useful for official adapters because their packages are separate and have very specific dependencies for each use case.
For a public builder API, the adapter package should theoretically be self-contained. For example, with azure-oss/storage-blob-flysystem
, there are two approaches: either Azure OSS provides a dedicated bundle to manage their adapter, or the adapter package includes the AdapterBuilder and users manually register it via the Kernel.
/** @var FlysystemExtension $extension */ | ||
$extension = $container->getExtension('flysystem'); | ||
$extension->addAdapterDefinitionBuilder(new Builder\AsyncAwsAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\AwsAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\AzureAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\FtpAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\GcloudAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\GridFSAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\LazyAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\LocalAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\MemoryAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\SftpAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\WebDAVAdapterDefinitionBuilder()); | ||
$extension->addAdapterDefinitionBuilder(new Builder\BunnyCDNAdapterDefinitionBuilder()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Features such as autoconfiguration cannot be used to automatically inject an adapter just by creating a class. This function must be called in the bundle or kernel to explicitly add the adapter class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it follows the same design pattern as Symfony's security authenticators. I don't think there's any other way (at least for now) to make them available in the TreeBuilder configuration.
409f306
to
ef2675f
Compare
Before merging this PR: #182
I suggested in this comment a rework of parts of the bundle to improve separation of concerns and provide a better developer experience (DX), in preparation for allowing third parties to create their own custom adapters.
Proposed changes
Externalize the
getRequiredPackages()
logicAdapterDefinitionBuilderInterface
.AdapterDefinitionFactory
handle the package check before callingcreateDefinition()
.AbstractAdapterDefinitionBuilder
Extract Unix visibility/permissions logic
configureUnixOptions()
andcreateUnixDefinition()
out of the abstract base class.ProvidesUnixPermissions
) – easier to share but harder to testUnixVisibilityHelper
) – more testable but less flexibleAbstractAdapterDefinitionBuilder
Make builder options discoverable via Symfony's config system
addConfiguration(NodeBuilder $builder)
(similar to what Symfony Security does)adapter
andoptions
-based configuration style.Clean up the abstract class
AbstractAdapterDefinitionBuilder
down to only keepcreateDefinition()
.configureOptions
configureDefinition
I started this PR as a first step, like an RFC. Any feedback is welcome 😉
Benefits of this new config format :
debug:config flysystem
andconfig:dump-reference
show all available adapters and their options.