Skip to content

Commit 161fd74

Browse files
committed
minor #18347 [DependencyInjection] Add support for generating lazy closures (alexandre-daubois)
This PR was merged into the 6.3 branch. Discussion ---------- [DependencyInjection] Add support for generating lazy closures Resolves #18090 I moved existing sections about those attributes and reworded them with (I hope) better examples. I followed what's been written [by Nicolas](#18090 (comment)). I feel this makes more sense to group those attributes in the `Autowiring` page. I still left a few `See also` redirecting to this new section. Commits ------- 1ffdcee [DependencyInjection] Add support for generating lazy closures
2 parents 91209c3 + 1ffdcee commit 161fd74

File tree

4 files changed

+110
-84
lines changed

4 files changed

+110
-84
lines changed

reference/attributes.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ Dependency Injection
3131
* :ref:`Autoconfigure <lazy-services_configuration>`
3232
* :ref:`AutoconfigureTag <di-instanceof>`
3333
* :ref:`Autowire <autowire-attribute>`
34-
* :ref:`AutowireCallable <container_closure-as-argument>`
34+
* :ref:`AutowireCallable <autowiring_closures>`
3535
* :doc:`AutowireDecorated </service_container/service_decoration>`
36-
* :doc:`AutowireServiceClosure </service_container/service_closures>`
36+
* :ref:`AutowireServiceClosure <autowiring_closures>`
3737
* :ref:`Exclude <service-psr4-loader>`
3838
* :ref:`TaggedIterator <tags_reference-tagged-services>`
3939
* :ref:`TaggedLocator <service-subscribers-locators_defining-service-locator>`

service_container.rst

+5-45
Original file line numberDiff line numberDiff line change
@@ -780,54 +780,14 @@ Our configuration looks like this:
780780
;
781781
};
782782
783-
.. versionadded:: 6.1
784-
785-
The ``closure`` argument type was introduced in Symfony 6.1.
786-
787-
It is also possible to convert a callable into an injected closure
788-
thanks to the
789-
:class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireCallable`
790-
attribute. Let's say our ``MessageHashGenerator`` class now has a ``generate()``
791-
method::
792-
793-
// src/Hash/MessageHashGenerator.php
794-
namespace App\Hash;
795-
796-
class MessageHashGenerator
797-
{
798-
public function generate(): string
799-
{
800-
// Compute and return a message hash
801-
}
802-
}
783+
.. seealso::
803784

804-
We can inject the ``generate()`` method of the ``MessageHashGenerator``
805-
like this::
806-
807-
// src/Service/MessageGenerator.php
808-
namespace App\Service;
809-
810-
use App\Hash\MessageHashGenerator;
811-
use Psr\Log\LoggerInterface;
812-
use Symfony\Component\DependencyInjection\Attribute\AutowireCallable;
785+
Closures can be injected :ref:`by using autowiring <autowiring_closures>`
786+
and its dedicated attributes.
813787

814-
class MessageGenerator
815-
{
816-
public function __construct(
817-
private LoggerInterface $logger,
818-
#[AutowireCallable(service: MessageHashGenerator::class, method: 'generate')]
819-
private \Closure $generateMessageHash
820-
) {
821-
// ...
822-
}
823-
824-
// ...
825-
}
826-
827-
.. versionadded:: 6.3
788+
.. versionadded:: 6.1
828789

829-
The :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireCallable`
830-
attribute was introduced in Symfony 6.3.
790+
The ``closure`` argument type was introduced in Symfony 6.1.
831791

832792
.. _services-binding:
833793

service_container/autowiring.rst

+98
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,104 @@ The ``#[Autowire]`` attribute can also be used for :ref:`parameters <service-par
643643

644644
The ``param`` and ``env`` arguments were introduced in Symfony 6.3.
645645

646+
.. _autowiring_closures:
647+
648+
Generate Closures With Autowiring
649+
---------------------------------
650+
651+
A **service closure** is an anonymous function that returns a service. This type
652+
of instanciation is handy when you are dealing with lazy-loading.
653+
654+
Automatically creating a closure encapsulating the service instanciation can be
655+
done with the
656+
:class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireServiceClosure`
657+
attribute::
658+
659+
// src/Service/Remote/MessageFormatter.php
660+
namespace App\Service\Remote;
661+
662+
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
663+
664+
#[AsAlias('third_party.remote_message_formatter')]
665+
class MessageFormatter
666+
{
667+
public function __construct()
668+
{
669+
// ...
670+
}
671+
672+
public function format(string $message): string
673+
{
674+
// ...
675+
}
676+
}
677+
678+
// src/Service/MessageGenerator.php
679+
namespace App\Service;
680+
681+
use App\Service\Remote\MessageFormatter;
682+
use Symfony\Component\DependencyInjection\Attribute\AutowireServiceClosure;
683+
684+
class MessageGenerator
685+
{
686+
public function __construct(
687+
#[AutowireServiceClosure('third_party.remote_message_formatter')]
688+
\Closure $messageFormatterResolver
689+
) {
690+
}
691+
692+
public function generate(string $message): void
693+
{
694+
$formattedMessage = ($this->messageFormatterResolver)()->format($message);
695+
696+
// ...
697+
}
698+
}
699+
700+
.. versionadded:: 6.3
701+
702+
The :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireServiceClosure`
703+
attribute was introduced in Symfony 6.3.
704+
705+
It is common that a service accepts a closure with a specific signature.
706+
In this case, you can use the
707+
:class:`Symfony\Component\DependencyInjection\Attribute\\AutowireCallable` attribute
708+
to generate a closure with the same signature as a specific method of a service. When
709+
this closure is called, it will pass all its arguments to the underlying service
710+
function::
711+
712+
// src/Service/MessageGenerator.php
713+
namespace App\Service;
714+
715+
use Symfony\Component\DependencyInjection\Attribute\AutowireCallable;
716+
717+
class MessageGenerator
718+
{
719+
public function __construct(
720+
#[AutowireCallable(service: 'third_party.remote_message_formatter', method: 'format')]
721+
\Closure $formatCallable
722+
) {
723+
}
724+
725+
public function generate(string $message): void
726+
{
727+
$formattedMessage = ($this->formatCallable)($message);
728+
729+
// ...
730+
}
731+
}
732+
733+
Finally, you can pass the ``lazy: true`` option to the
734+
:class:`Symfony\Component\DependencyInjection\Attribute\\AutowireCallable`
735+
attribute. By doing so, the callable will automatically be lazy, which means
736+
that the encapsulated service will be instantiated **only** at the
737+
closure's first call.
738+
739+
.. versionadded:: 6.3
740+
741+
The :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireCallable`
742+
attribute was introduced in Symfony 6.3.
743+
646744
.. _autowiring-calls:
647745

648746
Autowiring other Methods (e.g. Setters and Public Typed Properties)

service_container/service_closures.rst

+5-37
Original file line numberDiff line numberDiff line change
@@ -92,45 +92,13 @@ argument of type ``service_closure``:
9292
9393
.. seealso::
9494

95-
Another way to inject services lazily is via a
96-
:doc:`service locator </service_container/service_subscribers_locators>`.
97-
98-
Thanks to the
99-
:class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireServiceClosure`
100-
attribute, defining a service wrapped in a closure can directly be done
101-
in the service class, without further configuration::
102-
103-
// src/Service/MyService.php
104-
namespace App\Service;
105-
106-
use Symfony\Component\DependencyInjection\Attribute\AutowireServiceClosure;
107-
use Symfony\Component\Mailer\MailerInterface;
108-
109-
class MyService
110-
{
111-
public function __construct(
112-
#[AutowireServiceClosure('mailer')]
113-
private \Closure $mailer
114-
) {
115-
}
116-
117-
public function doSomething(): void
118-
{
119-
// ...
120-
121-
$this->getMailer()->send($email);
122-
}
95+
Service closures can be injected :ref:`by using autowiring <autowiring_closures>`
96+
and its dedicated attributes.
12397

124-
private function getMailer(): MailerInterface
125-
{
126-
return ($this->mailer)();
127-
}
128-
}
129-
130-
.. versionadded:: 6.3
98+
.. seealso::
13199

132-
The :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireServiceClosure`
133-
attribute was introduced in Symfony 6.3.
100+
Another way to inject services lazily is via a
101+
:doc:`service locator </service_container/service_subscribers_locators>`.
134102

135103
Using a Service Closure in a Compiler Pass
136104
------------------------------------------

0 commit comments

Comments
 (0)