diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst
index 989dba24549..09fed684b0d 100644
--- a/components/phpunit_bridge.rst
+++ b/components/phpunit_bridge.rst
@@ -621,7 +621,7 @@ toggle a behavior::
public function hello(): string
{
if (class_exists(DependencyClass::class)) {
- return 'The dependency bahavior.';
+ return 'The dependency behavior.';
}
return 'The default behavior.';
@@ -639,7 +639,7 @@ are installed during tests) would look like::
public function testHello()
{
$class = new MyClass();
- $result = $class->hello(); // "The dependency bahavior."
+ $result = $class->hello(); // "The dependency behavior."
// ...
}
@@ -663,7 +663,7 @@ classes, interfaces and/or traits for the code to run::
ClassExistsMock::withMockedClasses([DependencyClass::class => false]);
$class = new MyClass();
- $result = $class->hello(); // "The default bahavior."
+ $result = $class->hello(); // "The default behavior."
// ...
}
diff --git a/routing.rst b/routing.rst
index 810b9310203..0a452e2a8ac 100644
--- a/routing.rst
+++ b/routing.rst
@@ -431,7 +431,6 @@ defined as ``/blog/{slug}``:
-
.. code-block:: php
@@ -525,7 +524,6 @@ the ``{page}`` parameter using the ``requirements`` option:
-
.. code-block:: php
diff --git a/service_container/calls.rst b/service_container/calls.rst
index 925544822a1..f8667fa1a6f 100644
--- a/service_container/calls.rst
+++ b/service_container/calls.rst
@@ -77,3 +77,79 @@ To configure the container to call the ``setLogger`` method, use the ``calls`` k
->call('setLogger', [ref('logger')]);
};
+
+.. versionadded:: 4.3
+
+ The ``immutable-setter`` injection was introduced in Symfony 4.3.
+
+In order to provide immutable services, some classes implement immutable setters.
+Such setters return a new instance of the configured class
+instead of mutating the object they were called on::
+
+ namespace App\Service;
+
+ use Psr\Log\LoggerInterface;
+
+ class MessageGenerator
+ {
+ private $logger;
+
+ /**
+ * @return static
+ */
+ public function withLogger(LoggerInterface $logger)
+ {
+ $new = clone $this;
+ $new->logger = $logger;
+
+ return $new;
+ }
+
+ // ...
+ }
+
+Because the method returns a separate cloned instance, configuring such a service means using
+the return value of the wither method (``$service = $service->withLogger($logger);``).
+The configuration to tell the container it should do so would be like:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/services.yaml
+ services:
+ App\Service\MessageGenerator:
+ # ...
+ calls:
+ - method: withLogger
+ arguments:
+ - '@logger'
+ returns_clone: true
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/services.php
+ use App\Service\MessageGenerator;
+ use Symfony\Component\DependencyInjection\Reference;
+
+ $container->register(MessageGenerator::class)
+ ->addMethodCall('withLogger', [new Reference('logger')], true);
diff --git a/service_container/definitions.rst b/service_container/definitions.rst
index 370f0dc7f25..48ebd7b8818 100644
--- a/service_container/definitions.rst
+++ b/service_container/definitions.rst
@@ -117,6 +117,9 @@ any method calls in the definitions as well::
// configures a new method call
$definition->addMethodCall('setLogger', [new Reference('logger')]);
+ // configures an immutable-setter
+ $definition->addMethodCall('withLogger', [new Reference('logger')], true);
+
// replaces all previously configured method calls with the passed array
$definition->setMethodCalls($methodCalls);
diff --git a/service_container/injection_types.rst b/service_container/injection_types.rst
index 897bb922554..fd383ff11ed 100644
--- a/service_container/injection_types.rst
+++ b/service_container/injection_types.rst
@@ -105,6 +105,112 @@ working with optional dependencies. It is also more difficult to use in
combination with class hierarchies: if a class uses constructor injection
then extending it and overriding the constructor becomes problematic.
+Immutable-setter Injection
+--------------------------
+
+.. versionadded:: 4.3
+
+ The ``immutable-setter`` injection was introduced in Symfony 4.3.
+
+Another possible injection is to use a method which returns a separate instance
+by cloning the original service, this approach allows you to make a service immutable::
+
+ // ...
+ use Symfony\Component\Mailer\MailerInterface;
+
+ class NewsletterManager
+ {
+ private $mailer;
+
+ /**
+ * @required
+ * @return static
+ */
+ public function withMailer(MailerInterface $mailer)
+ {
+ $new = clone $this;
+ $new->mailer = $mailer;
+
+ return $new;
+ }
+
+ // ...
+ }
+
+In order to use this type of injection, don't forget to configure it:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/services.yaml
+ services:
+ # ...
+
+ app.newsletter_manager:
+ class: App\Mail\NewsletterManager
+ calls:
+ - [withMailer, ['@mailer'], true]
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/services.php
+ use App\Mail\NewsletterManager;
+ use Symfony\Component\DependencyInjection\Reference;
+
+ // ...
+ $container->register('app.newsletter_manager', NewsletterManager::class)
+ ->addMethodCall('withMailer', [new Reference('mailer')], true);
+
+.. note::
+
+ If you decide to use autowiring, this type of injection requires
+ that you add a ``@return static`` docblock in order for the container
+ to be capable of registering the method.
+
+This approach is useful if you need to configure your service according to your needs,
+so, here's the advantages of immutable-setters:
+
+* Immutable setters works with optional dependencies, this way, if you don't need
+ a dependency, the setter don't need to be called.
+
+* Like the constructor injection, using immutable setters force the dependency to stay
+ the same during the lifetime of a service.
+
+* This type of injection works well with traits as the service can be composed,
+ this way, adapting the service to your application requirements is easier.
+
+* The setter can be called multiple times, this way, adding a dependency to a collection
+ becomes easier and allows you to add a variable number of dependencies.
+
+The disadvantages are:
+
+* As the setter call is optional, a dependency can be null during execution,
+ you must check that the dependency is available before calling it.
+
+* Unless the service is declared lazy, it is incompatible with services
+ that reference each other in what are called circular loops.
+
Setter Injection
----------------
@@ -180,6 +286,9 @@ This time the advantages are:
the method adds the dependency to a collection. You can then have a variable
number of dependencies.
+* Like the immutable-setter one, this type of injection works well with
+ traits and allows you to compose your service.
+
The disadvantages of setter injection are:
* The setter can be called more than just at the time of construction so