diff --git a/security/remember_me.rst b/security/remember_me.rst
index b14b012202f..f7b68a974ff 100644
--- a/security/remember_me.rst
+++ b/security/remember_me.rst
@@ -354,3 +354,42 @@ service you created before:
->tokenProvider(DoctrineTokenProvider::class)
;
};
+
+Activating Remember Me When Using a Custom Authenticator
+--------------------------------------------------------
+
+When you use a custom authenticator, you must add a ``RememberMeBadge`` to the ``Passport``
+for the remember me function to be activated. Without the badge, remember me will not be
+active, regardless of any other remember me settings.
+
+For example::
+
+ // src/Service/LoginAuthenticator.php
+ namespace App\Service;
+
+ // ...
+ use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
+ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
+ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
+ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
+ use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
+ use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
+
+ class LoginAuthenticator extends AbstractAuthenticator
+ {
+ public function authenticate(Request $request): PassportInterface
+ {
+ $password = $request->request->get('password');
+ $username = $request->request->get('username');
+ $csrfToken = $request->request->get('csrf_token');
+
+ return new Passport(
+ new UserBadge($username),
+ new PasswordCredentials($password),
+ [
+ new CsrfTokenBadge('login', $csrfToken),
+ new RememberMeBadge(),
+ ]
+ );
+ }
+ }
diff --git a/service_container/service_decoration.rst b/service_container/service_decoration.rst
index 4c7f2ed0158..36f7d200808 100644
--- a/service_container/service_decoration.rst
+++ b/service_container/service_decoration.rst
@@ -12,12 +12,12 @@ When overriding an existing definition, the original service is lost:
# config/services.yaml
services:
- App\Mailer: ~
+ App\Mailer\Mailer: ~
- # this replaces the old App\Mailer definition with the new one, the
+ # this replaces the old App\Mailer\Mailer definition with the new one, the
# old definition is lost
- App\Mailer:
- class: App\NewMailer
+ App\Mailer\Mailer:
+ class: App\Mailer\NewMailer
.. code-block:: xml
@@ -28,11 +28,11 @@ When overriding an existing definition, the original service is lost:
xsd:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
-
+
-
-
+
@@ -41,15 +41,15 @@ When overriding an existing definition, the original service is lost:
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- use App\Mailer;
- use App\NewMailer;
+ use App\Mailer\Mailer;
+ use App\Mailer\NewMailer;
return function(ContainerConfigurator $configurator) {
$services = $configurator->services();
$services->set(Mailer::class);
- // this replaces the old App\Mailer definition with the new one, the
+ // this replaces the old App\Mailer\Mailer definition with the new one, the
// old definition is lost
$services->set(Mailer::class, NewMailer::class);
};
@@ -57,7 +57,7 @@ When overriding an existing definition, the original service is lost:
Most of the time, that's exactly what you want to do. But sometimes,
you might want to decorate the old one instead (i.e. apply the `Decorator pattern`_).
In this case, the old service should be kept around to be able to reference
-it in the new one. This configuration replaces ``App\Mailer`` with a new one,
+it in the new one. This configuration replaces ``App\Mailer\Mailer`` with a new one,
but keeps a reference of the old one as ``.inner``:
.. configuration-block::
@@ -66,12 +66,12 @@ but keeps a reference of the old one as ``.inner``:
# config/services.yaml
services:
- App\Mailer: ~
+ App\Mailer\Mailer: ~
- App\DecoratingMailer:
- # overrides the App\Mailer service
+ App\Mailer\DecoratingMailer:
+ # overrides the App\Mailer\Mailer service
# but that service is still available as ".inner"
- decorates: App\Mailer
+ decorates: App\Mailer\Mailer
.. code-block:: xml
@@ -82,12 +82,12 @@ but keeps a reference of the old one as ``.inner``:
xsd:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
-
+
-
-
@@ -98,8 +98,8 @@ but keeps a reference of the old one as ``.inner``:
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- use App\DecoratingMailer;
- use App\Mailer;
+ use App\Mailer\DecoratingMailer;
+ use App\Mailer\Mailer;
return function(ContainerConfigurator $configurator) {
$services = $configurator->services();
@@ -107,13 +107,13 @@ but keeps a reference of the old one as ``.inner``:
$services->set(Mailer::class);
$services->set(DecoratingMailer::class)
- // overrides the App\Mailer service
+ // overrides the App\Mailer\Mailer service
// but that service is still available as ".inner"
->decorate(Mailer::class);
};
-The ``decorates`` option tells the container that the ``App\DecoratingMailer``
-service replaces the ``App\Mailer`` service. If you're using the
+The ``decorates`` option tells the container that the ``App\Mailer\DecoratingMailer``
+service replaces the ``App\Mailer\Mailer`` service. If you're using the
:ref:`default services.yaml configuration `,
the decorated service is automatically injected when the constructor of the
decorating service has one argument type-hinted with the decorated service class.
@@ -129,10 +129,10 @@ automatically changed to ``'.inner'``):
# config/services.yaml
services:
- App\Mailer: ~
+ App\Mailer\Mailer: ~
- App\DecoratingMailer:
- decorates: App\Mailer
+ App\Mailer\DecoratingMailer:
+ decorates: App\Mailer\Mailer
# pass the old service as an argument
arguments: ['@.inner']
@@ -145,10 +145,10 @@ automatically changed to ``'.inner'``):
xsd:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd">
-
+
-
@@ -161,8 +161,8 @@ automatically changed to ``'.inner'``):
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- use App\DecoratingMailer;
- use App\Mailer;
+ use App\Mailer\DecoratingMailer;
+ use App\Mailer\Mailer;
return function(ContainerConfigurator $configurator) {
$services = $configurator->services();
@@ -183,14 +183,14 @@ automatically changed to ``'.inner'``):
.. tip::
- The visibility of the decorated ``App\Mailer`` service (which is an alias
- for the new service) will still be the same as the original ``App\Mailer``
+ The visibility of the decorated ``App\Mailer\Mailer`` service (which is an alias
+ for the new service) will still be the same as the original ``App\Mailer\Mailer``
visibility.
.. note::
The generated inner id is based on the id of the decorator service
- (``App\DecoratingMailer`` here), not of the decorated service (``App\Mailer``
+ (``App\Mailer\DecoratingMailer`` here), not of the decorated service (``App\Mailer\Mailer``
here). You can control the inner service name via the ``decoration_inner_name``
option:
@@ -200,10 +200,10 @@ automatically changed to ``'.inner'``):
# config/services.yaml
services:
- App\DecoratingMailer:
+ App\Mailer\DecoratingMailer:
# ...
- decoration_inner_name: App\DecoratingMailer.wooz
- arguments: ['@App\DecoratingMailer.wooz']
+ decoration_inner_name: App\Mailer\DecoratingMailer.wooz
+ arguments: ['@App\Mailer\DecoratingMailer.wooz']
.. code-block:: xml
@@ -217,12 +217,12 @@ automatically changed to ``'.inner'``):
-
+
@@ -233,8 +233,8 @@ automatically changed to ``'.inner'``):
// config/services.php
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
- use App\DecoratingMailer;
- use App\Mailer;
+ use App\Mailer\DecoratingMailer;
+ use App\Mailer\Mailer;
return function(ContainerConfigurator $configurator) {
$services = $configurator->services();
@@ -246,6 +246,213 @@ automatically changed to ``'.inner'``):
->args([service(DecoratingMailer::class.'.wooz')]);
};
+Two Different Ways to Decorate a Service
+----------------------------------------
+
+A service can be decorated by either making the decorating service:
+
+- Implement the same interface as the decorated service, or
+- Extend the decorated service.
+
+Implementing The Same Interface As The Decorated Service
+--------------------------------------------------------
+
+.. tip::
+
+ This first method is the recommended way to decorate a service.
+
+ However, it only works when the decorated class implements an injectable interface.
+
+Assume the following for the decorated class::
+
+ // src/Mailer/Mailer.php
+ namespace App\Mailer;
+
+ class Mailer implements MailerInterface {
+ }
+
+Also assume that your service definitions are configured so that you would type-hint
+``App\Mailer\MailerInterface`` instead of ``App\Mailer\Mailer`` to inject the service into other services.
+(If not, and you instead must type-hint ``App\Mailer\Mailer``, then skip to "Extend The Decorated Service".)
+
+In this case, you would inject the decorated mailer service as follows::
+
+ // src/Services/AcmeService.php
+ namespace App\Services;
+
+ use App\Mailer\MailerInterface;
+
+ class AcmeService {
+ public function __construct(MailerInterface $mailer) {
+ }
+ }
+
+Create your decorating class as follows::
+
+ // src/Mailer/DecoratingMailer.php
+ namespace App\Mailer;
+
+ class DecoratingMailer implements MailerInterface {
+ public function __construct(MailerInterface $mailer) {
+ }
+ }
+
+Your decoration configuration will be as follows:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/services.yaml
+ services:
+ App\Mailer\MailerInterface:
+ class: App\Mailer\Mailer
+
+ App\Mailer\DecoratingMailer:
+ decorates: App\Mailer\MailerInterface
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+ use App\Mailer\DecoratingMailer;
+ use App\Mailer\Mailer;
+ use App\Mailer\MailerInterface;
+
+ return function(ContainerConfigurator $configurator) {
+ $services = $configurator->services();
+
+ $services->set(MailerInterface::class)
+ ->class(Mailer::class);
+
+ $services->set(DecoratingMailer::class)
+ ->decorate(MailerInterface::class);
+ };
+
+Extend The Decorated Service
+----------------------------
+
+.. tip::
+
+ This second method of the decorating service extending the decorated service is
+ not strictly speaking "decoration" in accordance with the `Decorator pattern`_, but it does
+ allow, in Symfony, for your "decorating" class to be automatically injected when the "decorated" class is
+ type-hinted. In other words, the end result is the same as the first option. It could help you
+ with decorating services from third-party bundles that don't adhere to interface implementation
+ best-practices.
+
+ You might also notice that the ".inner" part of the decoration configuration is meaningless with this
+ decoration option, because the "decorated" service is not injected into the "decorating" service.
+
+Assume the following::
+
+ // src/Mailer/Mailer.php
+ namespace App\Mailer;
+
+ class Mailer {
+ }
+
+In this case, you would inject the ``App\Mailer\Mailer`` service as follows into another service::
+
+ // src/Services/AcmeService.php
+ namespace App\Services;
+
+ use App\Mailer\Mailer;
+
+ class AcmeService {
+ public function __construct(Mailer $mailer) {
+ }
+ }
+
+Create your decorating class as follows::
+
+ // src/Mailer/DecoratingMailer.php
+ namespace App\Mailer;
+
+ use App\Mailer\Mailer;
+
+ class DecoratingMailer extends Mailer {
+ public function __construct() {
+ }
+ }
+
+Your decoration configuration will be as follows:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/services.yaml
+ services:
+ App\Mailer\Mailer: ~
+
+ App\Mailer\DecoratingMailer:
+ decorates: App\Mailer\Mailer
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+ use App\Mailer\DecoratingMailer;
+ use App\Mailer\Mailer;
+
+ return function(ContainerConfigurator $configurator) {
+ $services = $configurator->services();
+
+ $services->set(Mailer::class);
+
+ $services->set(DecoratingMailer::class)
+ ->decorate(Mailer::class);
+ };
+
+.. tip::
+
+ If the ``App\Mailer\Mailer`` class is marked as ``final``, and it does not implement an injectable
+ interface, then you will not be able to decorate it, because a final class cannot be extended.
+
+Congratulations! With both options, the ``App\Mailer\DecoratingMailer`` class is automatically injected
+instead of the ``App\Mailer\Mailer`` class.
+
Decoration Priority
-------------------