mnapoli / fluent-symfony
Installs: 36 611
Dependents: 1
Suggesters: 0
Security: 0
Stars: 78
Watchers: 7
Forks: 3
Open Issues: 2
Requires
- php: >=7.0
- symfony/config: ^3.2
- symfony/dependency-injection: ^3.2
Requires (Dev)
- phpunit/phpunit: ^6.0
- symfony/symfony: ^3.2
README
This package offers an alternative configuration syntax for Symfony's container, inspired by PHP-DI's configuration.
- Why?
- Comparison with existing formats
- Installation
- Syntax
- Parameters
- Services
- Factories
- Aliases
- Tags
- Imports
- Extensions
Why?
The main goal is to benefit from stricter analysis from the PHP engine and IDEs. If you are interested you can also read why YAML was replaced by a similar syntax in PHP-DI 5.
-
auto-completion on classes or constants:
-
auto-completion when writing configuration:
-
real time validation in IDEs:
-
constant support:
-
better refactoring support
Comparison with existing formats
Currently, in Symfony, you can configure the container using:
-
YAML
parameters: mailer.transport: sendmail services: mailer: class: Mailer arguments: ['%mailer.transport%']
-
XML
<?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <parameters> <parameter key="mailer.transport">sendmail</parameter> </parameters> <services> <service id="mailer" class="Mailer"> <argument>%mailer.transport%</argument> </service> </services> </container>
-
PHP code
$container->setParameter('mailer.transport', 'sendmail'); $container ->register('mailer', 'Mailer') ->addArgument('%mailer.transport%');
With this package, you can now use a 4th alternative:
return [ 'mailer.transport' => 'sendmail', 'mailer' => create(Mailer::class) ->arguments('%mailer.transport%'), ];
Installation
composer require mnapoli/fluent-symfony
To enable the new format in a Symfony fullstack application, simply import the EnableFluentConfig
trait in app/AppKernel.php
, for example:
<?php use Fluent\EnableFluentConfig; use Symfony\Component\HttpKernel\Kernel; // ... class AppKernel extends Kernel { use EnableFluentConfig; // ... }
You can now either:
-
write all your config in "fluent" syntax, to do that change your
AppKernel
to load.php
files instead of.yml
:class AppKernel extends Kernel { use EnableFluentConfig; // ... public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.php'); } }
-
or import PHP config files from YAML config files:
imports: - services.php # ...
Be advised that PHP config files in the "traditional" form (see the documentation) are still supported and will continue to work.
Syntax
A configuration file must return
a PHP array. In that array, parameters, services and imports are defined altogether:
<?php # app/config/config.php return [ // ... ];
Parameters
Parameters are declared as simple values:
return [ 'foo' => 'bar', ];
This is the same as:
parameters: foo: 'bar'
Parameters and services can be mixed in the same array.
Services
Services can be declared simply using the create()
function helper:
use function Fluent\create; return [ 'mailer' => create(Mailer::class), ];
When calling $container->get('mailer')
an instance of the Mailer
class will be created and returned.
This is the same as:
services: mailer: class: Mailer
Using the class name as the entry ID
If the container entry ID is a class name, you can skip it when calling create()
.
return [ Mailer::class => create(), ];
Autowiring
Services can also be automatically wired using the autowire()
function helper in place of create()
:
use function Fluent\autowire; return [ Mailer::class => autowire(), ];
This is the same as:
services: Mailer: class: Mailer autowire: true
Constructor arguments
return [ 'mailer' => create(Mailer::class) ->arguments('smtp.google.com', 2525), ];
This is the same as:
services: mailer: class: Mailer arguments: ['smtp.google.com', 2525]
Dependencies
Parameters can be injected using the '%foo%'
syntax:
return [ 'mailer' => create(Mailer::class) ->arguments('%mailer.transport%'), ];
This is the same as:
services: mailer: class: Mailer arguments: ['%mailer.transport%']
Services can be injected using the get()
function helper:
use function Fluent\get; return [ 'newsletter_manager' => create(NewsletterManager::class) ->arguments(get('mailer')), ];
This is the same as:
services: newsletter_manager: class: NewsletterManager arguments: ['@mailer']
Setter injection
return [ 'mailer' => create(Mailer::class) ->method('setHostAndPort', 'smtp.google.com', 2525), ];
This is the same as:
services: mailer: class: Mailer calls: - [setHostAndPort, ['smtp.google.com', 2525]]
Property injection
return [ 'mailer' => create(Mailer::class) ->property('host', 'smtp.google.com'), ];
This is the same as:
services: mailer: class: Mailer properties: host: smtp.google.com
Optional service references
Services can have optional dependencies, so that the dependency is not required for it to work.
Setting missing dependencies to null
use function Fluent\create; use function Fluent\get; return [ 'newsletter_manager' => create(NewsletterManager::class) ->arguments(get('mailer')->nullIfMissing()), ];
This is the same as :
<?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <service id="newsletter_manager" class="NewsletterManager"> <argument type="service" id="mailer" on-invalid="null" /> </service> </services> </container>
Ignore missing dependencies
When used with setter injection, it's possible to remove the method call using ignoreIfMissing()
:
use function Fluent\create; use function Fluent\get; return [ 'newsletter_manager' => create(NewsletterManager::class) ->method('setMailer', get('mailer')->ignoreIfMissing()), ];
This is the same as :
services: app.newsletter_manager: class: AppBundle\Newsletter\NewsletterManager calls: - [setMailer, ['@?app.mailer']]
Private Services
return [ Mailer::class => create() ->private(), ];
This is the same as:
services: mailer: class: Mailer public: false
Decorated services
Services can be decorated with the decorate()
method
return [ 'decorating_mailer' => create(MailerDecorator::class) ->decorate('mailer') ->argument(get('decorating_mailer.inner')), ];
This is the same as:
services: decorating_mailer: class: 'MailerDecorator' decorates: 'mailer' arguments: ['@decorating_mailer.inner']
If you want to apply more than one decorator to a service, you can change the inner service name (IE the decorated service) and configure the priority of decoration :
return [ 'foo' => create(Foo::class), 'bar' => create(Bar::class) ->decorate('foo', 'bar.foo', 5) ->arguments(get('bar.foo')) , 'baz': create(Baz::class) ->decorate('foo', 'baz.foo', 1), ->arguments(get('baz.foo')) ];
This is the same as:
foo: class: Foo bar: class: Bar decorates: foo decoration_inner_name: 'bar.foo' decoration_priority: 5 arguments: ['@bar.foo'] baz: class: Baz decorates: foo decoration_inner_name: 'baz.foo' decoration_priority: 1 arguments: ['@baz.inner']
Non shared services
All services are shared by default. You can force the container to always create a new instance using the unshared()
function helper:
return [ 'app.phpmailer' => create(PhpMailer::class) ->unshared(), ];
This is the same as:
services: app.phpmailer: class: AppBundle\Mail\PhpMailer shared: false
Synthetic services
Services can be injected at runtime. You can inject a class instance as service, instead of configuring the container to create a new instance using the synthetic()
function helper:
return [ 'app.phpmailer' => create(PhpMailer::class) ->synthetic(), ];
This is the same as:
services: app.phpmailer: class: AppBundle\Mail\PhpMailer synthetic: true
Factories
Services can be created by factories using the factory()
function helper:
use function Fluent\factory; return [ 'newsletter_manager' => factory([NewsletterManager::class, 'create'], NewsletterManager::class) ->arguments('foo', 'bar'), ];
When calling $container->get('newsletter_manager')
the result of NewsletterManager::create('foo', 'bar')
will be returned.
This is the same as:
services: newsletter_manager: factory: ['AppBundle\Email\NewsletterManager', 'create'] class: 'AppBundle\Email\NewsletterManager' arguments: ['foo', 'bar']
When using the class name as service ID, you don't have to explicitly state the class name of the service:
return [ // you can write: NewsletterManager::class => factory([NewsletterManager::class, 'create']), // instead of: NewsletterManager::class => factory([NewsletterManager::class, 'create'], NewsletterManager::class), ];
Aliases
Services can be aliased using the alias()
function helper:
use function Fluent\create; use function Fluent\alias; return [ 'app.phpmailer' => create(PhpMailer::class), 'app.mailer' => alias('app.phpmailer'), ];
When calling $container->get('app.mailer')
the app.phpmailer
entry will be returned.
This is the same as:
services: app.phpmailer: class: AppBundle\Mail\PhpMailer app.mailer: alias: app.phpmailer
Private Aliases
return [ 'app.phpmailer' => create(PhpMailer::class), 'app.mailer' => alias('app.phpmailer') ->private(), ];
This is the same as:
services: app.phpmailer: class: AppBundle\Mail\PhpMailer app.mailer: alias: app.phpmailer public: false
Tags
Services can be tagged :
return [ 'mailer' => create(Mailer::class) ->tag('foo', ['fizz' => 'buzz', 'bar' => 'baz']) ->tag('bar'), ];
This is the same as:
services: mailer: class: Mailer tags: - {name: foo, fizz: buzz, bar: baz} - {name: bar}
Imports
Other configuration files can be imported using the import()
function helper:
use function Fluent\import; return [ import('services/mailer.php'), ];
You will notice that the array item is not indexed by an entry ID.
This is the same as:
imports: - { resource: services/mailer.yml }
Extensions
Extensions (like the framework configuration for example) can be configured using the extension()
function helper:
use function Fluent\extension; return [ extension('framework', [ 'http_method_override' => true, 'trusted_proxies' => ['192.0.0.1', '10.0.0.0/8'], ]), ];
This is the same as:
framework: http_method_override: true trusted_proxies: [192.0.0.1, 10.0.0.0/8]