Skip to content

Commit 4fc9596

Browse files
committed
Merge branch '6.1' into 6.2
* 6.1: [DI] Document proxifying interfaces for lazy services
2 parents 3964d9d + ca6d57a commit 4fc9596

File tree

1 file changed

+73
-1
lines changed

1 file changed

+73
-1
lines changed

service_container/lazy_services.rst

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ until you interact with the proxy in some way.
2525

2626
.. caution::
2727

28-
Lazy services do not support `final`_ classes.
28+
Lazy services do not support `final`_ classes, but you can use
29+
`Interface Proxifying`_ to work around this limitation.
2930

3031
In PHP versions prior to 8.0 lazy services do not support parameters with
3132
default values for built-in PHP classes (e.g. ``PDO``).
@@ -87,5 +88,76 @@ To check if your lazy service works you can check the interface of the received
8788
dump(class_implements($service));
8889
// the output should include "Symfony\Component\VarExporter\LazyGhostObjectInterface"
8990

91+
Interface Proxifying
92+
--------------------
93+
94+
Under the hood, proxies generated to lazily load services inherit from the class
95+
used by the service. However, sometimes this is not possible at all (e.g. because
96+
the class is `final`_ and can not be extended) or not convenient.
97+
98+
To workaround this limitation, you can configure a proxy to only implement
99+
specific interfaces.
100+
101+
.. configuration-block::
102+
103+
.. code-block:: yaml
104+
105+
# config/services.yaml
106+
services:
107+
App\Twig\AppExtension:
108+
lazy: 'Twig\Extension\ExtensionInterface'
109+
# or a complete definition:
110+
lazy: true
111+
tags:
112+
- { name: 'proxy', interface: 'Twig\Extension\ExtensionInterface' }
113+
114+
.. code-block:: xml
115+
116+
<!-- config/services.xml -->
117+
<?xml version="1.0" encoding="UTF-8" ?>
118+
<container xmlns="http://symfony.com/schema/dic/services"
119+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
120+
xsi:schemaLocation="http://symfony.com/schema/dic/services
121+
https://symfony.com/schema/dic/services/services-1.0.xsd">
122+
123+
<services>
124+
<service id="App\Twig\AppExtension" lazy="Twig\Extension\ExtensionInterface"/>
125+
<!-- or a complete definition: -->
126+
<service id="App\Twig\AppExtension" lazy="true">
127+
<tag name="proxy" interface="Twig\Extension\ExtensionInterface"/>
128+
</service>
129+
</services>
130+
</container>
131+
132+
.. code-block:: php
133+
134+
// config/services.php
135+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
136+
137+
use App\Twig\AppExtension;
138+
use Twig\Extension\ExtensionInterface;
139+
140+
return function(ContainerConfigurator $configurator) {
141+
$services = $configurator->services();
142+
143+
$services->set(AppExtension::class)
144+
->lazy()
145+
->tag('proxy', ['interface' => ExtensionInterface::class])
146+
;
147+
};
148+
149+
The virtual `proxy`_ injected into other services will only implement the
150+
specified interfaces and will not extend the original service class, allowing to
151+
lazy load services using `final`_ classes. You can configure the proxy to
152+
implement multiple interfaces by adding new "proxy" tags.
153+
154+
.. tip::
155+
156+
This feature can also act as a safe guard: given that the proxy does not
157+
extend the original class, only the methods defined by the interface can
158+
be called, preventing to call implementation specific methods. It also
159+
prevents injecting the dependency at all if you type-hinted a concrete
160+
implementation instead of the interface.
161+
90162
.. _`ghost object`: https://en.wikipedia.org/wiki/Lazy_loading#Ghost
91163
.. _`final`: https://www.php.net/manual/en/language.oop5.final.php

0 commit comments

Comments
 (0)