-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
[DependencyInjection] Fluent PHP DI Documentation #10824
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -288,17 +288,21 @@ config files: | |
|
||
.. code-block:: php | ||
|
||
use Symfony\Component\DependencyInjection\Reference; | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
return function(ContainerConfigurator $configurator) { | ||
$configurator->parameters() | ||
->set('mailer.transport', 'sendmail'); | ||
|
||
$container = $configurator->services(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not whether we should go for this or: return function (ContainerConfigurator $container) {
$container->parameters()
->set('mailer.transport', 'sendmail')
->set('xxx', 'XXX')
;
$container->services()
->set(...)->args(...)
->set(...)->call()->public()
->instancof()->tags()
// ... WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @HeahDude not sure it matters to much, I think I personally prefer a separate statement per definition just so that it makes the definitions more distinct in the code, but people can also style their stuff however they want. |
||
|
||
$container->set('mailer', 'Mailer') | ||
->args(['%mailer.transport%']); | ||
|
||
$container->set('newsletter_manager', 'NewsletterManager') | ||
->call('setMailer', [ref('mailer')]); | ||
}; | ||
HeahDude marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// ... | ||
$container->setParameter('mailer.transport', 'sendmail'); | ||
$container | ||
->register('mailer', 'Mailer') | ||
->addArgument('%mailer.transport%'); | ||
|
||
$container | ||
->register('newsletter_manager', 'NewsletterManager') | ||
->addMethodCall('setMailer', [new Reference('mailer')]); | ||
|
||
Learn More | ||
---------- | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -182,19 +182,18 @@ each time you ask for it. | |
.. code-block:: php | ||
|
||
// config/services.php | ||
use Symfony\Component\DependencyInjection\Definition; | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
// To use as default template | ||
$definition = new Definition(); | ||
return function(ContainerConfigurator $configurator) { | ||
$container = $configurator->services() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, what about: $container->services()
->defaults()->autowire()->autoconfigure()
->load('App\\', '../src/*')->exclude('../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}')
->set('x')->arg('$arg', ref('...'))
->alias('y', 'x')->public()
// ...
; ? |
||
->defaults() | ||
->autowire() | ||
->autoconfigure() | ||
->private(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Private should be removed every where on 4.2, this could be done in a dedicated PR too. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that just because services are now private by default? If so, should we have documentation describing that services are private by default starting in 4.2? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the case since Symfony 4. 4.2 is just now the lowest 4.x branch maintained. |
||
|
||
$definition | ||
->setAutowired(true) | ||
->setAutoconfigured(true) | ||
->setPublic(false) | ||
; | ||
|
||
// $this is a reference to the current loader | ||
$this->registerClasses($definition, 'App\\', '../src/*', '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'); | ||
$container->load('App\\', '../src/*') | ||
->exclude('../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'); | ||
}; | ||
|
||
.. tip:: | ||
|
||
|
@@ -396,7 +395,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume | |
# same as before | ||
App\: | ||
resource: '../src/*' | ||
exclude: '../src/{Entity,Migrations,Tests}' | ||
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' | ||
|
||
# explicitly configure the service | ||
App\Updates\SiteUpdateManager: | ||
|
@@ -416,6 +415,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume | |
<!-- ... --> | ||
|
||
<!-- Same as before --> | ||
|
||
<prototype namespace="App\" resource="../src/*" exclude="../src/{Entity,Migrations,Tests}"/> | ||
|
||
<!-- Explicitly configure the service --> | ||
|
@@ -428,23 +428,23 @@ pass here. No problem! In your configuration, you can explicitly set this argume | |
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
use App\Updates\SiteUpdateManager; | ||
use Symfony\Component\DependencyInjection\Definition; | ||
|
||
// Same as before | ||
$definition = new Definition(); | ||
return function(ContainerConfigurator $configurator) { | ||
$container = $configurator->services() | ||
->defaults() | ||
->autowire() | ||
->autoconfigure() | ||
->private(); | ||
|
||
$definition | ||
->setAutowired(true) | ||
->setAutoconfigured(true) | ||
->setPublic(false) | ||
; | ||
$container->load('App\\', '../src/*') | ||
->exclude('../src/{Entity,Migrations,Tests}'); | ||
|
||
$this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Migrations,Tests}'); | ||
$container->set(SiteUpdateManager::class)->arg('$adminEmail', 'manager@example.com'); | ||
}; | ||
|
||
// Explicitly configure the service | ||
$container->getDefinition(SiteUpdateManager::class) | ||
->setArgument('$adminEmail', 'manager@example.com'); | ||
|
||
Thanks to this, the container will pass ``manager@example.com`` to the ``$adminEmail`` | ||
argument of ``__construct`` when creating the ``SiteUpdateManager`` service. The | ||
|
@@ -503,13 +503,16 @@ parameter and in PHP config use the ``Reference`` class: | |
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
use App\Service\MessageGenerator; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
|
||
$container->autowire(MessageGenerator::class) | ||
->setAutoconfigured(true) | ||
->setPublic(false) | ||
->setArgument(0, new Reference('logger')); | ||
return function(ContainerConfigurator $configurator) { | ||
$container = $configurator->services(); | ||
$container->set(MessageGenerator::class) | ||
->autoconfigure() | ||
->args([ref('logger')]]); | ||
}; | ||
|
||
Working with container parameters is straightforward using the container's | ||
accessor methods for parameters:: | ||
|
@@ -605,13 +608,18 @@ But, you can control this and pass in a different logger: | |
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
use App\Service\MessageGenerator; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
|
||
$container->autowire(MessageGenerator::class) | ||
->setAutoconfigured(true) | ||
->setPublic(false) | ||
->setArgument('$logger', new Reference('monolog.logger.request')); | ||
return function(ContainerConfigurator $configurator) { | ||
$container = $configurator->services(); | ||
$container->set(SiteUpdateManager::class) | ||
->autowire() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please add the same comments as other examples and remove needless boilerplate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Y, definitely, not sure why I did it like that. |
||
->autoconfigure() | ||
->private(); | ||
->arg('$logger', ref('monolog.logger.request')); | ||
}; | ||
|
||
This tells the container that the ``$logger`` argument to ``__construct`` should use | ||
service whose id is ``monolog.logger.request``. | ||
|
@@ -693,21 +701,22 @@ You can also use the ``bind`` keyword to bind specific arguments by name or type | |
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
use App\Controller\LuckyController; | ||
use Psr\Log\LoggerInterface; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
|
||
$container->register(LuckyController::class) | ||
->setPublic(true) | ||
->setBindings([ | ||
'$adminEmail' => 'manager@example.com', | ||
'$requestLogger' => new Reference('monolog.logger.request'), | ||
LoggerInterface::class => new Reference('monolog.logger.request'), | ||
// optionally you can define both the name and type of the argument to match | ||
'string $adminEmail' => 'manager@example.com', | ||
LoggerInterface::class.' $requestLogger' => new Reference('monolog.logger.request'), | ||
]) | ||
; | ||
return function(ContainerConfigurator $configurator) { | ||
$container = $configurator->services()->defaults() | ||
->bind('$adminEmail', 'manager@example.com') | ||
->bind('$requestLogger', ref('monolog.logger.request')) | ||
->bind(LoggerInterface::class, ref('monolog.logger.request')) | ||
->bind('string $adminEmail', 'manager@example.com') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please don't remove comments |
||
->bind(LoggerInterface::class.' $requestLogger', ref('monolog.logger.request')); | ||
|
||
// ... | ||
}; | ||
|
||
By putting the ``bind`` key under ``_defaults``, you can specify the value of *any* | ||
argument for *any* service defined in this file! You can bind arguments by name | ||
|
@@ -809,6 +818,20 @@ But, if you *do* need to make a service public, override the ``public`` setting: | |
</services> | ||
</container> | ||
|
||
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
use App\Service\MessageGenerator; | ||
|
||
return function(ContainerConfigurator $configurator) { | ||
// ... same as code before | ||
|
||
$container->set(MessageGenerator::class) | ||
->public(); | ||
}; | ||
|
||
.. _service-psr4-loader: | ||
|
||
Importing Many Services at once with resource | ||
|
@@ -829,7 +852,7 @@ key. For example, the default Symfony configuration contains this: | |
# this creates a service per class whose id is the fully-qualified class name | ||
App\: | ||
resource: '../src/*' | ||
exclude: '../src/{Entity,Migrations,Tests}' | ||
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' | ||
|
||
.. code-block:: xml | ||
|
||
|
@@ -850,18 +873,14 @@ key. For example, the default Symfony configuration contains this: | |
.. code-block:: php | ||
|
||
// config/services.php | ||
use Symfony\Component\DependencyInjection\Definition; | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
// To use as default template | ||
$definition = new Definition(); | ||
|
||
$definition | ||
->setAutowired(true) | ||
->setAutoconfigured(true) | ||
->setPublic(false) | ||
; | ||
return function(ContainerConfigurator $configurator) { | ||
// ... | ||
|
||
$this->registerClasses($definition, 'App\\', '../src/*', '../src/{Entity,Migrations,Tests}'); | ||
$container->load('App\\', '../src/*') | ||
->exclude('../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'); | ||
}; | ||
|
||
.. tip:: | ||
|
||
|
@@ -998,27 +1017,30 @@ admin email. In this case, each needs to have a unique service id: | |
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
use App\Service\MessageGenerator; | ||
use App\Updates\SiteUpdateManager; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
|
||
$container->register('site_update_manager.superadmin', SiteUpdateManager::class) | ||
->setAutowired(false) | ||
->setArguments([ | ||
new Reference(MessageGenerator::class), | ||
new Reference('mailer'), | ||
'superadmin@example.com' | ||
]); | ||
|
||
$container->register('site_update_manager.normal_users', SiteUpdateManager::class) | ||
->setAutowired(false) | ||
->setArguments([ | ||
new Reference(MessageGenerator::class), | ||
new Reference('mailer'), | ||
'contact@example.com' | ||
]); | ||
|
||
$container->setAlias(SiteUpdateManager::class, 'site_update_manager.superadmin') | ||
return function(ContainerConfigurator $configurator) { | ||
// ... | ||
|
||
$container->set('site_update_manager.superadmin', SiteUpdateManager::class) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. $container->services()
// ...
->set('site_update_manager.superadmin', SiteUpdateManager::class)->autowire(false)->args([
ref(MessageGenerator::class),
ref('mailer'),
'superadmin@example.com',
])
->set(...) ? |
||
->autowire(false) | ||
->args([ | ||
ref(MessageGenerator::class), | ||
ref('mailer'), | ||
'superadmin@example.com' | ||
]); | ||
$container->set('site_update_manager.normal_users', SiteUpdateManager::class) | ||
->autowire(false) | ||
->args([ | ||
ref(MessageGenerator::class), | ||
ref('mailer'), | ||
'contact@example.com' | ||
]); | ||
$container->alias(SiteUpdateManager::class, 'site_update_manager.superadmin'); | ||
}; | ||
|
||
In this case, *two* services are registered: ``site_update_manager.superadmin`` | ||
and ``site_update_manager.normal_users``. Thanks to the alias, if you type-hint | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,9 +81,32 @@ what the file looks like in Symfony 4): | |
</services> | ||
</container> | ||
|
||
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
return function(ContainerConfigurator $configurator) { | ||
$container = $configurator->services() | ||
->defaults() | ||
->autowire() | ||
->autoconfigure() | ||
->private(); | ||
|
||
$container->load('App\\', '../src/*') | ||
->exclude('../src/{Entity,Migrations,Tests}'); | ||
|
||
$container->load('App\\Controller\\', '../src/Controller') | ||
->tag('controller.service_arguments'); | ||
}; | ||
|
||
This small bit of configuration contains a paradigm shift of how services | ||
are configured in Symfony. | ||
|
||
.. versionadded:: 3.4 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be removed while targeting 4.2 :). |
||
|
||
PHP Fluent DI was introduced in Symfony 3.4. | ||
|
||
.. _`service-33-changes-automatic-registration`: | ||
|
||
1) Services are Loaded Automatically | ||
|
@@ -126,6 +149,18 @@ thanks to the following config: | |
</services> | ||
</container> | ||
|
||
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
return function(ContainerConfigurator $configurator) { | ||
// ... | ||
|
||
$container->load('App\\', '../src/*') | ||
->exclude('../src/{Entity,Migrations,Tests}'); | ||
}; | ||
|
||
This means that every class in ``src/`` is *available* to be used as a | ||
service. And thanks to the ``_defaults`` section at the top of the file, all of | ||
these services are **autowired** and **private** (i.e. ``public: false``). | ||
|
@@ -319,11 +354,15 @@ The third big change is that, in a new Symfony 3.3 project, your controllers are | |
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
// ... | ||
return function(ContainerConfigurator $configurator) { | ||
// ... | ||
|
||
$container->load('App\\Controller\\', '../src/Controller') | ||
->tag('controller.service_arguments'); | ||
}; | ||
|
||
$definition->addTag('controller.service_arguments'); | ||
$this->registerClasses($definition, 'App\\Controller\\', '../src/Controller/*'); | ||
|
||
But, you might not even notice this. First, your controllers *can* still extend | ||
the same base controller class (``AbstractController``). | ||
|
@@ -464,6 +503,21 @@ inherited from an abstract definition: | |
</services> | ||
</container> | ||
|
||
.. code-block:: php | ||
|
||
// config/services.php | ||
namespace Symfony\Component\DependencyInjection\Loader\Configurator; | ||
|
||
use App\Domain\LoaderInterface; | ||
|
||
return function(ContainerConfigurator $configurator) { | ||
// ... | ||
|
||
$container->instanceof(LoaderInterface::class | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing parenthesis? I would go for a : $container->services()
// ...
->instanceof(LoaderInterface::class)->tag('app.domain_loader')->public()
; |
||
->public() | ||
->tag('app.domain_loader'); | ||
}; | ||
|
||
What about Performance | ||
---------------------- | ||
|
||
|
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 am in favor of the semi colon on a new line for both reduced diff and avoiding the care of dragging it while editing the files on the long run.
But that is already inconsistent in the docs and you're targeting 4.2 so I would also be in favor of having someone or me take care of this in a dedicated PR if others agree.
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.
semi-colon on newline feels weird to me personally, but the reduced diff argument is a good one. I'd imagine a separate PR to clean that up and make consistent would be advised.