From aabb62e72fc8173f454efe82564ece273f138dbc Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Mon, 15 Aug 2022 00:50:08 +0200 Subject: [PATCH 001/774] Removing 2 distracting scenarios Reason: Too many numbers are only distracting from the message - anybody can image that all numbers are just arbitrary examples... Other problem: The axis legends "1 hour window" in the SVG are overlapping: https://symfony.com/doc/5.4/rate_limiter.html#fixed-window-rate-limiter Maybe either reduce the font size, or reword to just "window" --- rate_limiter.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rate_limiter.rst b/rate_limiter.rst index 65a0243e5e8..a5b73ac4735 100644 --- a/rate_limiter.rst +++ b/rate_limiter.rst @@ -35,8 +35,7 @@ Fixed Window Rate Limiter ~~~~~~~~~~~~~~~~~~~~~~~~~ This is the simplest technique and it's based on setting a limit for a given -interval of time (e.g. 5,000 requests per hour or 3 login attempts every 15 -minutes). +interval of time. In the diagram below, the limit is set to "5 tokens per hour". Each window starts at the first hit (i.e. 10:15, 11:30 and 12:30). As soon as there are @@ -48,11 +47,11 @@ squares). Its main drawback is that resource usage is not evenly distributed in time and -it can overload the server at the window edges. In the previous example, +it can overload the server at the window edges. In this example, there were 6 accepted requests between 11:00 and 12:00. This is more significant with bigger limits. For instance, with 5,000 requests -per hour, a user could make the 4,999 requests in the last minute of some +per hour, a user could make 4,999 requests in the last minute of some hour and another 5,000 requests during the first minute of the next hour, making 9,999 requests in total in two minutes and possibly overloading the server. These periods of excessive usage are called "bursts". From 98270d12a343af129f9595d3589be0d928f798f1 Mon Sep 17 00:00:00 2001 From: Smaine Milianni Date: Fri, 6 Aug 2021 17:39:51 +0100 Subject: [PATCH 002/774] init doc --- notifier/events.rst | 74 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 notifier/events.rst diff --git a/notifier/events.rst b/notifier/events.rst new file mode 100644 index 00000000000..aef7c848cec --- /dev/null +++ b/notifier/events.rst @@ -0,0 +1,74 @@ +.. index:: + single: Notifier; Events + +Using Events +============ + +The class:``...\\..\\Transport`` of the Notifier component allows you to optionally hook +into the lifecycle via events. + +The ``MessageEvent::class`` Event +--------------------------------- + +**Typical Purposes**: Doing something before the message is send (like logging +which message is going to be send, or displaying something about the event +to be executed. + +Just before send the message, the event class ``MessageEvent`` is +dispatched. Listeners receive a +:class:`Symfony\\Component\\Notifier\\Event\\MessageEvent` event:: + + use Symfony\Component\Notifier\Event\MessageEvent; + + $dispatcher->addListener(MessageEvent::class, function (MessageEvent $event) { + // gets the message instance + $message = $event->getMessage(); + + // log something + $this->logger(sprintf('Message with subject: %s will be send to %s, $message->getSubject(), $message->getRecipientId()')); + }); + +The ``FailedMessageEvent`` Event +-------------------------------- + +**Typical Purposes**: Doing something before the exception is thrown (Retry to send the message or log additional information). + +Whenever an exception is thrown while sending the message, the event class ``FailedMessageEvent`` is +dispatched. A listener can do anything useful before the exception is thrown. + +Listeners receive a +:class:`Symfony\\Component\\Notifier\\Event\\FailedMessageEvent` event:: + + use Symfony\Component\Notifier\Event\FailedMessageEvent; + + $dispatcher->addListener(FailedMessageEvent::class, function (FailedMessageEvent $event) { + // gets the message instance + $message = $event->getMessage(); + + // gets the error instance + $error = $event->getError(); + + // log something + $this->logger(sprintf('The message with subject: %s has not been sent successfully. The error is: %s, $message->getSubject(), $error->getMessage()')); + }); + + +The ``SentMessageEvent`` Event +------------------------------ + +**Typical Purposes**: To perform some action when the message is successfully sent (like retrieve the id returned +when the message is sent). + +After the message has been successfully sent, the event class ``SentMessageEvent`` is +dispatched. Listeners receive a +:class:`Symfony\\Component\\Notifier\\Event\\SentMessageEvent` event:: + + use Symfony\Component\Notifier\Event\SentMessageEvent; + + $dispatcher->addListener(SentMessageEvent::class, function (SentMessageEvent $event) { + // gets the message instance + $message = $event->getOriginalMessage(); + + // log something + $this->logger(sprintf('The message has been successfully sent and have id: %s, $message->getMessageId()')); + }); From 4272159b6acc5ae59cea3ad819fb49867d54e74f Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Mon, 7 Nov 2022 05:30:20 +0100 Subject: [PATCH 003/774] Remove Magali from CARE --- contributing/code_of_conduct/care_team.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/contributing/code_of_conduct/care_team.rst b/contributing/code_of_conduct/care_team.rst index fb2c60faebd..f7f565a266f 100644 --- a/contributing/code_of_conduct/care_team.rst +++ b/contributing/code_of_conduct/care_team.rst @@ -37,12 +37,6 @@ of them at once by emailing ** care@symfony.com **. * *SymfonyConnect*: `zanbaldwin `_ * *SymfonySlack*: `@Zan `_ -* **Magali Milbergue** - - * *E-mail*: magali.milbergue [at] gmail.com - * *Twitter*: `@magalimilbergue `_ - * *SymfonyConnect*: `magali_milbergue `_ - * **Tobias Nyholm** * *E-mail*: tobias.nyholm [at] gmail.com From 9262483ce3346eda0dc167b53bfea42adf2c98d1 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Thu, 10 Nov 2022 22:54:22 +0100 Subject: [PATCH 004/774] Consistent naming of `FormLoginAuthenticator` Reason: Make it more clear that we're always talking about the same **built-in** thing. --- security.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security.rst b/security.rst index 583f5e19f9a..abfa7c1e7c8 100644 --- a/security.rst +++ b/security.rst @@ -660,7 +660,7 @@ Form Login Most websites have a login form where users authenticate using an identifier (e.g. email address or username) and a password. This -functionality is provided by the *form login authenticator*. +functionality is provided by the built-in :class:`Symfony\\Component\\Security\\Http\Authenticator\\FormLoginAuthenticator`. First, create a controller for the login form: @@ -691,7 +691,7 @@ First, create a controller for the login form: } } -Then, enable the form login authenticator using the ``form_login`` setting: +Then, enable the ``FormLoginAuthenticator`` using the ``form_login`` setting: .. configuration-block:: @@ -784,8 +784,8 @@ Edit the login controller to render the login form: } } -Don't let this controller confuse you. Its job is only to *render* the form: -the ``form_login`` authenticator will handle the form *submission* automatically. +Don't let this controller confuse you. Its job is only to *render* the form. +The ``FormLoginAuthenticator`` will handle the form *submission* automatically. If the user submits an invalid email or password, that authenticator will store the error and redirect back to this controller, where we read the error (using ``AuthenticationUtils``) so that it can be displayed back to the user. @@ -857,7 +857,7 @@ To review the whole process: #. The ``/login`` page renders login form via the route and controller created in this example; #. The user submits the login form to ``/login``; -#. The security system (i.e. the ``form_login`` authenticator) intercepts the +#. The security system (i.e. the ``FormLoginAuthenticator``) intercepts the request, checks the user's submitted credentials, authenticates the user if they are correct, and sends the user back to the login form if they are not. From 2493625aa5cebdb6534396e2bc309ff8c5149e25 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 11 Nov 2022 12:57:20 +0100 Subject: [PATCH 005/774] Minor --- reference/configuration/security.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index dda2c08e8e7..f3104fa3e41 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -365,7 +365,7 @@ username_parameter **type**: ``string`` **default**: ``_username`` -This is the field name that you should give to the username field of your +This is the name of the username field of your login form. When you submit the form to ``check_path``, the security system will look for a POST parameter with this name. @@ -374,7 +374,7 @@ password_parameter **type**: ``string`` **default**: ``_password`` -This is the field name that you should give to the password field of your +This is the name of the password field of your login form. When you submit the form to ``check_path``, the security system will look for a POST parameter with this name. @@ -385,7 +385,7 @@ post_only By default, you must submit your login form to the ``check_path`` URL as a POST request. By setting this option to ``false``, you can send a GET -request to the ``check_path`` URL. +request too. **Options Related to Redirecting after Login** From e2ef815754a01468e8642c41b99453c9a76338bc Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sat, 12 Nov 2022 20:16:27 +0100 Subject: [PATCH 006/774] Minor Removing "starting" --- form/data_transformers.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/form/data_transformers.rst b/form/data_transformers.rst index 4204b77cf23..e91a190a30f 100644 --- a/form/data_transformers.rst +++ b/form/data_transformers.rst @@ -8,8 +8,8 @@ Data transformers are used to translate the data for a field into a format that be displayed in a form (and back on submit). They're already used internally for many field types. For example, the :doc:`DateType ` field can be rendered as a ``yyyy-MM-dd``-formatted input text box. Internally, a data transformer -converts the starting ``DateTime`` value of the field into the ``yyyy-MM-dd`` string -to render the form, and then back into a ``DateTime`` object on submit. +converts the ``DateTime`` value of the field to the ``yyyy-MM-dd`` string +to render the form, and then back to a ``DateTime`` object on submit. .. caution:: From 59b3037c476dd6c45c2f972265f00cab322f3385 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sat, 12 Nov 2022 20:47:30 +0100 Subject: [PATCH 007/774] Merging two (almost identical) paragraphs The only new aspect is that Transformer is only for only one field, whereas Mapper is for one or more. --- form/data_mappers.rst | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/form/data_mappers.rst b/form/data_mappers.rst index 24ff0716f5f..6d322e3e043 100644 --- a/form/data_mappers.rst +++ b/form/data_mappers.rst @@ -19,13 +19,11 @@ The Difference between Data Transformers and Mappers It is important to know the difference between :doc:`data transformers ` and mappers. -* **Data transformers** change the representation of a value (e.g. from - ``"2016-08-12"`` to a ``DateTime`` instance); -* **Data mappers** map data (e.g. an object or array) to form fields, and vice versa. - -Changing a ``YYYY-mm-dd`` string value to a ``DateTime`` instance is done by a -data transformer. Populating inner fields (e.g year, hour, etc) of a compound date type using -a ``DateTime`` instance is done by the data mapper. +* **Data transformers** change the representation of a single value, e.g. from + ``"2016-08-12"`` to a ``DateTime`` instance; +* **Data mappers** map data (e.g. an object or array) to one or many form fields, and vice versa, + e.g. using a single ``DateTime`` instance to populate the inner fields (e.g year, hour, etc.) + of a compound date type. Creating a Data Mapper ---------------------- From 4559b9cd2d4b9a6df618c0656e647f30321fb04d Mon Sep 17 00:00:00 2001 From: Maxime Doutreluingne Date: Sat, 12 Nov 2022 11:24:37 +0100 Subject: [PATCH 008/774] [Security] Add form_only option --- reference/configuration/security.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index dda2c08e8e7..26081fa63bf 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -352,6 +352,19 @@ failure_path This is the route or path that the user is redirected to after a failed login attempt. It can be a relative/absolute URL or a Symfony route name. +form_only +............ + +**type**: ``boolean`` **default**: ``false`` + +By setting this option to ``true``, a content type check will be performed when the login form is submitted +(i.e. the login form will be processed if it is the form data, so with a +content type ``application/x-www-form-urlencoded``. + +.. versionadded:: 5.4 + + The ``form_only`` option was introduced in Symfony 5.4. + use_forward ........... From ef5d786f620ae7d0b3273d6919d44ac638b337d0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 18 Nov 2022 16:41:26 +0100 Subject: [PATCH 009/774] Reword --- reference/configuration/security.rst | 10 ++++++---- security.rst | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 9e885853c96..fa3258bbb23 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -353,13 +353,15 @@ This is the route or path that the user is redirected to after a failed login at It can be a relative/absolute URL or a Symfony route name. form_only -............ +......... **type**: ``boolean`` **default**: ``false`` -By setting this option to ``true``, a content type check will be performed when the login form is submitted -(i.e. the login form will be processed if it is the form data, so with a -content type ``application/x-www-form-urlencoded``. +Set this option to ``true`` to require that the login data is sent using a form +(it checks that the request content-type is ``application/x-www-form-urlencoded``). +This is useful for example to prevent the :ref:`form login authenticator ` +from responding to requests that should be handled by the +:ref:`JSON login authenticator `. .. versionadded:: 5.4 diff --git a/security.rst b/security.rst index 583f5e19f9a..845b86c3039 100644 --- a/security.rst +++ b/security.rst @@ -957,6 +957,8 @@ After this, you have protected your login form against CSRF attacks. the token ID by setting ``csrf_token_id`` in your configuration. See :ref:`reference-security-firewall-form-login` for more details. +.. _security-json-login: + JSON Login ~~~~~~~~~~ From bed2ec831d23a5713a9c531f41b502984d482ba1 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 11 Nov 2022 15:50:36 +0100 Subject: [PATCH 010/774] [Security] Minor rewording --- security.rst | 6 +++--- security/form_login.rst | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/security.rst b/security.rst index fd774597406..492fcd08d04 100644 --- a/security.rst +++ b/security.rst @@ -768,13 +768,13 @@ Finally, create or update the template:
- + - + {# If you want to control the URL the user is redirected to on success - #} + #}
diff --git a/security/form_login.rst b/security/form_login.rst index 5bae5c6e62b..85d8a150a8f 100644 --- a/security/form_login.rst +++ b/security/form_login.rst @@ -142,26 +142,26 @@ previously requested URL and always redirect to the default page: Control the Redirect Using Request Parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The URL to redirect after the login can be defined using the ``_target_path`` -parameter of GET and POST requests. Its value must be a relative or absolute +The URL to redirect to after the login can be dynamically defined using the ``_target_path`` +parameter of the GET or POST request. Its value must be a relative or absolute URL, not a Symfony route name. -Defining the redirect URL via GET using a query string parameter: +For GET, use a query string parameter: .. code-block:: text http://example.com/some/path?_target_path=/dashboard -Defining the redirect URL via POST using a hidden form field: +For POST, use a hidden form field: .. code-block:: html+twig - {# templates/security/login.html.twig #} -
+ {# templates/login/index.html.twig #} + {# ... #} - - + +
Using the Referring URL @@ -304,8 +304,8 @@ This option can also be set via the ``_failure_path`` request parameter:
{# ... #} - - + +
Customizing the Target and Failure Request Parameters @@ -383,7 +383,7 @@ are now fully customized:
{# ... #} - - - + + +
From dfc2982e1712f8b653bf426628a08c24819a845e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 18 Nov 2022 18:06:29 +0100 Subject: [PATCH 011/774] Minor reword --- form/data_transformers.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/form/data_transformers.rst b/form/data_transformers.rst index e91a190a30f..cf32ca134a0 100644 --- a/form/data_transformers.rst +++ b/form/data_transformers.rst @@ -8,8 +8,8 @@ Data transformers are used to translate the data for a field into a format that be displayed in a form (and back on submit). They're already used internally for many field types. For example, the :doc:`DateType ` field can be rendered as a ``yyyy-MM-dd``-formatted input text box. Internally, a data transformer -converts the ``DateTime`` value of the field to the ``yyyy-MM-dd`` string -to render the form, and then back to a ``DateTime`` object on submit. +converts the ``DateTime`` value of the field to a ``yyyy-MM-dd`` formatted string +when rendering the form, and then back to a ``DateTime`` object on submit. .. caution:: From 97c38f38f9f0ea77ab9ae41473e78068917abe74 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 18 Nov 2022 18:42:23 +0100 Subject: [PATCH 012/774] Merging "list" into text Reason: "a few guidelines" sounded odd, cause the first was no guideline (but a rule), and the second was still no guideline (but more details to first). --- security.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/security.rst b/security.rst index 845b86c3039..ae413392a74 100644 --- a/security.rst +++ b/security.rst @@ -1934,12 +1934,9 @@ database and every user is *always* given at least one role: ``ROLE_USER``:: } This is a nice default, but you can do *whatever* you want to determine which roles -a user should have. Here are a few guidelines: - -* Every role **must start with** ``ROLE_`` (otherwise, things won't work as expected) - -* Other than the above rule, a role is just a string and you can invent what you - need (e.g. ``ROLE_PRODUCT_ADMIN``). +a user should have. The only rule is that every role **must start with** ``ROLE_`` - +otherwise, things won't work as expected. Other than that, a role is just a string +and you can invent whatever you need (e.g. ``ROLE_PRODUCT_ADMIN``). You'll use these roles next to grant access to specific sections of your site. From d23bba2b6997cc0fbc0ad80d8bdb9de99d32aa01 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sat, 19 Nov 2022 17:54:18 +0100 Subject: [PATCH 013/774] Adding more info about chain provider Reason: The info that this is the way to go if you need *multiple* providers was lost somehow since v4.4: https://symfony.com/doc/4.0/security/multiple_user_providers.html The wording is mostly taken from there. --- security.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/security.rst b/security.rst index 845b86c3039..6880411c874 100644 --- a/security.rst +++ b/security.rst @@ -351,6 +351,8 @@ Symfony comes with several built-in user providers: Loads users from a configuration file; :ref:`Chain User Provider ` Merges two or more user providers into a new user provider. + Since each firewall has exactly *one* user provider, you can use this + to chain multiple providers together. The built-in user providers cover the most common needs for applications, but you can also create your own :ref:`custom user provider `. From e9e5086d904a318aa78160dfc3d0953a6fa02f11 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sat, 19 Nov 2022 17:58:23 +0100 Subject: [PATCH 014/774] Minor deletion Reason: The other providers don't have to be of different types (or do they?) --- security/user_providers.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/user_providers.rst b/security/user_providers.rst index 0e202283a83..57c50149bc0 100644 --- a/security/user_providers.rst +++ b/security/user_providers.rst @@ -240,8 +240,8 @@ After setting up hashing, you can configure all the user information in Chain User Provider ------------------- -This user provider combines two or more of the other provider types (e.g. -``entity`` and ``ldap``) to create a new user provider. The order in which +This user provider combines two or more of the other providers +to create a new user provider. The order in which providers are configured is important because Symfony will look for users starting from the first provider and will keep looking for in the other providers until the user is found: From 58e6ec27df67abe6e8a8be85fdd17b541cfa72c5 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 19 Nov 2022 18:58:12 +0100 Subject: [PATCH 015/774] clean up 5.4 versionadded directive --- reference/configuration/security.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 40aaf5ce826..fbf31177cbf 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -343,10 +343,6 @@ This is useful for example to prevent the :ref:`form login authenticator `. -.. versionadded:: 5.4 - - The ``form_only`` option was introduced in Symfony 5.4. - use_forward ........... From 9f1239962d981c21c59fc30b451c4e0fdfab0259 Mon Sep 17 00:00:00 2001 From: Moshe Weitzman Date: Sat, 19 Nov 2022 13:49:36 -0500 Subject: [PATCH 016/774] Fix incorrect example for CommandCompletionTester --- console/input.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/console/input.rst b/console/input.rst index 817a2cc67fe..69304b7551a 100644 --- a/console/input.rst +++ b/console/input.rst @@ -397,8 +397,10 @@ to help you unit test the completion logic:: $this->assertSame(['Fabien', 'Fabrice', 'Wouter'], $suggestions); // complete the input with "Fa" as input + // note that the list does not reduce because the completion tester doesn't run any shell, + // it only tests the PHP part of the completion logic, so it should always include all values $suggestions = $tester->complete(['Fa']); - $this->assertSame(['Fabien', 'Fabrice'], $suggestions); + $this->assertSame(['Fabien', 'Fabrice', 'Wouter'], $suggestions); } } From f69c9b54cb6ece5849767c3578699016a4ac333e Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Sun, 20 Nov 2022 11:56:51 +0100 Subject: [PATCH 017/774] Remove old `versionadded` directive --- reference/configuration/security.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index 40aaf5ce826..fbf31177cbf 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -343,10 +343,6 @@ This is useful for example to prevent the :ref:`form login authenticator `. -.. versionadded:: 5.4 - - The ``form_only`` option was introduced in Symfony 5.4. - use_forward ........... From fbae2e4796b929f7148f25c4cc635c2f8efe87f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Fouillet?= <35224226+ffouillet@users.noreply.github.com> Date: Sun, 20 Nov 2022 09:29:32 +0100 Subject: [PATCH 018/774] Update security.rst Removed an useless comma --- security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security.rst b/security.rst index 845b86c3039..22175632b0d 100644 --- a/security.rst +++ b/security.rst @@ -226,7 +226,7 @@ from the `MakerBundle`_: } /** - * Returning a salt is only needed, if you are not using a modern + * Returning a salt is only needed if you are not using a modern * hashing algorithm (e.g. bcrypt or sodium) in your security.yaml. * * @see UserInterface From 620e6b428c228e2808d6672e982e01dbab242e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Fouillet?= <35224226+ffouillet@users.noreply.github.com> Date: Sat, 19 Nov 2022 17:35:56 +0100 Subject: [PATCH 019/774] Update custom_constraint.rst Replace Annotation in custom constraint declaration by #[Attribute] which is more up to date --- validation/custom_constraint.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/validation/custom_constraint.rst b/validation/custom_constraint.rst index 1d95b64c822..f193535900c 100644 --- a/validation/custom_constraint.rst +++ b/validation/custom_constraint.rst @@ -214,9 +214,7 @@ email. First, create a constraint and override the ``getTargets()`` method:: use Symfony\Component\Validator\Constraint; - /** - * @Annotation - */ + #[\Attribute] class ConfirmedPaymentReceipt extends Constraint { public string $userDoesNotMatchMessage = 'User\'s e-mail address does not match that of the receipt'; From 728d75baa5ff8e2e30c0bbb8085de45c2a40887a Mon Sep 17 00:00:00 2001 From: Maxime Doutreluingne Date: Sun, 21 Aug 2022 15:16:10 +0200 Subject: [PATCH 020/774] Deprecate not configuring explicitly a provider for custom_authenticators when there is more than one registered provider --- components/console/helpers/questionhelper.rst | 24 +++++++++---------- security/custom_authenticator.rst | 8 ++++++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst index 863120dd52f..d3e7498049b 100644 --- a/components/console/helpers/questionhelper.rst +++ b/components/console/helpers/questionhelper.rst @@ -88,7 +88,7 @@ if you want to know a bundle name, you can add this to your command:: // ... do something with the bundleName - return Commande::SUCCESS; + return Command::SUCCESS; } The user will be asked "Please enter the name of the bundle". They can type @@ -124,7 +124,7 @@ from a predefined list:: // ... do something with the color - return Commande::SUCCESS; + return Command::SUCCESS; } .. versionadded:: 5.2 @@ -166,7 +166,7 @@ this use :method:`Symfony\\Component\\Console\\Question\\ChoiceQuestion::setMult $colors = $helper->ask($input, $output, $question); $output->writeln('You have just selected: ' . implode(', ', $colors)); - return Commande::SUCCESS; + return Command::SUCCESS; } Now, when the user enters ``1,2``, the result will be: @@ -197,7 +197,7 @@ will be autocompleted as the user types:: // ... do something with the bundleName - return Commande::SUCCESS; + return Command::SUCCESS; } In more complex use cases, it may be necessary to generate suggestions on the @@ -236,7 +236,7 @@ provide a callback function to dynamically generate suggestions:: // ... do something with the filePath - return Commande::SUCCESS; + return Command::SUCCESS; } Do not Trim the Answer @@ -260,7 +260,7 @@ You can also specify if you want to not trim the answer by setting it directly w // ... do something with the name - return Commande::SUCCESS; + return Command::SUCCESS; } Accept Multiline Answers @@ -291,7 +291,7 @@ the response to a question should allow multiline answers by passing ``true`` to // ... do something with the answer - return Commande::SUCCESS; + return Command::SUCCESS; } Multiline questions stop reading user input after receiving an end-of-transmission @@ -319,7 +319,7 @@ convenient for passwords:: // ... do something with the password - return Commande::SUCCESS; + return Command::SUCCESS; } .. caution:: @@ -351,7 +351,7 @@ convenient for passwords:: // ... - return Commande::SUCCESS; + return Command::SUCCESS; } Normalizing the Answer @@ -382,7 +382,7 @@ method:: // ... do something with the bundleName - return Commande::SUCCESS; + return Command::SUCCESS; } .. caution:: @@ -426,7 +426,7 @@ method:: // ... do something with the bundleName - return Commande::SUCCESS; + return Command::SUCCESS; } The ``$validator`` is a callback which handles the validation. It should @@ -488,7 +488,7 @@ You can also use a validator with a hidden question:: // ... do something with the password - return Commande::SUCCESS; + return Command::SUCCESS; } Testing a Command that Expects Input diff --git a/security/custom_authenticator.rst b/security/custom_authenticator.rst index 54861858baa..8dbeeaf287a 100644 --- a/security/custom_authenticator.rst +++ b/security/custom_authenticator.rst @@ -130,6 +130,12 @@ The authenticator can be enabled using the ``custom_authenticators`` setting: ; }; +.. deprecated:: 5.4 + + If you have registered multiple user providers, you must set the + ``provider`` key to one of the configured providers, even if your + custom authenticators don't use it. Not doing so is deprecated in Symfony 5.4. + .. versionadded:: 5.2 Starting with Symfony 5.2, the custom authenticator is automatically @@ -185,7 +191,7 @@ can define what happens in these cases: If your login method is interactive, which means that the user actively logged into your application, you may want your authenticator to implement the :class:`Symfony\\Component\\Security\\Http\\Authenticator\\InteractiveAuthenticatorInterface` - so that it dispatches an + so that it dispatches an :class:`Symfony\\Component\\Security\\Http\\Event\\InteractiveLoginEvent` .. _security-passport: From 1fb6ad0fc5a7e63fc75f249b82fd951f46c892df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Fouillet?= <35224226+ffouillet@users.noreply.github.com> Date: Sun, 20 Nov 2022 16:11:36 +0100 Subject: [PATCH 021/774] Update security.rst Propose attribute usage instead of annotations, this stick more to the example. --- security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security.rst b/security.rst index 891032aff2d..0bd944121eb 100644 --- a/security.rst +++ b/security.rst @@ -2148,7 +2148,7 @@ will happen: .. _security-securing-controller-annotations: Thanks to the SensioFrameworkExtraBundle, you can also secure your controller -using annotations: +using attributes: .. configuration-block:: From 4351c89192b518f6da538b1b24050624ec7992e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= <5175937+theofidry@users.noreply.github.com> Date: Mon, 21 Nov 2022 16:00:47 +0100 Subject: [PATCH 022/774] Fix bad copy/paste --- controller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller.rst b/controller.rst index e04bfd7a43d..092f032f055 100644 --- a/controller.rst +++ b/controller.rst @@ -381,7 +381,7 @@ object. To access it in your controller, add it as an argument and use Symfony\Component\HttpFoundation\Response; // ... - public function index(Request $request, string $firstName, string $lastName): Response + public function index(Request $request): Response { $page = $request->query->get('page', 1); From 155bbf49c55f3da83ea8176986e12eb1252f2a4f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 21 Nov 2022 17:46:48 +0100 Subject: [PATCH 023/774] Minor tweak --- security.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security.rst b/security.rst index 02c2e8879fa..c2066816dee 100644 --- a/security.rst +++ b/security.rst @@ -1934,9 +1934,9 @@ database and every user is *always* given at least one role: ``ROLE_USER``:: } This is a nice default, but you can do *whatever* you want to determine which roles -a user should have. The only rule is that every role **must start with** ``ROLE_`` - -otherwise, things won't work as expected. Other than that, a role is just a string -and you can invent whatever you need (e.g. ``ROLE_PRODUCT_ADMIN``). +a user should have. The only rule is that every role **must start with** the +``ROLE_`` prefix - otherwise, things won't work as expected. Other than that, +a role is just a string and you can invent whatever you need (e.g. ``ROLE_PRODUCT_ADMIN``). You'll use these roles next to grant access to specific sections of your site. From dfbf11a389b8a7bd5fbde0d2edc4306c8a2f789d Mon Sep 17 00:00:00 2001 From: Chris Taylor Date: Mon, 21 Nov 2022 08:58:58 +0100 Subject: [PATCH 024/774] Emphasise where a method is returning a value. --- testing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing.rst b/testing.rst index 778c2cb0dd0..a092a7b5bfa 100644 --- a/testing.rst +++ b/testing.rst @@ -247,7 +247,7 @@ Retrieving Services in the Test In your integration tests, you often need to fetch the service from the service container to call a specific method. After booting the kernel, -the container is stored in ``static::getContainer()``:: +the container is returned by ``static::getContainer()``:: // tests/Service/NewsletterGeneratorTest.php namespace App\Tests\Service; @@ -273,7 +273,7 @@ the container is stored in ``static::getContainer()``:: } } -The container in ``static::getContainer()`` is actually a special test container. +The container from ``static::getContainer()`` is actually a special test container. It gives you access to both the public services and the non-removed :ref:`private services `. From 3b4ebc0e5c6ef87ecafac980fe73aab5538a526d Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Tue, 22 Nov 2022 09:59:37 +0100 Subject: [PATCH 025/774] Update processors.rst --- logging/processors.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logging/processors.rst b/logging/processors.rst index 99a6bb2c069..b07783e72d8 100644 --- a/logging/processors.rst +++ b/logging/processors.rst @@ -32,7 +32,7 @@ using a processor:: } // this method is called for each log record; optimize it to not hurt performance - public function __invoke(array $record) + public function __invoke(LogRecord $record): LogRecord { try { $session = $this->requestStack->getSession(); @@ -45,7 +45,7 @@ using a processor:: $sessionId = substr($session->getId(), 0, 8) ?: '????????'; - $record['extra']['token'] = $sessionId.'-'.substr(uniqid('', true), -8); + $record->extra['token'] = $sessionId.'-'.substr(uniqid('', true), -8); return $record; } From 47a8c038831075d1e353fe0fee0f2b0f00badbfb Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 22 Nov 2022 13:26:05 +0100 Subject: [PATCH 026/774] [Mailer] Fix minor syntax issue --- mailer.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mailer.rst b/mailer.rst index de7c6ee8e30..cc0176a2f09 100644 --- a/mailer.rst +++ b/mailer.rst @@ -328,8 +328,9 @@ header, etc.) but most of the times you'll set text headers:: // use an array if you want to add a header with multiple values // (for example in the "References" or "In-Reply-To" header) - ->addIdHeader('References', ['123@example.com', '456@example.com']); - // ... + ->addIdHeader('References', ['123@example.com', '456@example.com']) + + // ... ; Message Contents From 17c0fe1955ca27a30599df425e9279a08db45040 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 22 Nov 2022 13:29:30 +0100 Subject: [PATCH 027/774] [Mailer] Fix some usages of DataPart and File --- mailer.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mailer.rst b/mailer.rst index c15f2833083..dfcdf6b5094 100644 --- a/mailer.rst +++ b/mailer.rst @@ -448,13 +448,17 @@ File Attachments Use the ``addPart()`` method with a ``BodyFile`` to add files that exist on your file system:: + use Symfony\Component\Mime\Part\DataPart; + use Symfony\Component\Mime\Part\File; + // ... + $email = (new Email()) // ... - ->addPart(new DataPart(new BodyFile('/path/to/documents/terms-of-use.pdf'))) + ->addPart(new DataPart(new File('/path/to/documents/terms-of-use.pdf'))) // optionally you can tell email clients to display a custom name for the file - ->addPart(new DataPart(new BodyFile('/path/to/documents/privacy.pdf', 'Privacy Policy'))) + ->addPart(new DataPart(new File('/path/to/documents/privacy.pdf'), 'Privacy Policy')) // optionally you can provide an explicit MIME type (otherwise it's guessed) - ->addPart(new DataPart(new BodyFile('/path/to/documents/contract.doc', 'Contract', 'application/msword'))) + ->addPart(new DataPart(new File('/path/to/documents/contract.doc'), 'Contract', 'application/msword')) ; Alternatively you can attach contents from a stream by passing it directly to the ``DataPart`` :: @@ -486,7 +490,7 @@ file or stream:: // get the image contents from a PHP resource ->addPart((new DataPart(fopen('/path/to/images/logo.png', 'r'), 'logo', 'image/png'))->asInline()) // get the image contents from an existing file - ->addPart((new DataPart(new BodyFile('/path/to/images/signature.gif', 'footer-signature', 'image/gif')))->asInline()) + ->addPart((new DataPart(new File('/path/to/images/signature.gif'), 'footer-signature', 'image/gif'))->asInline()) ; Use the ``asInline()`` method to embed the content instead of attaching it. @@ -498,7 +502,7 @@ images inside the HTML contents:: $email = (new Email()) // ... ->addPart((new DataPart(fopen('/path/to/images/logo.png', 'r'), 'logo', 'image/png'))->asInline()) - ->addPart((new DataPart(new BodyFile('/path/to/images/signature.gif', 'footer-signature', 'image/gif')))->asInline()) + ->addPart((new DataPart(new File('/path/to/images/signature.gif'), 'footer-signature', 'image/gif'))->asInline()) // reference images using the syntax 'cid:' + "image embed name" ->html(' ... ...') From 24e39949f5cc45c541bbcbdd9252273d02c7368a Mon Sep 17 00:00:00 2001 From: Denis Brumann Date: Wed, 16 Nov 2022 18:51:01 +0100 Subject: [PATCH 028/774] [Clock] Documentation for new Clock component --- components/clock.rst | 105 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 components/clock.rst diff --git a/components/clock.rst b/components/clock.rst new file mode 100644 index 00000000000..c76b3ede077 --- /dev/null +++ b/components/clock.rst @@ -0,0 +1,105 @@ +.. index:: + single: Clock + single: Components; Clock + +The Clock Component +=================== + +.. versionadded:: 6.2 + + The Clock component was introduced in Symfony 6.2 + +The Clock component decouples applications from the system clock. This allows +you to fix time to improve testability of time-sensitive logic. + +The component provides a ``ClockInterface`` with the following implementations +for different use cases: + +:class:`Symfony\\Component\\Clock\\NativeClock` + Provides a way to interact with the system clock, this is the same as doing + ``new \DateTimeImmutable()``. +:class:`Symfony\\Component\\Clock\\MockClock` + Commonly used in tests as a replacement for the ``NativeClock`` to be able + to freeze and change the current time using either ``sleep()`` or ``modify()``. +:class:`Symfony\\Component\\Clock\\MonotonicClock`` + Relies on ``hrtime()`` and provides a high resolution, monotonic clock, + when you need a precise stopwatch. + +Installation +------------ + +.. code-block:: terminal + + $ composer require symfony/clock + +.. include:: /components/require_autoload.rst.inc + +NativeClock +----------- + +A clock service replaces creating a new ``DateTime`` or +``DateTimeImmutable`` object for the current time. Instead, you inject the +``ClockInterface`` and call ``now()``. By default, your application will likely +use a ``NativeClock``, which always returns the current system time. In tests it is replaced with a ``MockClock``. + +The following example introduces a service utilizing the Clock component to +determine the current time:: + + use Symfony\Component\Clock\ClockInterface; + + class ExpirationChecker + { + public function __construct( + private readonly ClockInterface $clock + ) {} + + public function isExpired(DateTimeInterface $validUntil): bool + { + return $this->clock->now() > $validUntil; + } + } + +MockClock +--------- + +The ``MockClock`` is instantiated with a time and does not move forward on its own. The time is +fixed until ``sleep()`` or ``modify()`` are called. This gives you full control over what your code +assumes is the current time. + +When writing a test for this service, you can check both cases where something +is expired or not, by modifying the clock's time:: + + use PHPUnit\Framework\TestCase; + use Symfony\Component\Clock\MockClock; + + class ExpirationCheckerTest extends TestCase + { + public function testIsExpired(): void + { + $clock = new MockClock('2022-11-16 15:20:00'); + $expirationChecker = new ExpirationChecker($clock); + $validUntil = new DateTimeImmutable('2022-11-16 15:25:00'); + + // $validUntil is in the future, so it is not expired + static::assertFalse($expirationChecker->isExpired($validUntil)); + + // Clock sleeps for 10 minutes, so now is '2022-11-16 15:30:00' + $clock->sleep(600); // Instantly changes time as if we waited for 10 minutes (600secs) + + // modify the clock, accepts all formats supported by DateTimeImmutable::modify() + static::assertTrue($expirationChecker->isExpired($validUntil)); + + $clock->modify('2022-11-16 15:00:00'); + + // $validUntil is in the future again, so it is no longer expired + static::assertFalse($expirationChecker->isExpired($validUntil)); + } + } + +Monotonic Clock +--------------- + +The ``MonotonicClock`` allows you to implement a precise stopwatch, depending on the system up to +nanosecond precision. It can be used to measure the elapsed time between 2 calls without being +affected by inconsistencies sometimes introduced by the system clock, e.g. by updating it. Instead, +it consistently increases time, making it especially useful for measuring performance. From c1f8db7b83ede60dcfbb8091697fd416f6f3c98b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 24 Nov 2022 13:31:45 +0100 Subject: [PATCH 029/774] [Form] Mention PropertyAccess in the hash_property_option of PasswordType --- reference/forms/types/password.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/types/password.rst b/reference/forms/types/password.rst index 6d87ae57af7..9f6010675f0 100644 --- a/reference/forms/types/password.rst +++ b/reference/forms/types/password.rst @@ -46,7 +46,7 @@ entered into the box, set this to false and submit the form. If set, the password will be hashed using the :doc:`PasswordHasher component ` and stored in the -specified property. +property defined by the given :doc:`PropertyAccess expression `. Data passed to the form must be a :class:`Symfony\\Component\\Security\\Core\\User\\PasswordAuthenticatedUserInterface` From c59d4668af16a218956144dc4f4275f0d1abe0e4 Mon Sep 17 00:00:00 2001 From: Tac Tacelosky Date: Thu, 24 Nov 2022 08:50:51 -0500 Subject: [PATCH 030/774] add missing use for Template attribute --- templates.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/templates.rst b/templates.rst index 3ee4fd7dc4a..b527e18b57b 100644 --- a/templates.rst +++ b/templates.rst @@ -463,6 +463,7 @@ to define the template to render:: use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; + use Symfony\Bridge\Twig\Attribute\Template; class ProductController extends AbstractController { From 82b431af41aeaa6799f71c6f3c0de307247e8f81 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 24 Nov 2022 20:30:53 +0100 Subject: [PATCH 031/774] Minor tweaks --- components/clock.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/components/clock.rst b/components/clock.rst index c76b3ede077..1cd9757d69d 100644 --- a/components/clock.rst +++ b/components/clock.rst @@ -84,7 +84,7 @@ is expired or not, by modifying the clock's time:: static::assertFalse($expirationChecker->isExpired($validUntil)); // Clock sleeps for 10 minutes, so now is '2022-11-16 15:30:00' - $clock->sleep(600); // Instantly changes time as if we waited for 10 minutes (600secs) + $clock->sleep(600); // Instantly changes time as if we waited for 10 minutes (600 seconds) // modify the clock, accepts all formats supported by DateTimeImmutable::modify() static::assertTrue($expirationChecker->isExpired($validUntil)); @@ -99,7 +99,8 @@ is expired or not, by modifying the clock's time:: Monotonic Clock --------------- -The ``MonotonicClock`` allows you to implement a precise stopwatch, depending on the system up to -nanosecond precision. It can be used to measure the elapsed time between 2 calls without being -affected by inconsistencies sometimes introduced by the system clock, e.g. by updating it. Instead, -it consistently increases time, making it especially useful for measuring performance. +The ``MonotonicClock`` allows you to implement a precise stopwatch; depending on +the system up to nanosecond precision. It can be used to measure the elapsed +time between two calls without being affected by inconsistencies sometimes introduced +by the system clock, e.g. by updating it. Instead, it consistently increases time, +making it especially useful for measuring performance. From a26b165930a6c52098db781040ec97be0635ab98 Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Fri, 25 Nov 2022 09:44:58 +0100 Subject: [PATCH 032/774] [Security] use new class --- security.rst | 6 +++--- setup/bundles.rst | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/security.rst b/security.rst index d42d7ef0fbc..84d4546821b 100644 --- a/security.rst +++ b/security.rst @@ -1613,7 +1613,7 @@ Login Programmatically The :class:`Symfony\Bundle\SecurityBundle\Security ` class was introduced in Symfony 6.2. Prior to 6.2, it was called - ``Symfony\Component\Security\Core\Security``. + ``Symfony\Bundle\SecurityBundle\Security``. .. versionadded:: 6.2 @@ -1783,7 +1783,7 @@ Logout programmatically The :class:`Symfony\Bundle\SecurityBundle\Security ` class was introduced in Symfony 6.2. Prior to 6.2, it was called - ``Symfony\Component\Security\Core\Security``. + ``Symfony\Bundle\SecurityBundle\Security``. .. versionadded:: 6.2 @@ -1929,7 +1929,7 @@ If you need to get the logged in user from a service, use the The :class:`Symfony\\Bundle\\SecurityBundle\\Security` class was introduced in Symfony 6.2. In previous Symfony versions this class was - defined in ``Symfony\Component\Security\Core\Security``. + defined in ``Symfony\Bundle\SecurityBundle\Security``. Fetch the User in a Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/setup/bundles.rst b/setup/bundles.rst index fe4f59cb819..bf8c205e0b7 100644 --- a/setup/bundles.rst +++ b/setup/bundles.rst @@ -81,7 +81,7 @@ PHPUnit test report: Twig Function "form_enctype" is deprecated. Use "form_start" instead in ... - The Symfony\Component\Security\Core\SecurityContext class is deprecated since + The Symfony\Bundle\SecurityBundle\SecurityContext class is deprecated since version 2.6 and will be removed in 3.0. Use ... Fix the reported deprecations, run the test suite again and repeat the process From 72c3c318b27107ccdfcd54c63e856fbed9e247dc Mon Sep 17 00:00:00 2001 From: jmsche Date: Fri, 25 Nov 2022 13:25:07 +0100 Subject: [PATCH 033/774] Fix UX package list Dropzone display --- frontend/_ux-libraries.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/_ux-libraries.rst.inc b/frontend/_ux-libraries.rst.inc index 8ca7eb5ca3b..a40a51109f5 100644 --- a/frontend/_ux-libraries.rst.inc +++ b/frontend/_ux-libraries.rst.inc @@ -4,7 +4,7 @@ * `ux-chartjs`_: Easy charts with `Chart.js`_ (`see demo `_) * `ux-cropperjs`_: Form Type and tools for cropping images (`see demo `_) * `ux-dropzone`_: Form Type for stylized "drop zone" for file uploads - (`see demo `_) + (`see demo `_) * `ux-lazy-image`_: Optimize Image Loading with BlurHash (`see demo `_) * `ux-live-component`_: Build Dynamic Interfaces with Zero JavaScript From bf6c6ae2c73cbd89e241e46016485829ad31ad7f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 5 Nov 2022 10:45:40 +0100 Subject: [PATCH 034/774] Make various changes/updates on the Lock docs --- components/lock.rst | 172 ++++++++++++++++++++++++-------------------- lock.rst | 52 +++++++++----- 2 files changed, 127 insertions(+), 97 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index abbed7fc8b8..c1ceabd4438 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -42,10 +42,10 @@ resource. Then, a call to the :method:`Symfony\\Component\\Lock\\LockInterface:: method will try to acquire the lock:: // ... - $lock = $factory->createLock('pdf-invoice-generation'); + $lock = $factory->createLock('pdf-creation'); if ($lock->acquire()) { - // The resource "pdf-invoice-generation" is locked. + // The resource "pdf-creation" is locked. // You can compute and generate the invoice safely here. $lock->release(); @@ -81,20 +81,27 @@ continue the job in another process using the same lock:: use Symfony\Component\Lock\Lock; $key = new Key('article.'.$article->getId()); - $lock = new Lock($key, $this->store, 300, false); + $lock = new Lock( + $key, + $this->store, + 300, // ttl + false // autoRelease + ); $lock->acquire(true); $this->bus->dispatch(new RefreshTaxonomy($article, $key)); .. note:: - Don't forget to disable the autoRelease to avoid releasing the lock when - the destructor is called. + Don't forget to set the ``autoRelease`` argument to ``false`` in the + ``Lock`` constructor to avoid releasing the lock when the destructor is + called. -Not all stores are compatible with serialization and cross-process locking: -for example, the kernel will automatically release semaphores acquired by the +Not all stores are compatible with serialization and cross-process locking: for +example, the kernel will automatically release semaphores acquired by the :ref:`SemaphoreStore ` store. If you use an incompatible -store, an exception will be thrown when the application tries to serialize the key. +store (see :ref:`lock stores ` for supported stores), an +exception will be thrown when the application tries to serialize the key. .. _lock-blocking-locks: @@ -102,44 +109,40 @@ Blocking Locks -------------- By default, when a lock cannot be acquired, the ``acquire`` method returns -``false`` immediately. To wait (indefinitely) until the lock -can be created, pass ``true`` as the argument of the ``acquire()`` method. This -is called a **blocking lock** because the execution of your application stops -until the lock is acquired. - -Some of the built-in ``Store`` classes support this feature. When they don't, -they can be decorated with the ``RetryTillSaveStore`` class:: +``false`` immediately. To wait (indefinitely) until the lock can be created, +pass ``true`` as the argument of the ``acquire()`` method. This is called a +**blocking lock** because the execution of your application stops until the +lock is acquired:: use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\Store\RedisStore; - use Symfony\Component\Lock\Store\RetryTillSaveStore; $store = new RedisStore(new \Predis\Client('tcp://localhost:6379')); - $store = new RetryTillSaveStore($store); $factory = new LockFactory($store); - $lock = $factory->createLock('notification-flush'); + $lock = $factory->createLock('pdf-creation'); $lock->acquire(true); -When the provided store does not implement the -:class:`Symfony\\Component\\Lock\\BlockingStoreInterface` interface, the -``Lock`` class will retry to acquire the lock in a non-blocking way until the -lock is acquired. +When the store does not support blocking locks by implementing the +:class:`Symfony\\Component\\Lock\\BlockingStoreInterface` interface (see +:ref:`lock stores ` for supported stores), the ``Lock`` class +will retry to acquire the lock in a non-blocking way until the lock is +acquired. -.. deprecated:: 5.2 +.. versionadded:: 5.2 - As of Symfony 5.2, you don't need to use the ``RetryTillSaveStore`` class - anymore. The ``Lock`` class now provides the default logic to acquire locks - in blocking mode when the store does not implement the - ``BlockingStoreInterface`` interface. + Default logic to retry acquiring a non-blocking lock was introduced in + Symfony 5.2. Prior to 5.2, you needed to wrap a store without support + for blocking locks in :class:`Symfony\\Component\\Lock\\Store\\RetryTillSaveStore`. Expiring Locks -------------- Locks created remotely are difficult to manage because there is no way for the remote ``Store`` to know if the locker process is still alive. Due to bugs, -fatal errors or segmentation faults, it cannot be guaranteed that ``release()`` -method will be called, which would cause the resource to be locked infinitely. +fatal errors or segmentation faults, it cannot be guaranteed that the +``release()`` method will be called, which would cause the resource to be +locked infinitely. The best solution in those cases is to create **expiring locks**, which are released automatically after some amount of time has passed (called TTL for @@ -154,7 +157,7 @@ method, the resource will stay locked until the timeout:: // ... // create an expiring lock that lasts 30 seconds (default is 300.0) - $lock = $factory->createLock('charts-generation', 30); + $lock = $factory->createLock('pdf-creation', ttl: 30); if (!$lock->acquire()) { return; @@ -175,7 +178,7 @@ then use the :method:`Symfony\\Component\\Lock\\LockInterface::refresh` method to reset the TTL to its original value:: // ... - $lock = $factory->createLock('charts-generation', 30); + $lock = $factory->createLock('pdf-creation', ttl: 30); if (!$lock->acquire()) { return; @@ -196,7 +199,7 @@ to reset the TTL to its original value:: Another useful technique for long-running tasks is to pass a custom TTL as an argument of the ``refresh()`` method to change the default lock TTL:: - $lock = $factory->createLock('charts-generation', 30); + $lock = $factory->createLock('pdf-creation', ttl: 30); // ... // refresh the lock for 30 seconds $lock->refresh(); @@ -212,12 +215,12 @@ Automatically Releasing The Lock ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Locks are automatically released when their Lock objects are destroyed. This is -an implementation detail that will be important when sharing Locks between +an implementation detail that is important when sharing Locks between processes. In the example below, ``pcntl_fork()`` creates two processes and the Lock will be released automatically as soon as one process finishes:: // ... - $lock = $factory->createLock('report-generation', 3600); + $lock = $factory->createLock('pdf-creation'); if (!$lock->acquire()) { return; } @@ -236,9 +239,15 @@ Lock will be released automatically as soon as one process finishes:: } // ... -To disable this behavior, set to ``false`` the third argument of -``LockFactory::createLock()``. That will make the lock acquired for 3600 seconds -or until ``Lock::release()`` is called. +To disable this behavior, set the ``autoRelease`` argument of +``LockFactory::createLock()`` to ``false``. That will make the lock acquired +for 3600 seconds or until ``Lock::release()`` is called:: + + $lock = $factory->createLock( + 'pdf-creation', + 3600, // ttl + false // autoRelease + ); Shared Locks ------------ @@ -248,19 +257,19 @@ Shared Locks Shared locks (and the associated ``acquireRead()`` method and ``SharedLockStoreInterface``) were introduced in Symfony 5.2. -A shared or `readers–writer lock`_ is a synchronization primitive that allows +A shared or `readers-writer lock`_ is a synchronization primitive that allows concurrent access for read-only operations, while write operations require exclusive access. This means that multiple threads can read the data in parallel but an exclusive lock is needed for writing or modifying data. They are used for example for data structures that cannot be updated atomically and are invalid until the update is complete. -Use the :method:`Symfony\\Component\\Lock\\SharedLockInterface::acquireRead` method -to acquire a read-only lock, and the existing +Use the :method:`Symfony\\Component\\Lock\\SharedLockInterface::acquireRead` +method to acquire a read-only lock, and :method:`Symfony\\Component\\Lock\\LockInterface::acquire` method to acquire a write lock:: - $lock = $factory->createLock('user'.$user->id); + $lock = $factory->createLock('user-'.$user->id); if (!$lock->acquireRead()) { return; } @@ -268,7 +277,7 @@ write lock:: Similar to the ``acquire()`` method, pass ``true`` as the argument of ``acquireRead()`` to acquire the lock in a blocking mode:: - $lock = $factory->createLock('user'.$user->id); + $lock = $factory->createLock('user-'.$user->id); $lock->acquireRead(true); .. note:: @@ -276,26 +285,27 @@ to acquire the lock in a blocking mode:: The `priority policy`_ of Symfony's shared locks depends on the underlying store (e.g. Redis store prioritizes readers vs writers). -When a read-only lock is acquired with the method ``acquireRead()``, it's -possible to **promote** the lock, and change it to write lock, by calling the +When a read-only lock is acquired with the ``acquireRead()`` method, it's +possible to **promote** the lock, and change it to a write lock, by calling the ``acquire()`` method:: - $lock = $factory->createLock('user'.$userId); + $lock = $factory->createLock('user-'.$userId); $lock->acquireRead(true); if (!$this->shouldUpdate($userId)) { return; } - $lock->acquire(true); // Promote the lock to write lock + $lock->acquire(true); // Promote the lock to a write lock $this->update($userId); In the same way, it's possible to **demote** a write lock, and change it to a read-only lock by calling the ``acquireRead()`` method. When the provided store does not implement the -:class:`Symfony\\Component\\Lock\\SharedLockStoreInterface` interface, the -``Lock`` class will fallback to a write lock by calling the ``acquire()`` method. +:class:`Symfony\\Component\\Lock\\SharedLockStoreInterface` interface (see +:ref:`lock stores ` for supported stores), the ``Lock`` class +will fallback to a write lock by calling the ``acquire()`` method. The Owner of The Lock --------------------- @@ -308,8 +318,8 @@ a lock, you can use the ``isAcquired()`` method:: // We (still) own the lock } -Because of the fact that some lock stores have expiring locks (as seen and explained -above), it is possible for an instance to lose the lock it acquired automatically:: +Because some lock stores have expiring locks, it is possible for an instance to +lose the lock it acquired automatically:: // If we cannot acquire ourselves, it means some other process is already working on it if (!$lock->acquire()) { @@ -335,13 +345,15 @@ above), it is possible for an instance to lose the lock it acquired automaticall A common pitfall might be to use the ``isAcquired()`` method to check if a lock has already been acquired by any process. As you can see in this example you have to use ``acquire()`` for this. The ``isAcquired()`` method is used to check - if the lock has been acquired by the **current process** only! + if the lock has been acquired by the **current process** only. .. [1] Technically, the true owners of the lock are the ones that share the same instance of ``Key``, not ``Lock``. But from a user perspective, ``Key`` is internal and you will likely only be working with the ``Lock`` instance so it's easier to think of the ``Lock`` instance as being the one that is the owner of the lock. +.. _lock-stores: + Available Stores ---------------- @@ -391,7 +403,7 @@ when the PHP process ends):: Beware that some file systems (such as some types of NFS) do not support locking. In those cases, it's better to use a directory on a local disk - drive or a remote store based on PDO, Redis or Memcached. + drive or a remote store. .. _lock-store-memcached: @@ -448,7 +460,7 @@ Option Description gcProbablity Should a TTL Index be created expressed as a probability from 0.0 to 1.0 (Defaults to ``0.001``) database The name of the database collection The name of the collection -uriOptions Array of uri options for `MongoDBClient::__construct`_ +uriOptions Array of URI options for `MongoDBClient::__construct`_ driverOptions Array of driver options for `MongoDBClient::__construct`_ ============= ================================================================================================ @@ -548,7 +560,7 @@ locks:: use Symfony\Component\Lock\Store\PostgreSqlStore; // a PDO instance or DSN for lazy connecting through PDO - $databaseConnectionOrDSN = 'pgsql:host=localhost;port=5634;dbname=lock'; + $databaseConnectionOrDSN = 'pgsql:host=localhost;port=5634;dbname=app'; $store = new PostgreSqlStore($databaseConnectionOrDSN, ['db_username' => 'myuser', 'db_password' => 'mypassword']); In opposite to the ``PdoStore``, the ``PostgreSqlStore`` does not need a table to @@ -620,10 +632,10 @@ CombinedStore ~~~~~~~~~~~~~ The CombinedStore is designed for High Availability applications because it -manages several stores in sync (for example, several Redis servers). When a lock -is being acquired, it forwards the call to all the managed stores, and it -collects their responses. If a simple majority of stores have acquired the lock, -then the lock is considered as acquired; otherwise as not acquired:: +manages several stores in sync (for example, several Redis servers). When a +lock is acquired, it forwards the call to all the managed stores, and it +collects their responses. If a simple majority of stores have acquired the +lock, then the lock is considered acquired:: use Symfony\Component\Lock\Store\CombinedStore; use Symfony\Component\Lock\Store\RedisStore; @@ -641,14 +653,19 @@ then the lock is considered as acquired; otherwise as not acquired:: Instead of the simple majority strategy (``ConsensusStrategy``) an ``UnanimousStrategy`` can be used to require the lock to be acquired in all -the stores. +the stores:: + + use Symfony\Component\Lock\Store\CombinedStore; + use Symfony\Component\Lock\Strategy\UnanimousStrategy; + + $store = new CombinedStore($stores, new UnanimousStrategy()); .. caution:: In order to get high availability when using the ``ConsensusStrategy``, the minimum cluster size must be three servers. This allows the cluster to keep working when a single server fails (because this strategy requires that the - lock is acquired in more than half of the servers). + lock is acquired for more than half of the servers). .. _lock-store-zookeeper: @@ -692,7 +709,7 @@ the true owner of the lock. This token is stored in the :class:`Symfony\\Component\\Lock\\Key` object and is used internally by the ``Lock``. -Every concurrent process must store the ``Lock`` in the same server. Otherwise two +Every concurrent process must store the ``Lock`` on the same server. Otherwise two different machines may allow two different processes to acquire the same ``Lock``. .. caution:: @@ -716,10 +733,10 @@ The ``Lock`` provides several methods to check its health. The ``isExpired()`` method checks whether or not its lifetime is over and the ``getRemainingLifetime()`` method returns its time to live in seconds. -Using the above methods, a more robust code would be:: +Using the above methods, a robust code would be:: // ... - $lock = $factory->createLock('invoice-publication', 30); + $lock = $factory->createLock('pdf-creation', 30); if (!$lock->acquire()) { return; @@ -748,7 +765,7 @@ Using the above methods, a more robust code would be:: may increase that time a lot (up to a few seconds). Take that into account when choosing the right TTL. -By design, locks are stored in servers with a defined lifetime. If the date or +By design, locks are stored on servers with a defined lifetime. If the date or time of the machine changes, a lock could be released sooner than expected. .. caution:: @@ -778,15 +795,14 @@ Some file systems (such as some types of NFS) do not support locking. All concurrent processes must use the same physical file system by running on the same machine and using the same absolute path to the lock directory. - By definition, usage of ``FlockStore`` in an HTTP context is incompatible - with multiple front servers, unless to ensure that the same resource will - always be locked on the same machine or to use a well configured shared file - system. + Using a ``FlockStore`` in an HTTP context is incompatible with multiple + front servers, unless to ensure that the same resource will always be + locked on the same machine or to use a well configured shared file system. -Files on the file system can be removed during a maintenance operation. For instance, -to clean up the ``/tmp`` directory or after a reboot of the machine when a directory -uses tmpfs. It's not an issue if the lock is released when the process ended, but -it is in case of ``Lock`` reused between requests. +Files on the file system can be removed during a maintenance operation. For +instance, to clean up the ``/tmp`` directory or after a reboot of the machine +when a directory uses ``tmpfs``. It's not an issue if the lock is released when +the process ended, but it is in case of ``Lock`` reused between requests. .. caution:: @@ -832,8 +848,8 @@ MongoDbStore .. caution:: The locked resource name is indexed in the ``_id`` field of the lock - collection. Beware that in MongoDB an indexed field's value can be - `a maximum of 1024 bytes in length`_ inclusive of structural overhead. + collection. Beware that an indexed field's value in MongoDB can be + `a maximum of 1024 bytes in length`_ including the structural overhead. A TTL index must be used to automatically clean up expired locks. Such an index can be created manually: @@ -851,8 +867,8 @@ about `Expire Data from Collections by Setting TTL`_ in MongoDB. .. tip:: - ``MongoDbStore`` will attempt to automatically create a TTL index. - It's recommended to set constructor option ``gcProbablity = 0.0`` to + ``MongoDbStore`` will attempt to automatically create a TTL index. It's + recommended to set constructor option ``gcProbablity`` to ``0.0`` to disable this behavior if you have manually dealt with TTL index creation. .. caution:: @@ -868,7 +884,7 @@ the collection's settings will take effect. Read more about `Replica Set Read and Write Semantics`_ in MongoDB. PdoStore -~~~~~~~~~~ +~~~~~~~~ The PdoStore relies on the `ACID`_ properties of the SQL engine. @@ -1022,5 +1038,5 @@ are still running. .. _`PHP semaphore functions`: https://www.php.net/manual/en/book.sem.php .. _`Replica Set Read and Write Semantics`: https://docs.mongodb.com/manual/applications/replication/ .. _`ZooKeeper`: https://zookeeper.apache.org/ -.. _`readers–writer lock`: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock +.. _`readers-writer lock`: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock .. _`priority policy`: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock#Priority_policies diff --git a/lock.rst b/lock.rst index 5d864fb0089..739c79fcce3 100644 --- a/lock.rst +++ b/lock.rst @@ -12,7 +12,7 @@ time to prevent race conditions from happening. The following example shows a typical usage of the lock:: - $lock = $lockFactory->createLock('pdf-invoice-generation'); + $lock = $lockFactory->createLock('pdf-creation'); if (!$lock->acquire()) { return; } @@ -22,8 +22,8 @@ The following example shows a typical usage of the lock:: $lock->release(); -Installation ------------- +Installing +---------- In applications using :ref:`Symfony Flex `, run this command to install the Lock component: @@ -32,8 +32,8 @@ install the Lock component: $ composer require symfony/lock -Configuring Lock with FrameworkBundle -------------------------------------- +Configuring +----------- By default, Symfony provides a :ref:`Semaphore ` when available, or a :ref:`Flock ` otherwise. You can configure @@ -58,7 +58,7 @@ this behavior by using the ``lock`` key like: lock: 'sqlite:///%kernel.project_dir%/var/lock.db' lock: 'mysql:host=127.0.0.1;dbname=app' lock: 'pgsql:host=127.0.0.1;dbname=app' - lock: 'pgsql+advisory:host=127.0.0.1;dbname=lock' + lock: 'pgsql+advisory:host=127.0.0.1;dbname=app' lock: 'sqlsrv:server=127.0.0.1;Database=app' lock: 'oci:host=127.0.0.1;dbname=app' lock: 'mongodb://127.0.0.1/app?collection=lock' @@ -108,7 +108,7 @@ this behavior by using the ``lock`` key like: pgsql:host=127.0.0.1;dbname=app - pgsql+advisory:host=127.0.0.1;dbname=lock + pgsql+advisory:host=127.0.0.1;dbname=app sqlsrv:server=127.0.0.1;Database=app @@ -145,7 +145,7 @@ this behavior by using the ``lock`` key like: ->resource('default', ['sqlite:///%kernel.project_dir%/var/lock.db']) ->resource('default', ['mysql:host=127.0.0.1;dbname=app']) ->resource('default', ['pgsql:host=127.0.0.1;dbname=app']) - ->resource('default', ['pgsql+advisory:host=127.0.0.1;dbname=lock']) + ->resource('default', ['pgsql+advisory:host=127.0.0.1;dbname=app']) ->resource('default', ['sqlsrv:server=127.0.0.1;Database=app']) ->resource('default', ['oci:host=127.0.0.1;dbname=app']) ->resource('default', ['mongodb://127.0.0.1/app?collection=lock']) @@ -161,7 +161,7 @@ Locking a Resource ------------------ To lock the default resource, autowire the lock factory using -:class:`Symfony\\Component\\Lock\\LockFactory` (service id ``lock.factory``):: +:class:`Symfony\\Component\\Lock\\LockFactory`:: // src/Controller/PdfController.php namespace App\Controller; @@ -200,8 +200,8 @@ Locking a Dynamic Resource Sometimes the application is able to cut the resource into small pieces in order to lock a small subset of processes and let others through. The previous example -showed how to lock the ``$pdf->getOrCreatePdf('terms-of-use')`` for everybody, -now let's see how to lock ``$pdf->getOrCreatePdf($version)`` only for +showed how to lock the ``$pdf->getOrCreatePdf()`` call for everybody, +now let's see how to lock a ``$pdf->getOrCreatePdf($version)`` call only for processes asking for the same ``$version``:: // src/Controller/PdfController.php @@ -217,7 +217,7 @@ processes asking for the same ``$version``:: */ public function downloadPdf($version, LockFactory $lockFactory, MyPdfGeneratorService $pdf) { - $lock = $lockFactory->createLock($version); + $lock = $lockFactory->createLock('pdf-creation-'.$version); $lock->acquire(true); // heavy computation @@ -231,8 +231,8 @@ processes asking for the same ``$version``:: .. _lock-named-locks: -Named Lock ----------- +Naming Locks +------------ If the application needs different kind of Stores alongside each other, Symfony provides :ref:`named lock `: @@ -279,13 +279,27 @@ provides :ref:`named lock `: ; }; +An autowiring alias is created for each named lock with a name using the camel +case version of its name suffixed by ``LockFactory``. -Each name becomes a service where the service id is part of the name of the -lock (e.g. ``lock.invoice.factory``). An autowiring alias is also created for -each lock using the camel case version of its name suffixed by ``LockFactory`` -- e.g. ``invoice`` can be injected automatically by naming the argument +For instance, the ``invoice`` lock can be injected by naming the argument ``$invoiceLockFactory`` and type-hinting it with -:class:`Symfony\\Component\\Lock\\LockFactory`. +:class:`Symfony\\Component\\Lock\\LockFactory`: + + // src/Controller/PdfController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Lock\LockFactory; + + class PdfController extends AbstractController + { + #[Route('/download/terms-of-use.pdf')] + public function downloadPdf(LockFactory $invoiceLockFactory, MyPdfGeneratorService $pdf) + { + // ... + } + } Blocking Store -------------- From 8fe54b8cb1b68328d31148be37653919a357d4fb Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 25 Nov 2022 17:15:30 +0100 Subject: [PATCH 035/774] Sort imports alphabetically --- templates.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates.rst b/templates.rst index b527e18b57b..88e8f50b0a5 100644 --- a/templates.rst +++ b/templates.rst @@ -461,9 +461,9 @@ to define the template to render:: // src/Controller/ProductController.php namespace App\Controller; + use Symfony\Bridge\Twig\Attribute\Template; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; - use Symfony\Bridge\Twig\Attribute\Template; class ProductController extends AbstractController { From f61ca2aece1126eece00ac4f70e17aa208396c4c Mon Sep 17 00:00:00 2001 From: Maxime Doutreluingne Date: Sat, 26 Nov 2022 10:39:21 +0100 Subject: [PATCH 036/774] [Validator] Removal code-block php-annotations --- validation/custom_constraint.rst | 57 +++++++++----------------------- 1 file changed, 15 insertions(+), 42 deletions(-) diff --git a/validation/custom_constraint.rst b/validation/custom_constraint.rst index e320cd5e45a..c061c23f682 100644 --- a/validation/custom_constraint.rst +++ b/validation/custom_constraint.rst @@ -38,54 +38,27 @@ use it as an annotation/attribute in other classes. The ``#[HasNamedArguments]`` attribute was introduced in Symfony 6.1. -You can use ``#[HasNamedArguments]`` or ``getRequiredOptions()`` to make some constraint options required: +You can use ``#[HasNamedArguments]`` or ``getRequiredOptions()`` to make some constraint options required:: -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Validator/ContainsAlphanumeric.php - namespace App\Validator; - - use Symfony\Component\Validator\Constraint; - - /** - * @Annotation - */ - class ContainsAlphanumeric extends Constraint - { - public $message = 'The string "{{ string }}" contains an illegal character: it can only contain letters or numbers.'; - public $mode; - - public function getRequiredOptions(): array - { - return ['mode']; - } - } - - .. code-block:: php-attributes + // src/Validator/ContainsAlphanumeric.php + namespace App\Validator; - // src/Validator/ContainsAlphanumeric.php - namespace App\Validator; + use Symfony\Component\Validator\Attribute\HasNamedArguments; + use Symfony\Component\Validator\Constraint; - use Symfony\Component\Validator\Attribute\HasNamedArguments; - use Symfony\Component\Validator\Constraint; + #[\Attribute] + class ContainsAlphanumeric extends Constraint + { + public $message = 'The string "{{ string }}" contains an illegal character: it can only contain letters or numbers.'; + public string $mode; - #[\Attribute] - class ContainsAlphanumeric extends Constraint + #[HasNamedArguments] + public function __construct(string $mode, array $groups = null, mixed $payload = null) { - public $message = 'The string "{{ string }}" contains an illegal character: it can only contain letters or numbers.'; - - public string $mode; - - #[HasNamedArguments] - public function __construct(string $mode, array $groups = null, mixed $payload = null) - { - parent::__construct([], $groups, $payload); - - $this->mode = $mode; - } + parent::__construct([], $groups, $payload); + $this->mode = $mode; } + } Creating the Validator itself ----------------------------- From 600bb80835fc319e1fa87a314d939f30ceb97c3a Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Thu, 11 Aug 2022 13:44:45 +0200 Subject: [PATCH 037/774] Access Token Documentation --- security.rst | 9 ++ security/access_token.rst | 182 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 security/access_token.rst diff --git a/security.rst b/security.rst index 81a0aa4d0a2..5fe2a26d40d 100644 --- a/security.rst +++ b/security.rst @@ -1202,6 +1202,15 @@ website. You can learn all about this authenticator in :doc:`/security/login_link`. +Access Tokens +~~~~~~~~~~~~~ + +Access Tokens are often used in API contexts. +The user receives a token from an authorization server +which authenticates them. + +You can learn all about this authenticator in :doc:`/security/access_token`. + X.509 Client Certificates ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/security/access_token.rst b/security/access_token.rst new file mode 100644 index 00000000000..79eb7b9449d --- /dev/null +++ b/security/access_token.rst @@ -0,0 +1,182 @@ +.. index:: + single: Security; Access Token + +How to use Access Token Authentication +====================================== + +Access tokens are commonly used in API contexts. The access token is obtained +through an authorization server (or similar) whose role is to verify the user identity +and receive consent before the token is issued. + +Access Tokens can be of any kind: opaque strings, Json Web Tokens (JWT) or SAML2 (XML structures). +Please refer to the `RFC6750`_: *The OAuth 2.0 Authorization Framework: Bearer Token Usage*. + +Using the Access Token Authenticator +------------------------------------ + +This guide assumes you have setup security and have created a user object +in your application. Follow :doc:`the main security guide ` if +this is not yet the case. + +1) Configure the Access Token Authenticator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To use the access token authenticator, you must configure a ``token_handler``. +The token handler retrieves the user identifier from the token. +In order to get the user identifier, implementations may need to load and validate +the token (e.g. revocation, expiration time, digital signature...). + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + firewalls: + main: + access_token: + token_handler: App\Security\AccessTokenHandler + +This handler shall implement the interface +:class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenHandlerInterface`. +In the following example, the handler will retrieve the token from a database +using a fictive repository. + +.. configuration-block:: + + .. code-block:: php + + // src/Security/AccessTokenHandler.php + namespace App\Security; + + use App\Repository\AccessTokenRepository; + use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; + + class AccessTokenHandler implements AccessTokenHandlerInterface + { + public function __construct( + private readonly AccessTokenRepository $repository + ) { + } + + public function getUserIdentifierFrom(string $token): string + { + $accessToken = $this->repository->findOneByValue($token); + if ($accessToken === null || !$accessToken->isValid()) { + throw new BadCredentialsException('Invalid credentials.'); + } + + return $accessToken->getUserId(); + } + } + +.. caution:: + + It is important to check the token is valid. + For instance, in the example we verify the token has not expired. + With self-contained access tokens such as JWT, the handler is required to + verify the digital signature and understand all claims, + especially ``sub``, ``iat``, ``nbf`` and ``exp``. + +Customizing the Authenticator +----------------------------- + +1) Access Token Extractors + +By default, the access token is read from the request header parameter ``Authorization`` with the scheme ``Bearer``. +You can change the behavior and send the access token through different ways. + +This authenticator provides services able to extract the access token as per the RFC6750: + +- ``header`` or ``security.access_token_extractor.header``: the token is sent through the request header. Usually ``Authorization`` with the ``Bearer`` scheme. +- ``query_string`` or ``security.access_token_extractor.query_string``: the token is part of the query string. Usually ``access_token``. +- ``request_body`` or ``security.access_token_extractor.request_body``: the token is part of the request body during a POST request. Usually ``access_token``. + +.. caution:: + + Because of the security weaknesses associated with the URI method, + including the high likelihood that the URL or the request body containing the access token will be logged, + methods ``query_string`` and ``request_body`` **SHOULD NOT** be used unless it is impossible + to transport the access token in the request header field. + +Also, you can also create a custom extractor. The class shall implement the interface +:class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenExtractorInterface`. + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + firewalls: + main: + access_token: + token_handler: App\Security\AccessTokenHandler + token_extractors: 'my_custom_access_token_extractor' + +It is possible to set multiple extractors. +In this case, **the order is important**: the first in the list is called first. + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + firewalls: + main: + access_token: + token_handler: App\Security\AccessTokenHandler + token_extractors: + - 'header' + - 'request_body' + - 'query_string' + - 'my_custom_access_token_extractor' + +2) Customizing the Success Handler + +Sometimes, the default success handling does not fit your use-case (e.g. +when you need to generate and return additional response header parameters). +To customize how the success handler behaves, create your own handler as a class that implements +:class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationSuccessHandlerInterface`:: + + // src/Security/Authentication/AuthenticationSuccessHandler.php + namespace App\Security\Authentication; + + use Symfony\Component\HttpFoundation\JsonResponse; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; + + class AuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface + { + public function onAuthenticationSuccess(Request $request, TokenInterface $token): JsonResponse + { + $user = $token->getUser(); + $userApiToken = $user->getApiToken(); + + return new JsonResponse(['apiToken' => $userApiToken]); + } + } + +Then, configure this service ID as the ``success_handler``: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + firewalls: + main: + access_token: + token_handler: App\Security\AccessTokenHandler + success_handler: App\Security\Authentication\AuthenticationSuccessHandler + +.. tip:: + + If you want to customize the default failure handling, use the + ``failure_handler`` option and create a class that implements + :class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationFailureHandlerInterface`. + +.. _`RFC6750`: https://datatracker.ietf.org/doc/html/rfc6750 From b65c136850d2c50ba8d1b47f3a840a823771b26e Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sat, 5 Nov 2022 15:02:21 +0100 Subject: [PATCH 038/774] Finish the docs for the new Access token authenticator --- security/access_token.rst | 317 +++++++++++++++++++++++++++++--------- 1 file changed, 241 insertions(+), 76 deletions(-) diff --git a/security/access_token.rst b/security/access_token.rst index 79eb7b9449d..65fd072cc79 100644 --- a/security/access_token.rst +++ b/security/access_token.rst @@ -4,12 +4,16 @@ How to use Access Token Authentication ====================================== -Access tokens are commonly used in API contexts. The access token is obtained -through an authorization server (or similar) whose role is to verify the user identity -and receive consent before the token is issued. +Access tokens or API tokens are commonly used as authentication mechanism +in API contexts. The access token is a string, obtained during authentication +(using the application or an authorization server). The access token's role +is to verify the user identity and receive consent before the token is +issued. -Access Tokens can be of any kind: opaque strings, Json Web Tokens (JWT) or SAML2 (XML structures). -Please refer to the `RFC6750`_: *The OAuth 2.0 Authorization Framework: Bearer Token Usage*. +Access tokens can be of any kind, for instance opaque strings, +`JSON Web Tokens (JWT)`_ or `SAML2 (XML structures)`_. Please refer to the +`RFC6750`_: *The OAuth 2.0 Authorization Framework: Bearer Token Usage* for +a detailed specification. Using the Access Token Authenticator ------------------------------------ @@ -22,9 +26,10 @@ this is not yet the case. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To use the access token authenticator, you must configure a ``token_handler``. -The token handler retrieves the user identifier from the token. -In order to get the user identifier, implementations may need to load and validate -the token (e.g. revocation, expiration time, digital signature...). +The token handler receives the token from the request and returns the +correct user identifier. To get the user identifier, implementations may +need to load and validate the token (e.g. revocation, expiration time, +digital signature, etc.). .. configuration-block:: @@ -37,69 +42,108 @@ the token (e.g. revocation, expiration time, digital signature...). access_token: token_handler: App\Security\AccessTokenHandler -This handler shall implement the interface -:class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenHandlerInterface`. -In the following example, the handler will retrieve the token from a database -using a fictive repository. - -.. configuration-block:: + .. code-block:: xml + + + + + + + + + + + .. code-block:: php - // src/Security/AccessTokenHandler.php - namespace App\Security; + // config/packages/security.php + use App\Security\AccessTokenHandler; + use Symfony\Config\SecurityConfig; - use App\Repository\AccessTokenRepository; - use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; + return static function (SecurityConfig $security) { + $security->firewall('main') + ->accessToken() + ->tokenHandler(AccessTokenHandler::class) + ; + }; - class AccessTokenHandler implements AccessTokenHandlerInterface - { - public function __construct( - private readonly AccessTokenRepository $repository - ) { - } +This handler must implement +:class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenHandlerInterface`:: + + // src/Security/AccessTokenHandler.php + namespace App\Security; - public function getUserIdentifierFrom(string $token): string - { - $accessToken = $this->repository->findOneByValue($token); - if ($accessToken === null || !$accessToken->isValid()) { - throw new BadCredentialsException('Invalid credentials.'); - } + use App\Repository\AccessTokenRepository; + use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; - return $accessToken->getUserId(); + class AccessTokenHandler implements AccessTokenHandlerInterface + { + public function __construct( + private AccessTokenRepository $repository + ) { + } + + public function getUserIdentifierFrom(string $token): string + { + // e.g. query the "access token" database to search for this token + $accessToken = $this->repository->findOneByValue($token); + if ($accessToken === null || !$accessToken->isValid()) { + throw new BadCredentialsException('Invalid credentials.'); } + + // and return the user identifier from the found token + return $accessToken->getUserId(); } + } + +The access token authenticator will use the returned user identifier to +load the user using the :ref:`user provider `. .. caution:: - It is important to check the token is valid. - For instance, in the example we verify the token has not expired. - With self-contained access tokens such as JWT, the handler is required to - verify the digital signature and understand all claims, - especially ``sub``, ``iat``, ``nbf`` and ``exp``. + It is important to check the token if is valid. For instance, the + example above verifies whether the token has not expired. With + self-contained access tokens such as JWT, the handler is required to + verify the digital signature and understand all claims, especially + ``sub``, ``iat``, ``nbf`` and ``exp``. -Customizing the Authenticator ------------------------------ +2) Configure the Token Extractor (Optional) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1) Access Token Extractors +The application is now ready to handle incoming tokens. A *token extractor* +retrieves the token from the request (e.g. a header or request body). -By default, the access token is read from the request header parameter ``Authorization`` with the scheme ``Bearer``. -You can change the behavior and send the access token through different ways. +By default, the access token is read from the request header parameter +``Authorization`` with the scheme ``Bearer`` (e.g. ``Authorization: Bearer +the-token-value``). -This authenticator provides services able to extract the access token as per the RFC6750: +Symfony provides other extractors as per the `RFC6750`_: -- ``header`` or ``security.access_token_extractor.header``: the token is sent through the request header. Usually ``Authorization`` with the ``Bearer`` scheme. -- ``query_string`` or ``security.access_token_extractor.query_string``: the token is part of the query string. Usually ``access_token``. -- ``request_body`` or ``security.access_token_extractor.request_body``: the token is part of the request body during a POST request. Usually ``access_token``. +``header`` (default) + The token is sent through the request header. Usually ``Authorization`` + with the ``Bearer`` scheme. +``query_string`` + The token is part of the request query string. Usually ``access_token``. +``request_body`` + The token is part of the request body during a POST request. Usually + ``access_token``. .. caution:: Because of the security weaknesses associated with the URI method, - including the high likelihood that the URL or the request body containing the access token will be logged, - methods ``query_string`` and ``request_body`` **SHOULD NOT** be used unless it is impossible - to transport the access token in the request header field. + including the high likelihood that the URL or the request body + containing the access token will be logged, methods ``query_string`` + and ``request_body`` **SHOULD NOT** be used unless it is impossible to + transport the access token in the request header field. -Also, you can also create a custom extractor. The class shall implement the interface +You can also create a custom extractor. The class must implement :class:`Symfony\\Component\\Security\\Http\\AccessToken\\AccessTokenExtractorInterface`. .. configuration-block:: @@ -112,10 +156,60 @@ Also, you can also create a custom extractor. The class shall implement the inte main: access_token: token_handler: App\Security\AccessTokenHandler - token_extractors: 'my_custom_access_token_extractor' -It is possible to set multiple extractors. -In this case, **the order is important**: the first in the list is called first. + # use a different built-in extractor + token_extractors: request_body + + # or provide the service ID of a custom extractor + token_extractors: 'App\Security\CustomTokenExtractor' + + .. code-block:: xml + + + + + + + + + + request_body + + + App\Security\CustomTokenExtractor + + + + + + .. code-block:: php + + // config/packages/security.php + use App\Security\AccessTokenHandler; + use App\Security\CustomTokenExtractor; + use Symfony\Config\SecurityConfig; + + return static function (SecurityConfig $security) { + $security->firewall('main') + ->accessToken() + ->tokenHandler(AccessTokenHandler::class) + + // use a different built-in extractor + ->tokenExtractors('request_body') + + # or provide the service ID of a custom extractor + ->tokenExtractors(CustomTokenExtractor::class) + ; + }; + +It is possible to set multiple extractors. In this case, **the order is +important**: the first in the list is called first. .. configuration-block:: @@ -129,37 +223,70 @@ In this case, **the order is important**: the first in the list is called first. token_handler: App\Security\AccessTokenHandler token_extractors: - 'header' - - 'request_body' - - 'query_string' - - 'my_custom_access_token_extractor' + - 'App\Security\CustomTokenExtractor' + + .. code-block:: xml + + + + + + + + + header + App\Security\CustomTokenExtractor + + + + -2) Customizing the Success Handler + .. code-block:: php -Sometimes, the default success handling does not fit your use-case (e.g. -when you need to generate and return additional response header parameters). -To customize how the success handler behaves, create your own handler as a class that implements -:class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationSuccessHandlerInterface`:: + // config/packages/security.php + use App\Security\AccessTokenHandler; + use App\Security\CustomTokenExtractor; + use Symfony\Config\SecurityConfig; - // src/Security/Authentication/AuthenticationSuccessHandler.php - namespace App\Security\Authentication; + return static function (SecurityConfig $security) { + $security->firewall('main') + ->accessToken() + ->tokenHandler(AccessTokenHandler::class) + ->tokenExtractors([ + 'header', + CustomTokenExtractor::class, + ]) + ; + }; - use Symfony\Component\HttpFoundation\JsonResponse; - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; +3) Submit a Request +~~~~~~~~~~~~~~~~~~~ - class AuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface - { - public function onAuthenticationSuccess(Request $request, TokenInterface $token): JsonResponse - { - $user = $token->getUser(); - $userApiToken = $user->getApiToken(); +That's it! Your application can now authenticate incoming requests using an +API token. - return new JsonResponse(['apiToken' => $userApiToken]); - } - } +Using the default header extractor, you can test the feature by submitting +a request like this: -Then, configure this service ID as the ``success_handler``: +.. code-block:: terminal + + $ curl -H 'Authorization: Bearer an-accepted-token-value' \ + https://localhost:8000/api/some-route + +Customizing the Success Handler +------------------------------- + +By default, the request continues (e.g. the controller for the route is +run). If you want to customize success handling, create your own success +handler by creating a class that implements +:class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationSuccessHandlerInterface` +and configure the service ID as the ``success_handler``: .. configuration-block:: @@ -173,10 +300,48 @@ Then, configure this service ID as the ``success_handler``: token_handler: App\Security\AccessTokenHandler success_handler: App\Security\Authentication\AuthenticationSuccessHandler + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // config/packages/security.php + use App\Security\AccessTokenHandler; + use App\Security\Authentication\AuthenticationSuccessHandler; + use Symfony\Config\SecurityConfig; + + return static function (SecurityConfig $security) { + $security->firewall('main') + ->accessToken() + ->tokenHandler(AccessTokenHandler::class) + ->successHandler(AuthenticationSuccessHandler::class) + ; + }; + .. tip:: If you want to customize the default failure handling, use the ``failure_handler`` option and create a class that implements :class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationFailureHandlerInterface`. +.. _`Json Web Tokens (JWT)`: https://datatracker.ietf.org/doc/html/rfc7519 +.. _`SAML2 (XML structures)`: https://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html .. _`RFC6750`: https://datatracker.ietf.org/doc/html/rfc6750 From f3f47adb6446934f6b5338825e5cb7a2fe7fae1b Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Fri, 25 Nov 2022 09:40:47 +0100 Subject: [PATCH 039/774] Update docs for 6.2.0-RC1 changes to TokenHandlerInterface Co-authored-by: Vincent --- security/access_token.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/security/access_token.rst b/security/access_token.rst index 65fd072cc79..e29267585c6 100644 --- a/security/access_token.rst +++ b/security/access_token.rst @@ -82,6 +82,7 @@ This handler must implement use App\Repository\AccessTokenRepository; use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; + use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; class AccessTokenHandler implements AccessTokenHandlerInterface { @@ -90,16 +91,16 @@ This handler must implement ) { } - public function getUserIdentifierFrom(string $token): string + public function getUserBadgeFrom(string $accessToken): UserBadge { // e.g. query the "access token" database to search for this token $accessToken = $this->repository->findOneByValue($token); - if ($accessToken === null || !$accessToken->isValid()) { + if (null === $accessToken || !$accessToken->isValid()) { throw new BadCredentialsException('Invalid credentials.'); } - // and return the user identifier from the found token - return $accessToken->getUserId(); + // and return a UserBadge object containing the user identifier from the found token + return new UserBadge($accessToken->getUserId()); } } From 92ac566ac04ffe8192878f8f4827060152e91ce5 Mon Sep 17 00:00:00 2001 From: Jordane Vaspard Date: Sat, 26 Nov 2022 18:47:32 +0100 Subject: [PATCH 040/774] Fix assets path in all directories no matter the deep. Add fix for js' assets. Add fix for fontawesome' assets. --- _build/build.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/_build/build.php b/_build/build.php index 66470a0df59..989eaea7cbe 100755 --- a/_build/build.php +++ b/_build/build.php @@ -47,9 +47,18 @@ if ($result->isSuccessful()) { // fix assets URLs to make them absolute (otherwise, they don't work in subdirectories) - foreach (glob($outputDir.'/**/*.html') as $htmlFilePath) { + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($outputDir)); + + foreach (new RegexIterator($iterator, '/^.+\.html$/i', RegexIterator::GET_MATCH) as $match) { + $htmlFilePath = array_shift($match); + $htmlContents = file_get_contents($htmlFilePath); + file_put_contents($htmlFilePath, str_replace('', '', $htmlContents)); + } + + foreach (new RegexIterator($iterator, '/^.+\.css/i', RegexIterator::GET_MATCH) as $match) { + $htmlFilePath = array_shift($match); $htmlContents = file_get_contents($htmlFilePath); - file_put_contents($htmlFilePath, str_replace('href="assets/', 'href="/assets/', $htmlContents)); + file_put_contents($htmlFilePath, str_replace('fonts/', '../fonts/', $htmlContents)); } $io->success(sprintf("The Symfony Docs were successfully built at %s", realpath($outputDir))); From e97a574f9cd49e9f62f43522f62eeb9358fb72cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sun, 27 Nov 2022 17:41:23 +0100 Subject: [PATCH 041/774] Add documentation about doctrine's AsEventListener --- .doctor-rst.yaml | 1 + doctrine/events.rst | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml index 7323d53d1dd..fac523a64da 100644 --- a/.doctor-rst.yaml +++ b/.doctor-rst.yaml @@ -80,6 +80,7 @@ whitelist: - 'The bin/console Command' - '# username is your full Gmail or Google Apps email address' - '.. _`LDAP injection`: http://projects.webappsec.org/w/page/13246947/LDAP%20Injection' + - '.. versionadded:: 2.7.2' # Doctrine - '.. versionadded:: 1.9.0' # Encore - '.. versionadded:: 0.28.4' # Encore - '.. versionadded:: 2.4.0' # SwiftMailer diff --git a/doctrine/events.rst b/doctrine/events.rst index 9a2756a4e61..1b9f2d305a4 100644 --- a/doctrine/events.rst +++ b/doctrine/events.rst @@ -149,6 +149,23 @@ with the ``doctrine.event_listener`` tag: .. configuration-block:: + .. code-block:: attribute + + // src/App/EventListener/SearchIndexer.php + namespace App\EventListener; + + use Doctrine\Bundle\DoctrineBundle\Attribute\AsEventListener; + use Doctrine\ORM\Event\LifecycleEventArgs; + + #[AsEventListener('postPersist'/*, 500, 'default'*/)] + class SearchIndexer + { + public function postPersist(LifecycleEventArgs $event): void + { + // ... + } + } + .. code-block:: yaml # config/services.yaml @@ -219,6 +236,12 @@ with the ``doctrine.event_listener`` tag: ; }; + +.. versionadded:: 2.7.2 + + The :class:`Doctrine\\Bundle\\DoctrineBundle\\Attribute\\AsEventListener` + attribute was introduced in DoctrineBundle 2.7.2. + .. tip:: Symfony loads (and instantiates) Doctrine listeners only when the related From 36d91fe8b734bc391733696f026e3249b5330b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sun, 23 Oct 2022 17:17:26 +0200 Subject: [PATCH 042/774] Add documentation about the Doctrine Entity Value Resolver --- .doctor-rst.yaml | 1 + best_practices.rst | 13 ++- doctrine.rst | 266 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 265 insertions(+), 15 deletions(-) diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml index 7323d53d1dd..5606ed239a1 100644 --- a/.doctor-rst.yaml +++ b/.doctor-rst.yaml @@ -88,6 +88,7 @@ whitelist: - '.. versionadded:: 1.11' # Messenger (Middleware / DoctrineBundle) - '.. versionadded:: 1.18' # Flex in setup/upgrade_minor.rst - '.. versionadded:: 1.0.0' # Encore + - '.. versionadded:: 2.7.1' # Doctrine - '0 => 123' # assertion for var_dumper - components/var_dumper.rst - '1 => "foo"' # assertion for var_dumper - components/var_dumper.rst - '123,' # assertion for var_dumper - components/var_dumper.rst diff --git a/best_practices.rst b/best_practices.rst index bc2269b4236..efe15c1c6fa 100644 --- a/best_practices.rst +++ b/best_practices.rst @@ -246,16 +246,17 @@ Instead, you must use dependency injection to fetch services by :ref:`type-hinting action method arguments ` or constructor arguments. -Use ParamConverters If They Are Convenient -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Use EntityValueResolver If It Is Convenient +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you're using :doc:`Doctrine `, then you can *optionally* use the -`ParamConverter`_ to automatically query for an entity and pass it as an argument -to your controller. It will also show a 404 page if no entity can be found. +`EntityValueResolver`_ to automatically query for an entity and pass it as an +argument to your controller. It will also show a 404 page if no entity can be +found. If the logic to get an entity from a route variable is more complex, instead of -configuring the ParamConverter, it's better to make the Doctrine query inside -the controller (e.g. by calling to a :doc:`Doctrine repository method `). +configuring the EntityValueResolver, it's better to make the Doctrine query +inside the controller (e.g. by calling to a :doc:`Doctrine repository method `). Templates --------- diff --git a/doctrine.rst b/doctrine.rst index 632641d1f11..6ad5c6d665d 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -598,15 +598,59 @@ the :ref:`doctrine-queries` section. see the web debug toolbar, install the ``profiler`` :ref:`Symfony pack ` by running this command: ``composer require --dev symfony/profiler-pack``. -Automatically Fetching Objects (ParamConverter) ------------------------------------------------ +Automatically Fetching Objects (EntityValueResolver) +---------------------------------------------------- -In many cases, you can use the `SensioFrameworkExtraBundle`_ to do the query -for you automatically! First, install the bundle in case you don't have it: +In many cases, you can use the `EntityValueResolver`_ to do the query for you +automatically! First, enable the feature: -.. code-block:: terminal +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/doctrine.yaml + doctrine: + orm: + controller_resolver: + enabled: true + auto_mapping: true + evict_cache: false + + .. code-block:: xml + + + + - $ composer require sensio/framework-extra-bundle + + + + + + + + + + .. code-block:: php + + // config/packages/doctrine.php + use Symfony\Config\DoctrineConfig; + + return static function (DoctrineConfig $doctrine) { + $controllerResolver = $doctrine->orm() + ->entityManager('default') + // ... + ->controllerResolver(); + + $controllerResolver->autoMapping(true); + $controllerResolver->evictCache(true); + }; Now, simplify your controller:: @@ -621,7 +665,7 @@ Now, simplify your controller:: class ProductController extends AbstractController { - #[Route('/product/{id}', name: 'product_show')] + #[Route('/product/{id}')] public function show(Product $product): Response { // use the Product! @@ -632,7 +676,212 @@ Now, simplify your controller:: That's it! The bundle uses the ``{id}`` from the route to query for the ``Product`` by the ``id`` column. If it's not found, a 404 page is generated. -There are many more options you can use. Read more about the `ParamConverter`_. +This behavior can be enabled on all your controllers, by setting the ``auto_mapping`` +parameter to ``true``. Or individually on the desired controllers by using the +``MapEntity`` attribute: + + // src/Controller/ProductController.php + namespace App\Controller; + + use App\Entity\Product; + use App\Repository\ProductRepository; + use Symfony\Bridge\Doctrine\Attribute\MapEntity; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Routing\Annotation\Route; + // ... + + class ProductController extends AbstractController + { + #[Route('/product/{id}')] + public function show( + #[MapEntity] + Product $product + ): Response { + // use the Product! + // ... + } + } + +Fetch Automatically +~~~~~~~~~~~~~~~~~~~ + +If your route wildcards match properties on your entity, then the resolver +will automatically fetch them: + +.. configuration-block:: + + .. code-block:: php-attributes + + /** + * Fetch via primary key because {id} is in the route. + */ + #[Route('/product/{id}')] + public function showByPk(Post $post): Response + { + } + + /** + * Perform a findOneBy() where the slug property matches {slug}. + */ + #[Route('/product/{slug}')] + public function showBySlug(Post $post): Response + { + } + +Automatic fetching works in these situations: + +* If ``{id}`` is in your route, then this is used to fetch by + primary key via the ``find()`` method. + +* The resolver will attempt to do a ``findOneBy()`` fetch by using + *all* of the wildcards in your route that are actually properties + on your entity (non-properties are ignored). + +You can control this behavior by actually *adding* the ``MapEntity`` +attribute and using the `MapEntity options`_. + +Fetch via an Expression +~~~~~~~~~~~~~~~~~~~~~~~ + +If automatic fetching doesn't work, use an expression: + +.. configuration-block:: + + .. code-block:: php-attributes + + use Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity; + + #[Route('/product/{product_id}')] + public function show( + #[MapEntity(expr: 'repository.find(product_id)')] + Product $product + ): Response { + } + +Use the special ``MapEntity`` attribute with an ``expr`` option to +fetch the object by calling a method on your repository. The +``repository`` method will be your entity's Repository class and +any route wildcards - like ``{product_id}`` are available as variables. + +This can also be used to help resolve multiple arguments: + +.. configuration-block:: + + .. code-block:: php-attributes + + #[Route('/product/{id}/comments/{comment_id}')] + public function show( + Product $product + #[MapEntity(expr: 'repository.find(comment_id)')] + Comment $comment + ): Response { + } + +In the example above, the ``$product`` argument is handled automatically, +but ``$comment`` is configured with the attribute since they cannot both follow +the default convention. + +.. _`MapEntity options`: + + +MapEntity Options +~~~~~~~~~~~~~~~~~ + +A number of ``options`` are available on the ``MapEntity`` annotation to +control behavior: + +* ``id``: If an ``id`` option is configured and matches a route parameter, then + the resolver will find by the primary key: + + .. configuration-block:: + + .. code-block:: php-attributes + + #[Route('/product/{product_id}')] + public function show( + Product $product + #[MapEntity(id: 'product_id')] + Comment $comment + ): Response { + } + +* ``mapping``: Configures the properties and values to use with the ``findOneBy()`` + method: the key is the route placeholder name and the value is the Doctrine + property name: + + .. configuration-block:: + + .. code-block:: php-attributes + + #[Route('/product/{category}/{slug}/comments/{comment_slug}')] + public function show( + #[MapEntity(mapping: ['date' => 'date', 'slug' => 'slug'])] + Product $product + #[MapEntity(mapping: ['comment_slug' => 'slug'])] + Comment $comment + ): Response { + } + +* ``exclude`` Configures the properties that should be used in the ``findOneBy()`` + method by *excluding* one or more properties so that not *all* are used: + + .. configuration-block:: + + .. code-block:: php-attributes + + #[Route('/product/{slug}/{date}')] + public function show( + #[MapEntity(exclude: ['date'])] + Product $product + \DateTime $date + ): Response { + } + +* ``stripNull`` If true, then when ``findOneBy()`` is used, any values that + are ``null`` will not be used for the query. + +* ``entityManager`` By default, the ``EntityValueResolver`` uses the *default* + entity manager, but you can configure this: + + .. configuration-block:: + + .. code-block:: php-attributes + + #[Route('/product/{id}')] + public function show( + #[MapEntity(entityManager: ['foo'])] + Product $product + ): Response { + } + +* ``evictCache`` If true, forces Doctrine to always fetch the entity from the + database instead of cache. + +* ``disabled`` If true, the ``EntityValueResolver`` will not try to replace + the argument. + +.. tip:: + + When enabled globally, it's possible to disabled the behavior on a specific + controller, by using the ``MapEntity`` set to ``disabled``. + + public function show( + #[CurrentUser] + #[MapEntity(disabled: true)] + User $user + ): Response { + // User is not resolved by the EntityValueResolver + // ... + } + +.. versionadded:: 6.2 + + Entity Value Resolver was introduced in Symfony 6.2. + +.. versionadded:: 2.7.1 + + Autowiring of the ``EntityValueResolver`` was introduced in DoctrineBundle + 2.7.1. Updating an Object ------------------ @@ -896,7 +1145,6 @@ Learn more .. _`DoctrineMigrationsBundle`: https://github.com/doctrine/DoctrineMigrationsBundle .. _`NativeQuery`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/native-sql.html .. _`SensioFrameworkExtraBundle`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html -.. _`ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html .. _`limit of 767 bytes for the index key prefix`: https://dev.mysql.com/doc/refman/5.6/en/innodb-limits.html .. _`Doctrine screencast series`: https://symfonycasts.com/screencast/symfony-doctrine .. _`API Platform`: https://api-platform.com/docs/core/validation/ From 80095c666c972512dbb92dde3822945d02c4c735 Mon Sep 17 00:00:00 2001 From: Marco Woehr Date: Mon, 28 Nov 2022 09:45:59 +0100 Subject: [PATCH 043/774] Update property_info.rst Ref https://github.com/symfony/symfony-docs/pull/17459. --- components/property_info.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/property_info.rst b/components/property_info.rst index 276479d1e09..f9b693336a8 100644 --- a/components/property_info.rst +++ b/components/property_info.rst @@ -323,13 +323,13 @@ this returns ``true`` if: ``@var SomeClass``, ``@var SomeClass``, ``@var Doctrine\Common\Collections\Collection``, etc.) -``Type::getCollectionKeyType()`` & ``Type::getCollectionValueType()`` +``Type::getCollectionKeyTypes()`` & ``Type::getCollectionValueTypes()`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If the property is a collection, additional type objects may be returned for both the key and value types of the collection (if the information is -available), via the :method:`Type::getCollectionKeyType() ` -and :method:`Type::getCollectionValueType() ` +available), via the :method:`Type::getCollectionKeyTypes() ` +and :method:`Type::getCollectionValueTypes() ` methods. .. _`components-property-info-extractors`: From 65f10aca221561e6d6baff6f17af802e0403f862 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 28 Nov 2022 11:47:00 +0100 Subject: [PATCH 044/774] Fix build --- security/access_token.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/access_token.rst b/security/access_token.rst index e29267585c6..724979764a8 100644 --- a/security/access_token.rst +++ b/security/access_token.rst @@ -343,6 +343,6 @@ and configure the service ID as the ``success_handler``: ``failure_handler`` option and create a class that implements :class:`Symfony\\Component\\Security\\Http\\Authentication\\AuthenticationFailureHandlerInterface`. -.. _`Json Web Tokens (JWT)`: https://datatracker.ietf.org/doc/html/rfc7519 +.. _`JSON Web Tokens (JWT)`: https://datatracker.ietf.org/doc/html/rfc7519 .. _`SAML2 (XML structures)`: https://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0.html .. _`RFC6750`: https://datatracker.ietf.org/doc/html/rfc6750 From 1d78eb95e06c777e3498664704bc24953bba353e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 28 Nov 2022 11:58:56 +0100 Subject: [PATCH 045/774] Mark some Symfony CLI feature as experimental --- setup/symfony_server.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup/symfony_server.rst b/setup/symfony_server.rst index 8cc8d4dfd75..e065ba81b3b 100644 --- a/setup/symfony_server.rst +++ b/setup/symfony_server.rst @@ -280,6 +280,11 @@ server provides a ``run`` command to wrap them as follows: Configuring Workers ------------------- +.. caution:: + + This feature is experimental and could change or be removed at any time + without prior notice. + If you like some processes to start automatically, along with the webserver (``symfony server:start``), add a configuration file to your project: From 858cabccea0982ca9443f5176c73a1ac055f0300 Mon Sep 17 00:00:00 2001 From: Maxime Doutreluingne Date: Mon, 28 Nov 2022 18:45:08 +0100 Subject: [PATCH 046/774] [Validator] Remove annotation --- validation/custom_constraint.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/validation/custom_constraint.rst b/validation/custom_constraint.rst index c061c23f682..4887a126c02 100644 --- a/validation/custom_constraint.rst +++ b/validation/custom_constraint.rst @@ -31,14 +31,14 @@ First you need to create a Constraint class and extend :class:`Symfony\\Componen public string $mode = 'strict'; } -Add ``@Annotation`` or ``#[\Attribute]`` to the constraint class if you want to -use it as an annotation/attribute in other classes. +Add ``#[\Attribute]`` to the constraint class if you want to +use it as an attribute in other classes. .. versionadded:: 6.1 The ``#[HasNamedArguments]`` attribute was introduced in Symfony 6.1. -You can use ``#[HasNamedArguments]`` or ``getRequiredOptions()`` to make some constraint options required:: +You can use ``#[HasNamedArguments]`` to make some constraint options required:: // src/Validator/ContainsAlphanumeric.php namespace App\Validator; From 50bfc316f12a60957824d29ca14caf2cc576864c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Kie=C3=9Fling?= Date: Tue, 29 Nov 2022 09:47:25 +0100 Subject: [PATCH 047/774] Remove paragraph on legacy_error_messages config As the `legacy_error_messages` config node is deprecated since 6.2 (see https://github.com/symfony/symfony/pull/47598), it should probably not be mentioned in the docs any longer. --- forms.rst | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/forms.rst b/forms.rst index 1ba3410e29e..01c3b03445c 100644 --- a/forms.rst +++ b/forms.rst @@ -559,48 +559,6 @@ To see the second approach - adding constraints to the form - and to learn more about the validation constraints, please refer to the :doc:`Symfony validation documentation `. -Form Validation Messages -~~~~~~~~~~~~~~~~~~~~~~~~ - -The form types have default error messages that are more clear and -user-friendly than the ones provided by the validation constraints. To enable -these new messages set the ``legacy_error_messages`` option to ``false``: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - form: - legacy_error_messages: false - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // config/packages/framework.php - use Symfony\Config\FrameworkConfig; - - return static function (FrameworkConfig $framework) { - $framework->form()->legacyErrorMessages(false); - }; - Other Common Form Features -------------------------- From ae927c982631c0446b5a9bd520fd7ccedc24fc18 Mon Sep 17 00:00:00 2001 From: Hugo Alliaume Date: Tue, 29 Nov 2022 10:05:27 +0100 Subject: [PATCH 048/774] feat(symfony-cli): add "symfony proxy:url" command usage --- setup/symfony_server.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/setup/symfony_server.rst b/setup/symfony_server.rst index 8b692364caa..1f0742c03b5 100644 --- a/setup/symfony_server.rst +++ b/setup/symfony_server.rst @@ -237,7 +237,14 @@ domains work: .. code-block:: terminal - $ https_proxy=http://127.0.0.1:7080 curl https://my-domain.wip + # Example with curl + $ https_proxy=$(symfony proxy:url) curl https://my-domain.wip + + # Example with Blackfire and curl + $ https_proxy=$(symfony proxy:url) blackfire curl https://my-domain.wip + + # Example with Cypress + $ https_proxy=$(symfony proxy:url) ./node_modules/bin/cypress open .. note:: From 81ae7bc5e446a165e2ceb1ae8c321ae53bdac18b Mon Sep 17 00:00:00 2001 From: renepupil <117264860+renepupil@users.noreply.github.com> Date: Tue, 29 Nov 2022 10:20:54 +0100 Subject: [PATCH 049/774] Clarify how `CollectionType` entries are indexed Clarifying the indexing for `CollectionType`entries prevents further confusion about how to entries can be identified in the front end as well as when viewing the request data. --- reference/forms/types/collection.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/reference/forms/types/collection.rst b/reference/forms/types/collection.rst index c5572f93c6b..ab537de89ab 100644 --- a/reference/forms/types/collection.rst +++ b/reference/forms/types/collection.rst @@ -11,6 +11,8 @@ forms, which is useful when creating forms that expose one-to-many relationships (e.g. a product from where you can manage many related product photos). +When rendered, existing collection entries are indexed by the keys of the array that is passed as the collection type field data. + +---------------------------+--------------------------------------------------------------------------+ | Rendered as | depends on the `entry_type`_ option | +---------------------------+--------------------------------------------------------------------------+ From 26b4ef1ba6f25b2e19aca3b6eec9a23591ec6fc6 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 28 Nov 2022 22:25:39 +0100 Subject: [PATCH 050/774] Replace an URL by a URL --- components/dom_crawler.rst | 2 +- reference/configuration/framework.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst index e39a04224e5..14dee197db6 100644 --- a/components/dom_crawler.rst +++ b/components/dom_crawler.rst @@ -647,7 +647,7 @@ Resolving a URI The :class:`Symfony\\Component\\DomCrawler\\UriResolver` helper class was added in Symfony 5.1. -The :class:`Symfony\\Component\\DomCrawler\\UriResolver` class takes an URI +The :class:`Symfony\\Component\\DomCrawler\\UriResolver` class takes a URI (relative, absolute, fragment, etc.) and turns it into an absolute URI against another given base URI:: diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 06df82f51ef..fcbf68d1609 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -2359,7 +2359,7 @@ package: .. note:: - If an URL is set, the JSON manifest is downloaded on each request using the `http_client`_. + If a URL is set, the JSON manifest is downloaded on each request using the `http_client`_. .. _reference-assets-strict-mode: From ae02dda8ee2031f11bab512d5831120c9e42b7fb Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 30 Nov 2022 17:32:09 +0100 Subject: [PATCH 051/774] Example using `hash_property_path` with `RepeatedType` --- reference/forms/types/password.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/reference/forms/types/password.rst b/reference/forms/types/password.rst index 9f6010675f0..446363f1a7c 100644 --- a/reference/forms/types/password.rst +++ b/reference/forms/types/password.rst @@ -63,6 +63,15 @@ object. 'mapped' => false, ]); + or if you want to use it with the ``RepeatedType``:: + + $builder->add('plainPassword', RepeatedType::class, [ + 'type' => PasswordType::class, + 'first_options' => ['label' => 'Password', 'hash_property_path' => 'password'], + 'second_options' => ['label' => 'Repeat Password'], + 'mapped' => false, + ]); + Overridden Options ------------------ From d39d7539ddd58e0f2cd69dbc0227d1710e36497f Mon Sep 17 00:00:00 2001 From: Robin Willig Date: Wed, 30 Nov 2022 17:38:48 +0100 Subject: [PATCH 052/774] Better grammatical wording --- components/validator/metadata.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/validator/metadata.rst b/components/validator/metadata.rst index f5df3fa68de..1ab98a590d6 100755 --- a/components/validator/metadata.rst +++ b/components/validator/metadata.rst @@ -37,7 +37,7 @@ Getters Constraints can also be applied to the value returned by any public *getter* method, which are the methods whose names start with ``get``, ``has`` or ``is``. -This feature allows to validate your objects dynamically. +This feature allows validating your objects dynamically. Suppose that, for security reasons, you want to validate that a password field doesn't match the first name of the user. First, create a public method called From f9577a7d45fcd5e629f93ffc328ce08ff4e86600 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 1 Dec 2022 17:18:55 +0100 Subject: [PATCH 053/774] Minor tweak --- components/property_info.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/property_info.rst b/components/property_info.rst index f9b693336a8..7f20064fb69 100644 --- a/components/property_info.rst +++ b/components/property_info.rst @@ -324,7 +324,7 @@ this returns ``true`` if: ``@var Doctrine\Common\Collections\Collection``, etc.) ``Type::getCollectionKeyTypes()`` & ``Type::getCollectionValueTypes()`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If the property is a collection, additional type objects may be returned for both the key and value types of the collection (if the information is From 97b942faf685b6233fbea1621ceea48d4296e12b Mon Sep 17 00:00:00 2001 From: c33s Date: Sat, 19 Nov 2022 13:55:14 +0100 Subject: [PATCH 054/774] [Framework] clearified base_uri behavior with extra examples --- reference/configuration/framework.rst | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index fcbf68d1609..487808e293a 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -963,16 +963,21 @@ every request. Here are some common examples of how ``base_uri`` merging works in practice: -======================= ================== ========================== -``base_uri`` Relative URI Actual Requested URI -======================= ================== ========================== -http://example.org /bar http://example.org/bar -http://example.org/foo /bar http://example.org/bar -http://example.org/foo bar http://example.org/bar -http://example.org/foo/ bar http://example.org/foo/bar -http://example.org http://symfony.com http://symfony.com -http://example.org/?bar bar http://example.org/bar -======================= ================== ========================== +========================== ================== ========================== +``base_uri`` Relative URI Actual Requested URI +========================== ================== ========================== +http://example.org /bar http://example.org/bar +http://example.org/foo /bar http://example.org/bar +http://example.org/foo bar http://example.org/bar +http://example.org/foo/ /bar http://example.org/bar +http://example.org/foo/ bar http://example.org/foo/bar +http://example.org http://symfony.com http://symfony.com +http://example.org/?bar bar http://example.org/bar +http://example.org/api/v4 /bar http://example.org/bar +http://example.org/api/v4/ /bar http://example.org/bar +http://example.org/api/v4 bar http://example.org/api/bar +http://example.org/api/v4/ bar http://example.org/api/v4/bar +========================== ================== ========================== bindto ...... From 4e955279e0fec6cfedbd8ed6f637accb4997e645 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 1 Dec 2022 17:25:29 +0100 Subject: [PATCH 055/774] Minor tweak --- reference/configuration/framework.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 487808e293a..391db4ba386 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -963,9 +963,9 @@ every request. Here are some common examples of how ``base_uri`` merging works in practice: -========================== ================== ========================== +========================== ================== ============================= ``base_uri`` Relative URI Actual Requested URI -========================== ================== ========================== +========================== ================== ============================= http://example.org /bar http://example.org/bar http://example.org/foo /bar http://example.org/bar http://example.org/foo bar http://example.org/bar @@ -977,7 +977,7 @@ http://example.org/api/v4 /bar http://example.org/bar http://example.org/api/v4/ /bar http://example.org/bar http://example.org/api/v4 bar http://example.org/api/bar http://example.org/api/v4/ bar http://example.org/api/v4/bar -========================== ================== ========================== +========================== ================== ============================= bindto ...... From ef14fc4ca4c061a21a48170b520a8d77a8f23282 Mon Sep 17 00:00:00 2001 From: Greg Pluta Date: Thu, 1 Dec 2022 21:42:18 +0100 Subject: [PATCH 056/774] Fix "WordPress" spelling typo --- create_framework/event_dispatcher.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_framework/event_dispatcher.rst b/create_framework/event_dispatcher.rst index bf872a5bb50..181c75b00d2 100644 --- a/create_framework/event_dispatcher.rst +++ b/create_framework/event_dispatcher.rst @@ -8,7 +8,7 @@ hook into the framework life cycle to modify the way the request is handled. What kind of hooks are we talking about? Authentication or caching for instance. To be flexible, hooks must be plug-and-play; the ones you "register" for an application are different from the next one depending on your specific -needs. Many software have a similar concept like Drupal or Wordpress. In some +needs. Many software have a similar concept like Drupal or WordPress. In some languages, there is even a standard like `WSGI`_ in Python or `Rack`_ in Ruby. As there is no standard for PHP, we are going to use a well-known design From afb0b805a25840c740e885f842f3400d64cc8870 Mon Sep 17 00:00:00 2001 From: Marko Kaznovac Date: Thu, 1 Dec 2022 10:25:18 +0100 Subject: [PATCH 057/774] ValueResolver: fix resolver injection example --- controller/value_resolver.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/controller/value_resolver.rst b/controller/value_resolver.rst index 2ece1fcaa7d..f08aee9126d 100644 --- a/controller/value_resolver.rst +++ b/controller/value_resolver.rst @@ -265,7 +265,7 @@ and adding a priority: autowire: true # ... - App\ArgumentResolver\UserValueResolver: + App\ArgumentResolver\BookingIdValueResolver: tags: - { name: controller.argument_value_resolver, priority: 150 } @@ -283,7 +283,7 @@ and adding a priority: - + @@ -295,12 +295,12 @@ and adding a priority: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - use App\ArgumentResolver\UserValueResolver; + use App\ArgumentResolver\BookingIdValueResolver; return static function (ContainerConfigurator $container) { $services = $configurator->services(); - $services->set(UserValueResolver::class) + $services->set(BookingIdValueResolver::class) ->tag('controller.argument_value_resolver', ['priority' => 150]) ; }; From 1b07595b77089cde6e748a0c14d41e0a5c326684 Mon Sep 17 00:00:00 2001 From: Vasilij Dusko Date: Sun, 4 Dec 2022 16:12:28 +0200 Subject: [PATCH 058/774] setup version fix --- setup.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.rst b/setup.rst index db2c1fe10b0..cd602461783 100644 --- a/setup.rst +++ b/setup.rst @@ -49,10 +49,10 @@ application: .. code-block:: terminal # run this if you are building a traditional web application - $ symfony new my_project_directory --version="6.2.*@dev" --webapp + $ symfony new my_project_directory --version="6.2.*" --webapp # run this if you are building a microservice, console application or API - $ symfony new my_project_directory --version="6.2.*@dev" + $ symfony new my_project_directory --version="6.2.*" The only difference between these two commands is the number of packages installed by default. The ``--webapp`` option installs all the packages that you @@ -64,12 +64,12 @@ Symfony application using Composer: .. code-block:: terminal # run this if you are building a traditional web application - $ composer create-project symfony/skeleton:"6.2.*@dev" my_project_directory + $ composer create-project symfony/skeleton:"6.2.*" my_project_directory $ cd my_project_directory $ composer require webapp # run this if you are building a microservice, console application or API - $ composer create-project symfony/skeleton:"6.2.*@dev" my_project_directory + $ composer create-project symfony/skeleton:"6.2.*" my_project_directory No matter which command you run to create the Symfony application. All of them will create a new ``my_project_directory/`` directory, download some dependencies From 5d59bba309e2c3fbee208df121d32456d1076ae9 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Sun, 4 Dec 2022 21:24:37 +0100 Subject: [PATCH 059/774] Minor --- notifier.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifier.rst b/notifier.rst index 6408bbe2299..2764b5464fb 100644 --- a/notifier.rst +++ b/notifier.rst @@ -717,7 +717,7 @@ typical alert levels, which you can implement immediately using: # config/services.yaml services: - notifier.flash_message_importance_mapper: + notifier.flash_message_importance_mapper: class: Symfony\Component\Notifier\FlashMessage\BootstrapFlashMessageImportanceMapper .. code-block:: xml From c5cfa31c7c8fe918820174a1a7e434791e295f4b Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Sun, 4 Dec 2022 21:40:00 +0100 Subject: [PATCH 060/774] Add versionadded directive --- notifier/events.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/notifier/events.rst b/notifier/events.rst index aef7c848cec..2b9c9f6d1eb 100644 --- a/notifier/events.rst +++ b/notifier/events.rst @@ -4,6 +4,11 @@ Using Events ============ +.. versionadded:: 5.4 + + The ``MessageEvent``, ``FailedMessageEvent`` and ``SentMessageEvent`` were + introduced in Symfony 5.4. + The class:``...\\..\\Transport`` of the Notifier component allows you to optionally hook into the lifecycle via events. From a45341d49c965532b68e509784776bb8579d3799 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Sun, 4 Dec 2022 21:40:39 +0100 Subject: [PATCH 061/774] Remove obsolete versionadded directive --- notifier/events.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/notifier/events.rst b/notifier/events.rst index 2b9c9f6d1eb..aef7c848cec 100644 --- a/notifier/events.rst +++ b/notifier/events.rst @@ -4,11 +4,6 @@ Using Events ============ -.. versionadded:: 5.4 - - The ``MessageEvent``, ``FailedMessageEvent`` and ``SentMessageEvent`` were - introduced in Symfony 5.4. - The class:``...\\..\\Transport`` of the Notifier component allows you to optionally hook into the lifecycle via events. From 5f68f509f513d41f9fdae38154f128e9ff419240 Mon Sep 17 00:00:00 2001 From: Issam KHADIRI Date: Sun, 4 Dec 2022 22:05:38 +0100 Subject: [PATCH 062/774] Update security.rst i think the new namespace is Symfony\Bundle\SecurityBundle\Security --- security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security.rst b/security.rst index a927e4b2350..e3a6ccf9038 100644 --- a/security.rst +++ b/security.rst @@ -1940,7 +1940,7 @@ If you need to get the logged in user from a service, use the The :class:`Symfony\\Bundle\\SecurityBundle\\Security` class was introduced in Symfony 6.2. In previous Symfony versions this class was - defined in ``Symfony\Bundle\SecurityBundle\Security``. + defined in ``Symfony\Component\Security\Core\Security``. Fetch the User in a Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 246b7415ce994d3db6c4dd16a2d9837cd15fecca Mon Sep 17 00:00:00 2001 From: MatTheCat Date: Sun, 4 Dec 2022 22:20:30 +0100 Subject: [PATCH 063/774] Update `framework.exceptions` XML and PHP configuration examples --- reference/configuration/framework.rst | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 391db4ba386..06678374267 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -3579,12 +3579,11 @@ exceptions that match the given exception class: http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - - - debug - 422 - - + @@ -3596,13 +3595,9 @@ exceptions that match the given exception class: use Symfony\Config\FrameworkConfig; return static function (FrameworkConfig $framework) { - $framework - ->exceptions(BadRequestHttpException::class) - ->log_level('debug'); - - $framework - ->exceptions(BadRequestHttpException::class) - ->status_code(422); + $framework->exception(BadRequestHttpException::class) + ->logLevel('debug') + ->statusCode(422) ; }; From 5f3d75b24877b7be98ac3c74938f5ba169d84ade Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 5 Dec 2022 10:31:54 +0100 Subject: [PATCH 064/774] [Notifier] Fix class Fixes #17503 --- notifier/events.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/notifier/events.rst b/notifier/events.rst index 2b9c9f6d1eb..05a602a3bab 100644 --- a/notifier/events.rst +++ b/notifier/events.rst @@ -9,8 +9,8 @@ Using Events The ``MessageEvent``, ``FailedMessageEvent`` and ``SentMessageEvent`` were introduced in Symfony 5.4. -The class:``...\\..\\Transport`` of the Notifier component allows you to optionally hook -into the lifecycle via events. +The :class:`Symfony\\Component\\Notifier\\Transport`` class of the Notifier component +allows you to optionally hook into the lifecycle via events. The ``MessageEvent::class`` Event --------------------------------- From 177f62012a17c1c87721dda429666a9475a622c3 Mon Sep 17 00:00:00 2001 From: Thibaut Cheymol Date: Sat, 3 Dec 2022 11:59:22 +0100 Subject: [PATCH 065/774] [FrameworkBundle] Rename option `catch_all_throwables` to `handle_all_throwables` in documentation --- reference/configuration/framework.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 5b6efc18f32..e8e3a3d7ce2 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -53,8 +53,8 @@ will invalidate all signed URIs and Remember Me cookies. That's why, after changing this value, you should regenerate the application cache and log out all the application users. -catch_all_throwables -~~~~~~~~~~~~~~~~~~~~ +handle_all_throwables +~~~~~~~~~~~~~~~~~~~~~ **type**: ``boolean`` **default**: ``false`` @@ -65,7 +65,7 @@ Starting from Symfony 7.0, the default value of this option will be ``true``. .. versionadded:: 6.2 - The ``catch_all_throwables`` option was introduced in Symfony 6.2. + The ``handle_all_throwables`` option was introduced in Symfony 6.2. .. _configuration-framework-http_cache: From cb091ded711de2e99d87a58af47af836addeda6f Mon Sep 17 00:00:00 2001 From: HypeMC Date: Mon, 5 Dec 2022 20:54:18 +0100 Subject: [PATCH 066/774] [Security] User in expression cannot be "anon" --- security/expressions.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/expressions.rst b/security/expressions.rst index 16989fd75ed..338c5c94ab9 100644 --- a/security/expressions.rst +++ b/security/expressions.rst @@ -44,7 +44,8 @@ syntax, see :doc:`/components/expression_language/syntax`. Inside the expression, you have access to a number of variables: ``user`` - The user object (or the string ``anon`` if you're not authenticated). + An instance of :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface` that represents the current user + or ``null`` if you're not authenticated. ``role_names`` An array with the string representation of the roles the user has. This array includes any roles granted indirectly via the :ref:`role hierarchy ` but it From 204941b91df5ae8198a297bd6b8ad65ffec71b44 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Tue, 6 Dec 2022 00:28:45 +0100 Subject: [PATCH 067/774] Removing another self-closing HTML slash --- routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.rst b/routing.rst index 1d72af7bbe5..1db6fa4448a 100644 --- a/routing.rst +++ b/routing.rst @@ -301,7 +301,7 @@ Use the ``methods`` option to restrict the verbs each route should respond to: HTML forms only support ``GET`` and ``POST`` methods. If you're calling a route with a different method from an HTML form, add a hidden field called - ``_method`` with the method to use (e.g. ````). + ``_method`` with the method to use (e.g. ````). If you create your forms with :doc:`Symfony Forms ` this is done automatically for you. From dc7c6a1366c0c87c17b707256b61f6e5eb9c821f Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Tue, 6 Dec 2022 15:46:06 +0100 Subject: [PATCH 068/774] [#17393] Finish entity value resolver docs --- best_practices.rst | 13 +- doctrine.rst | 294 +++++++++++++++++---------------------------- 2 files changed, 116 insertions(+), 191 deletions(-) diff --git a/best_practices.rst b/best_practices.rst index efe15c1c6fa..26187fd4d17 100644 --- a/best_practices.rst +++ b/best_practices.rst @@ -246,13 +246,13 @@ Instead, you must use dependency injection to fetch services by :ref:`type-hinting action method arguments ` or constructor arguments. -Use EntityValueResolver If It Is Convenient -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Use Entity Value Resolvers If They Are Convenient +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you're using :doc:`Doctrine `, then you can *optionally* use the -`EntityValueResolver`_ to automatically query for an entity and pass it as an -argument to your controller. It will also show a 404 page if no entity can be -found. +If you're using :doc:`Doctrine `, then you can *optionally* use +the :ref:`EntityValueResolver ` to +automatically query for an entity and pass it as an argument to your +controller. It will also show a 404 page if no entity can be found. If the logic to get an entity from a route variable is more complex, instead of configuring the EntityValueResolver, it's better to make the Doctrine query @@ -451,7 +451,6 @@ you must set up a redirection. .. _`Symfony Demo`: https://github.com/symfony/demo .. _`download Symfony`: https://symfony.com/download .. _`Composer`: https://getcomposer.org/ -.. _`ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html .. _`feature toggles`: https://en.wikipedia.org/wiki/Feature_toggle .. _`smoke testing`: https://en.wikipedia.org/wiki/Smoke_testing_(software) .. _`Webpack`: https://webpack.js.org/ diff --git a/doctrine.rst b/doctrine.rst index 6ad5c6d665d..9e100a5e00b 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -598,61 +598,21 @@ the :ref:`doctrine-queries` section. see the web debug toolbar, install the ``profiler`` :ref:`Symfony pack ` by running this command: ``composer require --dev symfony/profiler-pack``. +.. _doctrine-entity-value-resolver: + Automatically Fetching Objects (EntityValueResolver) ---------------------------------------------------- -In many cases, you can use the `EntityValueResolver`_ to do the query for you -automatically! First, enable the feature: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/doctrine.yaml - doctrine: - orm: - controller_resolver: - enabled: true - auto_mapping: true - evict_cache: false - - .. code-block:: xml - - - - - - - - - - - - - - - .. code-block:: php +.. versionadded:: 6.2 - // config/packages/doctrine.php - use Symfony\Config\DoctrineConfig; + Entity Value Resolver was introduced in Symfony 6.2. - return static function (DoctrineConfig $doctrine) { - $controllerResolver = $doctrine->orm() - ->entityManager('default') - // ... - ->controllerResolver(); +.. versionadded:: 2.7.1 - $controllerResolver->autoMapping(true); - $controllerResolver->evictCache(true); - }; + Autowiring of the ``EntityValueResolver`` was introduced in DoctrineBundle 2.7.1. -Now, simplify your controller:: +In many cases, you can use the ``EntityValueResolver`` to do the query for you +automatically! You can simplify the controller to:: // src/Controller/ProductController.php namespace App\Controller; @@ -676,15 +636,17 @@ Now, simplify your controller:: That's it! The bundle uses the ``{id}`` from the route to query for the ``Product`` by the ``id`` column. If it's not found, a 404 page is generated. -This behavior can be enabled on all your controllers, by setting the ``auto_mapping`` -parameter to ``true``. Or individually on the desired controllers by using the -``MapEntity`` attribute: +This behavior is enabled by default on all your controllers. You can +disable it by setting the ``doctrine.orm.controller_resolver.auto_mapping`` +config option to ``false``. + +When disabled, you can enable it individually on the desired controllers by +using the ``MapEntity`` attribute:: // src/Controller/ProductController.php namespace App\Controller; use App\Entity\Product; - use App\Repository\ProductRepository; use Symfony\Bridge\Doctrine\Attribute\MapEntity; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -702,31 +664,41 @@ parameter to ``true``. Or individually on the desired controllers by using the } } +.. tip:: + + When enabled globally, it's possible to disabled the behavior on a specific + controller, by using the ``MapEntity`` set to ``disabled``. + + public function show( + #[CurrentUser] + #[MapEntity(disabled: true)] + User $user + ): Response { + // User is not resolved by the EntityValueResolver + // ... + } + Fetch Automatically ~~~~~~~~~~~~~~~~~~~ If your route wildcards match properties on your entity, then the resolver -will automatically fetch them: - -.. configuration-block:: - - .. code-block:: php-attributes +will automatically fetch them:: - /** - * Fetch via primary key because {id} is in the route. - */ - #[Route('/product/{id}')] - public function showByPk(Post $post): Response - { - } + /** + * Fetch via primary key because {id} is in the route. + */ + #[Route('/product/{id}')] + public function showByPk(Post $post): Response + { + } - /** - * Perform a findOneBy() where the slug property matches {slug}. - */ - #[Route('/product/{slug}')] - public function showBySlug(Post $post): Response - { - } + /** + * Perform a findOneBy() where the slug property matches {slug}. + */ + #[Route('/product/{slug}')] + public function showBySlug(Post $post): Response + { + } Automatic fetching works in these situations: @@ -743,145 +715,99 @@ attribute and using the `MapEntity options`_. Fetch via an Expression ~~~~~~~~~~~~~~~~~~~~~~~ -If automatic fetching doesn't work, use an expression: - -.. configuration-block:: - - .. code-block:: php-attributes +If automatic fetching doesn't work, you can write an expression using the +:doc:`ExpressionLanguage component `:: - use Sensio\Bundle\FrameworkExtraBundle\Configuration\Entity; - - #[Route('/product/{product_id}')] - public function show( - #[MapEntity(expr: 'repository.find(product_id)')] - Product $product - ): Response { - } - -Use the special ``MapEntity`` attribute with an ``expr`` option to -fetch the object by calling a method on your repository. The -``repository`` method will be your entity's Repository class and -any route wildcards - like ``{product_id}`` are available as variables. - -This can also be used to help resolve multiple arguments: + #[Route('/product/{product_id}')] + public function show( + #[MapEntity(expr: 'repository.find(product_id)')] + Product $product + ): Response { + } -.. configuration-block:: +In the expression, the ``repository`` variable will be your entity's +Repository class and any route wildcards - like ``{product_id}`` are +available as variables. - .. code-block:: php-attributes +This can also be used to help resolve multiple arguments:: - #[Route('/product/{id}/comments/{comment_id}')] - public function show( - Product $product - #[MapEntity(expr: 'repository.find(comment_id)')] - Comment $comment - ): Response { - } + #[Route('/product/{id}/comments/{comment_id}')] + public function show( + Product $product + #[MapEntity(expr: 'repository.find(comment_id)')] + Comment $comment + ): Response { + } -In the example above, the ``$product`` argument is handled automatically, +In the example above, the ``$product`` argument is handled automatically, but ``$comment`` is configured with the attribute since they cannot both follow the default convention. -.. _`MapEntity options`: - - MapEntity Options ~~~~~~~~~~~~~~~~~ -A number of ``options`` are available on the ``MapEntity`` annotation to +A number of options are available on the ``MapEntity`` annotation to control behavior: -* ``id``: If an ``id`` option is configured and matches a route parameter, then - the resolver will find by the primary key: - - .. configuration-block:: - - .. code-block:: php-attributes - - #[Route('/product/{product_id}')] - public function show( - Product $product - #[MapEntity(id: 'product_id')] - Comment $comment - ): Response { - } - -* ``mapping``: Configures the properties and values to use with the ``findOneBy()`` - method: the key is the route placeholder name and the value is the Doctrine - property name: - - .. configuration-block:: - - .. code-block:: php-attributes - - #[Route('/product/{category}/{slug}/comments/{comment_slug}')] - public function show( - #[MapEntity(mapping: ['date' => 'date', 'slug' => 'slug'])] - Product $product - #[MapEntity(mapping: ['comment_slug' => 'slug'])] - Comment $comment - ): Response { - } - -* ``exclude`` Configures the properties that should be used in the ``findOneBy()`` - method by *excluding* one or more properties so that not *all* are used: - - .. configuration-block:: - - .. code-block:: php-attributes - - #[Route('/product/{slug}/{date}')] - public function show( - #[MapEntity(exclude: ['date'])] - Product $product - \DateTime $date - ): Response { - } - -* ``stripNull`` If true, then when ``findOneBy()`` is used, any values that - are ``null`` will not be used for the query. - -* ``entityManager`` By default, the ``EntityValueResolver`` uses the *default* - entity manager, but you can configure this: +``id`` + If an ``id`` option is configured and matches a route parameter, then + the resolver will find by the primary key:: - .. configuration-block:: + #[Route('/product/{product_id}')] + public function show( + Product $product + #[MapEntity(id: 'product_id')] + Comment $comment + ): Response { + } - .. code-block:: php-attributes +``mapping`` + Configures the properties and values to use with the ``findOneBy()`` + method: the key is the route placeholder name and the value is the Doctrine + property name:: - #[Route('/product/{id}')] - public function show( - #[MapEntity(entityManager: ['foo'])] - Product $product - ): Response { - } + #[Route('/product/{category}/{slug}/comments/{comment_slug}')] + public function show( + #[MapEntity(mapping: ['date' => 'date', 'slug' => 'slug'])] + Product $product + #[MapEntity(mapping: ['comment_slug' => 'slug'])] + Comment $comment + ): Response { + } -* ``evictCache`` If true, forces Doctrine to always fetch the entity from the - database instead of cache. +``exclude`` + Configures the properties that should be used in the ``findOneBy()`` + method by *excluding* one or more properties so that not *all* are used: -* ``disabled`` If true, the ``EntityValueResolver`` will not try to replace - the argument. + #[Route('/product/{slug}/{date}')] + public function show( + #[MapEntity(exclude: ['date'])] + Product $product + \DateTime $date + ): Response { + } -.. tip:: +``stripNull`` + If true, then when ``findOneBy()`` is used, any values that are + ``null`` will not be used for the query. - When enabled globally, it's possible to disabled the behavior on a specific - controller, by using the ``MapEntity`` set to ``disabled``. +``entityManager`` + By default, the ``EntityValueResolver`` uses the *default* + entity manager, but you can configure this:: + #[Route('/product/{id}')] public function show( - #[CurrentUser] - #[MapEntity(disabled: true)] - User $user + #[MapEntity(entityManager: ['foo'])] + Product $product ): Response { - // User is not resolved by the EntityValueResolver - // ... } -.. versionadded:: 6.2 - - Entity Value Resolver was introduced in Symfony 6.2. - -.. versionadded:: 2.7.1 +``evictCache`` + If true, forces Doctrine to always fetch the entity from the database + instead of cache. - Autowiring of the ``EntityValueResolver`` was introduced in DoctrineBundle - 2.7.1. +``disabled`` + If true, the ``EntityValueResolver`` will not try to replace the argument. Updating an Object ------------------ From e82b467752412b183f07c6d23795fce5845016f2 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Tue, 6 Dec 2022 16:08:10 +0100 Subject: [PATCH 069/774] Fix lint issues --- doctrine.rst | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/doctrine.rst b/doctrine.rst index 9e100a5e00b..cd5806c4bbd 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -755,9 +755,9 @@ control behavior: #[Route('/product/{product_id}')] public function show( - Product $product - #[MapEntity(id: 'product_id')] - Comment $comment + Product $product + #[MapEntity(id: 'product_id')] + Comment $comment ): Response { } @@ -768,10 +768,10 @@ control behavior: #[Route('/product/{category}/{slug}/comments/{comment_slug}')] public function show( - #[MapEntity(mapping: ['date' => 'date', 'slug' => 'slug'])] - Product $product - #[MapEntity(mapping: ['comment_slug' => 'slug'])] - Comment $comment + #[MapEntity(mapping: ['date' => 'date', 'slug' => 'slug'])] + Product $product + #[MapEntity(mapping: ['comment_slug' => 'slug'])] + Comment $comment ): Response { } @@ -781,9 +781,9 @@ control behavior: #[Route('/product/{slug}/{date}')] public function show( - #[MapEntity(exclude: ['date'])] - Product $product - \DateTime $date + #[MapEntity(exclude: ['date'])] + Product $product + \DateTime $date ): Response { } @@ -797,8 +797,8 @@ control behavior: #[Route('/product/{id}')] public function show( - #[MapEntity(entityManager: ['foo'])] - Product $product + #[MapEntity(entityManager: ['foo'])] + Product $product ): Response { } @@ -1070,7 +1070,6 @@ Learn more .. _`Transactions and Concurrency`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/transactions-and-concurrency.html .. _`DoctrineMigrationsBundle`: https://github.com/doctrine/DoctrineMigrationsBundle .. _`NativeQuery`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/native-sql.html -.. _`SensioFrameworkExtraBundle`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html .. _`limit of 767 bytes for the index key prefix`: https://dev.mysql.com/doc/refman/5.6/en/innodb-limits.html .. _`Doctrine screencast series`: https://symfonycasts.com/screencast/symfony-doctrine .. _`API Platform`: https://api-platform.com/docs/core/validation/ From 51155cb1947e6db7326f483249ea904cccd22ccf Mon Sep 17 00:00:00 2001 From: Quentin ADADAIN <61509041+Qadadain@users.noreply.github.com> Date: Tue, 6 Dec 2022 15:05:14 +0100 Subject: [PATCH 070/774] Fix typo, change parameter $accesToken to $token Fix parameter name in documentation example. --- security/access_token.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/access_token.rst b/security/access_token.rst index 724979764a8..d5d607dbcb0 100644 --- a/security/access_token.rst +++ b/security/access_token.rst @@ -94,7 +94,7 @@ This handler must implement public function getUserBadgeFrom(string $accessToken): UserBadge { // e.g. query the "access token" database to search for this token - $accessToken = $this->repository->findOneByValue($token); + $accessToken = $this->repository->findOneByValue($accessToken); if (null === $accessToken || !$accessToken->isValid()) { throw new BadCredentialsException('Invalid credentials.'); } From 6c19fa7a835ca9407bde4b7f91c9b2cb6fbc052f Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Tue, 6 Dec 2022 16:46:57 +0100 Subject: [PATCH 071/774] Changing versionadded 5.1 to note Reasons: * Info about 5.1 doesn't make sense in 5.4 anymore. * Link to `configureContainer` was dead anyway. --- configuration.rst | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/configuration.rst b/configuration.rst index 6d2638008fa..bf00e080262 100644 --- a/configuration.rst +++ b/configuration.rst @@ -60,13 +60,11 @@ configure your applications. Symfony lets you choose between YAML, XML and PHP and throughout the Symfony documentation, all configuration examples will be shown in these three formats. -.. versionadded:: 5.1 +.. note:: - Starting from Symfony 5.1, by default Symfony only loads the configuration + By default, Symfony only loads the configuration files defined in YAML format. If you define configuration in XML and/or PHP - formats, update the ``src/Kernel.php`` file to add support for the ``.xml`` - and ``.php`` file extensions by overriding the - :method:`Symfony\\Component\\HttpKernel\\Kernel::configureContainer` method:: + formats, update the ``src/Kernel.php`` file:: // src/Kernel.php use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; @@ -93,8 +91,8 @@ shown in these three formats. } There isn't any practical difference between formats. In fact, Symfony -transforms and caches all of them into PHP before running the application, so -there's not even any performance difference between them. +transforms all of them into PHP and caches them before running the application, so +there's not even any performance difference. YAML is used by default when installing packages because it's concise and very readable. These are the main advantages and disadvantages of each format: From 173ddb5f6a363b77e87cb2f599feac92b63b0999 Mon Sep 17 00:00:00 2001 From: Romain Monteil Date: Wed, 7 Dec 2022 09:30:50 +0100 Subject: [PATCH 072/774] [DoctrineBridge] Add EntityValueResolver to the list of built-in value resolvers --- controller/value_resolver.rst | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/controller/value_resolver.rst b/controller/value_resolver.rst index f08aee9126d..a8c01407368 100644 --- a/controller/value_resolver.rst +++ b/controller/value_resolver.rst @@ -146,7 +146,7 @@ Symfony ships with the following value resolvers in the argument list. When the action is called, the last (variadic) argument will contain all the values of this array. -In addition, some components and official bundles provide other value resolvers: +In addition, some components, bridges and official bundles provide other value resolvers: :class:`Symfony\\Component\\Security\\Http\\Controller\\UserValueResolver` Injects the object that represents the current logged in user if type-hinted @@ -159,6 +159,33 @@ In addition, some components and official bundles provide other value resolvers: user has a user class not matching the type-hinted class, an ``AccessDeniedException`` is thrown by the resolver to prevent access to the controller. +:class:`Symfony\\Bridge\\Doctrine\\ArgumentResolver\\EntityValueResolver` + Automatically query for an entity and pass it as an argument to your controller. + + For example, the following will query the ``Product`` entity which has ``{id}`` as primary key:: + + // src/Controller/DefaultController.php + namespace App\Controller; + + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Routing\Annotation\Route; + + class DefaultController + { + #[Route('/product/{id}')] + public function share(Product $product): Response + { + // ... + } + } + + To learn more about the use of the ``EntityValueResolver``, see the dedicated + section :ref:`Automatically Fetching Objects `. + + .. versionadded:: 6.2 + + The ``EntityValueResolver`` was introduced in Symfony 6.2. + PSR-7 Objects Resolver: Injects a Symfony HttpFoundation ``Request`` object created from a PSR-7 object of type :class:`Psr\\Http\\Message\\ServerRequestInterface`, From 06374e86793bc9bd63365ef0f7a50466b55213cb Mon Sep 17 00:00:00 2001 From: Robin Willig Date: Tue, 6 Dec 2022 19:37:39 +0100 Subject: [PATCH 073/774] Add comma --- components/validator/resources.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/validator/resources.rst b/components/validator/resources.rst index cd02404f765..7af7d1a4622 100644 --- a/components/validator/resources.rst +++ b/components/validator/resources.rst @@ -76,7 +76,7 @@ configure the locations of these files:: .. note:: - If you want to load YAML mapping files then you will also need to install + If you want to load YAML mapping files, then you will also need to install :doc:`the Yaml component `. .. tip:: From bb2cee77f82eeb550f16430edb0bb7b5dadaed80 Mon Sep 17 00:00:00 2001 From: Robin Willig Date: Tue, 6 Dec 2022 20:20:24 +0100 Subject: [PATCH 074/774] Some little corrections --- README.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.markdown b/README.markdown index 79e6758c24e..3050aa12301 100644 --- a/README.markdown +++ b/README.markdown @@ -24,15 +24,15 @@ Contributing ------------ We love contributors! For more information on how you can contribute, please read -the [Symfony Docs Contributing Guide](https://symfony.com/doc/current/contributing/documentation/overview.html) +the [Symfony Docs Contributing Guide](https://symfony.com/doc/current/contributing/documentation/overview.html). **Important**: use `4.4` branch as the base of your pull requests, unless you are -documenting a feature that was introduced *after* Symfony 4.4 (e.g. in Symfony 5.4). +documenting a feature that was introduced *after* Symfony 4.4 (e.g., in Symfony 5.4). Build Documentation Locally --------------------------- -This is not needed for contributing, but it's useful if you want to debug some +This is not needed for contributing, but it's useful if you would like to debug some issue in the docs or if you want to read Symfony Documentation offline. ```bash From 01041a0b3c4b43e73525a6b37556be50c570f61e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 7 Dec 2022 18:00:18 +0100 Subject: [PATCH 075/774] Minor tweak --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 3050aa12301..b8c8f863b4d 100644 --- a/README.markdown +++ b/README.markdown @@ -27,7 +27,7 @@ We love contributors! For more information on how you can contribute, please rea the [Symfony Docs Contributing Guide](https://symfony.com/doc/current/contributing/documentation/overview.html). **Important**: use `4.4` branch as the base of your pull requests, unless you are -documenting a feature that was introduced *after* Symfony 4.4 (e.g., in Symfony 5.4). +documenting a feature that was introduced *after* Symfony 4.4 (e.g. in Symfony 5.4). Build Documentation Locally --------------------------- From 4ceb7733302dfe8d974b89ba8756f02e296fcd49 Mon Sep 17 00:00:00 2001 From: Romain Monteil Date: Thu, 8 Dec 2022 11:56:39 +0100 Subject: [PATCH 076/774] [Notifier] Add PushNotificationInterface to the list of interfaces to customize a notification --- notifier.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/notifier.rst b/notifier.rst index 40d3a80a027..d56cd3534c7 100644 --- a/notifier.rst +++ b/notifier.rst @@ -724,9 +724,10 @@ and its ``asChatMessage()`` method:: } The -:class:`Symfony\\Component\\Notifier\\Notification\\SmsNotificationInterface` -and +:class:`Symfony\\Component\\Notifier\\Notification\\SmsNotificationInterface`, :class:`Symfony\\Component\\Notifier\\Notification\\EmailNotificationInterface` +and +:class:`Symfony\\Component\\Notifier\\Notification\\PushNotificationInterface` also exists to modify messages sent to those channels. Disabling Delivery From 49e516a25cb77f45b11b9a44615152836fa1920c Mon Sep 17 00:00:00 2001 From: Robin Willig Date: Tue, 6 Dec 2022 19:25:39 +0100 Subject: [PATCH 077/774] Better wording --- components/validator/metadata.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/validator/metadata.rst b/components/validator/metadata.rst index 1ab98a590d6..226ffd46b06 100755 --- a/components/validator/metadata.rst +++ b/components/validator/metadata.rst @@ -67,7 +67,7 @@ Then, add the Validator component configuration to the class:: Classes ------- -Some constraints allow to validate the entire object. For example, the +Some constraints allow validating the entire object. For example, the :doc:`Callback ` constraint is a generic constraint that's applied to the class itself. From 368ed327bdfe4a5d4ffff2491b165bfa0cb83b69 Mon Sep 17 00:00:00 2001 From: Vincent Amstoutz Date: Wed, 7 Dec 2022 16:53:24 +0100 Subject: [PATCH 078/774] From SwiftMailer to Mailer --- service_container/tags.rst | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index 94d7d2036b3..ab1064e4ece 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -161,9 +161,9 @@ all services that were tagged with some specific tag. This is useful in compiler passes where you can find these services and use or modify them in some specific way. -For example, if you are using Swift Mailer you might imagine that you want +For example, if you are using the Symfony component Mailer you might imagine that you want to implement a "transport chain", which is a collection of classes implementing -``\Swift_Transport``. Using the chain, you'll want Swift Mailer to try several +``\MailerTransport``. Using the chain, you'll want Mailer to try several ways of transporting the message until one succeeds. To begin with, define the ``TransportChain`` class:: @@ -180,7 +180,7 @@ To begin with, define the ``TransportChain`` class:: $this->transports = []; } - public function addTransport(\Swift_Transport $transport): void + public function addTransport(\MailerTransport $transport): void { $this->transports[] = $transport; } @@ -227,7 +227,7 @@ Then, define the chain as a service: Define Services with a Custom Tag ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Now you might want several of the ``\Swift_Transport`` classes to be instantiated +Now you might want several of the ``\MailerTransport`` classes to be instantiated and added to the chain automatically using the ``addTransport()`` method. For example, you may add the following transports as services: @@ -237,11 +237,11 @@ For example, you may add the following transports as services: # config/services.yaml services: - Swift_SmtpTransport: + MailerSmtpTransport: arguments: ['%mailer_host%'] tags: ['app.mail_transport'] - Swift_SendmailTransport: + MailerSendmailTransport: tags: ['app.mail_transport'] .. code-block:: xml @@ -254,13 +254,13 @@ For example, you may add the following transports as services: https://symfony.com/schema/dic/services/services-1.0.xsd"> - + %mailer_host% - + @@ -274,13 +274,13 @@ For example, you may add the following transports as services: return function(ContainerConfigurator $configurator) { $services = $configurator->services(); - $services->set(\Swift_SmtpTransport::class) + $services->set(\MailerSmtpTransport::class) // the param() method was introduced in Symfony 5.2. ->args([param('mailer_host')]) ->tag('app.mail_transport') ; - $services->set(\Swift_SendmailTransport::class) + $services->set(\MailerSendmailTransport::class) ->tag('app.mail_transport') ; }; @@ -375,12 +375,12 @@ To begin with, change the ``TransportChain`` class:: $this->transports = []; } - public function addTransport(\Swift_Transport $transport, $alias): void + public function addTransport(\MailerTransport $transport, $alias): void { $this->transports[$alias] = $transport; } - public function getTransport($alias): ?\Swift_Transport + public function getTransport($alias): ?\MailerTransport { if (array_key_exists($alias, $this->transports)) { return $this->transports[$alias]; @@ -390,7 +390,7 @@ To begin with, change the ``TransportChain`` class:: } } -As you can see, when ``addTransport()`` is called, it takes not only a ``Swift_Transport`` +As you can see, when ``addTransport()`` is called, it takes not only a ``MailerTransport`` object, but also a string alias for that transport. So, how can you allow each tagged transport service to also supply an alias? @@ -402,12 +402,12 @@ To answer this, change the service declaration: # config/services.yaml services: - Swift_SmtpTransport: + MailerSmtpTransport: arguments: ['%mailer_host%'] tags: - { name: 'app.mail_transport', alias: 'smtp' } - Swift_SendmailTransport: + MailerSendmailTransport: tags: - { name: 'app.mail_transport', alias: 'sendmail' } @@ -421,13 +421,13 @@ To answer this, change the service declaration: https://symfony.com/schema/dic/services/services-1.0.xsd"> - + %mailer_host% - + @@ -441,13 +441,13 @@ To answer this, change the service declaration: return function(ContainerConfigurator $configurator) { $services = $configurator->services(); - $services->set(\Swift_SmtpTransport::class) + $services->set(\MailerSmtpTransport::class) // the param() method was introduced in Symfony 5.2. ->args([param('mailer_host')]) ->tag('app.mail_transport', ['alias' => 'smtp']) ; - $services->set(\Swift_SendmailTransport::class) + $services->set(\MailerSendmailTransport::class) ->tag('app.mail_transport', ['alias' => 'sendmail']) ; }; @@ -463,13 +463,13 @@ To answer this, change the service declaration: # config/services.yaml services: # Compact syntax - Swift_SendmailTransport: - class: \Swift_SendmailTransport + MailerSendmailTransport: + class: \MailerSendmailTransport tags: ['app.mail_transport'] # Verbose syntax - Swift_SendmailTransport: - class: \Swift_SendmailTransport + MailerSendmailTransport: + class: \MailerSendmailTransport tags: - { name: 'app.mail_transport' } From e3a159f4da10fe6135ee22efb0d7257ef1cc8730 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 8 Dec 2022 12:13:58 +0100 Subject: [PATCH 079/774] Minor tweak --- service_container/tags.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index ab1064e4ece..9c89ef8c85a 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -161,7 +161,7 @@ all services that were tagged with some specific tag. This is useful in compiler passes where you can find these services and use or modify them in some specific way. -For example, if you are using the Symfony component Mailer you might imagine that you want +For example, if you are using the Symfony Mailer component you might want to implement a "transport chain", which is a collection of classes implementing ``\MailerTransport``. Using the chain, you'll want Mailer to try several ways of transporting the message until one succeeds. From 5cf6f9e38fa847fa109664272d35e80e169a8142 Mon Sep 17 00:00:00 2001 From: MrYamous Date: Tue, 6 Dec 2022 23:48:58 +0100 Subject: [PATCH 080/774] add precision for stopwatch getEvent function --- performance.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/performance.rst b/performance.rst index 1db6359cda0..17c77d7c038 100644 --- a/performance.rst +++ b/performance.rst @@ -278,7 +278,7 @@ information about the current event, even while it's still running. This object can be converted to a string for a quick summary:: // ... - dump((string) $this->stopwatch->getEvent()); // dumps e.g. '4.50 MiB - 26 ms' + dump((string) $this->stopwatch->getEvent('export-data')); // dumps e.g. '4.50 MiB - 26 ms' You can also profile your template code with the :ref:`stopwatch Twig tag `: From 16fe8bd0cef7bfdcb535e7c0acc517f97e1bea33 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Tue, 6 Dec 2022 16:54:26 +0100 Subject: [PATCH 081/774] Adding link to Kernel configuration --- configuration.rst | 2 ++ routing.rst | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/configuration.rst b/configuration.rst index 6d2638008fa..d1ae76084a1 100644 --- a/configuration.rst +++ b/configuration.rst @@ -52,6 +52,8 @@ to change these files after package installation :doc:`Symfony Configuration Reference ` or run the ``config:dump-reference`` command. +.. _configuration-formats: + Configuration Formats ~~~~~~~~~~~~~~~~~~~~~ diff --git a/routing.rst b/routing.rst index 1d72af7bbe5..25ee1c2abe6 100644 --- a/routing.rst +++ b/routing.rst @@ -183,11 +183,11 @@ the ``BlogController``: ; }; -.. versionadded:: 5.1 +.. note:: - Starting from Symfony 5.1, by default Symfony only loads the routes defined - in YAML format. If you define routes in XML and/or PHP formats, update the - ``src/Kernel.php`` file to add support for the ``.xml`` and ``.php`` file extensions. + By default Symfony only loads the routes defined in YAML format. If you + define routes in XML and/or PHP formats, you need to + :ref:`update the ``src/Kernel.php`` file `. .. _routing-matching-http-methods: From 483ea4239f6e0cfeb807390a1cfe141c211355af Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Tue, 6 Dec 2022 17:52:01 +0100 Subject: [PATCH 082/774] Adding default PHP format Info is taken from https://github.com/symfony/symfony-docs/pull/17515#issuecomment-1339189865 --- routing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routing.rst b/routing.rst index 7dfdd791eb5..987018a76e7 100644 --- a/routing.rst +++ b/routing.rst @@ -151,8 +151,8 @@ the ``BlogController``: .. note:: - By default Symfony only loads the routes defined in YAML format. If you - define routes in XML and/or PHP formats, you need to + By default Symfony only loads the routes defined in YAML and PHP format. + If you define routes in XML, you need to :ref:`update the ``src/Kernel.php`` file `. .. _routing-matching-http-methods: From 4b3f0475a37efb28403e2165f052a465d49c505c Mon Sep 17 00:00:00 2001 From: Robin Willig Date: Tue, 6 Dec 2022 20:48:13 +0100 Subject: [PATCH 083/774] Some text improvements --- best_practices.rst | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/best_practices.rst b/best_practices.rst index 82ebdc94550..cb8825dfc89 100644 --- a/best_practices.rst +++ b/best_practices.rst @@ -81,8 +81,8 @@ Configuration Use Environment Variables for Infrastructure Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The values of these options change from one machine to another (e.g. from your -development machine to the production server) but they don't modify the +The values of these options change from one machine to another (e.g., from your +development machine to the production server), but they don't modify the application behavior. :ref:`Use env vars in your project ` to define these options @@ -93,7 +93,7 @@ and create multiple ``.env`` files to :ref:`configure env vars per environment < Use Secrets for Sensitive Information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -When your application has sensitive configuration - like an API key - you should +When your application has sensitive configuration, like an API key, you should store those securely via :doc:`Symfony’s secrets management system `. Use Parameters for Application Configuration @@ -119,7 +119,7 @@ Then, use just one or two words to describe the purpose of the parameter: # config/services.yaml parameters: - # don't do this: 'dir' is too generic and it doesn't convey any meaning + # don't do this: 'dir' is too generic, and it doesn't convey any meaning app.dir: '...' # do this: short but easy to understand names app.contents_dir: '...' @@ -164,7 +164,7 @@ InvoiceBundle, etc. However, a bundle is meant to be something that can be reused as a stand-alone piece of software. If you need to reuse some feature in your projects, create a bundle for it (in a -private repository, to not make it publicly available). For the rest of your +private repository, do not make it publicly available). For the rest of your application code, use PHP namespaces to organize code instead of bundles. Use Autowiring to Automate the Configuration of Application Services @@ -186,14 +186,14 @@ Services Should be Private Whenever Possible those services via ``$container->get()``. Instead, you will need to use proper dependency injection. -Use the YAML Format to Configure your Own Services +Use the YAML Format to Configure your own Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you use the :ref:`default services.yaml configuration `, most services will be configured automatically. However, in some edge cases you'll need to configure services (or parts of them) manually. -YAML is the format recommended to configure services because it's friendly to +YAML is the format recommended configuring services because it's friendly to newcomers and concise, but Symfony also supports XML and PHP configuration. Use Attributes to Define the Doctrine Entity Mapping @@ -228,13 +228,13 @@ important parts of your application. .. _best-practice-controller-annotations: -Use Attributes or Annotations to Configure Routing, Caching and Security +Use Attributes or Annotations to Configure Routing, Caching, and Security ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Using attributes or annotations for routing, caching and security simplifies +Using attributes or annotations for routing, caching, and security simplifies configuration. You don't need to browse several files created with different -formats (YAML, XML, PHP): all the configuration is just where you need it and -it only uses one format. +formats (YAML, XML, PHP): all the configuration is just where you require it, +and it only uses one format. Don't Use Annotations to Configure the Controller Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -271,7 +271,7 @@ Templates Use Snake Case for Template Names and Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use lowercase snake_case for template names, directories and variables (e.g. +Use lowercase snake_case for template names, directories, and variables (e.g., ``user_profile`` instead of ``userProfile`` and ``product/edit_form.html.twig`` instead of ``Product/EditForm.html.twig``). @@ -280,7 +280,7 @@ Prefix Template Fragments with an Underscore Template fragments, also called *"partial templates"*, allow to :ref:`reuse template contents `. Prefix their names -with an underscore to better differentiate them from complete templates (e.g. +with an underscore to better differentiate them from complete templates (e.g., ``_user_metadata.html.twig`` or ``_caution_message.html.twig``). Forms @@ -289,7 +289,7 @@ Forms Define your Forms as PHP Classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Creating :ref:`forms in classes ` allows to reuse +Creating :ref:`forms in classes ` allows reusing them in different parts of the application. Besides, not creating forms in controllers simplify the code and maintenance of the controllers. @@ -301,7 +301,7 @@ button of a form used to both create and edit items should change from "Add new" to "Save changes" depending on where it's used. Instead of adding buttons in form classes or the controllers, it's recommended -to add buttons in the templates. This also improves the separation of concerns, +to add buttons in the templates. This also improves the separation of concerns because the button styling (CSS class and other attributes) is defined in the template instead of in a PHP class. @@ -323,7 +323,7 @@ Use a Single Action to Render and Process the Form :ref:`Rendering forms ` and :ref:`processing forms ` are two of the main tasks when handling forms. Both are too similar (most of the -times, almost identical), so it's much simpler to let a single controller action +time, almost identical), so it's much simpler to let a single controller action handle both. .. _best-practice-internationalization: @@ -347,8 +347,8 @@ Use Keys for Translations Instead of Content Strings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Using keys simplifies the management of the translation files because you can -change the original contents in templates, controllers and services without -having to update all of the translation files. +change the original contents in templates, controllers, and services without +having to update all the translation files. Keys should always describe their *purpose* and *not* their location. For example, if a form has a field with the label "Username", then a nice key @@ -361,7 +361,7 @@ Define a Single Firewall ~~~~~~~~~~~~~~~~~~~~~~~~ Unless you have two legitimately different authentication systems and users -(e.g. form login for the main site and a token system for your API only), it's +(e.g., form login for the main site and a token system for your API only), it's recommended to have only one firewall to keep things simple. Additionally, you should use the ``anonymous`` key under your firewall. If you @@ -389,13 +389,13 @@ Web Assets Use Webpack Encore to Process Web Assets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Web assets are things like CSS, JavaScript and image files that make the -frontend of your site looks and works great. `Webpack`_ is the leading JavaScript +Web assets are things like CSS, JavaScript, and image files that make the +frontend of your site look and work great. `Webpack`_ is the leading JavaScript module bundler that compiles, transforms and packages assets for usage in a browser. :doc:`Webpack Encore ` is a JavaScript library that gets rid of most of Webpack complexity without hiding any of its features or distorting its usage -and philosophy. It was originally created for Symfony applications, but it works +and philosophy. It was created for Symfony applications, but it works for any application using any technology. Tests @@ -453,7 +453,7 @@ public URL changes, users won't be able to browse it unless you set up a redirection to the new URL. That's why it's recommended to use raw URLs in tests instead of generating them -from routes. Whenever a route changes, tests will fail and you'll know that +from routes. Whenever a route changes, tests will fail, and you'll know that you must set up a redirection. .. _`Symfony Demo`: https://github.com/symfony/demo From 9e02e5b87612630f0062d0cf7a124c5769896405 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 8 Dec 2022 14:41:37 +0100 Subject: [PATCH 084/774] Tweaks --- LICENSE.md | 6 +++--- best_practices.rst | 8 ++++---- contributing/code/security.rst | 2 +- translation.rst | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 01524e6ec84..547ac103984 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -195,7 +195,7 @@ b. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this -License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons +License (e.g. Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), @@ -221,7 +221,7 @@ Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or -Licensor designate another party or parties (e.g., a sponsor institute, +Licensor designate another party or parties (e.g. a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to @@ -229,7 +229,7 @@ the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in -the Adaptation (e.g., "French translation of the Work by Original Author," or +the Adaptation (e.g. "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such diff --git a/best_practices.rst b/best_practices.rst index cb8825dfc89..32af3400c0a 100644 --- a/best_practices.rst +++ b/best_practices.rst @@ -81,7 +81,7 @@ Configuration Use Environment Variables for Infrastructure Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The values of these options change from one machine to another (e.g., from your +The values of these options change from one machine to another (e.g. from your development machine to the production server), but they don't modify the application behavior. @@ -271,7 +271,7 @@ Templates Use Snake Case for Template Names and Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Use lowercase snake_case for template names, directories, and variables (e.g., +Use lowercase snake_case for template names, directories, and variables (e.g. ``user_profile`` instead of ``userProfile`` and ``product/edit_form.html.twig`` instead of ``Product/EditForm.html.twig``). @@ -280,7 +280,7 @@ Prefix Template Fragments with an Underscore Template fragments, also called *"partial templates"*, allow to :ref:`reuse template contents `. Prefix their names -with an underscore to better differentiate them from complete templates (e.g., +with an underscore to better differentiate them from complete templates (e.g. ``_user_metadata.html.twig`` or ``_caution_message.html.twig``). Forms @@ -361,7 +361,7 @@ Define a Single Firewall ~~~~~~~~~~~~~~~~~~~~~~~~ Unless you have two legitimately different authentication systems and users -(e.g., form login for the main site and a token system for your API only), it's +(e.g. form login for the main site and a token system for your API only), it's recommended to have only one firewall to keep things simple. Additionally, you should use the ``anonymous`` key under your firewall. If you diff --git a/contributing/code/security.rst b/contributing/code/security.rst index 7aab51ff919..558f564dfd8 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -152,7 +152,7 @@ score for Impact is capped at 6. Each area is scored between 0 and 4.* on an end-users system, or the server that it runs on? (0-4) * Availability: Is the availability of a service or application affected? Is it reduced availability or total loss of availability of a service / - application? Availability includes networked services (e.g., databases) or + application? Availability includes networked services (e.g. databases) or resources such as consumption of network bandwidth, processor cycles, or disk space. (0-4) diff --git a/translation.rst b/translation.rst index dc5288a09f0..d58b1c6e173 100644 --- a/translation.rst +++ b/translation.rst @@ -724,10 +724,10 @@ configure the ``providers`` option: .. tip:: - If you use Lokalise as provider and a locale format following the `ISO 639-1`_ (e.g., "en" or "fr"), + If you use Lokalise as provider and a locale format following the `ISO 639-1`_ (e.g. "en" or "fr"), you have to set the `Custom Language Name setting`_ in Lokalise for each of your locales, in order to override the default value (which follow the `ISO 639-1`_ succeeded by a sub-code - in capital letters that specifies the national variety (e.g., "GB" or "US" according to `ISO 3166-1 alpha-2`_)). + in capital letters that specifies the national variety (e.g. "GB" or "US" according to `ISO 3166-1 alpha-2`_)). Pushing and Pulling Translations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 7312eba53e0bb28574c16ce40bf52222e60b705f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 8 Dec 2022 16:25:13 +0100 Subject: [PATCH 085/774] Minor tweak --- security/expressions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/expressions.rst b/security/expressions.rst index 338c5c94ab9..e24506b1d0e 100644 --- a/security/expressions.rst +++ b/security/expressions.rst @@ -44,8 +44,8 @@ syntax, see :doc:`/components/expression_language/syntax`. Inside the expression, you have access to a number of variables: ``user`` - An instance of :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface` that represents the current user - or ``null`` if you're not authenticated. + An instance of :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface` + that represents the current user or ``null`` if you're not authenticated. ``role_names`` An array with the string representation of the roles the user has. This array includes any roles granted indirectly via the :ref:`role hierarchy ` but it From c654ef82110acb6a7c17b3476028b1d36301390c Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sun, 27 Nov 2022 18:40:39 +0100 Subject: [PATCH 086/774] enable_annotations read also attributes --- reference/configuration/framework.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 391db4ba386..cb963187290 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -2578,7 +2578,7 @@ enable_annotations **type**: ``boolean`` **default**: ``false`` -If this option is enabled, validation constraints can be defined using annotations. +If this option is enabled, validation constraints can be defined using annotations or attributes. translation_domain .................. @@ -2787,7 +2787,7 @@ enable_annotations **type**: ``boolean`` **default**: ``false`` -If this option is enabled, serialization groups can be defined using annotations. +If this option is enabled, serialization groups can be defined using annotations or attributes. .. seealso:: From 4addacde34edd808b60ed540527342062cc0d470 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Fri, 9 Dec 2022 02:31:28 +0100 Subject: [PATCH 087/774] [Routing] Fix link text --- routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.rst b/routing.rst index ec072be817e..9c07a7c66ec 100644 --- a/routing.rst +++ b/routing.rst @@ -187,7 +187,7 @@ the ``BlogController``: By default Symfony only loads the routes defined in YAML format. If you define routes in XML and/or PHP formats, you need to - :ref:`update the ``src/Kernel.php`` file `. + :ref:`update the src/Kernel.php file `. .. _routing-matching-http-methods: From b8a7ff82430587f10c9eba7fe07a0ef6dbcfd56b Mon Sep 17 00:00:00 2001 From: Lauri Date: Fri, 9 Dec 2022 16:04:31 +0200 Subject: [PATCH 088/774] Fix "Login Programmatically" code example --- security.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/security.rst b/security.rst index e3a6ccf9038..9e615323bbe 100644 --- a/security.rst +++ b/security.rst @@ -1648,16 +1648,16 @@ You can log in a user programmatically using the `login()` method of the $user = ...; // log the user in on the current firewall - $this->security->login($user); + $security->login($user); // if the firewall has more than one authenticator, you must pass it explicitly // by using the name of built-in authenticators... - $this->security->login($user, 'form_login'); + $security->login($user, 'form_login'); // ...or the service id of custom authenticators - $this->security->login($user, ExampleAuthenticator::class); + $security->login($user, ExampleAuthenticator::class); // you can also log in on a different firewall - $this->security->login($user, 'form_login', 'other_firewall'); + $security->login($user, 'form_login', 'other_firewall'); // ... redirect the user to its account page for instance } From 4cfa2ce3fa93f149499a1aa309790e11b93aa91c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 9 Dec 2022 16:06:43 +0100 Subject: [PATCH 089/774] Add SensitiveParameter attribute in the security hardening list --- contributing/code/security.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contributing/code/security.rst b/contributing/code/security.rst index 7aab51ff919..1d2468af388 100644 --- a/contributing/code/security.rst +++ b/contributing/code/security.rst @@ -22,8 +22,8 @@ email for confirmation): is set to ``true`` or ``APP_ENV`` set to anything but ``prod``); * Any fix that can be classified as **security hardening** like route - enumeration, login throttling bypasses, denial of service attacks, or timing - attacks. + enumeration, login throttling bypasses, denial of service attacks, timing + attacks, or lack of ``SensitiveParameter`` attributes. In any case, the core team has the final decision on which issues are considered security vulnerabilities. From 1e0800b29487c1db59af74e1e9c87325e8d70921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Sch=C3=A4dlich?= <1880467+jschaedl@users.noreply.github.com> Date: Thu, 8 Dec 2022 22:32:09 +0100 Subject: [PATCH 090/774] Update link to HAL specification --- serializer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serializer.rst b/serializer.rst index 1a478df6d0f..ee31efa0b12 100644 --- a/serializer.rst +++ b/serializer.rst @@ -424,4 +424,4 @@ take a look at how this bundle works. .. _`OpenAPI`: https://www.openapis.org .. _`GraphQL`: https://graphql.org .. _`JSON:API`: https://jsonapi.org -.. _`HAL`: http://stateless.co/hal_specification.html +.. _`HAL`: https://stateless.group/hal_specification.html From c9aa4e5f34c244433b609e834c9fe33a9aca9fcf Mon Sep 17 00:00:00 2001 From: W0rma Date: Sat, 10 Dec 2022 21:00:19 +0100 Subject: [PATCH 091/774] Fix typo in FQCN of deprecated security class --- security.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security.rst b/security.rst index 9e615323bbe..d8fb76b7db2 100644 --- a/security.rst +++ b/security.rst @@ -1624,7 +1624,7 @@ Login Programmatically The :class:`Symfony\Bundle\SecurityBundle\Security ` class was introduced in Symfony 6.2. Prior to 6.2, it was called - ``Symfony\Bundle\SecurityBundle\Security``. + ``Symfony\Component\Security\Core\Security``. .. versionadded:: 6.2 @@ -1794,7 +1794,7 @@ Logout programmatically The :class:`Symfony\Bundle\SecurityBundle\Security ` class was introduced in Symfony 6.2. Prior to 6.2, it was called - ``Symfony\Bundle\SecurityBundle\Security``. + ``Symfony\Component\Security\Core\Security``. .. versionadded:: 6.2 From 086a347684788b22321f4d2dd7a34745b3a89e70 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sun, 11 Dec 2022 11:02:07 +0100 Subject: [PATCH 092/774] Moving the new `env()` syntax upwards, to be shown as the preferred way --- configuration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration.rst b/configuration.rst index d1ae76084a1..4265c1419b2 100644 --- a/configuration.rst +++ b/configuration.rst @@ -646,9 +646,9 @@ This example shows how you could configure the database connection using an env $container->extension('doctrine', [ 'dbal' => [ // by convention the env var names are always uppercase - 'url' => '%env(resolve:DATABASE_URL)%', - // or 'url' => env('DATABASE_URL')->resolve(), + // or + 'url' => '%env(resolve:DATABASE_URL)%', ], ]); }; From 303c5cf6667fffa027ac8f7c98c4d822b2432ae1 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Mon, 12 Dec 2022 09:44:22 +0100 Subject: [PATCH 093/774] validator - add missing constraint when --- reference/constraints/map.rst.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 9f8eb4b8c3f..f13a92f58ac 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -94,6 +94,7 @@ Other Constraints * :doc:`Compound ` * :doc:`Callback ` * :doc:`Expression ` +* :doc:`When ` * :doc:`All ` * :doc:`Valid ` * :doc:`Traverse ` From 99eecb6442150ca249a9495e371e28cff051ddf8 Mon Sep 17 00:00:00 2001 From: Jeff Zohrab Date: Tue, 6 Dec 2022 11:29:02 -0600 Subject: [PATCH 094/774] Simplify migration LegacyBridge example --- migration.rst | 69 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/migration.rst b/migration.rst index b22ab90d893..c101d8e4146 100644 --- a/migration.rst +++ b/migration.rst @@ -275,16 +275,13 @@ could look something like this:: $request = Request::createFromGlobals(); $response = $kernel->handle($request); - /* - * LegacyBridge will take care of figuring out whether to boot up the - * existing application or to send the Symfony response back to the client. - */ - $scriptFile = LegacyBridge::prepareLegacyScript($request, $response, __DIR__); - if ($scriptFile !== null) { - require $scriptFile; - } else { + if (false === $response->isNotFound()) { + // Symfony successfully handled the route. $response->send(); + } else { + LegacyBridge::handleRequest($request, $response, __DIR__); } + $kernel->terminate($request, $response); There are 2 major deviations from the original file: @@ -297,10 +294,9 @@ Line 18 it over. For instance, by replacing outdated or redundant libraries with Symfony components. -Line 41 - 50 - Instead of sending the Symfony response directly, a ``LegacyBridge`` is - called to decide whether the legacy application should be booted and used to - create the response instead. +Line 41 - 46 + If Symfony handled the response, it is sent; otherwise, the ``LegacyBridge`` + handles the request. This legacy bridge is responsible for figuring out which file should be loaded in order to process the old application logic. This can either be a front @@ -316,19 +312,50 @@ somewhat like this:: class LegacyBridge { - public static function prepareLegacyScript(Request $request, Response $response, string $publicDirectory): ?string + + /** + * Map the incoming request to the right file. This is the + * key function of the LegacyBridge. + * + * Sample code only. Your implementation will vary, depending on the + * architecture of the legacy code and how it's executed. + * + * If your mapping is complicated, you may want to write unit tests + * to verify your logic, hence this is public static. + */ + public static function getLegacyScript(Request $request): string { - // If Symfony successfully handled the route, you do not have to do anything. - if (false === $response->isNotFound()) { - return null; + $requestPathInfo = $request->getPathInfo(); + $legacyRoot = __DIR__ . '/../'; + + // Map a route to a legacy script: + if ($requestPathInfo == '/customer/') { + return "{$legacyRoot}src/customers/list.php"; } - // Figure out how to map to the needed script file - // from the existing application and possibly (re-)set - // some env vars. - $legacyScriptFilename = ...; + // Map a direct file call, e.g. an ajax call: + if ($requestPathInfo == 'inc/ajax_cust_details.php') { + return "{$legacyRoot}inc/ajax_cust_details.php"; + } + + // ... etc. + + throw new \Exception("Unhandled legacy mapping for $requestPathInfo"); + } + + + public static function handleRequest(Request $request, Response $response, string $publicDirectory): ?string + { + $legacyScriptFilename = LegacyBridge::getLegacyScript($request); + + // Possibly (re-)set some env vars (e.g. to handle forms + // posting to PHP_SELF): + $p = $request->getPathInfo(); + $_SERVER['PHP_SELF'] = $p; + $_SERVER['SCRIPT_NAME'] = $p; + $_SERVER['SCRIPT_FILENAME'] = $legacyScriptFilename; - return $legacyScriptFilename; + require $legacyScriptFilename; } } From 5f625b4695094c5e3a9f3e925158ffe1a9a91cdd Mon Sep 17 00:00:00 2001 From: Robin Willig Date: Thu, 8 Dec 2022 22:39:13 +0100 Subject: [PATCH 095/774] [Workflow] Some minor text optimizations --- workflow.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/workflow.rst b/workflow.rst index ef6193d02c0..389231ba91a 100644 --- a/workflow.rst +++ b/workflow.rst @@ -1,7 +1,7 @@ Workflow ======== -Using the Workflow component inside a Symfony application requires to know first +Using the Workflow component inside a Symfony application requires knowing first some basic theory and concepts about workflows and state machines. :doc:`Read this article ` for a quick overview. @@ -29,8 +29,8 @@ Creating a Workflow ------------------- A workflow is a process or a lifecycle that your objects go through. Each -step or stage in the process is called a *place*. You do also define *transitions* -to that describes the action to get from one place to another. +step or stage in the process is called a *place*. You also define *transitions*, +which describe the action needed to get from one place to another. .. image:: /_images/components/workflow/states_transitions.png @@ -39,8 +39,8 @@ a ``Definition`` and a way to write the states to the objects (i.e. an instance of a :class:`Symfony\\Component\\Workflow\\MarkingStore\\MarkingStoreInterface`.) Consider the following example for a blog post. A post can have these places: -``draft``, ``reviewed``, ``rejected``, ``published``. You can define the workflow -like this: +``draft``, ``reviewed``, ``rejected``, ``published``. You could define the workflow as +follows: .. configuration-block:: From 3a35ff29c309a436fe3e06abff11bce7c231234e Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Mon, 12 Dec 2022 09:46:44 +0100 Subject: [PATCH 096/774] validator - replace expressionLanguageSyntax with new expressionSyntax --- reference/constraints.rst | 1 - reference/constraints/map.rst.inc | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/reference/constraints.rst b/reference/constraints.rst index f3851bdb97a..4351081e630 100644 --- a/reference/constraints.rst +++ b/reference/constraints.rst @@ -14,7 +14,6 @@ Validation Constraints Reference constraints/Type constraints/Email - constraints/ExpressionLanguageSyntax (deprecated) constraints/ExpressionSyntax constraints/Length constraints/Url diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 9f8eb4b8c3f..f3d75554cb5 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -16,7 +16,7 @@ String Constraints ~~~~~~~~~~~~~~~~~~ * :doc:`Email ` -* :doc:`ExpressionLanguageSyntax ` +* :doc:`ExpressionSyntax ` * :doc:`Length ` * :doc:`Url ` * :doc:`Regex ` From 5ce20ac4ab33a5ae4c6c747ddb6e1a3d9f8a1ab7 Mon Sep 17 00:00:00 2001 From: alex00ds <31631959+alex00ds@users.noreply.github.com> Date: Tue, 13 Dec 2022 06:22:16 +0200 Subject: [PATCH 097/774] Update doctrine.rst An example of MapEntity Options "exclude" have a wrong paragraph formatting (this is : when :: is expected). An example of MapEntity Options "mapping" contains a property is not synced with a placeholder (there is no 'date' in the route). --- doctrine.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doctrine.rst b/doctrine.rst index cd5806c4bbd..b75fdea7c87 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -768,7 +768,7 @@ control behavior: #[Route('/product/{category}/{slug}/comments/{comment_slug}')] public function show( - #[MapEntity(mapping: ['date' => 'date', 'slug' => 'slug'])] + #[MapEntity(mapping: ['category' => 'category', 'slug' => 'slug'])] Product $product #[MapEntity(mapping: ['comment_slug' => 'slug'])] Comment $comment @@ -777,7 +777,7 @@ control behavior: ``exclude`` Configures the properties that should be used in the ``findOneBy()`` - method by *excluding* one or more properties so that not *all* are used: + method by *excluding* one or more properties so that not *all* are used:: #[Route('/product/{slug}/{date}')] public function show( From 700ae4a161669531c9fbe0a02acbc8a2371f3b99 Mon Sep 17 00:00:00 2001 From: Fernando Aguirre Date: Mon, 21 Nov 2022 19:28:10 -0700 Subject: [PATCH 098/774] Update database.rst When Redis has user and password to login, the data must be entered as an array ['user', 'password'], added as comment --- session/database.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/session/database.rst b/session/database.rst index ba4642b7973..da2b6b070e1 100644 --- a/session/database.rst +++ b/session/database.rst @@ -50,6 +50,10 @@ First, define a Symfony service for the connection to the Redis server: # uncomment the following if your Redis server requires a password # - auth: # - '%env(REDIS_PASSWORD)%' + + # uncomment the following if your Redis server requires user and password (When user is not default) + # - auth: + # - ['%env(REDIS_USER)%','%env(REDIS_PASSWORD)%'] .. code-block:: xml From f1c80b83722d5ac6a6c5e5fa81ecf6a3840a5bec Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 13 Dec 2022 16:26:52 +0100 Subject: [PATCH 099/774] Add the examples for XML and PHP config --- session/database.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/session/database.rst b/session/database.rst index da2b6b070e1..de4d5213b81 100644 --- a/session/database.rst +++ b/session/database.rst @@ -51,7 +51,7 @@ First, define a Symfony service for the connection to the Redis server: # - auth: # - '%env(REDIS_PASSWORD)%' - # uncomment the following if your Redis server requires user and password (When user is not default) + # uncomment the following if your Redis server requires a user and a password (when user is not default) # - auth: # - ['%env(REDIS_USER)%','%env(REDIS_PASSWORD)%'] @@ -74,6 +74,12 @@ First, define a Symfony service for the connection to the Redis server: %env(REDIS_PASSWORD)% --> + + @@ -87,6 +93,8 @@ First, define a Symfony service for the connection to the Redis server: ->addMethodCall('connect', ['%env(REDIS_HOST)%', '%env(int:REDIS_PORT)%']) // uncomment the following if your Redis server requires a password: // ->addMethodCall('auth', ['%env(REDIS_PASSWORD)%']) + // uncomment the following if your Redis server requires a user and a password (when user is not default): + // ->addMethodCall('auth', ['%env(REDIS_USER)%', '%env(REDIS_PASSWORD)%']) ; Now pass this ``\Redis`` connection as an argument of the service associated to the From 65ece3e01e66e5231ae933b9d01a5e55b482ed16 Mon Sep 17 00:00:00 2001 From: Robin Brisa Date: Wed, 10 Aug 2022 17:58:02 +0200 Subject: [PATCH 100/774] [Form] Example of customizing EnumType labels Default behavior when creating a EnumType form element is that the choice labels displayed to the user are the enum names. This PR adds an example of how to use a function inside an enum to return labels and how to bind this function to the form element. --- reference/forms/types/enum.rst | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/reference/forms/types/enum.rst b/reference/forms/types/enum.rst index b2e960a21ec..4c9d3eeb8a5 100644 --- a/reference/forms/types/enum.rst +++ b/reference/forms/types/enum.rst @@ -56,6 +56,40 @@ This will display a ```` or ````. +Since the label displayed in the ```` tag with the three possible values defined in the ``TextAlign`` enum. Use the `expanded`_ and `multiple`_ options to display these values as ```` or ````. -Since the label displayed in the ```` is the enum +name. PHP defines some strict rules for these names (e.g. they can't contain +dots or spaces). If you need more flexibility for these labels, use the +``choice_label`` option and define a function that returns the custom label:: ->add('textAlign', EnumType::class, [ 'class' => TextAlign::class, - 'choice_label' => static function (TextAlign $choice): string { - return $choice->label(); + 'choice_label' => match ($choice) { + TextAlign::Left => 'text_align.left.label', + TextAlign::Center => 'text_align.center.label', + TextAlign::Right => 'text_align.right.label', }, - ]) + ]); Field Options ------------- From 02fc6fdde26598280ca0c5b0fdcef84984b1e8b9 Mon Sep 17 00:00:00 2001 From: jmsche Date: Wed, 14 Dec 2022 15:11:56 +0100 Subject: [PATCH 102/774] Symfony CLI: fix config directory path --- frontend/encore/dev-server.rst | 2 +- setup/symfony_server.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/encore/dev-server.rst b/frontend/encore/dev-server.rst index 6337a881471..52a4fa83b05 100644 --- a/frontend/encore/dev-server.rst +++ b/frontend/encore/dev-server.rst @@ -79,7 +79,7 @@ server SSL certificate: + options.server = { + type: 'https', + options: { - + pfx: path.join(process.env.HOME, '.symfony/certs/default.p12'), + + pfx: path.join(process.env.HOME, '.symfony5/certs/default.p12'), + } + } + }) diff --git a/setup/symfony_server.rst b/setup/symfony_server.rst index b9cdb4c2850..38244ca9c31 100644 --- a/setup/symfony_server.rst +++ b/setup/symfony_server.rst @@ -254,7 +254,7 @@ domains work: .. tip:: - If you prefer to use a different TLD, edit the ``~/.symfony/proxy.json`` + If you prefer to use a different TLD, edit the ``~/.symfony5/proxy.json`` file (where ``~`` means the path to your user directory) and change the value of the ``tld`` option from ``wip`` to any other TLD. From 2c49be3705fe3681ae774a54f670ca3c97be6a97 Mon Sep 17 00:00:00 2001 From: jmsche Date: Wed, 14 Dec 2022 15:21:07 +0100 Subject: [PATCH 103/774] Symfony CLI: add wildcard local domain information --- setup/symfony_server.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/setup/symfony_server.rst b/setup/symfony_server.rst index b9cdb4c2850..d9a6de22930 100644 --- a/setup/symfony_server.rst +++ b/setup/symfony_server.rst @@ -232,6 +232,14 @@ new custom domain. Browse the http://127.0.0.1:7080 URL to get the full list of local project directories, their custom domains, and port numbers. +You can also add a wildcard domain: + +.. code-block:: terminal + + $ symfony proxy:domain:attach "*.my-domain" + +So it will match all subdomains like ``https://admin.my-domain.wip``, ``https://other.my-domain.wip``... + When running console commands, add the ``https_proxy`` env var to make custom domains work: From 771dac342f098ed08e9b7abe0b4bdb5b73378c64 Mon Sep 17 00:00:00 2001 From: kez Date: Wed, 14 Dec 2022 18:47:20 +0100 Subject: [PATCH 104/774] Fix/bootstrap turbo docs --- frontend/encore/bootstrap.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/frontend/encore/bootstrap.rst b/frontend/encore/bootstrap.rst index a41475d8eec..561bef79dde 100644 --- a/frontend/encore/bootstrap.rst +++ b/frontend/encore/bootstrap.rst @@ -73,6 +73,25 @@ Now, require bootstrap from any of your JavaScript files: $('[data-toggle="popover"]').popover(); }); +Using Bootstrap with Turbo +--------------------------- + +If you are using bootstrap with Turbo Drive, to allow your JavaScript to load on each page change, +wrap the initialization in a ``turbo:load`` event listener: + +.. code-block:: javascript + + // app.js + + // this waits for Turbo Drive to load + document.addEventListener('turbo:load', function (e) { + // this enables bootstrap tooltips globally + let tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) + let tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new Tooltip(tooltipTriggerEl) + }); + }); + Using other Bootstrap / jQuery Plugins -------------------------------------- From c7ef41e177e090902f2e8b4bc66ba176d4449196 Mon Sep 17 00:00:00 2001 From: Yohann Tilotti Date: Thu, 15 Dec 2022 13:48:15 +0100 Subject: [PATCH 105/774] [Doctrine] Errors on EntityValueResolver exemple --- doctrine.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/doctrine.rst b/doctrine.rst index b75fdea7c87..4f4c7fbc98b 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -733,7 +733,7 @@ This can also be used to help resolve multiple arguments:: #[Route('/product/{id}/comments/{comment_id}')] public function show( - Product $product + Product $product, #[MapEntity(expr: 'repository.find(comment_id)')] Comment $comment ): Response { @@ -755,9 +755,8 @@ control behavior: #[Route('/product/{product_id}')] public function show( - Product $product #[MapEntity(id: 'product_id')] - Comment $comment + Product $product ): Response { } @@ -769,7 +768,7 @@ control behavior: #[Route('/product/{category}/{slug}/comments/{comment_slug}')] public function show( #[MapEntity(mapping: ['category' => 'category', 'slug' => 'slug'])] - Product $product + Product $product, #[MapEntity(mapping: ['comment_slug' => 'slug'])] Comment $comment ): Response { @@ -782,7 +781,7 @@ control behavior: #[Route('/product/{slug}/{date}')] public function show( #[MapEntity(exclude: ['date'])] - Product $product + Product $product, \DateTime $date ): Response { } From 15537d46957598a269622bf264cbd4005ea48216 Mon Sep 17 00:00:00 2001 From: Yohann Tilotti Date: Fri, 16 Dec 2022 15:48:54 +0100 Subject: [PATCH 106/774] [Controller] Errors on namespace value_resolver --- controller/value_resolver.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/controller/value_resolver.rst b/controller/value_resolver.rst index a8c01407368..33ad4671ca7 100644 --- a/controller/value_resolver.rst +++ b/controller/value_resolver.rst @@ -292,7 +292,7 @@ and adding a priority: autowire: true # ... - App\ArgumentResolver\BookingIdValueResolver: + App\ValueResolver\BookingIdValueResolver: tags: - { name: controller.argument_value_resolver, priority: 150 } @@ -310,7 +310,7 @@ and adding a priority: - + @@ -322,7 +322,7 @@ and adding a priority: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - use App\ArgumentResolver\BookingIdValueResolver; + use App\ValueResolver\BookingIdValueResolver; return static function (ContainerConfigurator $container) { $services = $configurator->services(); From 576df8523d87f8e5b3b6441e0957f1921a3c67f0 Mon Sep 17 00:00:00 2001 From: micter59 Date: Thu, 15 Dec 2022 12:38:21 +0100 Subject: [PATCH 107/774] Update routing.rst --- routing.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/routing.rst b/routing.rst index 9c07a7c66ec..91717399fd7 100644 --- a/routing.rst +++ b/routing.rst @@ -304,6 +304,13 @@ Use the ``methods`` option to restrict the verbs each route should respond to: ``_method`` with the method to use (e.g. ````). If you create your forms with :doc:`Symfony Forms ` this is done automatically for you. + Also note that ```framework.http_method_override``` needs to be set for this to work : + + .. code-block:: yaml + + # config/packages/framework.yaml + framework: + http_method_override: true .. _routing-matching-expressions: From f9aaa152ae1d46bed50e73d829826aba827f0d53 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 16 Dec 2022 16:27:22 +0100 Subject: [PATCH 108/774] Tweak --- routing.rst | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/routing.rst b/routing.rst index 91717399fd7..e65e7dde27f 100644 --- a/routing.rst +++ b/routing.rst @@ -303,14 +303,8 @@ Use the ``methods`` option to restrict the verbs each route should respond to: route with a different method from an HTML form, add a hidden field called ``_method`` with the method to use (e.g. ````). If you create your forms with :doc:`Symfony Forms ` this is done - automatically for you. - Also note that ```framework.http_method_override``` needs to be set for this to work : - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - http_method_override: true + automatically for you when the :ref:`framework.http_method_override ` + option is ``true``. .. _routing-matching-expressions: From 4717f04a3ff618ed480ad199fb8af38531096454 Mon Sep 17 00:00:00 2001 From: Warnar Boekkooi Date: Thu, 9 Jun 2022 16:19:13 +0200 Subject: [PATCH 109/774] Use SoapServer headers While debugging the istio error `Error dispatching received data: http/1.1 protocol error: HPE_UNEXPECTED_CONTENT_LENGTH` we noticed that the error was [triggered](https://github.com/nodejs/http-parser/blob/e13b274/http_parser.c#L1442) because of a duplicated Content-Length headers being send by the PHP application. After review we noticed that the header was send by both the SoapServer and the Symfony Response for this reason I decided to open this PR. --- controller/soap_web_service.rst | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/controller/soap_web_service.rst b/controller/soap_web_service.rst index 43224116704..a1d1cbeed34 100644 --- a/controller/soap_web_service.rst +++ b/controller/soap_web_service.rst @@ -66,24 +66,30 @@ can be retrieved via ``/soap?wsdl``:: use App\Service\HelloService; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; class HelloServiceController extends AbstractController { #[Route('/soap')] - public function index(HelloService $helloService) + public function index(HelloService $helloService, Request $request) { $soapServer = new \SoapServer('/path/to/hello.wsdl'); $soapServer->setObject($helloService); $response = new Response(); - $response->headers->set('Content-Type', 'text/xml; charset=ISO-8859-1'); ob_start(); - $soapServer->handle(); + $soapServer->handle($request->getContent()); $response->setContent(ob_get_clean()); + foreach (headers_list() as $header) { + $header = explode(':', $header, 2); + $response->headers->set($header[0], $header[1]); + } + header_remove(); + return $response; } } @@ -92,11 +98,13 @@ Take note of the calls to ``ob_start()`` and ``ob_get_clean()``. These methods control `output buffering`_ which allows you to "trap" the echoed output of ``$server->handle()``. This is necessary because Symfony expects your controller to return a ``Response`` object with the output as its "content". -You must also remember to set the ``"Content-Type"`` header to ``"text/xml"``, as -this is what the client will expect. So, you use ``ob_start()`` to start -buffering the STDOUT and use ``ob_get_clean()`` to dump the echoed output -into the content of the Response and clear the output buffer. Finally, you're -ready to return the ``Response``. +So, you use ``ob_start()`` to start buffering the STDOUT and use +``ob_get_clean()`` to dump the echoed output into the content of the Response +and clear the output buffer. Since ``$server->handle()`` can set headers it is +also necessary to "trap" these. For this we use ``headers_list`` which provides +the set headers, these are then parsed and added into the Response after which +``header_remove`` is used to remove the headers and to avoid duplicates. +Finally, you're ready to return the ``Response``. Below is an example of calling the service using a native `SoapClient`_ client. This example assumes that the ``index()`` method in the controller above is accessible via From 836a9739faef1f5d67e9ad23be4f68edb0767347 Mon Sep 17 00:00:00 2001 From: Pooyan Khanjankhani Date: Fri, 16 Dec 2022 22:31:17 +0330 Subject: [PATCH 110/774] chore: Add missing semicolon --- components/dependency_injection.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/dependency_injection.rst b/components/dependency_injection.rst index c1b5d454ba1..c2239a9225b 100644 --- a/components/dependency_injection.rst +++ b/components/dependency_injection.rst @@ -300,6 +300,7 @@ config files: $services = $container->services(); $services->set('mailer', 'Mailer') ->args(['%mailer.transport%']) + ; $services->set('mailer', 'Mailer') // the param() method was introduced in Symfony 5.2. From 0cae5b8fad62db36ac25f9e2f48888b2fcc11496 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 18 Dec 2022 19:03:09 +0100 Subject: [PATCH 111/774] Add a note about using Gmail vs an Email catcher --- mailer.rst | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/mailer.rst b/mailer.rst index a4a6daccc5d..c6696f8cf79 100644 --- a/mailer.rst +++ b/mailer.rst @@ -12,7 +12,6 @@ integration, CSS inlining, file attachments and a lot more. Get them installed w $ composer require symfony/mailer - .. _mailer-transport-setup: Transport Setup @@ -105,14 +104,13 @@ native ``native://default`` Mailer uses the sendmail Using a 3rd Party Transport ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Instead of using your own SMTP server or sendmail binary, you can send emails via a 3rd party -provider. Mailer supports several - install whichever you want: +Instead of using your own SMTP server or sendmail binary, you can send emails +via a 3rd party provider: ================== ============================================== Service Install with ================== ============================================== Amazon SES ``composer require symfony/amazon-mailer`` -Gmail ``composer require symfony/google-mailer`` MailChimp ``composer require symfony/mailchimp-mailer`` Mailgun ``composer require symfony/mailgun-mailer`` Mailjet ``composer require symfony/mailjet-mailer`` @@ -122,6 +120,14 @@ Sendinblue ``composer require symfony/sendinblue-mailer`` OhMySMTP ``composer require symfony/oh-my-smtp-mailer`` ================== ============================================== +.. note:: + + As a convenience, Symfony also provides support for Gmail (``composer + require symfony/google-mailer``), but this should not be used in + production. In development, you should probably use an :ref:`email catcher + ` instead. Note that most supported providers also provide a + free tier. + .. versionadded:: 5.2 The Sendinblue integration was introduced in Symfony 5.2. @@ -1404,6 +1410,17 @@ is sent:: Development & Debugging ----------------------- +.. _mail-catcher: + +Enabling an Email Catcher +~~~~~~~~~~~~~~~~~~~~~~~~~ + +When developing locally, it is recommended to use an email catcher. If you have +enabled Docker support via Symfony recipes, an email catcher is automatically +configured. In addition, if you are using the :doc:`Symfony local web server +`, the mailer DSN is automatically exposed via the +:ref:`symfony binary Docker integration `. + Disabling Delivery ~~~~~~~~~~~~~~~~~~ From 9664bf2254242ed935c6a1a346d11e9e2d51f242 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 19 Dec 2022 11:59:35 +0100 Subject: [PATCH 112/774] Minor tweak --- mailer.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mailer.rst b/mailer.rst index c6696f8cf79..93fc5b4d66f 100644 --- a/mailer.rst +++ b/mailer.rst @@ -105,7 +105,7 @@ Using a 3rd Party Transport ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Instead of using your own SMTP server or sendmail binary, you can send emails -via a 3rd party provider: +via a third-party provider: ================== ============================================== Service Install with @@ -125,7 +125,7 @@ OhMySMTP ``composer require symfony/oh-my-smtp-mailer`` As a convenience, Symfony also provides support for Gmail (``composer require symfony/google-mailer``), but this should not be used in production. In development, you should probably use an :ref:`email catcher - ` instead. Note that most supported providers also provide a + ` instead. Note that most supported providers also offer a free tier. .. versionadded:: 5.2 From 29b6dbd525897b5dd27f680feeb3f39ebbea3720 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sun, 18 Dec 2022 20:53:17 +0100 Subject: [PATCH 113/774] Tell composer action is need to regenerate autoload --- components/runtime.rst | 3 +++ configuration/override_dir_structure.rst | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/components/runtime.rst b/components/runtime.rst index fac78dd82dd..7b187acaeee 100644 --- a/components/runtime.rst +++ b/components/runtime.rst @@ -329,6 +329,9 @@ You can also configure ``extra.runtime`` in ``composer.json``: } } +Then, update your Composer files (running ``composer dump-autoload``, for instance), +so that the ``vendor/autoload_runtime.php`` files gets regenerated with the new option. + The following options are supported by the ``SymfonyRuntime``: ``env`` (default: ``APP_ENV`` environment variable, or ``"dev"``) diff --git a/configuration/override_dir_structure.rst b/configuration/override_dir_structure.rst index 73f65c9171f..808fb6f923f 100644 --- a/configuration/override_dir_structure.rst +++ b/configuration/override_dir_structure.rst @@ -49,7 +49,7 @@ define the ``runtime.dotenv_path`` option in the ``composer.json`` file: } } -Then, update your Composer files (running ``composer update``, for instance), +Then, update your Composer files (running ``composer dump-autoload``, for instance), so that the ``vendor/autoload_runtime.php`` files gets regenerated with the new ``.env`` path. From 229f9bd41aba0b8f99be4419deba68ae066c583e Mon Sep 17 00:00:00 2001 From: Evert Harmeling Date: Wed, 21 Dec 2022 11:27:54 +0100 Subject: [PATCH 114/774] Replace service name with FQCN class reference As there isn't any mention of `app.hello_controller` (no definition) and the previous chapter mentioned using the FQCN is what Symfony recommends (it eases refactoring, especially for `php` configuration) it makes much more sense to make use of the FQCN as the service identifier. And lined up the `yaml`-config (as done in the rest of the documentation; routing chapter) --- controller/service.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/controller/service.rst b/controller/service.rst index 724143e3046..f034f524f12 100644 --- a/controller/service.rst +++ b/controller/service.rst @@ -100,9 +100,9 @@ a service like: ``App\Controller\HelloController::index``: # config/routes.yaml hello: - path: /hello + path: /hello controller: App\Controller\HelloController::index - methods: GET + methods: GET .. code-block:: xml @@ -181,8 +181,8 @@ which is a common practice when following the `ADR pattern`_ # config/routes.yaml hello: - path: /hello/{name} - controller: app.hello_controller + path: /hello/{name} + controller: App\Controller\HelloController .. code-block:: xml @@ -194,16 +194,18 @@ which is a common practice when following the `ADR pattern`_ https://symfony.com/schema/routing/routing-1.0.xsd"> - app.hello_controller + App\Controller\HelloController .. code-block:: php + use App\Controller\HelloController; + // app/config/routing.php $collection->add('hello', new Route('/hello', [ - '_controller' => 'app.hello_controller', + '_controller' => HelloController::class, ])); Alternatives to base Controller Methods From 4e5b1d684436d7bc3cd55d81dd7c186d641070b3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 21 Dec 2022 11:49:50 +0100 Subject: [PATCH 115/774] Fix merge conflicts --- mailer.rst | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/mailer.rst b/mailer.rst index 0cb018a9a0f..45be91a7cc4 100644 --- a/mailer.rst +++ b/mailer.rst @@ -112,16 +112,12 @@ MailPace ``composer require symfony/mailpace-mailer`` Infobip ``composer require symfony/infobip-mailer`` ================== ============================================== -<<<<<<< HEAD .. versionadded:: 6.2 - The ``MailPace`` integration was introduced in Symfony 6.2 (in previous - Symfony versions it was called ``OhMySMTP``). + The Infobip integration was introduced in Symfony 6.2 and the ``MailPace`` + integration was renamed in Symfony 6.2 (in previous Symfony versions it was + called ``OhMySMTP``). -.. versionadded:: 6.2 - - The Infobip integration was introduced in Symfony 6.2. -======= .. note:: As a convenience, Symfony also provides support for Gmail (``composer @@ -129,7 +125,6 @@ Infobip ``composer require symfony/infobip-mailer`` production. In development, you should probably use an :ref:`email catcher ` instead. Note that most supported providers also offer a free tier. ->>>>>>> 6.1 Each library includes a :ref:`Symfony Flex recipe ` that will add a configuration example to your ``.env`` file. For example, suppose you want to From 198418d68e6973a17b96f79002655688bf232606 Mon Sep 17 00:00:00 2001 From: Xavier Laviron Date: Wed, 21 Dec 2022 15:48:00 +0100 Subject: [PATCH 116/774] fix: typo --- reference/configuration/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index ac11a23650b..70784bf4853 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -912,7 +912,7 @@ Learn more about user checkers in :doc:`/security/user_checkers`. providers --------- -This options defines how the application users are loaded (from a database, +This option defines how the application users are loaded (from a database, an LDAP server, a configuration file, etc.) Read :doc:`/security/user_providers` to learn more about each of those providers. From 7726f6bc6f420da7ded2998598cba07a974388fb Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 19 Dec 2022 21:13:03 -0500 Subject: [PATCH 117/774] Clarifying not about Encore outside of Symfony --- frontend/encore/installation.rst | 17 ++++++++++++++++- frontend/encore/simple-example.rst | 7 ++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/frontend/encore/installation.rst b/frontend/encore/installation.rst index bcd59f8395e..118e15e7b0e 100644 --- a/frontend/encore/installation.rst +++ b/frontend/encore/installation.rst @@ -139,6 +139,9 @@ is the main config file for both Webpack and Webpack Encore: module.exports = Encore.getWebpackConfig(); +Creating Other Supporting File +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Next, open the new ``assets/app.js`` file which contains some JavaScript code *and* imports some CSS: @@ -185,7 +188,7 @@ a system that you'll learn about soon: // register any custom, 3rd party controllers here // app.register('some_controller_name', SomeImportedController); -And finally, create an ``assets/controllers.json`` file, which also fits into +Then create an ``assets/controllers.json`` file, which also fits into the Stimulus system: .. code-block:: json @@ -195,6 +198,18 @@ the Stimulus system: "entrypoints": [] } +Finally, though it's optional, add the following ``scripts`` to your ``package.json`` +file so you can run the same commands in the rest of the documentation: + +.. code-block:: json + + "scripts": { + "dev-server": "encore dev-server", + "dev": "encore dev", + "watch": "encore dev --watch", + "build": "encore production --progress" + } + You'll customize and learn more about these files in :doc:`/frontend/encore/simple-example`. When you execute Encore, it will ask you to install a few more dependencies based on which features of Encore you have enabled. diff --git a/frontend/encore/simple-example.rst b/frontend/encore/simple-example.rst index 9a8f89e9b06..c2fc50289a5 100644 --- a/frontend/encore/simple-example.rst +++ b/frontend/encore/simple-example.rst @@ -154,9 +154,10 @@ template: the paths in ``entrypoints.json`` will always be the final, correct pa And if you use :doc:`splitEntryChunks() ` (where Webpack splits the output into even more files), all the necessary ``script`` and ``link`` tags will render automatically. -If you're *not* using Symfony, you can ignore the ``entrypoints.json`` file and -point to the final, built file directly. ``entrypoints.json`` is only required for -some optional features. +If you are not using Symfony you won't have the ``encore_entry_*`` functions available. +Instead, you can point directly to the final built files or write code to parse +``entrypoints.json`` manually. The entrypoints file is needed only if you're using +certain optional features, like ``splitEntryChunks()``. .. versionadded:: 1.9.0 From 98a1dcab20c5324b09fc7f088d63a06195a4133b Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sun, 18 Dec 2022 21:19:36 +0100 Subject: [PATCH 118/774] Remove reference to 2.x, 3.x, 4.x versions --- components/browser_kit.rst | 7 ------- components/config/definition.rst | 2 +- .../event_dispatcher/container_aware_dispatcher.rst | 10 ---------- components/filesystem/lock_handler.rst | 7 ------- contributing/code/bc.rst | 5 ----- profiler.rst | 12 ++++-------- service_container.rst | 7 ++----- 7 files changed, 7 insertions(+), 43 deletions(-) delete mode 100644 components/event_dispatcher/container_aware_dispatcher.rst delete mode 100644 components/filesystem/lock_handler.rst diff --git a/components/browser_kit.rst b/components/browser_kit.rst index a2afe27c0bc..bfaad93d4f9 100644 --- a/components/browser_kit.rst +++ b/components/browser_kit.rst @@ -8,13 +8,6 @@ The BrowserKit Component The BrowserKit component simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically. -.. note:: - - In Symfony versions prior to 4.3, the BrowserKit component could only make - internal requests to your application. Starting from Symfony 4.3, this - component can also :ref:`make HTTP requests to any public site ` - when using it in combination with the :doc:`HttpClient component `. - Installation ------------ diff --git a/components/config/definition.rst b/components/config/definition.rst index 8ad8ae1b0c9..29d0715e722 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -780,7 +780,7 @@ the following ways: - ``ifTrue()`` - ``ifString()`` - ``ifNull()`` -- ``ifEmpty()`` (since Symfony 3.2) +- ``ifEmpty()`` - ``ifArray()`` - ``ifInArray()`` - ``ifNotInArray()`` diff --git a/components/event_dispatcher/container_aware_dispatcher.rst b/components/event_dispatcher/container_aware_dispatcher.rst deleted file mode 100644 index 659a94cee7a..00000000000 --- a/components/event_dispatcher/container_aware_dispatcher.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. index:: - single: EventDispatcher; Service container aware - -The Container Aware Event Dispatcher -==================================== - -.. caution:: - - The ``ContainerAwareEventDispatcher`` was removed in Symfony 4.0. Use - ``EventDispatcher`` with closure-proxy injection instead. diff --git a/components/filesystem/lock_handler.rst b/components/filesystem/lock_handler.rst deleted file mode 100644 index 5997fd3887b..00000000000 --- a/components/filesystem/lock_handler.rst +++ /dev/null @@ -1,7 +0,0 @@ -LockHandler -=========== - -.. caution:: - - The ``LockHandler`` utility was removed in Symfony 4.0. Use the new Symfony - :doc:`Lock component ` instead. diff --git a/contributing/code/bc.rst b/contributing/code/bc.rst index 3f1e6164087..4c5eb1d4ca2 100644 --- a/contributing/code/bc.rst +++ b/contributing/code/bc.rst @@ -12,11 +12,6 @@ that release branch (5.x in the previous example). We also provide deprecation message triggered in the code base to help you with the migration process across major releases. -.. caution:: - - This promise was introduced with Symfony 2.3 and does not apply to previous - versions of Symfony. - However, backward compatibility comes in many different flavors. In fact, almost every change that we make to the framework can potentially break an application. For example, if we add a new method to a class, this will break an application diff --git a/profiler.rst b/profiler.rst index 49e804f45b2..42e925b89c8 100644 --- a/profiler.rst +++ b/profiler.rst @@ -110,16 +110,12 @@ need to create a custom data collector. Instead, use the built-in utilities to Consider using a professional profiler such as `Blackfire`_ to measure and analyze the execution of your application in detail. -Enabling the Profiler Conditionally ------------------------------------ +.. _enabling-the-profiler-conditionally: -.. caution:: - - The possibility to use a matcher to enable the profiler conditionally was - removed in Symfony 4.0. +Enabling the Profiler Programmatically +-------------------------------------- -Symfony Profiler cannot be enabled/disabled conditionally using matchers, because -that feature was removed in Symfony 4.0. However, you can use the ``enable()`` +Symfony Profiler can be enabled and disabled programmatically. You can use the ``enable()`` and ``disable()`` methods of the :class:`Symfony\\Component\\HttpKernel\\Profiler\\Profiler` class in your controllers to manage the profiler programmatically:: diff --git a/service_container.rst b/service_container.rst index d1ceb094333..f2be38267d6 100644 --- a/service_container.rst +++ b/service_container.rst @@ -1074,11 +1074,8 @@ unique string as the key of each service config: Explicitly Configuring Services and Arguments --------------------------------------------- -Prior to Symfony 3.3, all services and (typically) arguments were explicitly configured: -it was not possible to :ref:`load services automatically ` -and :ref:`autowiring ` was much less common. - -Both of these features are optional. And even if you use them, there may be some +:ref:`Loading services automatically ` +and :ref:`autowiring ` are optional. And even if you use them, there may be some cases where you want to manually wire a service. For example, suppose that you want to register *2* services for the ``SiteUpdateManager`` class - each with a different admin email. In this case, each needs to have a unique service id: From 1a25297db857a8cb41a7cb30a7ac215b4c00f92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Egyed?= Date: Fri, 23 Dec 2022 22:22:37 +0100 Subject: [PATCH 119/774] [Mailer] Add a note about how to use the Gmail provider --- mailer.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mailer.rst b/mailer.rst index 93fc5b4d66f..0cf1dc2a34c 100644 --- a/mailer.rst +++ b/mailer.rst @@ -177,7 +177,7 @@ party provider: Provider SMTP HTTP API ==================== ==================================================== =========================================== ======================================== Amazon SES ses+smtp://USERNAME:PASSWORD@default ses+https://ACCESS_KEY:SECRET_KEY@default ses+api://ACCESS_KEY:SECRET_KEY@default -Google Gmail gmail+smtp://USERNAME:PASSWORD@default n/a n/a +Google Gmail gmail+smtp://USERNAME:APP-PASSWORD@default n/a n/a Mailchimp Mandrill mandrill+smtp://USERNAME:PASSWORD@default mandrill+https://KEY@default mandrill+api://KEY@default Mailgun mailgun+smtp://USERNAME:PASSWORD@default mailgun+https://KEY:DOMAIN@default mailgun+api://KEY:DOMAIN@default Mailjet mailjet+smtp://ACCESS_KEY:SECRET_KEY@default n/a mailjet+api://ACCESS_KEY:SECRET_KEY@default @@ -214,6 +214,15 @@ OhMySMTP ohmysmtp+smtp://API_TOKEN@default n/a The usage of ``default_socket_timeout`` as the default timeout was introduced in Symfony 5.1. +.. note:: + + To use Google Gmail, you must have a Google Account with 2-Step-Verification (2FA) + enabled and you must use `App Password`_ to authenticate. Also note that Google + revokes your App Passwords when you change your Google Account password and then + you need to generate a new one. + Using other methods (like ``XOAUTH2`` or the ``Gmail API``) are not supported currently. + You should use Gmail for testing purposes only and use a real provider in production. + .. tip:: If you want to override the default host for a provider (to debug an issue using @@ -1564,3 +1573,4 @@ you can use the built in assertions:: .. _`PEM encoded`: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail .. _`default_socket_timeout`: https://www.php.net/manual/en/filesystem.configuration.php#ini.default-socket-timeout .. _`RFC 3986`: https://www.ietf.org/rfc/rfc3986.txt +.. _`App Password`: https://support.google.com/accounts/answer/185833 From 34745bf96c003ffb09aa8b376f1c239b8fc97f50 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 24 Dec 2022 13:24:19 +0100 Subject: [PATCH 120/774] [HttpKernel] Add option to render Surrogate fragment with absolute URIs --- http_cache/esi.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/http_cache/esi.rst b/http_cache/esi.rst index 1e10313b982..e52bbfacf3c 100644 --- a/http_cache/esi.rst +++ b/http_cache/esi.rst @@ -267,7 +267,7 @@ possible. signed when using the fragment renderer and the ``render_esi`` Twig function. -The ``render_esi`` helper supports two other useful options: +The ``render_esi`` helper supports three other useful options: ``alt`` Used as the ``alt`` attribute on the ESI tag, which allows you to specify an @@ -278,4 +278,11 @@ The ``render_esi`` helper supports two other useful options: of ``continue`` indicating that, in the event of a failure, the gateway cache will remove the ESI tag silently. +``absolute_uri`` + If set to true, an absolute URI will be generated. **default**: ``false`` + +.. versionadded:: 6.2 + + The ``absolute_uri`` option was introduced in Symfony 6.2. + .. _`ESI`: http://www.w3.org/TR/esi-lang From 608618e37ce1fa732bcd5b260dd7c6f7557d72e6 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 24 Dec 2022 16:46:34 +0100 Subject: [PATCH 121/774] Update confusing first class callable syntax --- components/validator.rst | 2 +- controller.rst | 6 +++--- form/data_transformers.rst | 2 +- form/validation_groups.rst | 2 +- http_client.rst | 2 +- logging/monolog_exclude_http_codes.rst | 2 +- messenger.rst | 6 +++--- testing.rst | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/components/validator.rst b/components/validator.rst index a88b13d0089..8698934c0a0 100644 --- a/components/validator.rst +++ b/components/validator.rst @@ -57,7 +57,7 @@ If you have lots of validation errors, you can filter them by error code:: use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; - $violations = $validator->validate(...); + $violations = $validator->validate(/* ... */); if (0 !== count($violations->findByCodes(UniqueEntity::NOT_UNIQUE_ERROR))) { // handle this specific error (display some message, send an email, etc.) } diff --git a/controller.rst b/controller.rst index 0fb8751cbf8..ee9bc0d410a 100644 --- a/controller.rst +++ b/controller.rst @@ -340,7 +340,7 @@ special type of exception:: // throw new NotFoundHttpException('The product does not exist'); } - return $this->render(...); + return $this->render(/* ... */); } The :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController::createNotFoundException` @@ -461,10 +461,10 @@ For example, imagine you're processing a :doc:`form ` submission:: ); // $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add() - return $this->redirectToRoute(...); + return $this->redirectToRoute(/* ... */); } - return $this->render(...); + return $this->render(/* ... */); } After processing the request, the controller sets a flash message in the session diff --git a/form/data_transformers.rst b/form/data_transformers.rst index cf32ca134a0..ff3b25147fc 100644 --- a/form/data_transformers.rst +++ b/form/data_transformers.rst @@ -112,7 +112,7 @@ slightly:: $builder->add( $builder ->create('tags', TextType::class) - ->addModelTransformer(...) + ->addModelTransformer(/* ... */) ); Example #2: Transforming an Issue Number into an Issue Entity diff --git a/form/validation_groups.rst b/form/validation_groups.rst index a215ed02aba..609afac8689 100644 --- a/form/validation_groups.rst +++ b/form/validation_groups.rst @@ -13,7 +13,7 @@ this as an option when :ref:`creating forms in controllers createFormBuilder($user, [ 'validation_groups' => ['registration'], - ])->add(...); + ])->add(/* ... */); When :ref:`creating forms in classes `, add the following to the ``configureOptions()`` method:: diff --git a/http_client.rst b/http_client.rst index 44498e9946a..30c19eba07d 100644 --- a/http_client.rst +++ b/http_client.rst @@ -1257,7 +1257,7 @@ that network errors can happen when calling e.g. ``getStatusCode()`` too:: // ... try { // both lines can potentially throw - $response = $client->request(...); + $response = $client->request(/* ... */); $headers = $response->getHeaders(); // ... } catch (TransportExceptionInterface $e) { diff --git a/logging/monolog_exclude_http_codes.rst b/logging/monolog_exclude_http_codes.rst index a064370d0c5..d698752f06a 100644 --- a/logging/monolog_exclude_http_codes.rst +++ b/logging/monolog_exclude_http_codes.rst @@ -55,7 +55,7 @@ logging these HTTP codes based on the MonologBundle configuration: $mainHandler = $monolog->handler('main') // ... ->type('fingers_crossed') - ->handler(...) + ->handler('...') ; $mainHandler->excludedHttpCode()->code(403); diff --git a/messenger.rst b/messenger.rst index c6100b0fd62..a49de0d2276 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1917,7 +1917,7 @@ this globally (or for each transport) to a service that implements ->context('foo', 'bar'); $messenger->transport('async_priority_normal') - ->dsn(...) + ->dsn('...') ->serializer('messenger.transport.symfony_serializer'); }; @@ -2180,8 +2180,8 @@ Then, make sure to "route" your message to *both* transports: return static function (FrameworkConfig $framework) { $messenger = $framework->messenger(); - $messenger->transport('async_priority_normal')->dsn(...); - $messenger->transport('image_transport')->dsn(...); + $messenger->transport('async_priority_normal')->dsn('...'); + $messenger->transport('image_transport')->dsn('...'); $messenger->routing('App\Message\UploadedImage') ->senders(['image_transport', 'async_priority_normal']); diff --git a/testing.rst b/testing.rst index a092a7b5bfa..0e7d861894f 100644 --- a/testing.rst +++ b/testing.rst @@ -267,7 +267,7 @@ the container is returned by ``static::getContainer()``:: // (3) run some service & test the result $newsletterGenerator = $container->get(NewsletterGenerator::class); - $newsletter = $newsletterGenerator->generateMonthlyNews(...); + $newsletter = $newsletterGenerator->generateMonthlyNews(/* ... */); $this->assertEquals('...', $newsletter->getContent()); } From 637d688f462d049ca4f6068faa9a385994cd51ec Mon Sep 17 00:00:00 2001 From: Grzegorz Zdanowski Date: Sat, 24 Dec 2022 22:56:14 -0600 Subject: [PATCH 122/774] Fix invalid yaml for password hashing in test env --- security/passwords.rst | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/security/passwords.rst b/security/passwords.rst index 47f5e7f0424..2a2f7acc336 100644 --- a/security/passwords.rst +++ b/security/passwords.rst @@ -134,19 +134,22 @@ Further in this article, you can find a .. configuration-block:: .. code-block:: yaml - + # config/packages/test/security.yaml - password_hashers: - # Use your user class name here - App\Entity\User: - algorithm: plaintext # disable hashing (only do this in tests!) - - # or use the lowest possible values - App\Entity\User: - algorithm: auto # This should be the same value as in config/packages/security.yaml - cost: 4 # Lowest possible value for bcrypt - time_cost: 3 # Lowest possible value for argon - memory_cost: 10 # Lowest possible value for argon + security: + # ... + + password_hashers: + # Use your user class name here + App\Entity\User: + algorithm: plaintext # disable hashing (only do this in tests!) + + # or use the lowest possible values + App\Entity\User: + algorithm: auto # This should be the same value as in config/packages/security.yaml + cost: 4 # Lowest possible value for bcrypt + time_cost: 3 # Lowest possible value for argon + memory_cost: 10 # Lowest possible value for argon .. code-block:: xml From 804f01772635581df66e5bd65ea7130f2ad874a2 Mon Sep 17 00:00:00 2001 From: Grzegorz Zdanowski Date: Sat, 24 Dec 2022 22:59:28 -0600 Subject: [PATCH 123/774] Bump Symfony version --- contributing/documentation/overview.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/contributing/documentation/overview.rst b/contributing/documentation/overview.rst index 78e90d04483..2ea1054eb7b 100644 --- a/contributing/documentation/overview.rst +++ b/contributing/documentation/overview.rst @@ -112,16 +112,16 @@ memorable name for the new branch (if you are fixing a reported issue, use .. code-block:: terminal - $ git checkout -b improve_install_article upstream/4.4 + $ git checkout -b improve_install_article upstream/5.4 In this example, the name of the branch is ``improve_install_article`` and the -``upstream/4.4`` value tells Git to create this branch based on the ``4.4`` +``upstream/5.4`` value tells Git to create this branch based on the ``5.4`` branch of the ``upstream`` remote, which is the original Symfony Docs repository. Fixes should always be based on the **oldest maintained branch** which contains -the error. Nowadays this is the ``4.4`` branch. If you are instead documenting a +the error. Nowadays this is the ``5.4`` branch. If you are instead documenting a new feature, switch to the first Symfony version that included it, e.g. -``upstream/5.4``. +``upstream/6.2``. **Step 5.** Now make your changes in the documentation. Add, tweak, reword and even remove any content and do your best to comply with the @@ -155,7 +155,7 @@ changes should be applied: :align: center In this example, the **base fork** should be ``symfony/symfony-docs`` and -the **base** branch should be the ``4.4``, which is the branch that you selected +the **base** branch should be the ``5.4``, which is the branch that you selected to base your changes on. The **head fork** should be your forked copy of ``symfony-docs`` and the **compare** branch should be ``improve_install_article``, which is the name of the branch you created and where you made your changes. @@ -205,7 +205,7 @@ contribution to the Symfony docs: # create a new branch based on the oldest maintained version $ cd projects/symfony-docs/ $ git fetch upstream - $ git checkout -b my_changes upstream/4.4 + $ git checkout -b my_changes upstream/5.4 # ... do your changes @@ -254,8 +254,8 @@ into multiple branches, corresponding to the different versions of Symfony itsel The latest (e.g. ``5.x``) branch holds the documentation for the development branch of the code. -Unless you're documenting a feature that was introduced after Symfony 4.4, -your changes should always be based on the ``4.4`` branch. Documentation managers +Unless you're documenting a feature that was introduced after Symfony 5.4, +your changes should always be based on the ``5.4`` branch. Documentation managers will use the necessary Git-magic to also apply your changes to all the active branches of the documentation. From 955d73198ae8a9caae57db2f289378562e289009 Mon Sep 17 00:00:00 2001 From: Nextpage <92269411+nxtpge@users.noreply.github.com> Date: Sun, 25 Dec 2022 15:42:32 +0100 Subject: [PATCH 124/774] [Contributing] [Code] Add a step for forking in "Proposing a Change" --- contributing/code/pull_requests.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contributing/code/pull_requests.rst b/contributing/code/pull_requests.rst index 40e11e73364..fa2306f613c 100644 --- a/contributing/code/pull_requests.rst +++ b/contributing/code/pull_requests.rst @@ -87,6 +87,8 @@ Get the Symfony source code: * Fork the `Symfony repository`_ (click on the "Fork" button); +* Uncheck the "Copy the ``X.Y`` branch only"; + * After the "forking action" has completed, clone your fork locally (this will create a ``symfony`` directory): From 852824e301a2bfdf7280cd78cfb67de03c89371e Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 26 Dec 2022 21:18:20 +0100 Subject: [PATCH 125/774] [FrameworkBundle][HttpKernel][TwigBridge] Add an helper to generate fragments URL --- reference/twig_reference.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index e905a4d2b05..c0e074dfa8b 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -63,6 +63,28 @@ falls back to the behavior of `render`_ otherwise. in the function name, e.g. ``render_hinclude()`` will use the hinclude.js strategy. This works for all ``render_*()`` functions. +fragment_uri +~~~~~~~~~~~~ + +.. code-block:: twig + + {{ fragment_uri(controller, absolute = false, strict = true, sign = true) }} + +``controller`` + **type**: ``ControllerReference`` +``absolute`` *(optional)* + **type**: ``boolean`` **default**: ``false`` +``strict`` *(optional)* + **type**: ``boolean`` **default**: ``true`` +``sign`` *(optional)* + **type**: ``boolean`` **default**: ``true`` + +Generates the URI of a fragment. + +.. versionadded:: 5.3 + + The ``fragment_uri()`` function was introduced in Symfony 5.3. + controller ~~~~~~~~~~ From e4f30166c2908c3e93327f3661db53c5a93241ca Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 26 Dec 2022 21:32:10 +0100 Subject: [PATCH 126/774] [Translation] Translatable parameters --- translation.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/translation.rst b/translation.rst index 366c64c8517..de6bdd12b84 100644 --- a/translation.rst +++ b/translation.rst @@ -345,6 +345,10 @@ Templates are now much simpler because you can pass translatable objects to the

{{ message|trans }}

{{ status|trans }}

+.. tip:: + + The translation parameters can also be a :class:`Symfony\\Component\\Translation\\TranslatableMessage`. + .. tip:: There's also a :ref:`function called t() `, From 5c1771fbd5b88b0caac5d8f8d319a1d0e50e765a Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 26 Dec 2022 22:02:41 +0100 Subject: [PATCH 127/774] [Mailer] Add a way to change the Bus transport dynamically --- mailer.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mailer.rst b/mailer.rst index 45be91a7cc4..e87e985d96d 100644 --- a/mailer.rst +++ b/mailer.rst @@ -1333,6 +1333,18 @@ disable asynchronous delivery. The :method:`Symfony\\Component\\Mailer\\Transport\\Smtp\\SmtpTransport::stop` method was made public in Symfony 6.1. +Bus transport can also be selected by +adding an ``X-Bus-Transport`` header (which will remove automatically from +the final message):: + + // Use the bus transport "app.another_bus": + $email->getHeaders()->addTextHeader('X-Bus-Transport', 'app.another_bus'); + $mailer->send($email); + +.. versionadded:: 6.2 + + The ``X-Bus-Transport`` header support was introduced in Symfony 6.2. + Adding Tags and Metadata to Emails ---------------------------------- From 50cdaf61974297e7f11d213757e9fefe226f84d5 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Tue, 27 Dec 2022 09:39:52 +0100 Subject: [PATCH 128/774] fix tilde not rendered in filesystem doc --- components/filesystem.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/filesystem.rst b/components/filesystem.rst index cf6166d78a5..3b6c92ad6fa 100644 --- a/components/filesystem.rst +++ b/components/filesystem.rst @@ -352,7 +352,7 @@ following rules iteratively until no further processing can be done: - root paths ("/" and "C:/") always terminate with a slash; - non-root paths never terminate with a slash; - schemes (such as "phar://") are kept; -- replace "~" with the user's home directory. +- replace ``~`` with the user's home directory. You can canonicalize a path with :method:`Symfony\\Component\\Filesystem\\Path::canonicalize`:: From a6f75ecad4ea6137fd0b5f4d43e809856724989c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 Dec 2022 12:56:41 +0100 Subject: [PATCH 129/774] Link to a reference --- reference/twig_reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index c0e074dfa8b..5f89688991b 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -79,7 +79,7 @@ fragment_uri ``sign`` *(optional)* **type**: ``boolean`` **default**: ``true`` -Generates the URI of a fragment. +Generates the URI of :ref:`a fragment `. .. versionadded:: 5.3 From cab13941d19fd4ff07d66208ce8f890729c6a8aa Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 Dec 2022 13:01:35 +0100 Subject: [PATCH 130/774] Remove a versionadded directive --- reference/twig_reference.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 735979f8c76..169ba176bb3 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -81,10 +81,6 @@ fragment_uri Generates the URI of :ref:`a fragment `. -.. versionadded:: 5.3 - - The ``fragment_uri()`` function was introduced in Symfony 5.3. - controller ~~~~~~~~~~ From e435d2c9be5aab0c6c3f3d7a773bde7727f64ee4 Mon Sep 17 00:00:00 2001 From: Tac Tacelosky Date: Mon, 26 Dec 2022 06:53:53 -0600 Subject: [PATCH 131/774] inline the constructor --- form/data_transformers.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/form/data_transformers.rst b/form/data_transformers.rst index ff3b25147fc..8258ee2794a 100644 --- a/form/data_transformers.rst +++ b/form/data_transformers.rst @@ -177,11 +177,8 @@ to and from the issue number and the ``Issue`` object:: class IssueToNumberTransformer implements DataTransformerInterface { - private $entityManager; - - public function __construct(EntityManagerInterface $entityManager) + public function __construct(private EntityManagerInterface $entityManager) { - $this->entityManager = $entityManager; } /** From 399076b9508b45cb83f11df80d7f35be9f09ed70 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 Dec 2022 15:20:48 +0100 Subject: [PATCH 132/774] Minor tweak --- mailer.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mailer.rst b/mailer.rst index 2e8b729a90d..a1d2577def5 100644 --- a/mailer.rst +++ b/mailer.rst @@ -1342,9 +1342,8 @@ disable asynchronous delivery. The :method:`Symfony\\Component\\Mailer\\Transport\\Smtp\\SmtpTransport::stop` method was made public in Symfony 6.1. -Bus transport can also be selected by -adding an ``X-Bus-Transport`` header (which will remove automatically from -the final message):: +You can also select the transport by adding an ``X-Bus-Transport`` header (which +will be remove automatically from the final message):: // Use the bus transport "app.another_bus": $email->getHeaders()->addTextHeader('X-Bus-Transport', 'app.another_bus'); From 76341b28d62a6d6d986d0a96abf04225704b9f9e Mon Sep 17 00:00:00 2001 From: Erwan Richard Date: Tue, 27 Dec 2022 11:18:50 +0100 Subject: [PATCH 133/774] Fix choice_label --- reference/forms/types/enum.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/types/enum.rst b/reference/forms/types/enum.rst index 2fa79d37a22..63bca396c4b 100644 --- a/reference/forms/types/enum.rst +++ b/reference/forms/types/enum.rst @@ -63,7 +63,7 @@ dots or spaces). If you need more flexibility for these labels, use the ->add('textAlign', EnumType::class, [ 'class' => TextAlign::class, - 'choice_label' => match ($choice) { + 'choice_label' => fn ($choice) => match ($choice) { TextAlign::Left => 'text_align.left.label', TextAlign::Center => 'text_align.center.label', TextAlign::Right => 'text_align.right.label', From 4f17fcb9e271c45492d383eb79dfdd28e1a34211 Mon Sep 17 00:00:00 2001 From: jmsche Date: Wed, 14 Dec 2022 15:55:02 +0100 Subject: [PATCH 134/774] Symfony CLI: document the .symfony.local.yaml config file --- setup/symfony_server.rst | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/setup/symfony_server.rst b/setup/symfony_server.rst index b9cdb4c2850..37cbc767ccd 100644 --- a/setup/symfony_server.rst +++ b/setup/symfony_server.rst @@ -284,16 +284,46 @@ server provides a ``run`` command to wrap them as follows: # stop the web server (and all the associated commands) when you are finished $ symfony server:stop -Configuring Workers -------------------- +Configuration file +------------------ .. caution:: This feature is experimental and could change or be removed at any time without prior notice. +There are several options that you can set using a ``.symfony.local.yaml`` config file: + +.. code-block:: yaml + + # Sets domain1.wip and domain2.wip for the current project + proxy: + domains: + - domain1 + - domain2 + + http: + document_root: public/ # Path to the project document root + passthru: index.php # Project passthru index + port: 8000 # Force the port that will be used to run the server + preferred_port: 8001 # Preferred HTTP port [default: 8000] + p12: path/to/p12_cert # Name of the file containing the TLS certificate to use in p12 format + allow_http: true # Prevent auto-redirection from HTTP to HTTPS + no_tls: true # Use HTTP instead of HTTPS + daemon: true # Run the server in the background + use_gzip: true # Toggle GZIP compression + +.. caution:: + + Setting domains in this configuration file will override any domains you set + using the ``proxy:domain:attach`` command for the current project when you start + the server. + +Configuring Workers +~~~~~~~~~~~~~~~~~~~ + If you like some processes to start automatically, along with the webserver -(``symfony server:start``), add a configuration file to your project: +(``symfony server:start``), you can set them in the YAML configuration file: .. code-block:: yaml From 0b41f1cdf5a20be9516f4a61008592677b0f27db Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Wed, 28 Dec 2022 09:21:47 +0100 Subject: [PATCH 135/774] notifier - do not display todo doc on site --- notifier.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/notifier.rst b/notifier.rst index d56cd3534c7..040723bf6d9 100644 --- a/notifier.rst +++ b/notifier.rst @@ -749,9 +749,9 @@ all configured texter and chatter transports only in the ``dev`` (and/or slack: 'null://null' .. TODO - - Using the message bus for asynchronous notification - - Describe notifier monolog handler - - Describe notification_on_failed_messages integration +.. - Using the message bus for asynchronous notification +.. - Describe notifier monolog handler +.. - Describe notification_on_failed_messages integration Learn more ---------- From d937f048474fd9b7f8747250fb4b5770426fb24a Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 30 Dec 2022 08:53:16 +0100 Subject: [PATCH 136/774] Add html_to_text_converter config docs --- mailer.rst | 32 +++++++++++++++++++++----------- reference/configuration/twig.rst | 19 +++++++++++++++++++ 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/mailer.rst b/mailer.rst index a1d2577def5..7c8ab512493 100644 --- a/mailer.rst +++ b/mailer.rst @@ -717,12 +717,22 @@ method of the ``TemplatedEmail`` class and also to a special variable called Text Content ~~~~~~~~~~~~ -When the text content of a ``TemplatedEmail`` is not explicitly defined, mailer -will generate it automatically by converting the HTML contents into text. If you -have `league/html-to-markdown`_ installed in your application, -it uses that to turn HTML into Markdown (so the text email has some visual appeal). -Otherwise, it applies the :phpfunction:`strip_tags` PHP function to the original -HTML contents. +When the text content of a ``TemplatedEmail`` is not explicitly defined, it is +automatically generated from the HTML contents. + +Symfony uses the following strategy when generating the text version of an +email: + +* If an explicit HTML to text converter has been configured (see + :ref:`twig.mailer.html_to_text_converter + `), it calls it; + +* If not, and if you have `league/html-to-markdown`_ installed in your + application, it uses it to turn HTML into Markdown (so the text email has + some visual appeal); + +* Otherwise, it applies the :phpfunction:`strip_tags` PHP function to the + original HTML contents. If you want to define the text content yourself, use the ``text()`` method explained in the previous sections or the ``textTemplate()`` method provided by @@ -732,13 +742,13 @@ the ``TemplatedEmail`` class: + use Symfony\Bridge\Twig\Mime\TemplatedEmail; - $email = (new TemplatedEmail()) - // ... + $email = (new TemplatedEmail()) + // ... - ->htmlTemplate('emails/signup.html.twig') + ->htmlTemplate('emails/signup.html.twig') + ->textTemplate('emails/signup.txt.twig') - // ... - ; + // ... + ; .. _mailer-twig-embedding-images: diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index 54961707833..ce6dd45a772 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -302,6 +302,25 @@ globals It defines the global variables injected automatically into all Twig templates. Learn more about :doc:`Twig global variables `. +mailer +~~~~~~ + +.. _config-twig-html-to-text-converter: + +html_to_text_converter +...................... + +**type**: ``string`` **default**: ```` + +.. versionadded:: 6.2 + + The ``html_to_text_converter`` option was introduced in Symfony 6.2. + +The service implementing +:class:`Symfony\\Component\\Mime\\HtmlToTextConverter\\HtmlToTextConverterInterface` +that will be used to automatically create the text part of an email from its +HTML contents when not explicitely defined. + number_format ~~~~~~~~~~~~~ From f6bd7ebd7883c7c3eb20671b8ef228b87971195b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 30 Dec 2022 15:02:26 +0100 Subject: [PATCH 137/774] Typo --- reference/configuration/twig.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index ce6dd45a772..5e5ad16c675 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -319,7 +319,7 @@ html_to_text_converter The service implementing :class:`Symfony\\Component\\Mime\\HtmlToTextConverter\\HtmlToTextConverterInterface` that will be used to automatically create the text part of an email from its -HTML contents when not explicitely defined. +HTML contents when not explicitly defined. number_format ~~~~~~~~~~~~~ From df5014399b6c2b793510d94c3dc8ebb1aac7c187 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Sun, 1 Jan 2023 16:17:29 +0100 Subject: [PATCH 138/774] [DependencyInjection] Allow injecting the current env into php config closures --- service_container.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/service_container.rst b/service_container.rst index f2be38267d6..9eca7a49d1d 100644 --- a/service_container.rst +++ b/service_container.rst @@ -1186,6 +1186,22 @@ If you want to pass the second, you'll need to :ref:`manually wire the service < and the automatically loaded service will be passed - by default - when you type-hint ``SiteUpdateManager``. That's why creating the alias is a good idea. +When using PHP closures to configure your services, it is possible to automatically +inject the current environment value by adding a string argument named ``$env`` to +the closure:: + +.. configuration-block:: + + .. code-block:: php + + // config/packages/my_config.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + return function(ContainerConfigurator $configurator, string $env) { + // `$env` is automatically filled in, you can configure your + // services depending on which environment you're on + }; + Learn more ---------- From aac1e7db83e38238754f8e83cb185942c851e8de Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 3 Jan 2023 13:57:22 +0100 Subject: [PATCH 139/774] [DependencyInjection] Add exclude to TaggedIterator and TaggedLocator --- service_container/tags.rst | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/service_container/tags.rst b/service_container/tags.rst index 9c89ef8c85a..e5e996a076e 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -591,6 +591,76 @@ application handlers:: } } +If for some reason you need to exclude one or multiple services when using a tagged +iterator, this can be done by using the ``exclude`` option: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + + # This is the service we want to exclude, even if the 'app.handler' tag is attached + App\Handler\Three: + tags: ['app.handler'] + + App\HandlerCollection: + arguments: + - !tagged_iterator { tag: app.handler, exclude: ['App\Handler\Three'] } + + .. code-block:: xml + + + + + + + + + + + + + + + + + App\Handler\Three + + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + return function(ContainerConfigurator $configurator) { + $services = $configurator->services(); + + // ... + + // This is the service we want to exclude, even if the 'app.handler' tag is attached + $services->set(App\Handler\Three::class) + ->tag('app.handler') + ; + + $services->set(App\HandlerCollection::class) + // inject all services tagged with app.handler as first argument + ->args([tagged_iterator('app.handler', exclude: [App\Handler\Three::class])]) + ; + }; + +.. versionadded:: 6.1 + + The ``exclude`` option was introduced in Symfony 6.1. + .. seealso:: See also :doc:`tagged locator services ` From 1d0949338e3e4afd39e7429d53897ec8ca81ab6d Mon Sep 17 00:00:00 2001 From: Mathieu Date: Mon, 2 Jan 2023 09:21:31 +0100 Subject: [PATCH 140/774] [Security] Prevent unneeded implementation of `PasswordHasherAwareInterface` when migrating passwords --- security/passwords.rst | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/security/passwords.rst b/security/passwords.rst index 2a2f7acc336..76ff9651c08 100644 --- a/security/passwords.rst +++ b/security/passwords.rst @@ -134,7 +134,7 @@ Further in this article, you can find a .. configuration-block:: .. code-block:: yaml - + # config/packages/test/security.yaml security: # ... @@ -544,8 +544,10 @@ migration by returning ``true`` in the ``needsRehash()`` method:: } } -Named Password Hashers ----------------------- +.. _named-password-hashers: + +Dynamic Password Hashers +------------------------ Usually, the same password hasher is used for all users by configuring it to apply to all instances of a specific class. Another option is to use a @@ -646,6 +648,12 @@ the name of the hasher to use:: } } +.. caution:: + + When :ref:`migrating passwords `, you don't need to implement ``PasswordHasherAwareInterface`` + to return the legacy hasher name: + Symfony will detect it from your ``migrate_from`` configuration. + If you created your own password hasher implementing the :class:`Symfony\\Component\\PasswordHasher\\PasswordHasherInterface`, you must register a service for it in order to use it as a named hasher: From d3e4443f02f74ac40f105427ed359d6a6dfec4e1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 3 Jan 2023 16:04:32 +0100 Subject: [PATCH 141/774] Minor tweak --- security/passwords.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/passwords.rst b/security/passwords.rst index 4eae756172c..030abe90a5d 100644 --- a/security/passwords.rst +++ b/security/passwords.rst @@ -639,9 +639,9 @@ the name of the hasher to use:: .. caution:: - When :ref:`migrating passwords `, you don't need to implement ``PasswordHasherAwareInterface`` - to return the legacy hasher name: - Symfony will detect it from your ``migrate_from`` configuration. + When :ref:`migrating passwords `, you don't + need to implement ``PasswordHasherAwareInterface`` to return the legacy + hasher name: Symfony will detect it from your ``migrate_from`` configuration. If you created your own password hasher implementing the :class:`Symfony\\Component\\PasswordHasher\\PasswordHasherInterface`, From 565e0e3bd1e798727ffc79e9130d0d3cf308e310 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 3 Jan 2023 08:54:04 +1000 Subject: [PATCH 142/774] Adds information on AsEntityListener attribute to the Doctrine event documentation --- doctrine/events.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/doctrine/events.rst b/doctrine/events.rst index 9a2756a4e61..71bb44d6fe0 100644 --- a/doctrine/events.rst +++ b/doctrine/events.rst @@ -353,6 +353,30 @@ with the ``doctrine.orm.entity_listener`` tag: ; }; + +Doctrine Entity Listeners may also be defined using PHP attributes. When using PHP attributes it is not necessary to create a service for the listener. + + .. code-block:: php + + // src/EventListener/UserChangedNotifier.php + namespace App\EventListener; + + use App\Entity\User; + use Doctrine\Persistence\Event\LifecycleEventArgs; + use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener; + + #[AsEntityListener(event:'postUpdate', method:'postUpdate', entity:User::class)] + class UserChangedNotifier + { + // the entity listener methods receive two arguments: + // the entity instance and the lifecycle event + public function postUpdate(User $user, LifecycleEventArgs $event): void + { + // ... do something to notify the changes + } + } + + Doctrine Lifecycle Subscribers ------------------------------ From 7b93c1f9b0386f98012febc5edc38e549a6ff48a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 3 Jan 2023 17:56:44 +0100 Subject: [PATCH 143/774] Reword --- doctrine/events.rst | 53 ++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/doctrine/events.rst b/doctrine/events.rst index 71bb44d6fe0..59fe440c2b6 100644 --- a/doctrine/events.rst +++ b/doctrine/events.rst @@ -235,8 +235,9 @@ Doctrine Entity Listeners Entity listeners are defined as PHP classes that listen to a single Doctrine event on a single entity class. For example, suppose that you want to send some -notifications whenever a ``User`` entity is modified in the database. To do so, -define a listener for the ``postUpdate`` Doctrine event:: +notifications whenever a ``User`` entity is modified in the database. + +First, define a PHP class that handles the ``postUpdate`` Doctrine event:: // src/EventListener/UserChangedNotifier.php namespace App\EventListener; @@ -254,9 +255,27 @@ define a listener for the ``postUpdate`` Doctrine event:: } } -The next step is to enable the Doctrine listener in the Symfony application by -creating a new service for it and :doc:`tagging it ` -with the ``doctrine.orm.entity_listener`` tag: +Then, add the ``#[AsDoctrineListener]`` attribute to the class to enable it as +a Doctrine entity listener in your application: + + .. code-block:: php + + // src/EventListener/UserChangedNotifier.php + namespace App\EventListener; + + // ... + use App\Entity\User; + use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; + + #[AsDoctrineListener(event: 'postUpdate', method: 'postUpdate', entity: User::class)] + class UserChangedNotifier + { + // ... + } + +That's it. Alternatively, if you prefer to not use PHP attributes, you must +configure a service for the entity listener and :doc:`tag it ` +with the ``doctrine.orm.entity_listener`` tag as follows: .. configuration-block:: @@ -353,30 +372,6 @@ with the ``doctrine.orm.entity_listener`` tag: ; }; - -Doctrine Entity Listeners may also be defined using PHP attributes. When using PHP attributes it is not necessary to create a service for the listener. - - .. code-block:: php - - // src/EventListener/UserChangedNotifier.php - namespace App\EventListener; - - use App\Entity\User; - use Doctrine\Persistence\Event\LifecycleEventArgs; - use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener; - - #[AsEntityListener(event:'postUpdate', method:'postUpdate', entity:User::class)] - class UserChangedNotifier - { - // the entity listener methods receive two arguments: - // the entity instance and the lifecycle event - public function postUpdate(User $user, LifecycleEventArgs $event): void - { - // ... do something to notify the changes - } - } - - Doctrine Lifecycle Subscribers ------------------------------ From cf1b86e1c1371c08a8520226f7a6b4756e1a4c5d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 4 Jan 2023 12:59:53 +0100 Subject: [PATCH 144/774] Minor tweak --- service_container/tags.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index e5e996a076e..26ce6077fee 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -591,8 +591,8 @@ application handlers:: } } -If for some reason you need to exclude one or multiple services when using a tagged -iterator, this can be done by using the ``exclude`` option: +If for some reason you need to exclude one or more services when using a tagged +iterator, add the ``exclude`` option: .. configuration-block:: From a8c81fe9c12acff8d46f3a36cdfd74d73281190c Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 4 Jan 2023 13:24:40 +0100 Subject: [PATCH 145/774] [FrameworkBundle] Add support for route attributes in kernel controller methods --- configuration/micro_kernel_trait.rst | 128 +++++++++++++++++++-------- 1 file changed, 91 insertions(+), 37 deletions(-) diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index 3d479829a94..97781fa383c 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -20,55 +20,109 @@ via Composer: symfony/http-foundation symfony/routing \ symfony/dependency-injection symfony/framework-bundle -Next, create an ``index.php`` file that defines the kernel class and runs it:: +Next, create an ``index.php`` file that defines the kernel class and runs it: - // index.php - use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; - use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - use Symfony\Component\HttpFoundation\JsonResponse; - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpKernel\Kernel as BaseKernel; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; +.. configuration-block:: - require __DIR__.'/vendor/autoload.php'; + .. code-block:: php - class Kernel extends BaseKernel - { - use MicroKernelTrait; + // index.php + use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; + use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; + use Symfony\Component\HttpFoundation\JsonResponse; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - public function registerBundles(): array - { - return [ - new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - ]; - } + require __DIR__.'/vendor/autoload.php'; - protected function configureContainer(ContainerConfigurator $c): void + class Kernel extends BaseKernel { - // PHP equivalent of config/packages/framework.yaml - $c->extension('framework', [ - 'secret' => 'S0ME_SECRET' - ]); - } + use MicroKernelTrait; - protected function configureRoutes(RoutingConfigurator $routes): void - { - $routes->add('random_number', '/random/{limit}')->controller([$this, 'randomNumber']); + public function registerBundles(): array + { + return [ + new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), + ]; + } + + protected function configureContainer(ContainerConfigurator $c): void + { + // PHP equivalent of config/packages/framework.yaml + $c->extension('framework', [ + 'secret' => 'S0ME_SECRET' + ]); + } + + protected function configureRoutes(RoutingConfigurator $routes): void + { + $routes->add('random_number', '/random/{limit}')->controller([$this, 'randomNumber']); + } + + public function randomNumber(int $limit): JsonResponse + { + return new JsonResponse([ + 'number' => random_int(0, $limit), + ]); + } } - public function randomNumber(int $limit): JsonResponse + $kernel = new Kernel('dev', true); + $request = Request::createFromGlobals(); + $response = $kernel->handle($request); + $response->send(); + $kernel->terminate($request, $response); + + .. code-block:: php-attributes + + // index.php + use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; + use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; + use Symfony\Component\HttpFoundation\JsonResponse; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpKernel\Kernel as BaseKernel; + use Symfony\Component\Routing\Annotation\Route; + + require __DIR__.'/vendor/autoload.php'; + + class Kernel extends BaseKernel { - return new JsonResponse([ - 'number' => random_int(0, $limit), - ]); + use MicroKernelTrait; + + public function registerBundles(): array + { + return [ + new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), + ]; + } + + protected function configureContainer(ContainerConfigurator $c): void + { + // PHP equivalent of config/packages/framework.yaml + $c->extension('framework', [ + 'secret' => 'S0ME_SECRET' + ]); + } + + #[Route('/random/{limit}', name='random_number')] + public function randomNumber(int $limit): JsonResponse + { + return new JsonResponse([ + 'number' => random_int(0, $limit), + ]); + } } - } - $kernel = new Kernel('dev', true); - $request = Request::createFromGlobals(); - $response = $kernel->handle($request); - $response->send(); - $kernel->terminate($request, $response); + $kernel = new Kernel('dev', true); + $request = Request::createFromGlobals(); + $response = $kernel->handle($request); + $response->send(); + $kernel->terminate($request, $response); + +.. versionadded:: 6.1 + + The PHP attributes notation has been introduced in Symfony 6.1. That's it! To test it, start the :doc:`Symfony Local Web Server `: From 0db72289d3e167731fe5e198c2062f25ce6725a2 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 4 Jan 2023 14:01:39 +0100 Subject: [PATCH 146/774] [DependencyInjection] Autowire arguments using the #[TaggedIterator] attribute --- service_container/tags.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/service_container/tags.rst b/service_container/tags.rst index 9c89ef8c85a..c834c0e6b17 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -591,6 +591,26 @@ application handlers:: } } +Injecting tagged services can be also be done through autowiring thanks to the +``#[TaggedIterator]`` attribute. This attribute must be directly used on the +argument to autowire:: + + // src/HandlerCollection.php + namespace App; + + use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; + + class HandlerCollection + { + public function __construct(#[TaggedIterator('app.handler')] iterable $handlers) + { + } + } + +.. versionadded:: 5.3 + + The ``#[TaggedIterator]`` attribute was introduced in Symfony 5.3 and requires PHP 8. + .. seealso:: See also :doc:`tagged locator services ` From 8de38ef1d6249cd5404a68f6ebc27e761dbe6479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Riehl?= Date: Wed, 4 Jan 2023 14:03:12 +0100 Subject: [PATCH 147/774] Update getContainerExtension() Since Symfony 6.x call to getContainerExtension() has evolved. Now, it's necessary to set a return type, else it generates this error : Declaration of Acme\TestBundle\AcmeTestBundle::getContainerExtension() must be compatible with Symfony\Component\HttpKernel\Bundle\Bundle::getContainerExtension(): ?Symfony\Component\DependencyInjection\Extension\ExtensionInterface" --- bundles/extension.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bundles/extension.rst b/bundles/extension.rst index edbcb5cd270..69f34dbc604 100644 --- a/bundles/extension.rst +++ b/bundles/extension.rst @@ -54,10 +54,11 @@ method to return the instance of the extension:: // ... use Acme\HelloBundle\DependencyInjection\UnconventionalExtensionClass; + use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; class AcmeHelloBundle extends Bundle { - public function getContainerExtension() + public function getContainerExtension(): ?ExtensionInterface { return new UnconventionalExtensionClass(); } From e891e95474a3692a2837303bcd60d713cb8afe4a Mon Sep 17 00:00:00 2001 From: Alexis Lefebvre Date: Thu, 5 Jan 2023 02:52:11 +0100 Subject: [PATCH 148/774] remove mention to @required --- service_container/autowiring.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/service_container/autowiring.rst b/service_container/autowiring.rst index 20c6e1223b6..d70ccbdaa53 100644 --- a/service_container/autowiring.rst +++ b/service_container/autowiring.rst @@ -578,11 +578,8 @@ Autowiring will automatically call *any* method with the ``#[Required]`` attribu above it, autowiring each argument. If you need to manually wire some of the arguments to a method, you can always explicitly :doc:`configure the method call `. -If your PHP version doesn't support attributes (they were introduced in PHP 8), -you can use the ``@required`` annotation instead. - Despite property injection having some :ref:`drawbacks `, -autowiring with ``#[Required]`` or ``@required`` can also be applied to public +autowiring with ``#[Required]`` can also be applied to public typed properties: .. configuration-block:: From 76c04248e8bf032419a0470e9f8e1ca55a77ae45 Mon Sep 17 00:00:00 2001 From: Alexis Lefebvre Date: Thu, 5 Jan 2023 03:20:02 +0100 Subject: [PATCH 149/774] remove mention to required in service_container --- service_container/injection_types.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/service_container/injection_types.rst b/service_container/injection_types.rst index 243856f1472..419bad912d0 100644 --- a/service_container/injection_types.rst +++ b/service_container/injection_types.rst @@ -116,15 +116,16 @@ by cloning the original service, this approach allows you to make a service immu // ... use Symfony\Component\Mailer\MailerInterface; + use Symfony\Contracts\Service\Attribute\Required; class NewsletterManager { private $mailer; /** - * @required * @return static */ + #[Required] public function withMailer(MailerInterface $mailer): self { $new = clone $this; @@ -220,14 +221,14 @@ that accepts the dependency:: // src/Mail/NewsletterManager.php namespace App\Mail; + use Symfony\Contracts\Service\Attribute\Required; + // ... class NewsletterManager { private $mailer; - /** - * @required - */ + #[Required] public function setMailer(MailerInterface $mailer): void { $this->mailer = $mailer; From d072fbd6daa7d250a71a1ba4843fd892a7948b8a Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 5 Jan 2023 13:25:18 +0100 Subject: [PATCH 150/774] [DependencyInjection] Autowire arguments using the #[TaggedLocator] attribute --- .../service_subscribers_locators.rst | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index db90d3e275c..0989941a1cc 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -305,6 +305,26 @@ As shown in the previous sections, the constructor of the ``CommandBus`` class must type-hint its argument with ``ContainerInterface``. Then, you can get any of the service locator services via their ID (e.g. ``$this->locator->get('App\FooCommand')``). +The same behavior can be achieved using the ``#[TaggedLocator]`` attribute. This +attribute must be directly used on a ``ServiceLocator`` argument:: + + // src/HandlerCollection.php + namespace App; + + use Symfony\Component\DependencyInjection\Attribute\TaggedLocator; + use Symfony\Component\DependencyInjection\ServiceLocator; + + class HandlerCollection + { + public function __construct(#[TaggedLocator('app.handler')] ServiceLocator $locator) + { + } + } + +.. versionadded:: 5.3 + + The ``#[TaggedLocator]`` attribute was introduced in Symfony 5.3 and requires PHP 8. + Reusing a Service Locator in Multiple Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -459,7 +479,7 @@ will share identical locators among all the services referencing them:: // ... 'logger' => new Reference('logger'), ]; - + $myService = $container->findDefinition(MyService::class); $myService->addArgument(ServiceLocatorTagPass::register($container, $locateableServices)); From d9b62bb7a9f9e4239656d75f13071d01a1bc8e5f Mon Sep 17 00:00:00 2001 From: cedric lombardot Date: Thu, 5 Jan 2023 12:00:55 +0100 Subject: [PATCH 151/774] We need to use AsEntityListener instead of AsDoctrineListener --- doctrine/events.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doctrine/events.rst b/doctrine/events.rst index 59fe440c2b6..77d25db35ac 100644 --- a/doctrine/events.rst +++ b/doctrine/events.rst @@ -255,7 +255,7 @@ First, define a PHP class that handles the ``postUpdate`` Doctrine event:: } } -Then, add the ``#[AsDoctrineListener]`` attribute to the class to enable it as +Then, add the ``#[AsEntityListener]`` attribute to the class to enable it as a Doctrine entity listener in your application: .. code-block:: php @@ -265,9 +265,10 @@ a Doctrine entity listener in your application: // ... use App\Entity\User; - use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; + use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener; + use Doctrine\ORM\Events; - #[AsDoctrineListener(event: 'postUpdate', method: 'postUpdate', entity: User::class)] + #[AsEntityListener(event: Events::postUpdate, method: 'postUpdate', entity: User::class)] class UserChangedNotifier { // ... From ac95c9033be9e10768cd872fb26cbc9d4f2ad6cd Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 5 Jan 2023 15:44:24 +0100 Subject: [PATCH 152/774] Change the order of code blocks in the configuration block --- configuration/micro_kernel_trait.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index 97781fa383c..a4cd11dd80c 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -24,7 +24,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: .. configuration-block:: - .. code-block:: php + .. code-block:: php-attributes // index.php use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; @@ -32,7 +32,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Kernel as BaseKernel; - use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + use Symfony\Component\Routing\Annotation\Route; require __DIR__.'/vendor/autoload.php'; @@ -55,11 +55,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: ]); } - protected function configureRoutes(RoutingConfigurator $routes): void - { - $routes->add('random_number', '/random/{limit}')->controller([$this, 'randomNumber']); - } - + #[Route('/random/{limit}', name='random_number')] public function randomNumber(int $limit): JsonResponse { return new JsonResponse([ @@ -74,7 +70,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: $response->send(); $kernel->terminate($request, $response); - .. code-block:: php-attributes + .. code-block:: php // index.php use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; @@ -82,7 +78,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Kernel as BaseKernel; - use Symfony\Component\Routing\Annotation\Route; + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; require __DIR__.'/vendor/autoload.php'; @@ -105,7 +101,11 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: ]); } - #[Route('/random/{limit}', name='random_number')] + protected function configureRoutes(RoutingConfigurator $routes): void + { + $routes->add('random_number', '/random/{limit}')->controller([$this, 'randomNumber']); + } + public function randomNumber(int $limit): JsonResponse { return new JsonResponse([ From 63da2464ffd0018c38695cfc8b317c6dfa13c0ce Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 5 Jan 2023 16:41:21 +0100 Subject: [PATCH 153/774] Merged the new example into the existing configuration block --- service_container/tags.rst | 59 ++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index c834c0e6b17..bda979d6a6b 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -511,11 +511,37 @@ Symfony provides a shortcut to inject all services tagged with a specific tag, which is a common need in some applications, so you don't have to write a compiler pass just for that. -In the following example, all services tagged with ``app.handler`` are passed as -first constructor argument to the ``App\HandlerCollection`` service: +Consider the following ``HandlerCollection`` class where you want to inject +all services tagged with ``app.handler`` into its constructor argument:: + + // src/HandlerCollection.php + namespace App; + + class HandlerCollection + { + public function __construct(iterable $handlers) + { + } + } .. configuration-block:: + .. code-block:: php-attributes + + // src/HandlerCollection.php + namespace App; + + use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; + + class HandlerCollection + { + public function __construct( + // the attribute must be applied directly to the argument to autowire + #[TaggedIterator('app.handler')] iterable $handlers + ) { + } + } + .. code-block:: yaml # config/services.yaml @@ -578,35 +604,6 @@ first constructor argument to the ``App\HandlerCollection`` service: ; }; -After compilation the ``HandlerCollection`` service is able to iterate over your -application handlers:: - - // src/HandlerCollection.php - namespace App; - - class HandlerCollection - { - public function __construct(iterable $handlers) - { - } - } - -Injecting tagged services can be also be done through autowiring thanks to the -``#[TaggedIterator]`` attribute. This attribute must be directly used on the -argument to autowire:: - - // src/HandlerCollection.php - namespace App; - - use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; - - class HandlerCollection - { - public function __construct(#[TaggedIterator('app.handler')] iterable $handlers) - { - } - } - .. versionadded:: 5.3 The ``#[TaggedIterator]`` attribute was introduced in Symfony 5.3 and requires PHP 8. From a5b8e76ad5624ed7724867ddd09b0766dab9c1eb Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 5 Jan 2023 16:59:46 +0100 Subject: [PATCH 154/774] Some tweaks --- .../service_subscribers_locators.rst | 53 +++++++++++++------ service_container/tags.rst | 3 ++ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index 0989941a1cc..a7d9971b8db 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -247,10 +247,45 @@ Defining a Service Locator -------------------------- To manually define a service locator and inject it to another service, create an -argument of type ``service_locator``: +argument of type ``service_locator``. + +Consider the following ``CommandBus`` class where you want to inject +some services into it via a service locator:: + + // src/HandlerCollection.php + namespace App; + + use Symfony\Component\DependencyInjection\ServiceLocator; + + class CommandBus + { + public function __construct(ServiceLocator $locator) + { + } + } + +Symfony allows you to inject the service locator using YAML/XML/PHP configuration +or directly via PHP attributes: .. configuration-block:: + .. conde-block:: php-attributes + + // src/CommandBus.php + namespace App; + + use Symfony\Component\DependencyInjection\Attribute\TaggedLocator; + use Symfony\Component\DependencyInjection\ServiceLocator; + + class CommandBus + { + public function __construct( + // creates a service locator with all the services tagged with 'app.handler' + #[TaggedLocator('app.handler')] ServiceLocator $locator + ) { + } + } + .. code-block:: yaml # config/services.yaml @@ -305,22 +340,6 @@ As shown in the previous sections, the constructor of the ``CommandBus`` class must type-hint its argument with ``ContainerInterface``. Then, you can get any of the service locator services via their ID (e.g. ``$this->locator->get('App\FooCommand')``). -The same behavior can be achieved using the ``#[TaggedLocator]`` attribute. This -attribute must be directly used on a ``ServiceLocator`` argument:: - - // src/HandlerCollection.php - namespace App; - - use Symfony\Component\DependencyInjection\Attribute\TaggedLocator; - use Symfony\Component\DependencyInjection\ServiceLocator; - - class HandlerCollection - { - public function __construct(#[TaggedLocator('app.handler')] ServiceLocator $locator) - { - } - } - .. versionadded:: 5.3 The ``#[TaggedLocator]`` attribute was introduced in Symfony 5.3 and requires PHP 8. diff --git a/service_container/tags.rst b/service_container/tags.rst index bda979d6a6b..8e990dadee0 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -524,6 +524,9 @@ all services tagged with ``app.handler`` into its constructor argument:: } } +Symfony allows you to inject the services using YAML/XML/PHP configuration or +directly via PHP attributes: + .. configuration-block:: .. code-block:: php-attributes From 9a2b2612dd33ec2a15646c5ccce5973b01c94f04 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 5 Jan 2023 17:01:13 +0100 Subject: [PATCH 155/774] Remove some unneeded versionadded directives --- service_container/service_subscribers_locators.rst | 4 ---- service_container/tags.rst | 4 ---- 2 files changed, 8 deletions(-) diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index 3a5c24e7b0a..a1947135635 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -339,10 +339,6 @@ As shown in the previous sections, the constructor of the ``CommandBus`` class must type-hint its argument with ``ContainerInterface``. Then, you can get any of the service locator services via their ID (e.g. ``$this->locator->get('App\FooCommand')``). -.. versionadded:: 5.3 - - The ``#[TaggedLocator]`` attribute was introduced in Symfony 5.3 and requires PHP 8. - Reusing a Service Locator in Multiple Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/service_container/tags.rst b/service_container/tags.rst index 8e990dadee0..599e83d42ff 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -607,10 +607,6 @@ directly via PHP attributes: ; }; -.. versionadded:: 5.3 - - The ``#[TaggedIterator]`` attribute was introduced in Symfony 5.3 and requires PHP 8. - .. seealso:: See also :doc:`tagged locator services ` From 48621f3d0d21d50c510464c1d83ac44c1a58b393 Mon Sep 17 00:00:00 2001 From: Agustin Gomes Date: Thu, 5 Jan 2023 18:59:17 +0100 Subject: [PATCH 156/774] Fix build by correcting a typo Signed-off-by: Agustin Gomes --- service_container/service_subscribers_locators.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index a7d9971b8db..aca2c8de45c 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -269,7 +269,7 @@ or directly via PHP attributes: .. configuration-block:: - .. conde-block:: php-attributes + .. code-block:: php-attributes // src/CommandBus.php namespace App; From a9cbf8bba0d9135a4033ea3efab5e7d13f82ac13 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 5 Jan 2023 23:10:23 +0100 Subject: [PATCH 157/774] Standardize the name of the container configurator variable --- bundles/best_practices.rst | 4 +- bundles/prepend_extension.rst | 6 +-- cache.rst | 4 +- components/dependency_injection.rst | 6 +-- .../_imports-parameters-note.rst.inc | 4 +- .../http_foundation/session_configuration.rst | 4 +- components/serializer.rst | 4 +- components/uid.rst | 2 +- components/var_dumper.rst | 4 +- configuration.rst | 52 +++++++++---------- configuration/micro_kernel_trait.rst | 14 ++--- configuration/multiple_kernels.rst | 12 ++--- controller/argument_value_resolver.rst | 4 +- controller/upload_file.rst | 4 +- doctrine/events.rst | 12 ++--- event_dispatcher.rst | 4 +- frontend/custom_version_strategy.rst | 4 +- messenger/multiple_buses.rst | 4 +- profiler/data_collector.rst | 4 +- reference/dic_tags.rst | 8 +-- routing/custom_route_loader.rst | 4 +- security.rst | 4 +- service_container.rst | 22 ++++---- service_container/alias_private.rst | 20 +++---- service_container/autowiring.rst | 10 ++-- service_container/calls.rst | 2 +- service_container/configurators.rst | 8 +-- service_container/expression_language.rst | 6 +-- service_container/factories.rst | 16 +++--- service_container/import.rst | 8 +-- service_container/injection_types.rst | 12 ++--- service_container/lazy_services.rst | 8 +-- service_container/optional_dependencies.rst | 8 +-- service_container/parent_services.rst | 8 +-- service_container/service_decoration.rst | 32 ++++++------ .../service_subscribers_locators.rst | 24 ++++----- service_container/shared.rst | 4 +- service_container/synthetic_services.rst | 4 +- service_container/tags.rst | 40 +++++++------- session.rst | 4 +- session/database.rst | 16 +++--- testing.rst | 4 +- 42 files changed, 212 insertions(+), 212 deletions(-) diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst index 4ef81080637..f1c8e4ad555 100644 --- a/bundles/best_practices.rst +++ b/bundles/best_practices.rst @@ -442,8 +442,8 @@ The end user can provide values in any configuration file: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->parameters() + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->parameters() ->set('acme_blog.author.email', 'fabien@example.com') ; }; diff --git a/bundles/prepend_extension.rst b/bundles/prepend_extension.rst index fe551f31083..9478f045f46 100644 --- a/bundles/prepend_extension.rst +++ b/bundles/prepend_extension.rst @@ -145,13 +145,13 @@ registered and the ``entity_manager_name`` setting for ``acme_hello`` is set to // config/packages/acme_something.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->extension('acme_something', [ + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->extension('acme_something', [ // ... 'use_acme_goodbye' => false, 'entity_manager_name' => 'non_default', ]); - $container->extension('acme_other', [ + $containerConfigurator->extension('acme_other', [ // ... 'use_acme_goodbye' => false, ]); diff --git a/cache.rst b/cache.rst index aff21f9c030..1676fc0773c 100644 --- a/cache.rst +++ b/cache.rst @@ -387,8 +387,8 @@ with either :class:`Symfony\\Contracts\\Cache\\CacheInterface` or // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $configurator->services() + return function(ContainerConfigurator $containerConfigurator) { + $containerConfigurator->services() // ... ->set('app.cache.adapter.redis') diff --git a/components/dependency_injection.rst b/components/dependency_injection.rst index c2239a9225b..470bcc7f2fc 100644 --- a/components/dependency_injection.rst +++ b/components/dependency_injection.rst @@ -291,13 +291,13 @@ config files: namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->parameters() + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->parameters() // ... ->set('mailer.transport', 'sendmail') ; - $services = $container->services(); + $services = $containerConfigurator->services(); $services->set('mailer', 'Mailer') ->args(['%mailer.transport%']) ; diff --git a/components/dependency_injection/_imports-parameters-note.rst.inc b/components/dependency_injection/_imports-parameters-note.rst.inc index 50c6b736353..1df99833e86 100644 --- a/components/dependency_injection/_imports-parameters-note.rst.inc +++ b/components/dependency_injection/_imports-parameters-note.rst.inc @@ -31,6 +31,6 @@ // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->import('%kernel.project_dir%/somefile.yaml'); + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->import('%kernel.project_dir%/somefile.yaml'); }; diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst index 36ca212b006..f8efaf0fd18 100644 --- a/components/http_foundation/session_configuration.rst +++ b/components/http_foundation/session_configuration.rst @@ -200,8 +200,8 @@ configuration: // config/packages/framework.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->extension('framework', [ + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->extension('framework', [ 'session' => [ 'gc_probability' => null, ], diff --git a/components/serializer.rst b/components/serializer.rst index cfe3930f028..adeb1328c2b 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -998,8 +998,8 @@ faster alternative to the use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; - return static function (ContainerConfigurator $container) { - $container->services() + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->services() // ... ->set('get_set_method_normalizer', GetSetMethodNormalizer::class) ->tag('serializer.normalizer') diff --git a/components/uid.rst b/components/uid.rst index c2a0c79315b..32cf9211e79 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -452,7 +452,7 @@ configuration in your application before using these commands: use Symfony\Component\Uid\Command\InspectUlidCommand; use Symfony\Component\Uid\Command\InspectUuidCommand; - return static function (ContainerConfigurator $configurator): void { + return static function (ContainerConfigurator $containerConfigurator): void { // ... $services diff --git a/components/var_dumper.rst b/components/var_dumper.rst index af68586de50..480ec326967 100644 --- a/components/var_dumper.rst +++ b/components/var_dumper.rst @@ -147,8 +147,8 @@ the :ref:`dump_destination option ` of the // config/packages/debug.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->extension('debug', [ + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->extension('debug', [ 'dump_destination' => 'tcp://%env(VAR_DUMPER_SERVER)%', ]); }; diff --git a/configuration.rst b/configuration.rst index 4265c1419b2..d88b72ad3af 100644 --- a/configuration.rst +++ b/configuration.rst @@ -78,18 +78,18 @@ shown in these three formats. { // ... - private function configureContainer(ContainerConfigurator $container): void + private function configureContainer(ContainerConfigurator $containerConfigurator): void { $configDir = $this->getConfigDir(); - $container->import($configDir.'/{packages}/*.{yaml,php}'); - $container->import($configDir.'/{packages}/'.$this->environment.'/*.{yaml,php}'); + $containerConfigurator->import($configDir.'/{packages}/*.{yaml,php}'); + $containerConfigurator->import($configDir.'/{packages}/'.$this->environment.'/*.{yaml,php}'); if (is_file($configDir.'/services.yaml')) { - $container->import($configDir.'/services.yaml'); - $container->import($configDir.'/{services}_'.$this->environment.'.yaml'); + $containerConfigurator->import($configDir.'/services.yaml'); + $containerConfigurator->import($configDir.'/{services}_'.$this->environment.'.yaml'); } else { - $container->import($configDir.'/{services}.php'); + $containerConfigurator->import($configDir.'/{services}.php'); } } } @@ -163,17 +163,17 @@ configuration files, even if they use a different format: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->import('legacy_config.php'); + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->import('legacy_config.php'); // glob expressions are also supported to load multiple files - $container->import('/etc/myapp/*.yaml'); + $containerConfigurator->import('/etc/myapp/*.yaml'); // the third optional argument of import() is 'ignore_errors' // 'ignore_errors' set to 'not_found' silently discards errors if the loaded file doesn't exist - $container->import('my_config_file.yaml', null, 'not_found'); + $containerConfigurator->import('my_config_file.yaml', null, 'not_found'); // 'ignore_errors' set to true silently discards all errors (including invalid code and not found) - $container->import('my_config_file.yaml', null, true); + $containerConfigurator->import('my_config_file.yaml', null, true); }; // ... @@ -262,8 +262,8 @@ reusable configuration value. By convention, parameters are defined under the use App\Entity\BlogPost; - return static function (ContainerConfigurator $container) { - $container->parameters() + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->parameters() // the parameter name is an arbitrary string (the 'app.' prefix is recommended // to better differentiate your parameters from Symfony parameters). ->set('app.admin_email', 'something@example.com') @@ -334,8 +334,8 @@ configuration file using a special syntax: wrap the parameter name in two ``%`` // config/packages/some_package.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->extension('some_package', [ + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->extension('some_package', [ // any string surrounded by two % is replaced by that parameter value 'email_address' => '%app.admin_email%', @@ -371,8 +371,8 @@ configuration file using a special syntax: wrap the parameter name in two ``%`` // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->parameters() + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->parameters() ->set('url_pattern', 'http://symfony.com/?foo=%%s&bar=%%d'); }; @@ -508,7 +508,7 @@ files directly in the ``config/packages/`` directory. use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Config\WebpackEncoreConfig; - return static function (WebpackEncoreConfig $webpackEncore, ContainerConfigurator $container) { + return static function (WebpackEncoreConfig $webpackEncore, ContainerConfigurator $containerConfigurator) { $webpackEncore ->outputPath('%kernel.project_dir%/public/build') ->strictMode(true) @@ -516,12 +516,12 @@ files directly in the ``config/packages/`` directory. ; // cache is enabled only in the "prod" environment - if ('prod' === $container->env()) { + if ('prod' === $containerConfigurator->env()) { $webpackEncore->cache(true); } // disable strict mode only in the "test" environment - if ('test' === $container->env()) { + if ('test' === $containerConfigurator->env()) { $webpackEncore->strictMode(false); } }; @@ -642,8 +642,8 @@ This example shows how you could configure the database connection using an env // config/packages/doctrine.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return static function (ContainerConfigurator $container) { - $container->extension('doctrine', [ + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->extension('doctrine', [ 'dbal' => [ // by convention the env var names are always uppercase 'url' => env('DATABASE_URL')->resolve(), @@ -991,8 +991,8 @@ doesn't work for parameters: use App\Service\MessageGenerator; - return static function (ContainerConfigurator $container) { - $container->parameters() + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->parameters() ->set('app.contents_dir', '...'); $container->services() @@ -1048,8 +1048,8 @@ whenever a service/controller defines a ``$projectDir`` argument, use this: use App\Controller\LuckyController; - return static function (ContainerConfigurator $container) { - $container->services() + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->services() ->defaults() // pass this value to any $projectDir argument for any service // that's created in this file (including controller arguments) diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index e08787d136d..ce4b0ac46c2 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -43,10 +43,10 @@ Next, create an ``index.php`` file that defines the kernel class and runs it:: ]; } - protected function configureContainer(ContainerConfigurator $c): void + protected function configureContainer(ContainerConfigurator $containerConfigurator): void { // PHP equivalent of config/packages/framework.yaml - $c->extension('framework', [ + $containerConfigurator->extension('framework', [ 'secret' => 'S0ME_SECRET' ]); } @@ -88,7 +88,7 @@ that define your bundles, your services and your routes: **registerBundles()** This is the same ``registerBundles()`` that you see in a normal kernel. -**configureContainer(ContainerConfigurator $c)** +**configureContainer(ContainerConfigurator $containerConfigurator)** This method builds and configures the container. In practice, you will use ``extension()`` to configure different bundles (this is the equivalent of what you see in a normal ``config/packages/*`` file). You can also register @@ -191,12 +191,12 @@ hold the kernel. Now it looks like this:: return $bundles; } - protected function configureContainer(ContainerConfigurator $c): void + protected function configureContainer(ContainerConfigurator $containerConfigurator): void { - $c->import(__DIR__.'/../config/framework.yaml'); + $containerConfigurator->import(__DIR__.'/../config/framework.yaml'); // register all classes in /src/ as service - $c->services() + $containerConfigurator->services() ->load('App\\', __DIR__.'/*') ->autowire() ->autoconfigure() @@ -204,7 +204,7 @@ hold the kernel. Now it looks like this:: // configure WebProfilerBundle only if the bundle is enabled if (isset($this->bundles['WebProfilerBundle'])) { - $c->extension('web_profiler', [ + $containerConfigurator->extension('web_profiler', [ 'toolbar' => true, 'intercept_redirects' => false, ]); diff --git a/configuration/multiple_kernels.rst b/configuration/multiple_kernels.rst index bec83cb530c..f840b2875f5 100644 --- a/configuration/multiple_kernels.rst +++ b/configuration/multiple_kernels.rst @@ -106,16 +106,16 @@ files so they don't collide with the files from ``src/Kernel.php``:: return $this->getProjectDir().'/var/log/api'; } - protected function configureContainer(ContainerConfigurator $container): void + protected function configureContainer(ContainerConfigurator $containerConfigurator): void { - $container->import('../config/api/{packages}/*.yaml'); - $container->import('../config/api/{packages}/'.$this->environment.'/*.yaml'); + $containerConfigurator->import('../config/api/{packages}/*.yaml'); + $containerConfigurator->import('../config/api/{packages}/'.$this->environment.'/*.yaml'); if (is_file(\dirname(__DIR__).'/config/api/services.yaml')) { - $container->import('../config/api/services.yaml'); - $container->import('../config/api/{services}_'.$this->environment.'.yaml'); + $containerConfigurator->import('../config/api/services.yaml'); + $containerConfigurator->import('../config/api/{services}_'.$this->environment.'.yaml'); } else { - $container->import('../config/api/{services}.php'); + $containerConfigurator->import('../config/api/{services}.php'); } } diff --git a/controller/argument_value_resolver.rst b/controller/argument_value_resolver.rst index 2cea87964ab..0670357bb0f 100644 --- a/controller/argument_value_resolver.rst +++ b/controller/argument_value_resolver.rst @@ -233,8 +233,8 @@ and adding a priority. use App\ArgumentResolver\UserValueResolver; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(UserValueResolver::class) ->tag('controller.argument_value_resolver', ['priority' => 50]) diff --git a/controller/upload_file.rst b/controller/upload_file.rst index 8f64fb10f80..46cf3230566 100644 --- a/controller/upload_file.rst +++ b/controller/upload_file.rst @@ -321,8 +321,8 @@ Then, define a service for this class: use App\Service\FileUploader; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(FileUploader::class) ->arg('$targetDirectory', '%brochures_directory%') diff --git a/doctrine/events.rst b/doctrine/events.rst index 89cfb269447..bb9517aff19 100644 --- a/doctrine/events.rst +++ b/doctrine/events.rst @@ -227,8 +227,8 @@ with the ``doctrine.event_listener`` tag: use App\EventListener\SearchIndexer; - return static function (ContainerConfigurator $configurator) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // listeners are applied by default to all Doctrine connections $services->set(SearchIndexer::class) @@ -360,8 +360,8 @@ with the ``doctrine.orm.entity_listener`` tag: use App\Entity\User; use App\EventListener\UserChangedNotifier; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(UserChangedNotifier::class) ->tag('doctrine.orm.entity_listener', [ @@ -501,8 +501,8 @@ Doctrine connection to use) you must do that in the manual service configuration use App\EventListener\DatabaseActivitySubscriber; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(DatabaseActivitySubscriber::class) ->tag('doctrine.event_subscriber'[ diff --git a/event_dispatcher.rst b/event_dispatcher.rst index 3c6020c145d..f10a93bc90f 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -104,8 +104,8 @@ using a special "tag": use App\EventListener\ExceptionListener; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(ExceptionListener::class) ->tag('kernel.event_listener', ['event' => 'kernel.exception']) diff --git a/frontend/custom_version_strategy.rst b/frontend/custom_version_strategy.rst index 336acfbd295..8a5d77cae5e 100644 --- a/frontend/custom_version_strategy.rst +++ b/frontend/custom_version_strategy.rst @@ -144,8 +144,8 @@ After creating the strategy PHP class, register it as a Symfony service. use App\Asset\VersionStrategy\GulpBusterVersionStrategy; use Symfony\Component\DependencyInjection\Definition; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(GulpBusterVersionStrategy::class) ->args( diff --git a/messenger/multiple_buses.rst b/messenger/multiple_buses.rst index 724c58d5e3f..dba1ebf5930 100644 --- a/messenger/multiple_buses.rst +++ b/messenger/multiple_buses.rst @@ -207,8 +207,8 @@ you can determine the message bus based on an implemented interface: use App\MessageHandler\CommandHandlerInterface; use App\MessageHandler\QueryHandlerInterface; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // ... diff --git a/profiler/data_collector.rst b/profiler/data_collector.rst index ef377c47974..44545614da2 100644 --- a/profiler/data_collector.rst +++ b/profiler/data_collector.rst @@ -290,8 +290,8 @@ you'll need to configure the data collector explicitly: use App\DataCollector\RequestCollector; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(RequestCollector::class) ->tag('data_collector', [ diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index e163756835e..14227fe6b9e 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -122,8 +122,8 @@ services: use App\Lock\PostgresqlLock; use App\Lock\SqliteLock; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set('app.mysql_lock', MysqlLock::class); $services->set('app.postgresql_lock', PostgresqlLock::class); @@ -184,8 +184,8 @@ the generic ``app.lock`` service can be defined as follows: use App\Lock\PostgresqlLock; use App\Lock\SqliteLock; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set('app.mysql_lock', MysqlLock::class); $services->set('app.postgresql_lock', PostgresqlLock::class); diff --git a/routing/custom_route_loader.rst b/routing/custom_route_loader.rst index c9b2853088a..b8b9f4c1d76 100644 --- a/routing/custom_route_loader.rst +++ b/routing/custom_route_loader.rst @@ -331,8 +331,8 @@ Now define a service for the ``ExtraLoader``: use App\Routing\ExtraLoader; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(ExtraLoader::class) ->tag('routing.loader') diff --git a/security.rst b/security.rst index 51bd9f2a8d9..13743996749 100644 --- a/security.rst +++ b/security.rst @@ -2601,8 +2601,8 @@ for these events. use App\EventListener\LogoutSubscriber; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(LogoutSubscriber::class) ->tag('kernel.event_subscriber', [ diff --git a/service_container.rst b/service_container.rst index f7b29b86ee3..c9628c19628 100644 --- a/service_container.rst +++ b/service_container.rst @@ -212,9 +212,9 @@ each time you ask for it. // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // default configuration for services in *this* file - $services = $configurator->services() + $services = $containerConfigurator->services() ->defaults() ->autowire() // Automatically injects dependencies in your services. ->autoconfigure() // Automatically registers your services as commands, event subscribers, etc. @@ -505,7 +505,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume use App\Service\SiteUpdateManager; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... // same as before @@ -580,8 +580,8 @@ parameter and in PHP config use the ``service()`` function: use App\Service\MessageGenerator; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(MessageGenerator::class) // In versions earlier to Symfony 5.1 the service() function was called ref() @@ -687,7 +687,7 @@ But, you can control this and pass in a different logger: use App\Service\MessageGenerator; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... same code as before // explicitly configure the service @@ -788,8 +788,8 @@ You can also use the ``bind`` keyword to bind specific arguments by name or type use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services() + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services() ->defaults() // pass this value to any $adminEmail argument for any service // that's defined in this file (including controller arguments) @@ -923,7 +923,7 @@ setting: use App\Service\PublicService; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... same as code before // explicitly configure the service @@ -980,7 +980,7 @@ key. For example, the default Symfony configuration contains this: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... // makes classes in src/ available to be used as services @@ -1162,7 +1162,7 @@ admin email. In this case, each needs to have a unique service id: use App\Service\MessageGenerator; use App\Service\SiteUpdateManager; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... // site_update_manager.superadmin is the service's id diff --git a/service_container/alias_private.rst b/service_container/alias_private.rst index da8eaf86328..7f39478a247 100644 --- a/service_container/alias_private.rst +++ b/service_container/alias_private.rst @@ -58,8 +58,8 @@ You can also control the ``public`` option on a service-by-service basis: use App\Service\Foo; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(Foo::class) ->public(); @@ -130,8 +130,8 @@ services. use App\Mail\PhpMailer; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(PhpMailer::class) ->private(); @@ -278,8 +278,8 @@ The following example shows how to inject an anonymous service into another serv use App\AnonymousBar; use App\Foo; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(Foo::class) // In versions earlier to Symfony 5.1 the inline_service() function was called inline() @@ -330,8 +330,8 @@ Using an anonymous service as a factory looks like this: use App\AnonymousBar; use App\Foo; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(Foo::class) ->factory([inline_service(AnonymousBar::class), 'constructFoo']); @@ -376,8 +376,8 @@ or you decided not to maintain it anymore), you can deprecate its definition: use App\Service\OldService; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(OldService::class) ->deprecate( diff --git a/service_container/autowiring.rst b/service_container/autowiring.rst index bb89ee3451a..d74b445a054 100644 --- a/service_container/autowiring.rst +++ b/service_container/autowiring.rst @@ -107,8 +107,8 @@ both services: .. code-block:: php // config/services.php - return function(ContainerConfigurator $configurator) { - $services = $configurator->services() + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services() ->defaults() ->autowire() ->autoconfigure() @@ -246,7 +246,7 @@ adding a service alias: use App\Util\Rot13Transformer; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... // the id is not a class, so it won't be used for autowiring @@ -353,7 +353,7 @@ To fix that, add an :ref:`alias `: use App\Util\Rot13Transformer; use App\Util\TransformerInterface; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... $services->set(Rot13Transformer::class); @@ -497,7 +497,7 @@ the injection:: use App\Util\TransformerInterface; use App\Util\UppercaseTransformer; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... $services->set(Rot13Transformer::class)->autowire(); diff --git a/service_container/calls.rst b/service_container/calls.rst index 9f7ac768976..5e6036421df 100644 --- a/service_container/calls.rst +++ b/service_container/calls.rst @@ -69,7 +69,7 @@ To configure the container to call the ``setLogger`` method, use the ``calls`` k use App\Service\MessageGenerator; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... $services->set(MessageGenerator::class) diff --git a/service_container/configurators.rst b/service_container/configurators.rst index 7cf3f4e09c5..4fab69c5551 100644 --- a/service_container/configurators.rst +++ b/service_container/configurators.rst @@ -172,8 +172,8 @@ all the classes are already loaded as services. All you need to do is specify th use App\Mail\GreetingCardManager; use App\Mail\NewsletterManager; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // Registers all 4 classes as services, including App\Mail\EmailConfigurator $services->load('App\\', '../src/*'); @@ -242,8 +242,8 @@ Services can be configured via invokable configurators (replacing the use App\Mail\GreetingCardManager; use App\Mail\NewsletterManager; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // Registers all 4 classes as services, including App\Mail\EmailConfigurator $services->load('App\\', '../src/*'); diff --git a/service_container/expression_language.rst b/service_container/expression_language.rst index 972d7286c88..f755057e240 100644 --- a/service_container/expression_language.rst +++ b/service_container/expression_language.rst @@ -61,7 +61,7 @@ to another service: ``App\Mailer``. One way to do this is with an expression: use App\Mail\MailerConfiguration; use App\Mailer; - return function(ContainerConfigurator $configurator) { + return function(ContainerConfigurator $containerConfigurator) { // ... $services->set(MailerConfiguration::class); @@ -116,8 +116,8 @@ via a ``container`` variable. Here's another example: use App\Mailer; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(Mailer::class) ->args([expr("container.hasParameter('some_param') ? parameter('some_param') : 'default_value'")]); diff --git a/service_container/factories.rst b/service_container/factories.rst index d2fda053923..36a9a2e7db8 100644 --- a/service_container/factories.rst +++ b/service_container/factories.rst @@ -83,8 +83,8 @@ create its object: use App\Email\NewsletterManager; use App\Email\NewsletterManagerStaticFactory; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(NewsletterManager::class) // the first argument is the class and the second argument is the static method @@ -154,8 +154,8 @@ Configuration of the service container then looks like this: use App\Email\NewsletterManager; use App\Email\NewsletterManagerFactory; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // first, create a service for the factory $services->set(NewsletterManagerFactory::class); @@ -233,8 +233,8 @@ method name: use App\Email\NewsletterManager; use App\Email\NewsletterManagerFactory; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(NewsletterManager::class) ->factory(service(InvokableNewsletterManagerFactory::class)); @@ -293,8 +293,8 @@ previous examples takes the ``templating`` service as an argument: use App\Email\NewsletterManager; use App\Email\NewsletterManagerFactory; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(NewsletterManager::class) ->factory([service(NewsletterManagerFactory::class), 'createNewsletterManager']) diff --git a/service_container/import.rst b/service_container/import.rst index b37c8360388..433b03d9812 100644 --- a/service_container/import.rst +++ b/service_container/import.rst @@ -123,12 +123,12 @@ a relative or absolute path to the imported file: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $configurator->import('services/mailer.php'); + return function(ContainerConfigurator $containerConfigurator) { + $containerConfigurator->import('services/mailer.php'); // If you want to import a whole directory: - $configurator->import('services/'); + $containerConfigurator->import('services/'); - $services = $configurator->services() + $services = $containerConfigurator->services() ->defaults() ->autowire() ->autoconfigure() diff --git a/service_container/injection_types.rst b/service_container/injection_types.rst index fd47fcef56c..81d06810f9f 100644 --- a/service_container/injection_types.rst +++ b/service_container/injection_types.rst @@ -74,8 +74,8 @@ service container configuration: use App\Mail\NewsletterManager; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(NewsletterManager::class) // In versions earlier to Symfony 5.1 the service() function was called ref() @@ -277,8 +277,8 @@ that accepts the dependency:: use App\Mail\NewsletterManager; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(NewsletterManager::class) ->call('setMailer', [service('mailer')]); @@ -359,8 +359,8 @@ Another possibility is setting public fields of the class directly:: use App\Mail\NewsletterManager; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set('app.newsletter_manager', NewsletterManager::class) ->property('mailer', service('mailer')); diff --git a/service_container/lazy_services.rst b/service_container/lazy_services.rst index b259895b9f5..7b0bd0442c5 100644 --- a/service_container/lazy_services.rst +++ b/service_container/lazy_services.rst @@ -76,8 +76,8 @@ You can mark the service as ``lazy`` by manipulating its definition: use App\Twig\AppExtension; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(AppExtension::class)->lazy(); }; @@ -150,8 +150,8 @@ specific interfaces. use App\Twig\AppExtension; use Twig\Extension\ExtensionInterface; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(AppExtension::class) ->lazy() diff --git a/service_container/optional_dependencies.rst b/service_container/optional_dependencies.rst index e05e050ba9c..8317cd363df 100644 --- a/service_container/optional_dependencies.rst +++ b/service_container/optional_dependencies.rst @@ -38,8 +38,8 @@ if the service does not exist: use App\Newsletter\NewsletterManager; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(NewsletterManager::class) // In versions earlier to Symfony 5.1 the service() function was called ref() @@ -95,8 +95,8 @@ call if the service exists and remove the method call if it does not: use App\Newsletter\NewsletterManager; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(NewsletterManager::class) ->call('setLogger', [service('logger')->ignoreOnInvalid()]) diff --git a/service_container/parent_services.rst b/service_container/parent_services.rst index 7df74b37a43..3c1db4d9a73 100644 --- a/service_container/parent_services.rst +++ b/service_container/parent_services.rst @@ -122,8 +122,8 @@ avoid duplicated service definitions: use App\Repository\DoctrinePostRepository; use App\Repository\DoctrineUserRepository; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(BaseDoctrineRepository::class) ->abstract() @@ -232,8 +232,8 @@ the child class: use App\Repository\DoctrineUserRepository; // ... - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(BaseDoctrineRepository::class) // ... diff --git a/service_container/service_decoration.rst b/service_container/service_decoration.rst index 06f7a0df1ab..1b09c3b54f9 100644 --- a/service_container/service_decoration.rst +++ b/service_container/service_decoration.rst @@ -44,8 +44,8 @@ When overriding an existing definition, the original service is lost: use App\Mailer; use App\NewMailer; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(Mailer::class); @@ -101,8 +101,8 @@ but keeps a reference of the old one as ``.inner``: use App\DecoratingMailer; use App\Mailer; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(Mailer::class); @@ -164,8 +164,8 @@ automatically changed to ``'.inner'``): use App\DecoratingMailer; use App\Mailer; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(Mailer::class); @@ -236,8 +236,8 @@ automatically changed to ``'.inner'``): use App\DecoratingMailer; use App\Mailer; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(Mailer::class); @@ -298,8 +298,8 @@ the ``decoration_priority`` option. Its value is an integer that defaults to // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(\Foo::class); @@ -385,8 +385,8 @@ ordered services, each one decorating the next: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $container) { - $container->services() + return function(ContainerConfigurator $containerConfigurator) { + $containerConfigurator->services() ->stack('decorated_foo_stack', [ inline_service(\Baz::class)->args([service('.inner')]), inline_service(\Bar::class)->args([service('.inner')]), @@ -468,8 +468,8 @@ advanced example of composition: use App\Decorated; use App\Decorator; - return function(ContainerConfigurator $container) { - $container->services() + return function(ContainerConfigurator $containerConfigurator) { + $containerConfigurator->services() ->set('some_decorator', Decorator::class) ->stack('embedded_stack', [ @@ -586,8 +586,8 @@ Three different behaviors are available: use Symfony\Component\DependencyInjection\ContainerInterface; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(Foo::class); diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index a7d9971b8db..6ca1f976622 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -231,8 +231,8 @@ service type to a service. use App\CommandBus; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(CommandBus::class) ->tag('container.service_subscriber', ['key' => 'logger', 'id' => 'monolog.logger.event']); @@ -323,8 +323,8 @@ or directly via PHP attributes: use App\CommandBus; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(CommandBus::class) ->args([service_locator([ @@ -409,8 +409,8 @@ other services. To do so, create a new service definition using the use Symfony\Component\DependencyInjection\ServiceLocator; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set('app.command_handler_locator', ServiceLocator::class) // In versions earlier to Symfony 5.1 the service() function was called ref() @@ -471,8 +471,8 @@ Now you can inject the service locator in any other services: use App\CommandBus; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(CommandBus::class) ->args([service('app.command_handler_locator')]); @@ -562,8 +562,8 @@ of the ``key`` tag attribute (as defined in the ``index_by`` locator option): // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(App\Handler\One::class) ->tag('app.handler', ['key' => 'handler_one']) @@ -652,8 +652,8 @@ attribute to the locator service defining the name of this custom method: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $configurator->services() + return function(ContainerConfigurator $containerConfigurator) { + $containerConfigurator->services() ->set(App\HandlerCollection::class) ->args([tagged_locator('app.handler', 'key', 'myOwnMethodName')]) ; diff --git a/service_container/shared.rst b/service_container/shared.rst index d676f592125..0b87976dc39 100644 --- a/service_container/shared.rst +++ b/service_container/shared.rst @@ -36,8 +36,8 @@ in your service definition: use App\SomeNonSharedService; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(SomeNonSharedService::class) ->share(false); diff --git a/service_container/synthetic_services.rst b/service_container/synthetic_services.rst index 59869d5d7f3..0a83bebed9e 100644 --- a/service_container/synthetic_services.rst +++ b/service_container/synthetic_services.rst @@ -66,8 +66,8 @@ configuration: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // synthetic services don't specify a class $services->set('app.synthetic_service') diff --git a/service_container/tags.rst b/service_container/tags.rst index 8e990dadee0..2874fb103f2 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -41,8 +41,8 @@ example: use App\Twig\AppExtension; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(AppExtension::class) ->tag('twig.extension'); @@ -107,8 +107,8 @@ If you want to apply tags automatically for your own services, use the use App\Security\CustomInterface; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // this config only applies to the services created by this file $services @@ -217,8 +217,8 @@ Then, define the chain as a service: use App\Mail\TransportChain; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(TransportChain::class); }; @@ -271,8 +271,8 @@ For example, you may add the following transports as services: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(\MailerSmtpTransport::class) // the param() method was introduced in Symfony 5.2. @@ -438,8 +438,8 @@ To answer this, change the service declaration: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(\MailerSmtpTransport::class) // the param() method was introduced in Symfony 5.2. @@ -590,8 +590,8 @@ directly via PHP attributes: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(App\Handler\One::class) ->tag('app.handler') @@ -655,8 +655,8 @@ the number, the earlier the tagged service will be located in the collection: use App\Handler\One; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(One::class) ->tag('app.handler', ['priority' => 20]) @@ -715,8 +715,8 @@ you can define it in the configuration of the collecting service: use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; - return function (ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // ... @@ -787,8 +787,8 @@ indexed by the ``key`` attribute: use App\Handler\Two; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; - return function (ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(One::class) ->tag('app.handler', ['key' => 'handler_one']); @@ -885,8 +885,8 @@ array element. For example, to retrieve the ``handler_two`` handler:: use App\HandlerCollection; use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; - return function (ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // ... diff --git a/session.rst b/session.rst index c4c6714c226..90ef240013e 100644 --- a/session.rst +++ b/session.rst @@ -240,8 +240,8 @@ your ``Session`` object with the default ``AttributeBag`` by the ``NamespacedAtt use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag; use Symfony\Component\HttpFoundation\Session\Session; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set('session', Session::class) ->public() diff --git a/session/database.rst b/session/database.rst index de4d5213b81..f14ef7a0656 100644 --- a/session/database.rst +++ b/session/database.rst @@ -249,8 +249,8 @@ first register a new handler service with your database credentials: use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(PdoSessionHandler::class) ->args([ @@ -354,8 +354,8 @@ passed to the ``PdoSessionHandler`` service: use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(PdoSessionHandler::class) ->args([ @@ -524,8 +524,8 @@ the MongoDB connection as argument: use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(MongoDbSessionHandler::class) ->args([ @@ -633,8 +633,8 @@ configure these values with the second argument passed to the use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(MongoDbSessionHandler::class) ->args([ diff --git a/testing.rst b/testing.rst index 0e7d861894f..8f1b7692ca2 100644 --- a/testing.rst +++ b/testing.rst @@ -360,8 +360,8 @@ the ``test`` environment as follows: use App\Contracts\Repository\NewsRepositoryInterface; use App\Repository\NewsRepository; - return static function (ContainerConfigurator $container) { - $container->services() + return static function (ContainerConfigurator $containerConfigurator) { + $containerConfigurator->services() // redefine the alias as it should be while making it public ->alias(NewsRepositoryInterface::class, NewsRepository::class) ->public() From 23593c6ec4879eb9eac56fecacf9e20f01a3d008 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 6 Jan 2023 16:44:57 +0100 Subject: [PATCH 158/774] Minor rewordings --- configuration.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/configuration.rst b/configuration.rst index d88b72ad3af..a93080c9946 100644 --- a/configuration.rst +++ b/configuration.rst @@ -18,14 +18,13 @@ directory, which has this default structure: │ ├─ bundles.php │ ├─ routes.yaml │ └─ services.yaml - ├─ ... -The ``routes.yaml`` file defines the :doc:`routing configuration `; -the ``services.yaml`` file configures the services of the -:doc:`service container `; the ``bundles.php`` file enables/ -disables packages in your application. +* The ``routes.yaml`` file defines the :doc:`routing configuration `; +* the ``services.yaml`` file configures the services of the +:doc:`service container `; +* the ``bundles.php`` file enables/disables packages in your application. -You'll be working mostly in the ``config/packages/`` directory. This directory +The ``config/packages/`` directory stores the configuration of every package installed in your application. Packages (also called "bundles" in Symfony and "plugins/modules" in other projects) add ready-to-use features to your projects. @@ -33,7 +32,7 @@ projects) add ready-to-use features to your projects. When using :ref:`Symfony Flex `, which is enabled by default in Symfony applications, packages update the ``bundles.php`` file and create new files in ``config/packages/`` automatically during their installation. For -example, this is the default file created by the "API Platform" package: +example, this is the default file created by the "API Platform" bundle: .. code-block:: yaml @@ -42,9 +41,9 @@ example, this is the default file created by the "API Platform" package: mapping: paths: ['%kernel.project_dir%/src/Entity'] -Splitting the configuration into lots of small files is intimidating for some +Splitting the configuration into lots of small files might appear intimidating for some Symfony newcomers. However, you'll get used to them quickly and you rarely need -to change these files after package installation +to change these files after package installation. .. tip:: From b0e43407b7b21e55682ec2a762af2ae7b905aa8b Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 6 Jan 2023 16:50:55 +0100 Subject: [PATCH 159/774] More minor rewordings --- configuration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration.rst b/configuration.rst index d88b72ad3af..f1f66be2fdf 100644 --- a/configuration.rst +++ b/configuration.rst @@ -58,8 +58,8 @@ Configuration Formats ~~~~~~~~~~~~~~~~~~~~~ Unlike other frameworks, Symfony doesn't impose a specific format on you to -configure your applications. Symfony lets you choose between YAML, XML and PHP -and throughout the Symfony documentation, all configuration examples will be +configure your applications, but lets you choose between YAML, XML and PHP. +Throughout the Symfony documentation, all configuration examples will be shown in these three formats. .. versionadded:: 5.1 From f0c7716180c328f093ce0975d425676974f65339 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 6 Jan 2023 17:03:51 +0100 Subject: [PATCH 160/774] Emphasizing *NOT* working --- .../dependency_injection/_imports-parameters-note.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/dependency_injection/_imports-parameters-note.rst.inc b/components/dependency_injection/_imports-parameters-note.rst.inc index 1df99833e86..45a75652fda 100644 --- a/components/dependency_injection/_imports-parameters-note.rst.inc +++ b/components/dependency_injection/_imports-parameters-note.rst.inc @@ -2,7 +2,7 @@ Due to the way in which parameters are resolved, you cannot use them to build paths in imports dynamically. This means that something like - the following doesn't work: + **the following does not work:** .. configuration-block:: From 60dab0108936e6200c7a47948c281252afdb99c9 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Fri, 6 Jan 2023 22:57:57 +0100 Subject: [PATCH 161/774] Update constraint Type types --- reference/constraints/Type.rst | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/reference/constraints/Type.rst b/reference/constraints/Type.rst index be6149f53aa..ec8d400c570 100644 --- a/reference/constraints/Type.rst +++ b/reference/constraints/Type.rst @@ -206,22 +206,24 @@ This required option defines the type or collection of types allowed for the given value. Each type is either the FQCN (fully qualified class name) of some PHP class/interface or a valid PHP datatype (checked by PHP's ``is_()`` functions): -* :phpfunction:`array ` * :phpfunction:`bool ` -* :phpfunction:`callable ` -* :phpfunction:`float ` -* :phpfunction:`double ` +* :phpfunction:`boolean ` * :phpfunction:`int ` -* :phpfunction:`integer ` -* :phpfunction:`iterable ` -* :phpfunction:`long ` -* :phpfunction:`null ` +* :phpfunction:`integer ` +* :phpfunction:`long ` +* :phpfunction:`float ` +* :phpfunction:`double ` +* :phpfunction:`real ` * :phpfunction:`numeric ` +* :phpfunction:`string ` +* :phpfunction:`scalar ` +* :phpfunction:`array ` +* :phpfunction:`iterable ` +* :phpfunction:`countable ` +* :phpfunction:`callable ` * :phpfunction:`object ` -* :phpfunction:`real ` * :phpfunction:`resource ` -* :phpfunction:`scalar ` -* :phpfunction:`string ` +* :phpfunction:`null ` Also, you can use ``ctype_*()`` functions from corresponding `built-in PHP extension`_. Consider `a list of ctype functions`_: From 0055c991d092448d98ed3aa9f8f451cea857708f Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 31 Dec 2022 14:12:39 +0100 Subject: [PATCH 162/774] Sessions refactoring --- _build/redirection_map | 8 +- components/http_foundation.rst | 5 +- .../http_foundation/session_configuration.rst | 321 ---- .../http_foundation/session_php_bridge.rst | 48 - .../http_foundation/session_testing.rst | 58 - components/http_foundation/sessions.rst | 356 ---- controller.rst | 130 +- doctrine.rst | 1 - reference/configuration/framework.rst | 2 +- security/impersonating_user.rst | 6 +- session.rst | 1533 +++++++++++++++-- session/database.rst | 663 ------- session/locale_sticky_session.rst | 188 -- session/php_bridge.rst | 108 -- session/proxy_examples.rst | 145 -- translation.rst | 2 +- translation/locale.rst | 4 +- 17 files changed, 1474 insertions(+), 2104 deletions(-) delete mode 100644 components/http_foundation/session_configuration.rst delete mode 100644 components/http_foundation/session_php_bridge.rst delete mode 100644 components/http_foundation/session_testing.rst delete mode 100644 components/http_foundation/sessions.rst delete mode 100644 session/database.rst delete mode 100644 session/locale_sticky_session.rst delete mode 100644 session/php_bridge.rst delete mode 100644 session/proxy_examples.rst diff --git a/_build/redirection_map b/_build/redirection_map index c24c5087601..9175f68d5e3 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -248,7 +248,8 @@ /cookbook/session/index /session /cookbook/session/limit_metadata_writes /reference/configuration/framework /session/limit_metadata_writes /reference/configuration/framework -/cookbook/session/locale_sticky_session /session/locale_sticky_session +/cookbook/session/locale_sticky_session /session#locale-sticky-session +/cookbook/locale_sticky_session /session#locale-sticky-session /cookbook/session/php_bridge /session/php_bridge /cookbook/session/proxy_examples /session/proxy_examples /cookbook/session/sessions_directory /session/sessions_directory @@ -477,8 +478,9 @@ /components/translation/custom_message_formatter https://github.com/symfony/translation /components/notifier https://github.com/symfony/notifier /components/routing https://github.com/symfony/routing -/doctrine/pdo_session_storage /session/database -/doctrine/mongodb_session_storage /session/database +/session/database /session#session-database +/doctrine/pdo_session_storage /session#session-database-pdo +/doctrine/mongodb_session_storage /session#session-database-mongodb /components/dotenv https://github.com/symfony/dotenv /components/mercure /mercure /components/polyfill_apcu https://github.com/symfony/polyfill-apcu diff --git a/components/http_foundation.rst b/components/http_foundation.rst index 8780745738c..062a21e4e87 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -752,7 +752,7 @@ the response content will look like this: Session ------- -The session information is in its own document: :doc:`/components/http_foundation/sessions`. +The session information is in its own document: :doc:`/session`. Safe Content Preference ----------------------- @@ -829,10 +829,9 @@ Learn More :maxdepth: 1 :glob: - /components/http_foundation/* /controller /controller/* - /session/* + /session /http_cache/* .. _nginx: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/ diff --git a/components/http_foundation/session_configuration.rst b/components/http_foundation/session_configuration.rst deleted file mode 100644 index f8efaf0fd18..00000000000 --- a/components/http_foundation/session_configuration.rst +++ /dev/null @@ -1,321 +0,0 @@ -.. index:: - single: HTTP - single: HttpFoundation, Sessions - -Configuring Sessions and Save Handlers -====================================== - -This article deals with how to configure session management and fine tune it -to your specific needs. This documentation covers save handlers, which -store and retrieve session data, and configuring session behavior. - -Save Handlers -~~~~~~~~~~~~~ - -The PHP session workflow has 6 possible operations that may occur. The normal -session follows ``open``, ``read``, ``write`` and ``close``, with the possibility -of ``destroy`` and ``gc`` (garbage collection which will expire any old sessions: -``gc`` is called randomly according to PHP's configuration and if called, it is -invoked after the ``open`` operation). You can read more about this at -`php.net/session.customhandler`_ - -Native PHP Save Handlers ------------------------- - -So-called native handlers, are save handlers which are either compiled into -PHP or provided by PHP extensions, such as PHP-SQLite, PHP-Memcached and so on. - -All native save handlers are internal to PHP and as such, have no public facing API. -They must be configured by ``php.ini`` directives, usually ``session.save_path`` and -potentially other driver specific directives. Specific details can be found in -the docblock of the ``setOptions()`` method of each class. For instance, the one -provided by the Memcached extension can be found on :phpmethod:`php.net `. - -While native save handlers can be activated by directly using -``ini_set('session.save_handler', $name);``, Symfony provides a convenient way to -activate these in the same way as it does for custom handlers. - -Symfony provides drivers for the following native save handler as an example: - -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NativeFileSessionHandler` - -Example usage:: - - use Symfony\Component\HttpFoundation\Session\Session; - use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; - use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; - - $sessionStorage = new NativeSessionStorage([], new NativeFileSessionHandler()); - $session = new Session($sessionStorage); - -.. note:: - - With the exception of the ``files`` handler which is built into PHP and - always available, the availability of the other handlers depends on those - PHP extensions being active at runtime. - -.. note:: - - Native save handlers provide a quick solution to session storage, however, - in complex systems where you need more control, custom save handlers may - provide more freedom and flexibility. Symfony provides several implementations - which you may further customize as required. - -Custom Save Handlers --------------------- - -Custom handlers are those which completely replace PHP's built-in session save -handlers by providing six callback functions which PHP calls internally at -various points in the session workflow. - -The Symfony HttpFoundation component provides some by default and these can -serve as examples if you wish to write your own. - -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler` -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcachedSessionHandler` -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MigratingSessionHandler` -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\RedisSessionHandler` -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler` -* :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\NullSessionHandler` - -Example usage:: - - use Symfony\Component\HttpFoundation\Session\Session; - use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; - - $pdo = new \PDO(...); - $sessionStorage = new NativeSessionStorage([], new PdoSessionHandler($pdo)); - $session = new Session($sessionStorage); - -Migrating Between Save Handlers -------------------------------- - -If your application changes the way sessions are stored, use the -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MigratingSessionHandler` -to migrate between old and new save handlers without losing session data. - -This is the recommended migration workflow: - -#. Switch to the migrating handler, with your new handler as the write-only one. - The old handler behaves as usual and sessions get written to the new one:: - - $sessionStorage = new MigratingSessionHandler($oldSessionStorage, $newSessionStorage); - -#. After your session gc period, verify that the data in the new handler is correct. -#. Update the migrating handler to use the old handler as the write-only one, so - the sessions will now be read from the new handler. This step allows easier rollbacks:: - - $sessionStorage = new MigratingSessionHandler($newSessionStorage, $oldSessionStorage); - -#. After verifying that the sessions in your application are working, switch - from the migrating handler to the new handler. - -Configuring PHP Sessions -~~~~~~~~~~~~~~~~~~~~~~~~ - -The :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage` -can configure most of the ``php.ini`` configuration directives which are documented -at `php.net/session.configuration`_. - -To configure these settings, pass the keys (omitting the initial ``session.`` part -of the key) as a key-value array to the ``$options`` constructor argument. -Or set them via the -:method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage::setOptions` -method. - -For the sake of clarity, some key options are explained in this documentation. - -Session Cookie Lifetime -~~~~~~~~~~~~~~~~~~~~~~~ - -For security, session tokens are generally recommended to be sent as session cookies. -You can configure the lifetime of session cookies by specifying the lifetime -(in seconds) using the ``cookie_lifetime`` key in the constructor's ``$options`` -argument in :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage`. - -Setting a ``cookie_lifetime`` to ``0`` will cause the cookie to live only as -long as the browser remains open. Generally, ``cookie_lifetime`` would be set to -a relatively large number of days, weeks or months. It is not uncommon to set -cookies for a year or more depending on the application. - -Since session cookies are just a client-side token, they are less important in -controlling the fine details of your security settings which ultimately can only -be securely controlled from the server side. - -.. note:: - - The ``cookie_lifetime`` setting is the number of seconds the cookie should live - for, it is not a Unix timestamp. The resulting session cookie will be stamped - with an expiry time of ``time()`` + ``cookie_lifetime`` where the time is taken - from the server. - -Configuring Garbage Collection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When a session opens, PHP will call the ``gc`` handler randomly according to the -probability set by ``session.gc_probability`` / ``session.gc_divisor`` in ``php.ini``. -For example if these were set to ``5/100``, it would mean a probability of 5%. - -If the garbage collection handler is invoked, PHP will pass the value of -``session.gc_maxlifetime``, meaning that any stored session that was saved more -than ``gc_maxlifetime`` seconds ago should be deleted. This allows to expire records -based on idle time. - -However, some operating systems (e.g. Debian) do their own session handling and set -the ``session.gc_probability`` directive to ``0`` to stop PHP doing garbage -collection. That's why Symfony now overwrites this value to ``1``. - -If you wish to use the original value set in your ``php.ini``, add the following -configuration: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - session: - gc_probability: null - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // config/packages/framework.php - namespace Symfony\Component\DependencyInjection\Loader\Configurator; - - return static function (ContainerConfigurator $containerConfigurator) { - $containerConfigurator->extension('framework', [ - 'session' => [ - 'gc_probability' => null, - ], - ]); - }; - -You can configure these settings by passing ``gc_probability``, ``gc_divisor`` -and ``gc_maxlifetime`` in an array to the constructor of -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage` -or to the :method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage::setOptions` -method. - -Session Lifetime -~~~~~~~~~~~~~~~~ - -When a new session is created, meaning Symfony issues a new session cookie -to the client, the cookie will be stamped with an expiry time. This is -calculated by adding the PHP runtime configuration value in -``session.cookie_lifetime`` with the current server time. - -.. note:: - - PHP will only issue a cookie once. The client is expected to store that cookie - for the entire lifetime. A new cookie will only be issued when the session is - destroyed, the browser cookie is deleted, or the session ID is regenerated - using the ``migrate()`` or ``invalidate()`` methods of the ``Session`` class. - - The initial cookie lifetime can be set by configuring ``NativeSessionStorage`` - using the ``setOptions(['cookie_lifetime' => 1234])`` method. - -.. note:: - - A cookie lifetime of ``0`` means the cookie expires when the browser is closed. - -Session Idle Time/Keep Alive -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There are often circumstances where you may want to protect, or minimize -unauthorized use of a session when a user steps away from their terminal while -logged in by destroying the session after a certain period of idle time. For -example, it is common for banking applications to log the user out after just -5 to 10 minutes of inactivity. Setting the cookie lifetime here is not -appropriate because that can be manipulated by the client, so we must do the expiry -on the server side. The easiest way is to implement this via garbage collection -which runs reasonably frequently. The ``cookie_lifetime`` would be set to a -relatively high value, and the garbage collection ``gc_maxlifetime`` would be set -to destroy sessions at whatever the desired idle period is. - -The other option is specifically check if a session has expired after the -session is started. The session can be destroyed as required. This method of -processing can allow the expiry of sessions to be integrated into the user -experience, for example, by displaying a message. - -Symfony records some basic metadata about each session to give you complete -freedom in this area. - -Session Cache Limiting -~~~~~~~~~~~~~~~~~~~~~~ - -To avoid users seeing stale data, it's common for session-enabled resources to be -sent with headers that disable caching. For this purpose PHP Sessions has the -``sessions.cache_limiter`` option, which determines which headers, if any, will be -sent with the response when the session in started. - -Upon construction, -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage` -sets this global option to ``""`` (send no headers) in case the developer wishes to -use a :class:`Symfony\\Component\\HttpFoundation\\Response` object to manage -response headers. - -.. caution:: - - If you rely on PHP Sessions to manage HTTP caching, you *must* manually set the - ``cache_limiter`` option in - :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage` - to a non-empty value. - - For example, you may set it to PHP's default value during construction: - - Example usage:: - - use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; - - $options['cache_limiter'] = session_cache_limiter(); - $sessionStorage = new NativeSessionStorage($options); - -Session Metadata -~~~~~~~~~~~~~~~~ - -Sessions are decorated with some basic metadata to enable fine control over the -security settings. The session object has a getter for the metadata, -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getMetadataBag` which -exposes an instance of :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\MetadataBag`:: - - $session->getMetadataBag()->getCreated(); - $session->getMetadataBag()->getLastUsed(); - -Both methods return a Unix timestamp (relative to the server). - -This metadata can be used to explicitly expire a session on access, e.g.:: - - $session->start(); - if (time() - $session->getMetadataBag()->getLastUsed() > $maxIdleTime) { - $session->invalidate(); - throw new SessionExpired(); // redirect to expired session page - } - -It is also possible to tell what the ``cookie_lifetime`` was set to for a -particular cookie by reading the ``getLifetime()`` method:: - - $session->getMetadataBag()->getLifetime(); - -The expiry time of the cookie can be determined by adding the created -timestamp and the lifetime. - -.. _`php.net/session.customhandler`: https://www.php.net/session.customhandler -.. _`php.net/session.configuration`: https://www.php.net/session.configuration diff --git a/components/http_foundation/session_php_bridge.rst b/components/http_foundation/session_php_bridge.rst deleted file mode 100644 index 00f57e59e4f..00000000000 --- a/components/http_foundation/session_php_bridge.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. index:: - single: HTTP - single: HttpFoundation, Sessions - -Integrating with Legacy Sessions -================================ - -Sometimes it may be necessary to integrate Symfony into a legacy application -where you do not initially have the level of control you require. - -As stated elsewhere, Symfony Sessions are designed to replace the use of -PHP's native ``session_*()`` functions and use of the ``$_SESSION`` -superglobal. Additionally, it is mandatory for Symfony to start the session. - -However, when there really are circumstances where this is not possible, you -can use a special storage bridge -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\PhpBridgeSessionStorage` -which is designed to allow Symfony to work with a session started outside of -the Symfony HttpFoundation component. You are warned that things can interrupt -this use-case unless you are careful: for example the legacy application -erases ``$_SESSION``. - -A typical use of this might look like this:: - - use Symfony\Component\HttpFoundation\Session\Session; - use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; - - // legacy application configures session - ini_set('session.save_handler', 'files'); - ini_set('session.save_path', '/tmp'); - session_start(); - - // Get Symfony to interface with this existing session - $session = new Session(new PhpBridgeSessionStorage()); - - // symfony will now interface with the existing PHP session - $session->start(); - -This will allow you to start using the Symfony Session API and allow migration -of your application to Symfony sessions. - -.. note:: - - Symfony sessions store data like attributes in special 'Bags' which use a - key in the ``$_SESSION`` superglobal. This means that a Symfony session - cannot access arbitrary keys in ``$_SESSION`` that may be set by the legacy - application, although all the ``$_SESSION`` contents will be saved when - the session is saved. diff --git a/components/http_foundation/session_testing.rst b/components/http_foundation/session_testing.rst deleted file mode 100644 index 7d8a570c17e..00000000000 --- a/components/http_foundation/session_testing.rst +++ /dev/null @@ -1,58 +0,0 @@ -.. index:: - single: HTTP - single: HttpFoundation, Sessions - -Testing with Sessions -===================== - -Symfony is designed from the ground up with code-testability in mind. In order -to test your code which utilizes sessions, we provide two separate mock storage -mechanisms for both unit testing and functional testing. - -Testing code using real sessions is tricky because PHP's workflow state is global -and it is not possible to have multiple concurrent sessions in the same PHP -process. - -The mock storage engines simulate the PHP session workflow without actually -starting one allowing you to test your code without complications. You may also -run multiple instances in the same PHP process. - -The mock storage drivers do not read or write the system globals -``session_id()`` or ``session_name()``. Methods are provided to simulate this if -required: - -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface::getId`: Gets the - session ID. - -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface::setId`: Sets the - session ID. - -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface::getName`: Gets the - session name. - -* :method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageInterface::setName`: Sets the - session name. - -Unit Testing ------------- - -For unit testing where it is not necessary to persist the session, you should -swap out the default storage engine with -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\MockArraySessionStorage`:: - - use Symfony\Component\HttpFoundation\Session\Session; - use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; - - $session = new Session(new MockArraySessionStorage()); - -Functional Testing ------------------- - -For functional testing where you may need to persist session data across -separate PHP processes, change the storage engine to -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\MockFileSessionStorage`:: - - use Symfony\Component\HttpFoundation\Session\Session; - use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage; - - $session = new Session(new MockFileSessionStorage()); diff --git a/components/http_foundation/sessions.rst b/components/http_foundation/sessions.rst deleted file mode 100644 index dfebd9cc326..00000000000 --- a/components/http_foundation/sessions.rst +++ /dev/null @@ -1,356 +0,0 @@ -.. index:: - single: HTTP - single: HttpFoundation, Sessions - -Session Management -================== - -The Symfony HttpFoundation component has a very powerful and flexible session -subsystem which is designed to provide session management through a clear -object-oriented interface using a variety of session storage drivers. - -Sessions are used via the :class:`Symfony\\Component\\HttpFoundation\\Session\\Session` -implementation of :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface` interface. - -.. caution:: - - Make sure your PHP session isn't already started before using the Session - class. If you have a legacy session system that starts your session, see - :doc:`Legacy Sessions `. - -Quick example:: - - use Symfony\Component\HttpFoundation\Session\Session; - - $session = new Session(); - $session->start(); - - // set and get session attributes - $session->set('name', 'Drak'); - $session->get('name'); - - // set flash messages - $session->getFlashBag()->add('notice', 'Profile updated'); - - // retrieve messages - foreach ($session->getFlashBag()->get('notice', []) as $message) { - echo '
'.$message.'
'; - } - -.. note:: - - Symfony sessions are designed to replace several native PHP functions. - Applications should avoid using ``session_start()``, ``session_regenerate_id()``, - ``session_id()``, ``session_name()``, and ``session_destroy()`` and instead - use the APIs in the following section. - -.. note:: - - While it is recommended to explicitly start a session, a session will actually - start on demand, that is, if any session request is made to read/write session - data. - -.. caution:: - - Symfony sessions are incompatible with ``php.ini`` directive ``session.auto_start = 1`` - This directive should be turned off in ``php.ini``, in the web server directives or - in ``.htaccess``. - -Session API -~~~~~~~~~~~ - -The :class:`Symfony\\Component\\HttpFoundation\\Session\\Session` class implements -:class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface`. - -The :class:`Symfony\\Component\\HttpFoundation\\Session\\Session` has the -following API, divided into a couple of groups. - -Session Workflow -................ - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::start` - Starts the session - do not use ``session_start()``. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::migrate` - Regenerates the session ID - do not use ``session_regenerate_id()``. - This method can optionally change the lifetime of the new cookie that will - be emitted by calling this method. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::invalidate` - Clears all session data and regenerates session ID. Do not use ``session_destroy()``. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getId` - Gets the session ID. Do not use ``session_id()``. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::setId` - Sets the session ID. Do not use ``session_id()``. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getName` - Gets the session name. Do not use ``session_name()``. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::setName` - Sets the session name. Do not use ``session_name()``. - -Session Attributes -.................. - -The session attributes are stored internally in a "Bag", a PHP object that acts -like an array. They can be set, removed, checked, etc. using the methods -explained later in this article for the ``AttributeBagInterface`` class. See -:ref:`attribute-bag-interface`. - -In addition, a few methods exist for "Bag" management: - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::registerBag` - Registers a :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface`. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getBag` - Gets a :class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface` by - bag name. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getFlashBag` - Gets the :class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface`. - This is just a shortcut for convenience. - -Session Metadata -................ - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Session::getMetadataBag` - Gets the :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\MetadataBag` - which contains information about the session. - -Session Data Management -~~~~~~~~~~~~~~~~~~~~~~~ - -PHP's session management requires the use of the ``$_SESSION`` super-global, -however, this interferes somewhat with code testability and encapsulation in an -OOP paradigm. To help overcome this, Symfony uses *session bags* linked to the -session to encapsulate a specific dataset of attributes or flash messages. - -This approach also mitigates namespace pollution within the ``$_SESSION`` -super-global because each bag stores all its data under a unique namespace. -This allows Symfony to peacefully co-exist with other applications or libraries -that might use the ``$_SESSION`` super-global and all data remains completely -compatible with Symfony's session management. - -Symfony provides two kinds of storage bags, with two separate implementations. -Everything is written against interfaces so you may extend or create your own -bag types if necessary. - -:class:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface` has -the following API which is intended mainly for internal purposes: - -:method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::getStorageKey` - Returns the key which the bag will ultimately store its array under in ``$_SESSION``. - Generally this value can be left at its default and is for internal use. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::initialize` - This is called internally by Symfony session storage classes to link bag data - to the session. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::getName` - Returns the name of the session bag. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\SessionBagInterface::clear` - Clears out data from the bag. - -.. _attribute-bag-interface: - -Attributes -~~~~~~~~~~ - -The purpose of the bags implementing the :class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface` -is to handle session attribute storage. This might include things like user ID, -and "Remember Me" login settings or other user based state information. - -:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBag` - This is the standard default implementation. - -:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\NamespacedAttributeBag` - This implementation allows for attributes to be stored in a structured namespace. - - .. deprecated:: 5.3 - - The ``NamespacedAttributeBag`` class is deprecated since Symfony 5.3. - If you need this feature, you will have to implement the class yourself. - -:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface` -has the API - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::set` - Sets an attribute by name (``set('name', 'value')``). - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::get` - Gets an attribute by name (``get('name')``) and can define a default - value when the attribute doesn't exist (``get('name', 'default_value')``). - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::all` - Gets all attributes as an associative array of ``name => value``. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::has` - Returns ``true`` if the attribute exists. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::replace` - Sets multiple attributes at once using an associative array (``name => value``). - If the attributes existed, they are replaced; if not, they are created. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::remove` - Deletes an attribute by name and returns its value. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface::clear` - Deletes all attributes. - -Example:: - - use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; - use Symfony\Component\HttpFoundation\Session\Session; - use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; - - $session = new Session(new NativeSessionStorage(), new AttributeBag()); - $session->set('token', 'a6c1e0b6'); - // ... - $token = $session->get('token'); - // if the attribute may or may not exist, you can define a default value for it - $token = $session->get('attribute-name', 'default-attribute-value'); - // ... - $session->clear(); - -.. _namespaced-attributes: - -Namespaced Attributes -..................... - -Any plain key-value storage system is limited in the extent to which -complex data can be stored since each key must be unique. You can achieve -namespacing by introducing a naming convention to the keys so different parts of -your application could operate without clashing. For example, ``module1.foo`` and -``module2.foo``. However, sometimes this is not very practical when the attributes -data is an array, for example a set of tokens. In this case, managing the array -becomes a burden because you have to retrieve the array then process it and -store it again:: - - $tokens = [ - 'tokens' => [ - 'a' => 'a6c1e0b6', - 'b' => 'f4a7b1f3', - ], - ]; - -So any processing of this might quickly get ugly, even adding a token to the array:: - - $tokens = $session->get('tokens'); - $tokens['c'] = $value; - $session->set('tokens', $tokens); - -.. deprecated:: 5.3 - - The ``NamespacedAttributeBag`` class is deprecated since Symfony 5.3. - If you need this feature, you will have to implement the class yourself. - -With structured namespacing, the key can be translated to the array -structure like this using a namespace character (which defaults to ``/``):: - - // ... - use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag; - - $session = new Session(new NativeSessionStorage(), new NamespacedAttributeBag()); - $session->set('tokens/c', $value); - -Flash Messages -~~~~~~~~~~~~~~ - -The purpose of the :class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface` -is to provide a way of setting and retrieving messages on a per session basis. -The usual workflow would be to set flash messages in a request and to display them -after a page redirect. For example, a user submits a form which hits an update -controller, and after processing the controller redirects the page to either the -updated page or an error page. Flash messages set in the previous page request -would be displayed immediately on the subsequent page load for that session. -This is however just one application for flash messages. - -:class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\AutoExpireFlashBag` - In this implementation, messages set in one page-load will - be available for display only on the next page load. These messages will auto - expire regardless of if they are retrieved or not. - -:class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBag` - In this implementation, messages will remain in the session until - they are explicitly retrieved or cleared. This makes it possible to use ESI - caching. - -:class:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface` -has the API - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::add` - Adds a flash message to the stack of specified type. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::set` - Sets flashes by type; This method conveniently takes both single messages as - a ``string`` or multiple messages in an ``array``. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::get` - Gets flashes by type and clears those flashes from the bag. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::setAll` - Sets all flashes, accepts a keyed array of arrays ``type => [messages]``. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::all` - Gets all flashes (as a keyed array of arrays) and clears the flashes from the bag. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::peek` - Gets flashes by type (read only). - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::peekAll` - Gets all flashes (read only) as a keyed array of arrays. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::has` - Returns true if the type exists, false if not. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::keys` - Returns an array of the stored flash types. - -:method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::clear` - Clears the bag. - -For simple applications it is usually sufficient to have one flash message per -type, for example a confirmation notice after a form is submitted. However, -flash messages are stored in a keyed array by flash ``$type`` which means your -application can issue multiple messages for a given type. This allows the API -to be used for more complex messaging in your application. - -Examples of setting multiple flashes:: - - use Symfony\Component\HttpFoundation\Session\Session; - - $session = new Session(); - $session->start(); - - // add flash messages - $session->getFlashBag()->add( - 'warning', - 'Your config file is writable, it should be set read-only' - ); - $session->getFlashBag()->add('error', 'Failed to update name'); - $session->getFlashBag()->add('error', 'Another error'); - -Displaying the flash messages might look as follows. - -Display one type of message:: - - // display warnings - foreach ($session->getFlashBag()->get('warning', []) as $message) { - echo '
'.$message.'
'; - } - - // display errors - foreach ($session->getFlashBag()->get('error', []) as $message) { - echo '
'.$message.'
'; - } - -Compact method to process display all flashes at once:: - - foreach ($session->getFlashBag()->all() as $type => $messages) { - foreach ($messages as $message) { - echo '
'.$message.'
'; - } - } diff --git a/controller.rst b/controller.rst index ee9bc0d410a..13537ce27c7 100644 --- a/controller.rst +++ b/controller.rst @@ -391,128 +391,44 @@ Request object. single: Controller; The session single: Session -.. _session-intro: - Managing the Session -------------------- -Symfony provides a session object that you can use to store information -about the user between requests. Session is enabled by default, but will only be -started if you read or write from it. - -Session storage and other configuration can be controlled under the -:ref:`framework.session configuration ` in -``config/packages/framework.yaml``. - -To get the session, add an argument and type-hint it with -:class:`Symfony\\Component\\HttpFoundation\\Session\\SessionInterface`:: - - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\HttpFoundation\Session\SessionInterface; - // ... - - public function index(SessionInterface $session): Response - { - // stores an attribute for reuse during a later user request - $session->set('foo', 'bar'); - - // gets the attribute set by another controller in another request - $foobar = $session->get('foobar'); - - // uses a default value if the attribute doesn't exist - $filters = $session->get('filters', []); - - // ... - } - -Stored attributes remain in the session for the remainder of that user's session. - -For more info, see :doc:`/session`. - -.. index:: - single: Session; Flash messages - -.. _flash-messages: - -Flash Messages -~~~~~~~~~~~~~~ - -You can also store special messages, called "flash" messages, on the user's -session. By design, flash messages are meant to be used exactly once: they vanish -from the session automatically as soon as you retrieve them. This feature makes +You can store special messages, called "flash" messages, on the user's session. +By design, flash messages are meant to be used exactly once: they vanish from +the session automatically as soon as you retrieve them. This feature makes "flash" messages particularly great for storing user notifications. For example, imagine you're processing a :doc:`form ` submission:: - use Symfony\Component\HttpFoundation\Request; - use Symfony\Component\HttpFoundation\Response; - // ... +.. configuration-block:: - public function update(Request $request): Response - { - // ... + .. code-block:: php-symfony - if ($form->isSubmitted() && $form->isValid()) { - // do some sort of processing + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + // ... - $this->addFlash( - 'notice', - 'Your changes were saved!' - ); - // $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add() + public function update(Request $request): Response + { + // ... - return $this->redirectToRoute(/* ... */); - } + if ($form->isSubmitted() && $form->isValid()) { + // do some sort of processing - return $this->render(/* ... */); - } + $this->addFlash( + 'notice', + 'Your changes were saved!' + ); + // $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add() -After processing the request, the controller sets a flash message in the session -and then redirects. The message key (``notice`` in this example) can be anything: -you'll use this key to retrieve the message. - -In the template of the next page (or even better, in your base layout template), -read any flash messages from the session using the ``flashes()`` method provided -by the :ref:`Twig global app variable `: - -.. code-block:: html+twig - - {# templates/base.html.twig #} - - {# read and display just one flash message type #} - {% for message in app.flashes('notice') %} -
- {{ message }} -
- {% endfor %} - - {# read and display several types of flash messages #} - {% for label, messages in app.flashes(['success', 'warning']) %} - {% for message in messages %} -
- {{ message }} -
- {% endfor %} - {% endfor %} - - {# read and display all flash messages #} - {% for label, messages in app.flashes %} - {% for message in messages %} -
- {{ message }} -
- {% endfor %} - {% endfor %} - -It's common to use ``notice``, ``warning`` and ``error`` as the keys of the -different types of flash messages, but you can use any key that fits your -needs. + return $this->redirectToRoute(/* ... */); + } -.. tip:: + return $this->render(/* ... */); + } - You can use the - :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::peek` - method instead to retrieve the message while keeping it in the bag. +:ref:`Reading ` for more information about using Sessions. .. index:: single: Controller; Response object diff --git a/doctrine.rst b/doctrine.rst index c7c5fb86b80..04cbe6710ca 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -882,7 +882,6 @@ Learn more doctrine/multiple_entity_managers doctrine/resolve_target_entity doctrine/reverse_engineering - session/database testing/database .. _`Doctrine`: https://www.doctrine-project.org/ diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index cb963187290..48249f572a3 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1582,7 +1582,7 @@ handler_id The service id used for session storage. The default value ``'session.handler.native_file'`` will let Symfony manage the sessions itself using files to store the session metadata. Set it to ``null`` to use the native PHP session mechanism. -You can also :doc:`store sessions in a database `. +You can also :ref:`store sessions in a database `. .. _name: diff --git a/security/impersonating_user.rst b/security/impersonating_user.rst index 924cacf83a3..d596d473845 100644 --- a/security/impersonating_user.rst +++ b/security/impersonating_user.rst @@ -370,9 +370,9 @@ The firewall dispatches the ``security.switch_user`` event right after the imper is completed. The :class:`Symfony\\Component\\Security\\Http\\Event\\SwitchUserEvent` is passed to the listener, and you can use this to get the user that you are now impersonating. -The :doc:`/session/locale_sticky_session` article does not update the locale -when you impersonate a user. If you *do* want to be sure to update the locale when -you switch users, add an event subscriber on this event:: +The :ref:`locale-sticky-session` section does not update the locale when you +impersonate a user. If you *do* want to be sure to update the locale when you +switch users, add an event subscriber on this event:: // src/EventListener/SwitchUserSubscriber.php namespace App\EventListener; diff --git a/session.rst b/session.rst index 90ef240013e..2d6c9cd2012 100644 --- a/session.rst +++ b/session.rst @@ -1,15 +1,262 @@ +.. index:: + single: HTTP + single: HttpFoundation, Sessions + Sessions ======== -Symfony provides a session object and several utilities that you can use to -store information about the user between requests. +The Symfony HttpFoundation component has a very powerful and flexible session +subsystem which is designed to provide session management that you can use to +store information about the user between requests through a clear +object-oriented interface using a variety of session storage drivers. + +Symfony sessions are designed to replace the usage of the ``$_SESSION`` super +global and native PHP functions related to manipulating the session like +``session_start()``, ``session_regenerate_id()``, ``session_id()``, +``session_name()``, and ``session_destroy()``. + +.. note:: + + Sessions are only started if you read or write from it. + +Installation +------------ + +You need to install the HttpFoundation component to handle sessions: + +.. code-block:: terminal + + $ composer require symfony/http-foundation + +.. _session-intro: + +Basic Usage +----------- + +The session is available through the ``Request`` object and the ``RequestStack`` +service. Symfony injects the ``request_stack`` service in services and controllers +if you type-hint an argument with :class:`Symfony\\Component\\HttpFoundation\\RequestStack`:: + +.. configuration-block:: + + .. code-block:: php-symfony + + use Symfony\Component\HttpFoundation\RequestStack; + + class SomeService + { + private $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + + // Accessing the session in the constructor is *NOT* recommended, since + // it might not be accessible yet or lead to unwanted side-effects + // $this->session = $requestStack->getSession(); + } + + public function someMethod() + { + $session = $this->requestStack->getSession(); + + // ... + } + } + + .. code-block:: php-standalone + + use Symfony\Component\HttpFoundation\Session\Session; + + $session = new Session(); + $session->start(); + +From a Symfony controller, you can also type-hint an argument with +:class:`Symfony\\Component\\HttpFoundation\\Request`:: + + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + + public function index(Request $request): Response + { + $session = $request->getSession(); + + // ... + } + +Session Attributes +------------------ + +PHP's session management requires the use of the ``$_SESSION`` super-global. +However, this interferes with code testability and encapsulation in an OOP +paradigm. To help overcome this, Symfony uses *session bags* linked to the +session to encapsulate a specific dataset of **attributes**. + +This approach mitigates namespace pollution within the ``$_SESSION`` +super-global because each bag stores all its data under a unique namespace. +This allows Symfony to peacefully co-exist with other applications or libraries +that might use the ``$_SESSION`` super-global and all data remains completely +compatible with Symfony's session management. + +A session bag is a PHP object that acts like an array:: + + // stores an attribute for reuse during a later user request + $session->set('attribute-name', 'attribute-value'); + + // gets an attribute by name + $foo = $session->get('foo'); + + // the second argument is the value returned when the attribute doesn't exist + $filters = $session->get('filters', []); + +Stored attributes remain in the session for the remainder of that user's session. +By default, session attributes are key-value pairs managed with the +:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBag` +class. + +.. tip:: + + Sessions are automatically started whenever you read, write or even check + for the existence of data in the session. This may hurt your application + performance because all users will receive a session cookie. In order to + prevent starting sessions for anonymous users, you must *completely* avoid + accessing the session. + +.. index:: + single: Session; Flash messages + +.. _flash-messages: + +Flash Messages +-------------- + +You can store special messages, called "flash" messages, on the user's session. +By design, flash messages are meant to be used exactly once: they vanish from +the session automatically as soon as you retrieve them. This feature makes +"flash" messages particularly great for storing user notifications. + +For example, imagine you're processing a :doc:`form ` submission:: + +.. configuration-block:: + + .. code-block:: php-symfony + + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + // ... + + public function update(Request $request): Response + { + // ... + + if ($form->isSubmitted() && $form->isValid()) { + // do some sort of processing + + $this->addFlash( + 'notice', + 'Your changes were saved!' + ); + // $this->addFlash() is equivalent to $request->getSession()->getFlashBag()->add() + + return $this->redirectToRoute(/* ... */); + } + + return $this->render(/* ... */); + } + + .. code-block:: php-standalone + + use Symfony\Component\HttpFoundation\Session\Session; + + $session = new Session(); + $session->start(); + + // retrieve the flash messages bag + $flashes = $session->getFlashBag(); + + // add flash messages + $flashes->add( + 'warning', + 'Your config file is writable, it should be set read-only' + ); + $flashes->add('error', 'Failed to update name'); + $flashes->add('error', 'Another error'); + +After processing the request, the controller sets a flash message in the session +and then redirects. The message key (``notice`` in this example) can be anything: +you'll use this key to retrieve the message. + +In the template of the next page (or even better, in your base layout template), +read any flash messages from the session using the ``flashes()`` method provided +by the :ref:`Twig global app variable `: + +.. configuration-block:: + + .. code-block:: html+twig + + {# templates/base.html.twig #} + + {# read and display just one flash message type #} + {% for message in app.flashes('notice') %} +
+ {{ message }} +
+ {% endfor %} + + {# read and display several types of flash messages #} + {% for label, messages in app.flashes(['success', 'warning']) %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endfor %} + + {# read and display all flash messages #} + {% for label, messages in app.flashes %} + {% for message in messages %} +
+ {{ message }} +
+ {% endfor %} + {% endfor %} + + .. code-block:: php-standalone + + // display warnings + foreach ($session->getFlashBag()->get('warning', []) as $message) { + echo '
'.$message.'
'; + } + + // display errors + foreach ($session->getFlashBag()->get('error', []) as $message) { + echo '
'.$message.'
'; + } + + // display all flashes at once + foreach ($session->getFlashBag()->all() as $type => $messages) { + foreach ($messages as $message) { + echo '
'.$message.'
'; + } + } + +It's common to use ``notice``, ``warning`` and ``error`` as the keys of the +different types of flash messages, but you can use any key that fits your +needs. + +.. tip:: + + You can use the + :method:`Symfony\\Component\\HttpFoundation\\Session\\Flash\\FlashBagInterface::peek` + method instead to retrieve the message while keeping it in the bag. Configuration ------------- -Sessions are provided by the `HttpFoundation component`_, which is included in -all Symfony applications, no matter how you installed it. Before using the -sessions, check their default configuration: +In the Symfony framework, sessions are enabled by default. Session storage and +other configuration can be controlled under the :ref:`framework.session +configuration ` in +``config/packages/framework.yaml``: .. configuration-block:: @@ -75,6 +322,19 @@ sessions, check their default configuration: ; }; + .. code-block:: php-standalone + + use Symfony\Component\HttpFoundation\Cookie; + use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; + use Symfony\Component\HttpFoundation\Session\Session; + use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; + + $storage = new NativeSessionStorage([ + 'cookie_secure' => 'auto', + 'cookie_samesite' => Cookie::SAMESITE_LAX, + ]); + $session = new Session($storage); + Setting the ``handler_id`` config option to ``null`` means that Symfony will use the native PHP session mechanism. The session metadata files will be stored outside of the Symfony application, in a directory controlled by PHP. Although @@ -129,87 +389,281 @@ session metadata files: ; }; + .. code-block:: php-standalone + + use Symfony\Component\HttpFoundation\Cookie; + use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; + use Symfony\Component\HttpFoundation\Session\Session; + use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler; + use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; + + $handler = new NativeFileSessionHandler('/var/sessions'); + $storage = new NativeSessionStorage([], $handler); + $session = new Session($storage); + Check out the Symfony config reference to learn more about the other available -:ref:`Session configuration options `. You can also -:doc:`store sessions in a database `. +:ref:`Session configuration options `. -Basic Usage ------------ +.. caution:: -The session is available through the ``Request`` object and the ``RequestStack`` -service. Symfony injects the ``request_stack`` service in services and controllers -if you type-hint an argument with :class:`Symfony\\Component\\HttpFoundation\\RequestStack`:: + Symfony sessions are incompatible with ``php.ini`` directive + ``session.auto_start = 1`` This directive should be turned off in + ``php.ini``, in the web server directives or in ``.htaccess``. - use Symfony\Component\HttpFoundation\RequestStack; +Session Idle Time/Keep Alive +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - class SomeService - { - private $requestStack; +There are often circumstances where you may want to protect, or minimize +unauthorized use of a session when a user steps away from their terminal while +logged in by destroying the session after a certain period of idle time. For +example, it is common for banking applications to log the user out after just +5 to 10 minutes of inactivity. Setting the cookie lifetime here is not +appropriate because that can be manipulated by the client, so we must do the expiry +on the server side. The easiest way is to implement this via garbage collection +which runs reasonably frequently. The ``cookie_lifetime`` would be set to a +relatively high value, and the garbage collection ``gc_maxlifetime`` would be set +to destroy sessions at whatever the desired idle period is. - public function __construct(RequestStack $requestStack) - { - $this->requestStack = $requestStack; +The other option is specifically check if a session has expired after the +session is started. The session can be destroyed as required. This method of +processing can allow the expiry of sessions to be integrated into the user +experience, for example, by displaying a message. - // Accessing the session in the constructor is *NOT* recommended, since - // it might not be accessible yet or lead to unwanted side-effects - // $this->session = $requestStack->getSession(); - } +Symfony records some metadata about each session to give you fine control over +the security settings:: - public function someMethod() - { - $session = $this->requestStack->getSession(); + $session->getMetadataBag()->getCreated(); + $session->getMetadataBag()->getLastUsed(); + +Both methods return a Unix timestamp (relative to the server). + +This metadata can be used to explicitly expire a session on access:: + + $session->start(); + if (time() - $session->getMetadataBag()->getLastUsed() > $maxIdleTime) { + $session->invalidate(); + throw new SessionExpired(); // redirect to expired session page + } + +It is also possible to tell what the ``cookie_lifetime`` was set to for a +particular cookie by reading the ``getLifetime()`` method:: + + $session->getMetadataBag()->getLifetime(); + +The expiry time of the cookie can be determined by adding the created +timestamp and the lifetime. + +.. index:: + single: Session; Database Storage + +.. _session-database: + +Store Sessions in a Database +---------------------------- + +Symfony stores sessions in files by default. If your application is served by +multiple servers, you'll need to use a database instead to make sessions work +across different servers. - // stores an attribute in the session for later reuse - $session->set('attribute-name', 'attribute-value'); +Symfony can store sessions in all kinds of databases (relational, NoSQL and +key-value) but recommends key-value databases like Redis to get best +performance. - // gets an attribute by name - $foo = $session->get('foo'); +Store Sessions in a key-value Database (Redis) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // the second argument is the value returned when the attribute doesn't exist - $filters = $session->get('filters', []); +This section assumes that you have a fully-working Redis server and have also +installed and configured the `phpredis extension`_. +You have two different options to use Redis to store sessions: + +The first PHP-based option is to configure Redis session handler directly +in the server ``php.ini`` file: + +.. code-block:: ini + + ; php.ini + session.save_handler = redis + session.save_path = "tcp://192.168.0.178:6379?auth=REDIS_PASSWORD" + +The second option is to configure Redis sessions in Symfony. First, define +a Symfony service for the connection to the Redis server: + +.. configuration-block:: + + # config/services.yaml + services: + # ... + Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler: + arguments: + - '@Redis' + # you can optionally pass an array of options. The only options are 'prefix' and 'ttl', + # which define the prefix to use for the keys to avoid collision on the Redis server + # and the expiration time for any given entry (in seconds), defaults are 'sf_s' and null: + # - { 'prefix': 'my_prefix', 'ttl': 600 } + + Redis: + # you can also use \RedisArray, \RedisCluster or \Predis\Client classes + class: Redis + calls: + - connect: + - '%env(REDIS_HOST)%' + - '%env(int:REDIS_PORT)%' + + # uncomment the following if your Redis server requires a password + # - auth: + # - '%env(REDIS_PASSWORD)%' + + # uncomment the following if your Redis server requires a user and a password (when user is not default) + # - auth: + # - ['%env(REDIS_USER)%','%env(REDIS_PASSWORD)%'] + + .. code-block:: xml + + + + + + + + + %env(REDIS_HOST)% + %env(int:REDIS_PORT)% + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + use Symfony\Component\DependencyInjection\Reference; + use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler; + + $container + // you can also use \RedisArray, \RedisCluster or \Predis\Client classes + ->register('Redis', \Redis::class) + ->addMethodCall('connect', ['%env(REDIS_HOST)%', '%env(int:REDIS_PORT)%']) + // uncomment the following if your Redis server requires a password: + // ->addMethodCall('auth', ['%env(REDIS_PASSWORD)%']) + // uncomment the following if your Redis server requires a user and a password (when user is not default): + // ->addMethodCall('auth', ['%env(REDIS_USER)%', '%env(REDIS_PASSWORD)%']) + + ->register(RedisSessionHandler::class) + ->addArgument( + new Reference('Redis'), + // you can optionally pass an array of options. The only options are 'prefix' and 'ttl', + // which define the prefix to use for the keys to avoid collision on the Redis server + // and the expiration time for any given entry (in seconds), defaults are 'sf_s' and null: + // ['prefix' => 'my_prefix', 'ttl' => 600], + ) + ; + +Next, use the :ref:`handler_id ` +configuration option to tell Symfony to use this service as the session handler: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/framework.yaml + framework: + # ... + session: + handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // config/packages/framework.php + use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler; + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework) { // ... - } - } + $framework->session() + ->handlerId(RedisSessionHandler::class) + ; + }; -.. deprecated:: 5.3 +Symfony will now use your Redis server to read and write the session data. The +main drawback of this solution is that Redis does not perform session locking, +so you can face *race conditions* when accessing sessions. For example, you may +see an *"Invalid CSRF token"* error because two requests were made in parallel +and only the first one stored the CSRF token in the session. - The ``SessionInterface`` and ``session`` service were deprecated in - Symfony 5.3. Instead, inject the ``RequestStack`` service to get the session - object of the current request. +.. seealso:: -Stored attributes remain in the session for the remainder of that user's session. -By default, session attributes are key-value pairs managed with the -:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBag` -class. + If you use Memcached instead of Redis, follow a similar approach but + replace ``RedisSessionHandler`` by + :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcachedSessionHandler`. -.. deprecated:: 5.3 +.. _session-database-pdo: - The ``NamespacedAttributeBag`` class is deprecated since Symfony 5.3. - If you need this feature, you will have to implement the class yourself. +Store Sessions in a Relational Database (MariaDB, MySQL, PostgreSQL) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If your application needs are complex, you may prefer to use -:ref:`namespaced session attributes ` which are managed with the -:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\NamespacedAttributeBag` -class. Before using them, override the ``session_listener`` service definition to build -your ``Session`` object with the default ``AttributeBag`` by the ``NamespacedAttributeBag``: +Symfony includes a +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler` +to store sessions in relational databases like MariaDB, MySQL and PostgreSQL. +To use it, first register a new handler service with your database credentials: .. configuration-block:: .. code-block:: yaml # config/services.yaml - session.factory: - autoconfigure: true - class: App\Session\SessionFactory - arguments: - - '@request_stack' - - '@session.storage.factory' - - ['@session_listener', 'onSessionUsage'] - - '@session.namespacedattributebag' - - session.namespacedattributebag: - class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag + services: + # ... + + Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler: + arguments: + - '%env(DATABASE_URL)%' + + # you can also use PDO configuration, but requires passing two arguments + # - 'mysql:dbname=mydatabase; host=myhost; port=myport' + # - { db_username: myuser, db_password: mypassword } .. code-block:: xml @@ -217,18 +671,22 @@ your ``Session`` object with the default ``AttributeBag`` by the ``NamespacedAtt + xmlns:framework="http://symfony.com/schema/dic/symfony" + xsi:schemaLocation="http://symfony.com/schema/dic/services + https://symfony.com/schema/dic/services/services-1.0.xsd + https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - - - - - + + %env(DATABASE_URL)% - + + + @@ -237,43 +695,926 @@ your ``Session`` object with the default ``AttributeBag`` by the ``NamespacedAtt // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag; - use Symfony\Component\HttpFoundation\Session\Session; + use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - return function(ContainerConfigurator $containerConfigurator) { - $services = $containerConfigurator->services(); + return static function (ContainerConfigurator $container) { + $services = $configurator->services(); - $services->set('session', Session::class) - ->public() + $services->set(PdoSessionHandler::class) ->args([ - ref('session.storage'), - ref('session.namespacedattributebag'), - ref('session.flash_bag'), + env('DATABASE_URL'), + // you can also use PDO configuration, but requires passing two arguments: + // 'mysql:dbname=mydatabase; host=myhost; port=myport', + // ['db_username' => 'myuser', 'db_password' => 'mypassword'], ]) ; + }; + +.. tip:: + + When using MySQL as the database, the DSN defined in ``DATABASE_URL`` can + contain the ``charset`` and ``unix_socket`` options as query string parameters. + + .. versionadded:: 5.3 + + The support for ``charset`` and ``unix_socket`` options was introduced + in Symfony 5.3. + +Next, use the :ref:`handler_id ` +configuration option to tell Symfony to use this service as the session handler: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/framework.yaml + framework: + session: + # ... + handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler - $services->set('session.namespacedattributebag', NamespacedAttributeBag::class); + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // config/packages/framework.php + use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework) { + // ... + $framework->session() + ->handlerId(PdoSessionHandler::class) + ; }; -.. _session-avoid-start: +Configuring the Session Table and Column Names +.............................................. -Avoid Starting Sessions for Anonymous Users -------------------------------------------- +The table used to store sessions is called ``sessions`` by default and defines +certain column names. You can configure these values with the second argument +passed to the ``PdoSessionHandler`` service: -Sessions are automatically started whenever you read, write or even check for -the existence of data in the session. This may hurt your application performance -because all users will receive a session cookie. In order to prevent that, you -must *completely* avoid accessing the session. +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + + Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler: + arguments: + - '%env(DATABASE_URL)%' + - { db_table: 'customer_session', db_id_col: 'guid' } + + .. code-block:: xml -More about Sessions -------------------- + + + + + + + %env(DATABASE_URL)% + + customer_session + guid + + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; + + return static function (ContainerConfigurator $container) { + $services = $configurator->services(); + + $services->set(PdoSessionHandler::class) + ->args([ + env('DATABASE_URL'), + ['db_table' => 'customer_session', 'db_id_col' => 'guid'], + ]) + ; + }; + +These are parameters that you can configure: + +``db_table`` (default ``sessions``): + The name of the session table in your database; + +``db_username``: (default: ``''``) + The username used to connect when using the PDO configuration (when using + the connection based on the ``DATABASE_URL`` env var, it overrides the + username defined in the env var). + +``db_password``: (default: ``''``) + The password used to connect when using the PDO configuration (when using + the connection based on the ``DATABASE_URL`` env var, it overrides the + password defined in the env var). + +``db_id_col`` (default ``sess_id``): + The name of the column where to store the session ID (column type: ``VARCHAR(128)``); + +``db_data_col`` (default ``sess_data``): + The name of the column where to store the session data (column type: ``BLOB``); + +``db_time_col`` (default ``sess_time``): + The name of the column where to store the session creation timestamp (column type: ``INTEGER``); + +``db_lifetime_col`` (default ``sess_lifetime``): + The name of the column where to store the session lifetime (column type: ``INTEGER``); + +``db_connection_options`` (default: ``[]``) + An array of driver-specific connection options; + +``lock_mode`` (default: ``LOCK_TRANSACTIONAL``) + The strategy for locking the database to avoid *race conditions*. Possible + values are ``LOCK_NONE`` (no locking), ``LOCK_ADVISORY`` (application-level + locking) and ``LOCK_TRANSACTIONAL`` (row-level locking). + +Preparing the Database to Store Sessions +........................................ + +Before storing sessions in the database, you must create the table that stores +the information. The session handler provides a method called +:method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler::createTable` +to set up this table for you according to the database engine used:: + + try { + $sessionHandlerService->createTable(); + } catch (\PDOException $exception) { + // the table could not be created for some reason + } + +If you prefer to set up the table yourself, it's recommended to generate an +empty database migration with the following command: + +.. code-block:: terminal + + $ php bin/console doctrine:migrations:generate + +Then, find the appropriate SQL for your database below, add it to the migration +file and run the migration with the following command: + +.. code-block:: terminal + + $ php bin/console doctrine:migrations:migrate + +.. _mysql: + +MariaDB/MySQL ++++++++++++++ + +.. code-block:: sql + + CREATE TABLE `sessions` ( + `sess_id` VARBINARY(128) NOT NULL PRIMARY KEY, + `sess_data` BLOB NOT NULL, + `sess_lifetime` INTEGER UNSIGNED NOT NULL, + `sess_time` INTEGER UNSIGNED NOT NULL, + INDEX `sessions_sess_lifetime_idx` (`sess_lifetime`) + ) COLLATE utf8mb4_bin, ENGINE = InnoDB; + +.. note:: + + A ``BLOB`` column type (which is the one used by default by ``createTable()``) + stores up to 64 kb. If the user session data exceeds this, an exception may + be thrown or their session will be silently reset. Consider using a ``MEDIUMBLOB`` + if you need more space. + +PostgreSQL +++++++++++ + +.. code-block:: sql + + CREATE TABLE sessions ( + sess_id VARCHAR(128) NOT NULL PRIMARY KEY, + sess_data BYTEA NOT NULL, + sess_lifetime INTEGER NOT NULL, + sess_time INTEGER NOT NULL + ); + CREATE INDEX sessions_sess_lifetime_idx ON sessions (sess_lifetime); + +Microsoft SQL Server +++++++++++++++++++++ + +.. code-block:: sql + + CREATE TABLE sessions ( + sess_id VARCHAR(128) NOT NULL PRIMARY KEY, + sess_data NVARCHAR(MAX) NOT NULL, + sess_lifetime INTEGER NOT NULL, + sess_time INTEGER NOT NULL, + INDEX sessions_sess_lifetime_idx (sess_lifetime) + ); + +.. _session-database-mongodb: + +Store Sessions in a NoSQL Database (MongoDB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Symfony includes a +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler` +to store sessions in the MongoDB NoSQL database. First, make sure to have a +working MongoDB connection in your Symfony application as explained in the +`DoctrineMongoDBBundle configuration`_ article. + +Then, register a new handler service for ``MongoDbSessionHandler`` and pass it +the MongoDB connection as argument: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + + Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler: + arguments: + - '@doctrine_mongodb.odm.default_connection' + + .. code-block:: xml + + + + + + + + doctrine_mongodb.odm.default_connection + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; + + return static function (ContainerConfigurator $container) { + $services = $configurator->services(); + + $services->set(MongoDbSessionHandler::class) + ->args([ + service('doctrine_mongodb.odm.default_connection'), + ]) + ; + }; + +Next, use the :ref:`handler_id ` +configuration option to tell Symfony to use this service as the session handler: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/framework.yaml + framework: + session: + # ... + handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // config/packages/framework.php + use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework) { + // ... + $framework->session() + ->handlerId(MongoDbSessionHandler::class) + ; + }; + +.. note:: + + MongoDB ODM 1.x only works with the legacy driver, which is no longer + supported by the Symfony session class. Install the ``alcaeus/mongo-php-adapter`` + package to retrieve the underlying ``\MongoDB\Client`` object or upgrade to + MongoDB ODM 2.0. + +That's all! Symfony will now use your MongoDB server to read and write the +session data. You do not need to do anything to initialize your session +collection. However, you may want to add an index to improve garbage collection +performance. Run this from the `MongoDB shell`_: + +.. code-block:: javascript + + use session_db + db.session.createIndex( { "expires_at": 1 }, { expireAfterSeconds: 0 } ) + +Configuring the Session Field Names +................................... + +The collection used to store sessions defines certain field names. You can +configure these values with the second argument passed to the +``MongoDbSessionHandler`` service: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + + Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler: + arguments: + - '@doctrine_mongodb.odm.default_connection' + - { id_field: '_guid', 'expiry_field': 'eol' } + + .. code-block:: xml + + + + + + + + doctrine_mongodb.odm.default_connection + + _guid + eol + + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; + + return static function (ContainerConfigurator $container) { + $services = $configurator->services(); + + $services->set(MongoDbSessionHandler::class) + ->args([ + service('doctrine_mongodb.odm.default_connection'), + ['id_field' => '_guid', 'expiry_field' => 'eol'], + ]) + ; + }; + +These are parameters that you can configure: + +``id_field`` (default ``_id``): + The name of the field where to store the session ID; + +``data_field`` (default ``data``): + The name of the field where to store the session data; + +``time_field`` (default ``time``): + The name of the field where to store the session creation timestamp; + +``expiry_field`` (default ``expires_at``): + The name of the field where to store the session lifetime. + +.. index:: + single: Sessions, saving locale + +Migrating Between Session Handlers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your application changes the way sessions are stored, use the +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MigratingSessionHandler` +to migrate between old and new save handlers without losing session data. + +This is the recommended migration workflow: + +#. Switch to the migrating handler, with your new handler as the write-only one. + The old handler behaves as usual and sessions get written to the new one:: + + $sessionStorage = new MigratingSessionHandler($oldSessionStorage, $newSessionStorage); + +#. After your session gc period, verify that the data in the new handler is correct. +#. Update the migrating handler to use the old handler as the write-only one, so + the sessions will now be read from the new handler. This step allows easier rollbacks:: + + $sessionStorage = new MigratingSessionHandler($newSessionStorage, $oldSessionStorage); + +#. After verifying that the sessions in your application are working, switch + from the migrating handler to the new handler. + +.. index:: + single: Sessions, saving locale + +.. _locale-sticky-session: + +Making the Locale "Sticky" during a User's Session +-------------------------------------------------- + +Symfony stores the locale setting in the Request, which means that this setting +is not automatically saved ("sticky") across requests. But, you *can* store the +locale in the session, so that it's used on subsequent requests. + +Creating a LocaleSubscriber +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Create a :ref:`new event subscriber `. Typically, +``_locale`` is used as a routing parameter to signify the locale, though you +can determine the correct locale however you want:: + + // src/EventSubscriber/LocaleSubscriber.php + namespace App\EventSubscriber; + + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + use Symfony\Component\HttpKernel\Event\RequestEvent; + use Symfony\Component\HttpKernel\KernelEvents; + + class LocaleSubscriber implements EventSubscriberInterface + { + private $defaultLocale; + + public function __construct(string $defaultLocale = 'en') + { + $this->defaultLocale = $defaultLocale; + } + + public function onKernelRequest(RequestEvent $event) + { + $request = $event->getRequest(); + if (!$request->hasPreviousSession()) { + return; + } + + // try to see if the locale has been set as a _locale routing parameter + if ($locale = $request->attributes->get('_locale')) { + $request->getSession()->set('_locale', $locale); + } else { + // if no explicit locale has been set on this request, use one from the session + $request->setLocale($request->getSession()->get('_locale', $this->defaultLocale)); + } + } + + public static function getSubscribedEvents() + { + return [ + // must be registered before (i.e. with a higher priority than) the default Locale listener + KernelEvents::REQUEST => [['onKernelRequest', 20]], + ]; + } + } + +If you're using the :ref:`default services.yaml configuration +`, you're done! Symfony will +automatically know about the event subscriber and call the ``onKernelRequest`` +method on each request. + +To see it working, either set the ``_locale`` key on the session manually (e.g. +via some "Change Locale" route & controller), or create a route with the +:ref:`_locale default `. + +.. sidebar:: Explicitly Configure the Subscriber + + You can also explicitly configure it, in order to pass in the + :ref:`default_locale `: + + .. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + + App\EventSubscriber\LocaleSubscriber: + arguments: ['%kernel.default_locale%'] + # uncomment the next line if you are not using autoconfigure + # tags: [kernel.event_subscriber] + + .. code-block:: xml + + + + + + + + %kernel.default_locale% + + + + + + + + .. code-block:: php + + // config/services.php + use App\EventSubscriber\LocaleSubscriber; + + $container->register(LocaleSubscriber::class) + ->addArgument('%kernel.default_locale%') + // uncomment the next line if you are not using autoconfigure + // ->addTag('kernel.event_subscriber') + ; + +Now celebrate by changing the user's locale and seeing that it's sticky +throughout the request. + +Remember, to get the user's locale, always use the :method:`Request::getLocale +` method:: + + // from a controller... + use Symfony\Component\HttpFoundation\Request; + + public function index(Request $request) + { + $locale = $request->getLocale(); + } + +Setting the Locale Based on the User's Preferences +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You might want to improve this technique even further and define the locale +based on the user entity of the logged in user. However, since the +``LocaleSubscriber`` is called before the ``FirewallListener``, which is +responsible for handling authentication and setting the user token on the +``TokenStorage``, you have no access to the user which is logged in. + +Suppose you have a ``locale`` property on your ``User`` entity and want to use +this as the locale for the given user. To accomplish this, you can hook into +the login process and update the user's session with this locale value before +they are redirected to their first page. + +To do this, you need an event subscriber on the ``security.interactive_login`` +event:: + + // src/EventSubscriber/UserLocaleSubscriber.php + namespace App\EventSubscriber; + + use Symfony\Component\EventDispatcher\EventSubscriberInterface; + use Symfony\Component\HttpFoundation\RequestStack; + use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; + use Symfony\Component\Security\Http\SecurityEvents; + + /** + * Stores the locale of the user in the session after the + * login. This can be used by the LocaleSubscriber afterwards. + */ + class UserLocaleSubscriber implements EventSubscriberInterface + { + private $requestStack; + + public function __construct(RequestStack $requestStack) + { + $this->requestStack = $requestStack; + } + + public function onInteractiveLogin(InteractiveLoginEvent $event) + { + $user = $event->getAuthenticationToken()->getUser(); + + if (null !== $user->getLocale()) { + $this->requestStack->getSession()->set('_locale', $user->getLocale()); + } + } + + public static function getSubscribedEvents() + { + return [ + SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin', + ]; + } + } + +.. caution:: + + In order to update the language immediately after a user has changed their + language preferences, you also need to update the session when you change + the ``User`` entity. + +.. index:: + single: Sessions, Session Proxy, Proxy + +Session Proxies +--------------- + +The session proxy mechanism has a variety of uses and this article demonstrates +two common ones. Rather than using the regular session handler, you can create +a custom save handler by defining a class that extends the +:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy` +class. + +Then, define the class as a :ref:`service +`. If you're using the :ref:`default +services.yaml configuration `, that +happens automatically. + +Finally, use the ``framework.session.handler_id`` configuration option to tell +Symfony to use your session handler instead of the default one: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/framework.yaml + framework: + session: + # ... + handler_id: App\Session\CustomSessionHandler + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/packages/framework.php + use App\Session\CustomSessionHandler; + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework) { + // ... + $framework->session() + ->handlerId(CustomSessionHandler::class) + ; + }; + +Keep reading the next sections to learn how to use the session handlers in +practice to solve two common use cases: encrypt session information and define +read-only guest sessions. + +Encryption of Session Data +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want to encrypt the session data, you can use the proxy to encrypt and +decrypt the session as required. The following example uses the `php-encryption`_ +library, but you can adapt it to any other library that you may be using:: + + // src/Session/EncryptedSessionProxy.php + namespace App\Session; + + use Defuse\Crypto\Crypto; + use Defuse\Crypto\Key; + use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + + class EncryptedSessionProxy extends SessionHandlerProxy + { + private $key; + + public function __construct(\SessionHandlerInterface $handler, Key $key) + { + $this->key = $key; + + parent::__construct($handler); + } + + public function read($id) + { + $data = parent::read($id); + + return Crypto::decrypt($data, $this->key); + } + + public function write($id, $data) + { + $data = Crypto::encrypt($data, $this->key); + + return parent::write($id, $data); + } + } + +Read-only Guest Sessions +~~~~~~~~~~~~~~~~~~~~~~~~ + +There are some applications where a session is required for guest users, but +where there is no particular need to persist the session. In this case you can +intercept the session before it is written:: + + // src/Session/ReadOnlySessionProxy.php + namespace App\Session; + + use App\Entity\User; + use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; + use Symfony\Component\Security\Core\Security; + + class ReadOnlySessionProxy extends SessionHandlerProxy + { + private $security; + + public function __construct(\SessionHandlerInterface $handler, Security $security) + { + $this->security = $security; + + parent::__construct($handler); + } + + public function write($id, $data) + { + if ($this->getUser() && $this->getUser()->isGuest()) { + return; + } + + return parent::write($id, $data); + } + + private function getUser() + { + $user = $this->security->getUser(); + if (is_object($user)) { + return $user; + } + } + } + +.. _session-avoid-start: + +Integrating with Legacy Applications +------------------------------------ + +If you're integrating the Symfony full-stack Framework into a legacy +application that starts the session with ``session_start()``, you may still be +able to use Symfony's session management by using the PHP Bridge session. + +If the application has its own PHP save handler, you can specify ``null`` +for the ``handler_id``: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/framework.yaml + framework: + session: + storage_factory_id: session.storage.factory.php_bridge + handler_id: ~ + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/packages/framework.php + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework) { + $framework->session() + ->storageFactoryId('session.storage.factory.php_bridge') + ->handlerId(null) + ; + }; + + .. code-block:: php-standalone + + use Symfony\Component\HttpFoundation\Session\Session; + use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; + + // legacy application configures session + ini_set('session.save_handler', 'files'); + ini_set('session.save_path', '/tmp'); + session_start(); + + // Get Symfony to interface with this existing session + $session = new Session(new PhpBridgeSessionStorage()); + + // symfony will now interface with the existing PHP session + $session->start(); + +Otherwise, if the problem is that you cannot avoid the application +starting the session with ``session_start()``, you can still make use of +a Symfony based session save handler by specifying the save handler as in +the example below: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/framework.yaml + framework: + session: + storage_factory_id: session.storage.factory.php_bridge + handler_id: session.handler.native_file + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/packages/framework.php + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework) { + $framework->session() + ->storageFactoryId('session.storage.factory.php_bridge') + ->handlerId('session.storage.native_file') + ; + }; -.. toctree:: - :maxdepth: 1 +.. note:: - session/database - session/locale_sticky_session - session/php_bridge - session/proxy_examples + If the legacy application requires its own session save handler, do not + override this. Instead set ``handler_id: ~``. Note that a save handler + cannot be changed once the session has been started. If the application + starts the session before Symfony is initialized, the save handler will + have already been set. In this case, you will need ``handler_id: ~``. + Only override the save handler if you are sure the legacy application + can use the Symfony save handler without side effects and that the session + has not been started before Symfony is initialized. -.. _`HttpFoundation component`: https://symfony.com/components/HttpFoundation +.. _`phpredis extension`: https://github.com/phpredis/phpredis +.. _`DoctrineMongoDBBundle configuration`: https://symfony.com/doc/master/bundles/DoctrineMongoDBBundle/config.html +.. _`MongoDB shell`: https://docs.mongodb.com/manual/mongo/ +.. _`php-encryption`: https://github.com/defuse/php-encryption diff --git a/session/database.rst b/session/database.rst deleted file mode 100644 index f14ef7a0656..00000000000 --- a/session/database.rst +++ /dev/null @@ -1,663 +0,0 @@ -.. index:: - single: Session; Database Storage - -Store Sessions in a Database -============================ - -Symfony stores sessions in files by default. If your application is served by -multiple servers, you'll need to use a database instead to make sessions work -across different servers. - -Symfony can store sessions in all kinds of databases (relational, NoSQL and -key-value) but recommends key-value databases like Redis to get best performance. - -Store Sessions in a key-value Database (Redis) ----------------------------------------------- - -This section assumes that you have a fully-working Redis server and have also -installed and configured the `phpredis extension`_. - -You have two different options to use Redis to store sessions: - -(1) The first PHP-based option is to configure Redis session handler directly in -the server ``php.ini`` file: - -.. code-block:: ini - - ; php.ini - session.save_handler = redis - session.save_path = "tcp://192.168.0.178:6379?auth=REDIS_PASSWORD" - -(2) The second Symfony-based option is to configure Redis sessions as follows. - -First, define a Symfony service for the connection to the Redis server: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - Redis: - # you can also use \RedisArray, \RedisCluster or \Predis\Client classes - class: Redis - calls: - - connect: - - '%env(REDIS_HOST)%' - - '%env(int:REDIS_PORT)%' - - # uncomment the following if your Redis server requires a password - # - auth: - # - '%env(REDIS_PASSWORD)%' - - # uncomment the following if your Redis server requires a user and a password (when user is not default) - # - auth: - # - ['%env(REDIS_USER)%','%env(REDIS_PASSWORD)%'] - - .. code-block:: xml - - - - - - - - - %env(REDIS_HOST)% - %env(int:REDIS_PORT)% - - - - - - - - - - .. code-block:: php - - // ... - $container - // you can also use \RedisArray, \RedisCluster or \Predis\Client classes - ->register('Redis', \Redis::class) - ->addMethodCall('connect', ['%env(REDIS_HOST)%', '%env(int:REDIS_PORT)%']) - // uncomment the following if your Redis server requires a password: - // ->addMethodCall('auth', ['%env(REDIS_PASSWORD)%']) - // uncomment the following if your Redis server requires a user and a password (when user is not default): - // ->addMethodCall('auth', ['%env(REDIS_USER)%', '%env(REDIS_PASSWORD)%']) - ; - -Now pass this ``\Redis`` connection as an argument of the service associated to the -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\RedisSessionHandler`. -This argument can also be a ``\RedisArray``, ``\RedisCluster``, ``\Predis\Client``, -and ``RedisProxy``: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler: - arguments: - - '@Redis' - # you can optionally pass an array of options. The only options are 'prefix' and 'ttl', - # which define the prefix to use for the keys to avoid collision on the Redis server - # and the expiration time for any given entry (in seconds), defaults are 'sf_s' and null: - # - { 'prefix': 'my_prefix', 'ttl': 600 } - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // config/services.php - use Symfony\Component\DependencyInjection\Reference; - use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler; - - $container - ->register(RedisSessionHandler::class) - ->addArgument( - new Reference('Redis'), - // you can optionally pass an array of options. The only options are 'prefix' and 'ttl', - // which define the prefix to use for the keys to avoid collision on the Redis server - // and the expiration time for any given entry (in seconds), defaults are 'sf_s' and null: - // ['prefix' => 'my_prefix', 'ttl' => 600], - ); - -Next, use the :ref:`handler_id ` -configuration option to tell Symfony to use this service as the session handler: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - # ... - session: - handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler - - .. code-block:: xml - - - - - - - - .. code-block:: php - - // config/packages/framework.php - use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler; - use Symfony\Config\FrameworkConfig; - - return static function (FrameworkConfig $framework) { - // ... - $framework->session() - ->handlerId(RedisSessionHandler::class) - ; - }; - -That's all! Symfony will now use your Redis server to read and write the session -data. The main drawback of this solution is that Redis does not perform session -locking, so you can face *race conditions* when accessing sessions. For example, -you may see an *"Invalid CSRF token"* error because two requests were made in -parallel and only the first one stored the CSRF token in the session. - -.. seealso:: - - If you use Memcached instead of Redis, follow a similar approach but replace - ``RedisSessionHandler`` by :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MemcachedSessionHandler`. - -Store Sessions in a Relational Database (MariaDB, MySQL, PostgreSQL) --------------------------------------------------------------------- - -Symfony includes a :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler` -to store sessions in relational databases like MariaDB, MySQL and PostgreSQL. To use it, -first register a new handler service with your database credentials: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - - Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler: - arguments: - - '%env(DATABASE_URL)%' - - # you can also use PDO configuration, but requires passing two arguments - # - 'mysql:dbname=mydatabase; host=myhost; port=myport' - # - { db_username: myuser, db_password: mypassword } - - .. code-block:: xml - - - - - - - - %env(DATABASE_URL)% - - - - - - - - .. code-block:: php - - // config/services.php - namespace Symfony\Component\DependencyInjection\Loader\Configurator; - - use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - - return static function (ContainerConfigurator $containerConfigurator) { - $services = $containerConfigurator->services(); - - $services->set(PdoSessionHandler::class) - ->args([ - env('DATABASE_URL'), - // you can also use PDO configuration, but requires passing two arguments: - // 'mysql:dbname=mydatabase; host=myhost; port=myport', - // ['db_username' => 'myuser', 'db_password' => 'mypassword'], - ]) - ; - }; - -.. tip:: - - When using MySQL as the database, the DSN defined in ``DATABASE_URL`` can - contain the ``charset`` and ``unix_socket`` options as query string parameters. - - .. versionadded:: 5.3 - - The support for ``charset`` and ``unix_socket`` options was introduced - in Symfony 5.3. - -Next, use the :ref:`handler_id ` -configuration option to tell Symfony to use this service as the session handler: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - session: - # ... - handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler - - .. code-block:: xml - - - - - - - - .. code-block:: php - - // config/packages/framework.php - use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - use Symfony\Config\FrameworkConfig; - - return static function (FrameworkConfig $framework) { - // ... - $framework->session() - ->handlerId(PdoSessionHandler::class) - ; - }; - -Configuring the Session Table and Column Names -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The table used to store sessions is called ``sessions`` by default and defines -certain column names. You can configure these values with the second argument -passed to the ``PdoSessionHandler`` service: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - - Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler: - arguments: - - '%env(DATABASE_URL)%' - - { db_table: 'customer_session', db_id_col: 'guid' } - - .. code-block:: xml - - - - - - - - %env(DATABASE_URL)% - - customer_session - guid - - - - - - .. code-block:: php - - // config/services.php - namespace Symfony\Component\DependencyInjection\Loader\Configurator; - - use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - - return static function (ContainerConfigurator $containerConfigurator) { - $services = $containerConfigurator->services(); - - $services->set(PdoSessionHandler::class) - ->args([ - env('DATABASE_URL'), - ['db_table' => 'customer_session', 'db_id_col' => 'guid'], - ]) - ; - }; - -These are parameters that you can configure: - -``db_table`` (default ``sessions``): - The name of the session table in your database; - -``db_username``: (default: ``''``) - The username used to connect when using the PDO configuration (when using - the connection based on the ``DATABASE_URL`` env var, it overrides the - username defined in the env var). - -``db_password``: (default: ``''``) - The password used to connect when using the PDO configuration (when using - the connection based on the ``DATABASE_URL`` env var, it overrides the - password defined in the env var). - -``db_id_col`` (default ``sess_id``): - The name of the column where to store the session ID (column type: ``VARCHAR(128)``); - -``db_data_col`` (default ``sess_data``): - The name of the column where to store the session data (column type: ``BLOB``); - -``db_time_col`` (default ``sess_time``): - The name of the column where to store the session creation timestamp (column type: ``INTEGER``); - -``db_lifetime_col`` (default ``sess_lifetime``): - The name of the column where to store the session lifetime (column type: ``INTEGER``); - -``db_connection_options`` (default: ``[]``) - An array of driver-specific connection options; - -``lock_mode`` (default: ``LOCK_TRANSACTIONAL``) - The strategy for locking the database to avoid *race conditions*. Possible - values are ``LOCK_NONE`` (no locking), ``LOCK_ADVISORY`` (application-level - locking) and ``LOCK_TRANSACTIONAL`` (row-level locking). - -Preparing the Database to Store Sessions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Before storing sessions in the database, you must create the table that stores -the information. The session handler provides a method called -:method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\PdoSessionHandler::createTable` -to set up this table for you according to the database engine used:: - - try { - $sessionHandlerService->createTable(); - } catch (\PDOException $exception) { - // the table could not be created for some reason - } - -If you prefer to set up the table yourself, it's recommended to generate an -empty database migration with the following command: - -.. code-block:: terminal - - $ php bin/console doctrine:migrations:generate - -Then, find the appropriate SQL for your database below, add it to the migration -file and run the migration with the following command: - -.. code-block:: terminal - - $ php bin/console doctrine:migrations:migrate - -.. _mysql: - -MariaDB/MySQL -............. - -.. code-block:: sql - - CREATE TABLE `sessions` ( - `sess_id` VARBINARY(128) NOT NULL PRIMARY KEY, - `sess_data` BLOB NOT NULL, - `sess_lifetime` INTEGER UNSIGNED NOT NULL, - `sess_time` INTEGER UNSIGNED NOT NULL, - INDEX `sessions_sess_lifetime_idx` (`sess_lifetime`) - ) COLLATE utf8mb4_bin, ENGINE = InnoDB; - -.. note:: - - A ``BLOB`` column type (which is the one used by default by ``createTable()``) - stores up to 64 kb. If the user session data exceeds this, an exception may - be thrown or their session will be silently reset. Consider using a ``MEDIUMBLOB`` - if you need more space. - -PostgreSQL -.......... - -.. code-block:: sql - - CREATE TABLE sessions ( - sess_id VARCHAR(128) NOT NULL PRIMARY KEY, - sess_data BYTEA NOT NULL, - sess_lifetime INTEGER NOT NULL, - sess_time INTEGER NOT NULL - ); - CREATE INDEX sessions_sess_lifetime_idx ON sessions (sess_lifetime); - -Microsoft SQL Server -.................... - -.. code-block:: sql - - CREATE TABLE sessions ( - sess_id VARCHAR(128) NOT NULL PRIMARY KEY, - sess_data NVARCHAR(MAX) NOT NULL, - sess_lifetime INTEGER NOT NULL, - sess_time INTEGER NOT NULL, - INDEX sessions_sess_lifetime_idx (sess_lifetime) - ); - -Store Sessions in a NoSQL Database (MongoDB) --------------------------------------------- - -Symfony includes a :class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Handler\\MongoDbSessionHandler` -to store sessions in the MongoDB NoSQL database. First, make sure to have a -working MongoDB connection in your Symfony application as explained in the -`DoctrineMongoDBBundle configuration`_ article. - -Then, register a new handler service for ``MongoDbSessionHandler`` and pass it -the MongoDB connection as argument: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - - Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler: - arguments: - - '@doctrine_mongodb.odm.default_connection' - - .. code-block:: xml - - - - - - - - doctrine_mongodb.odm.default_connection - - - - - .. code-block:: php - - // config/services.php - namespace Symfony\Component\DependencyInjection\Loader\Configurator; - - use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - - return static function (ContainerConfigurator $containerConfigurator) { - $services = $containerConfigurator->services(); - - $services->set(MongoDbSessionHandler::class) - ->args([ - service('doctrine_mongodb.odm.default_connection'), - ]) - ; - }; - -Next, use the :ref:`handler_id ` -configuration option to tell Symfony to use this service as the session handler: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - session: - # ... - handler_id: Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler - - .. code-block:: xml - - - - - - - - .. code-block:: php - - // config/packages/framework.php - use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - use Symfony\Config\FrameworkConfig; - - return static function (FrameworkConfig $framework) { - // ... - $framework->session() - ->handlerId(MongoDbSessionHandler::class) - ; - }; - -.. note:: - - MongoDB ODM 1.x only works with the legacy driver, which is no longer - supported by the Symfony session class. Install the ``alcaeus/mongo-php-adapter`` - package to retrieve the underlying ``\MongoDB\Client`` object or upgrade to - MongoDB ODM 2.0. - -That's all! Symfony will now use your MongoDB server to read and write the -session data. You do not need to do anything to initialize your session -collection. However, you may want to add an index to improve garbage collection -performance. Run this from the `MongoDB shell`_: - -.. code-block:: javascript - - use session_db - db.session.createIndex( { "expires_at": 1 }, { expireAfterSeconds: 0 } ) - -Configuring the Session Field Names -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The collection used to store sessions defines certain field names. You can -configure these values with the second argument passed to the -``MongoDbSessionHandler`` service: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - - Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler: - arguments: - - '@doctrine_mongodb.odm.default_connection' - - { id_field: '_guid', 'expiry_field': 'eol' } - - .. code-block:: xml - - - - - - - - doctrine_mongodb.odm.default_connection - - _guid - eol - - - - - - .. code-block:: php - - // config/services.php - namespace Symfony\Component\DependencyInjection\Loader\Configurator; - - use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - - return static function (ContainerConfigurator $containerConfigurator) { - $services = $containerConfigurator->services(); - - $services->set(MongoDbSessionHandler::class) - ->args([ - service('doctrine_mongodb.odm.default_connection'), - ['id_field' => '_guid', 'expiry_field' => 'eol'], - ]) - ; - }; - -These are parameters that you can configure: - -``id_field`` (default ``_id``): - The name of the field where to store the session ID; - -``data_field`` (default ``data``): - The name of the field where to store the session data; - -``time_field`` (default ``time``): - The name of the field where to store the session creation timestamp; - -``expiry_field`` (default ``expires_at``): - The name of the field where to store the session lifetime. - -.. _`phpredis extension`: https://github.com/phpredis/phpredis -.. _`DoctrineMongoDBBundle configuration`: https://symfony.com/doc/master/bundles/DoctrineMongoDBBundle/config.html -.. _`MongoDB shell`: https://docs.mongodb.com/manual/mongo/ diff --git a/session/locale_sticky_session.rst b/session/locale_sticky_session.rst deleted file mode 100644 index 483c581adb9..00000000000 --- a/session/locale_sticky_session.rst +++ /dev/null @@ -1,188 +0,0 @@ -.. index:: - single: Sessions, saving locale - -Making the Locale "Sticky" during a User's Session -================================================== - -Symfony stores the locale setting in the Request, which means that this setting -is not automatically saved ("sticky") across requests. But, you *can* store the locale -in the session, so that it's used on subsequent requests. - -.. _creating-a-LocaleSubscriber: - -Creating a LocaleSubscriber ---------------------------- - -Create a :ref:`new event subscriber `. Typically, ``_locale`` -is used as a routing parameter to signify the locale, though you can determine the -correct locale however you want:: - - // src/EventSubscriber/LocaleSubscriber.php - namespace App\EventSubscriber; - - use Symfony\Component\EventDispatcher\EventSubscriberInterface; - use Symfony\Component\HttpKernel\Event\RequestEvent; - use Symfony\Component\HttpKernel\KernelEvents; - - class LocaleSubscriber implements EventSubscriberInterface - { - private $defaultLocale; - - public function __construct(string $defaultLocale = 'en') - { - $this->defaultLocale = $defaultLocale; - } - - public function onKernelRequest(RequestEvent $event) - { - $request = $event->getRequest(); - if (!$request->hasPreviousSession()) { - return; - } - - // try to see if the locale has been set as a _locale routing parameter - if ($locale = $request->attributes->get('_locale')) { - $request->getSession()->set('_locale', $locale); - } else { - // if no explicit locale has been set on this request, use one from the session - $request->setLocale($request->getSession()->get('_locale', $this->defaultLocale)); - } - } - - public static function getSubscribedEvents() - { - return [ - // must be registered before (i.e. with a higher priority than) the default Locale listener - KernelEvents::REQUEST => [['onKernelRequest', 20]], - ]; - } - } - -If you're using the :ref:`default services.yaml configuration `, -you're done! Symfony will automatically know about the event subscriber and call -the ``onKernelRequest`` method on each request. - -To see it working, either set the ``_locale`` key on the session manually (e.g. -via some "Change Locale" route & controller), or create a route with the :ref:`_locale default `. - -.. sidebar:: Explicitly Configure the Subscriber - - You can also explicitly configure it, in order to pass in the :ref:`default_locale `: - - .. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - - App\EventSubscriber\LocaleSubscriber: - arguments: ['%kernel.default_locale%'] - # uncomment the next line if you are not using autoconfigure - # tags: [kernel.event_subscriber] - - .. code-block:: xml - - - - - - - - %kernel.default_locale% - - - - - - - - .. code-block:: php - - // config/services.php - use App\EventSubscriber\LocaleSubscriber; - - $container->register(LocaleSubscriber::class) - ->addArgument('%kernel.default_locale%') - // uncomment the next line if you are not using autoconfigure - // ->addTag('kernel.event_subscriber') - ; - -That's it! Now celebrate by changing the user's locale and seeing that it's -sticky throughout the request. - -Remember, to get the user's locale, always use the :method:`Request::getLocale ` -method:: - - // from a controller... - use Symfony\Component\HttpFoundation\Request; - - public function index(Request $request) - { - $locale = $request->getLocale(); - } - -Setting the Locale Based on the User's Preferences --------------------------------------------------- - -You might want to improve this technique even further and define the locale based on -the user entity of the logged in user. However, since the ``LocaleSubscriber`` is called -before the ``FirewallListener``, which is responsible for handling authentication and -setting the user token on the ``TokenStorage``, you have no access to the user -which is logged in. - -Suppose you have a ``locale`` property on your ``User`` entity and -want to use this as the locale for the given user. To accomplish this, -you can hook into the login process and update the user's session with this -locale value before they are redirected to their first page. - -To do this, you need an event subscriber on the ``security.interactive_login`` -event:: - - // src/EventSubscriber/UserLocaleSubscriber.php - namespace App\EventSubscriber; - - use Symfony\Component\EventDispatcher\EventSubscriberInterface; - use Symfony\Component\HttpFoundation\RequestStack; - use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; - use Symfony\Component\Security\Http\SecurityEvents; - - /** - * Stores the locale of the user in the session after the - * login. This can be used by the LocaleSubscriber afterwards. - */ - class UserLocaleSubscriber implements EventSubscriberInterface - { - private $requestStack; - - public function __construct(RequestStack $requestStack) - { - $this->requestStack = $requestStack; - } - - public function onInteractiveLogin(InteractiveLoginEvent $event) - { - $user = $event->getAuthenticationToken()->getUser(); - - if (null !== $user->getLocale()) { - $this->requestStack->getSession()->set('_locale', $user->getLocale()); - } - } - - public static function getSubscribedEvents() - { - return [ - SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin', - ]; - } - } - -.. caution:: - - In order to update the language immediately after a user has changed - their language preferences, you also need to update the session when you change - the ``User`` entity. diff --git a/session/php_bridge.rst b/session/php_bridge.rst deleted file mode 100644 index a0fbfc8e06b..00000000000 --- a/session/php_bridge.rst +++ /dev/null @@ -1,108 +0,0 @@ -.. index:: - single: Sessions - -Bridge a legacy Application with Symfony Sessions -================================================= - -If you're integrating the Symfony full-stack Framework into a legacy application -that starts the session with ``session_start()``, you may still be able to -use Symfony's session management by using the PHP Bridge session. - -If the application has its own PHP save handler, you can specify null -for the ``handler_id``: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - session: - storage_factory_id: session.storage.factory.php_bridge - handler_id: ~ - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // config/packages/framework.php - use Symfony\Config\FrameworkConfig; - - return static function (FrameworkConfig $framework) { - $framework->session() - ->storageFactoryId('session.storage.factory.php_bridge') - ->handlerId(null) - ; - }; - -Otherwise, if the problem is that you cannot avoid the application -starting the session with ``session_start()``, you can still make use of -a Symfony based session save handler by specifying the save handler as in -the example below: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - session: - storage_factory_id: session.storage.factory.php_bridge - handler_id: session.handler.native_file - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // config/packages/framework.php - use Symfony\Config\FrameworkConfig; - - return static function (FrameworkConfig $framework) { - $framework->session() - ->storageFactoryId('session.storage.factory.php_bridge') - ->handlerId('session.storage.native_file') - ; - }; - -.. note:: - - If the legacy application requires its own session save handler, do not - override this. Instead set ``handler_id: ~``. Note that a save handler - cannot be changed once the session has been started. If the application - starts the session before Symfony is initialized, the save handler will - have already been set. In this case, you will need ``handler_id: ~``. - Only override the save handler if you are sure the legacy application - can use the Symfony save handler without side effects and that the session - has not been started before Symfony is initialized. - -For more details, see :doc:`/components/http_foundation/session_php_bridge`. diff --git a/session/proxy_examples.rst b/session/proxy_examples.rst deleted file mode 100644 index 67d46adb27b..00000000000 --- a/session/proxy_examples.rst +++ /dev/null @@ -1,145 +0,0 @@ -.. index:: - single: Sessions, Session Proxy, Proxy - -Session Proxy Examples -====================== - -The session proxy mechanism has a variety of uses and this article demonstrates -two common uses. Rather than using the regular session handler, you can create -a custom save handler by defining a class that extends the -:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\Proxy\\SessionHandlerProxy` -class. - -Then, define the class as a :ref:`service `. -If you're using the :ref:`default services.yaml configuration `, -that happens automatically. - -Finally, use the ``framework.session.handler_id`` configuration option to tell -Symfony to use your session handler instead of the default one: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - session: - # ... - handler_id: App\Session\CustomSessionHandler - - .. code-block:: xml - - - - - - - - - - - .. code-block:: php - - // config/packages/framework.php - use App\Session\CustomSessionHandler; - use Symfony\Config\FrameworkConfig; - - return static function (FrameworkConfig $framework) { - // ... - $framework->session() - ->handlerId(CustomSessionHandler::class) - ; - }; - -Keep reading the next sections to learn how to use the session handlers in practice -to solve two common use cases: encrypt session information and define read-only -guest sessions. - -Encryption of Session Data --------------------------- - -If you want to encrypt the session data, you can use the proxy to encrypt and -decrypt the session as required. The following example uses the `php-encryption`_ -library, but you can adapt it to any other library that you may be using:: - - // src/Session/EncryptedSessionProxy.php - namespace App\Session; - - use Defuse\Crypto\Crypto; - use Defuse\Crypto\Key; - use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; - - class EncryptedSessionProxy extends SessionHandlerProxy - { - private $key; - - public function __construct(\SessionHandlerInterface $handler, Key $key) - { - $this->key = $key; - - parent::__construct($handler); - } - - public function read($id) - { - $data = parent::read($id); - - return Crypto::decrypt($data, $this->key); - } - - public function write($id, $data) - { - $data = Crypto::encrypt($data, $this->key); - - return parent::write($id, $data); - } - } - -Read-only Guest Sessions ------------------------- - -There are some applications where a session is required for guest users, but -where there is no particular need to persist the session. In this case you -can intercept the session before it is written:: - - // src/Session/ReadOnlySessionProxy.php - namespace App\Session; - - use App\Entity\User; - use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; - use Symfony\Component\Security\Core\Security; - - class ReadOnlySessionProxy extends SessionHandlerProxy - { - private $security; - - public function __construct(\SessionHandlerInterface $handler, Security $security) - { - $this->security = $security; - - parent::__construct($handler); - } - - public function write($id, $data) - { - if ($this->getUser() && $this->getUser()->isGuest()) { - return; - } - - return parent::write($id, $data); - } - - private function getUser() - { - $user = $this->security->getUser(); - if (is_object($user)) { - return $user; - } - } - } - -.. _`php-encryption`: https://github.com/defuse/php-encryption diff --git a/translation.rst b/translation.rst index d58b1c6e173..b11136dffb6 100644 --- a/translation.rst +++ b/translation.rst @@ -39,7 +39,7 @@ The translation process has several steps: #. Determine, :doc:`set and manage the user's locale ` for the request and optionally - :doc:`on the user's entire session `. + :ref:`on the user's entire session `. Installation ------------ diff --git a/translation/locale.rst b/translation/locale.rst index d4a28f74961..efd7cc077a2 100644 --- a/translation/locale.rst +++ b/translation/locale.rst @@ -33,8 +33,8 @@ it:: listener priority to a higher value than ``LocaleListener`` priority (which you can obtain by running the ``debug:event kernel.request`` command). -Read :doc:`/session/locale_sticky_session` for more information on making -the user's locale "sticky" to their session. +Read :ref:`locale-sticky-session` for more information on making the user's +locale "sticky" to their session. .. note:: From 0c5fe69750fe0f11f60e72fb1160d85a14815cf2 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sat, 7 Jan 2023 12:21:03 +0100 Subject: [PATCH 163/774] Minor fixes after review --- session.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/session.rst b/session.rst index 2d6c9cd2012..aa40cbb20e0 100644 --- a/session.rst +++ b/session.rst @@ -513,7 +513,7 @@ a Symfony service for the connection to the Redis server: # uncomment the following if your Redis server requires a password # - auth: # - '%env(REDIS_PASSWORD)%' - + # uncomment the following if your Redis server requires a user and a password (when user is not default) # - auth: # - ['%env(REDIS_USER)%','%env(REDIS_PASSWORD)%'] @@ -677,7 +677,7 @@ To use it, first register a new handler service with your database credentials: https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - + %env(DATABASE_URL)% @@ -697,8 +697,8 @@ To use it, first register a new handler service with your database credentials: use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfiguratorConfigurator) { + $services = $containerConfigurator->services(); $services->set(PdoSessionHandler::class) ->args([ @@ -795,7 +795,7 @@ passed to the ``PdoSessionHandler`` service: https://symfony.com/schema/dic/services/services-1.0.xsd"> - + %env(DATABASE_URL)% customer_session @@ -812,8 +812,8 @@ passed to the ``PdoSessionHandler`` service: use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfiguratorConfigurator) { + $services = $containerConfigurator->services(); $services->set(PdoSessionHandler::class) ->args([ @@ -972,7 +972,7 @@ the MongoDB connection as argument: https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - + doctrine_mongodb.odm.default_connection @@ -985,8 +985,8 @@ the MongoDB connection as argument: use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfiguratorConfigurator) { + $services = $containerConfigurator->services(); $services->set(MongoDbSessionHandler::class) ->args([ @@ -1087,7 +1087,7 @@ configure these values with the second argument passed to the https://symfony.com/schema/dic/services/services-1.0.xsd"> - + doctrine_mongodb.odm.default_connection _guid @@ -1104,8 +1104,8 @@ configure these values with the second argument passed to the use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - return static function (ContainerConfigurator $container) { - $services = $configurator->services(); + return static function (ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(MongoDbSessionHandler::class) ->args([ From 9eb7378456cee69fc39a67b49eba0a2b2d588930 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sat, 7 Jan 2023 12:58:30 +0100 Subject: [PATCH 164/774] Move subguides into main session article --- _build/redirection_map | 1 + session.rst | 120 +++++++++++++++++++++++++++++++++++ session/configuring_ttl.rst | 121 ------------------------------------ 3 files changed, 121 insertions(+), 121 deletions(-) delete mode 100644 session/configuring_ttl.rst diff --git a/_build/redirection_map b/_build/redirection_map index 83ff253c51b..9a9826d91f4 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -407,6 +407,7 @@ /security/entity_provider /security/user_provider /session/avoid_session_start /session /session/sessions_directory /session +/session/configuring_ttl /session#session-configure-ttl /frontend/encore/legacy-apps /frontend/encore/legacy-applications /configuration/external_parameters /configuration/environment_variables /contributing/code/patches /contributing/code/pull_requests diff --git a/session.rst b/session.rst index ba969dc9187..0779199fdfb 100644 --- a/session.rst +++ b/session.rst @@ -1150,6 +1150,126 @@ This is the recommended migration workflow: #. After verifying that the sessions in your application are working, switch from the migrating handler to the new handler. +.. _session-configure-ttl: + +.. index:: + single: Sessions, defining TTL + +Configuring the Session TTL +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Symfony by default will use PHP's ini setting ``session.gc_maxlifetime`` as +session lifetime. When you store sessions in a database, you can also +configure your own TTL in the framework configuration or even at runtime. + +.. note:: + + Changing the ini setting is not possible once the session is started so + if you want to use a different TTL depending on which user is logged + in, you must do it at runtime using the callback method below. + +Configure the TTL +................. + +You need to pass the TTL in the options array of the session handler you are using: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler: + arguments: + - '@Redis' + - { 'ttl': 600 } + + .. code-block:: xml + + + + + + + 600 + + + + + .. code-block:: php + + // config/services.php + use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler; + + $services + ->set(RedisSessionHandler::class) + ->args([ + service('Redis'), + ['ttl' => 600], + ]); + +Configure the TTL Dynamically at Runtime +........................................ + +If you would like to have a different TTL for different users or sessions +for whatever reason, this is also possible by passing a callback as the TTL +value. The callback will be called right before the session is written and +has to return an integer which will be used as TTL. + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler: + arguments: + - '@Redis' + - { 'ttl': !closure '@my.ttl.handler' } + + my.ttl.handler: + class: Some\InvokableClass # some class with an __invoke() method + arguments: + # Inject whatever dependencies you need to be able to resolve a TTL for the current session + - '@security' + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler; + + $services + ->set(RedisSessionHandler::class) + ->args([ + service('Redis'), + ['ttl' => closure(service('my.ttl.handler'))], + ]); + + $services + // some class with an __invoke() method + ->set('my.ttl.handler', 'Some\InvokableClass') + // Inject whatever dependencies you need to be able to resolve a TTL for the current session + ->args([service('security')]); + .. index:: single: Sessions, saving locale diff --git a/session/configuring_ttl.rst b/session/configuring_ttl.rst deleted file mode 100644 index cf059efc7bb..00000000000 --- a/session/configuring_ttl.rst +++ /dev/null @@ -1,121 +0,0 @@ -.. index:: - single: Sessions, defining TTL - -Configuring the Session TTL -=========================== - -Symfony by default will use PHP's ini setting ``session.gc_maxlifetime`` as -session lifetime. However if you :doc:`store sessions in a database ` -you can also configure your own TTL in the framework configuration or even at runtime. - -Changing the ini setting is not possible once the session is started so if you -want to use a different TTL depending on which user is logged in, you really need -to do it at runtime using the callback method below. - -.. _configuring-the-TTL: - -Configuring the TTL -------------------- - -You need to pass the TTL in the options array of the session handler you are using: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler: - arguments: - - '@Redis' - - { 'ttl': 600 } - - .. code-block:: xml - - - - - - - 600 - - - - - .. code-block:: php - - // config/services.php - use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler; - - $services - ->set(RedisSessionHandler::class) - ->args([ - service('Redis'), - ['ttl' => 600], - ]); - -.. _configuring-the-TTL-dynamically-at-runtime: - -Configuring the TTL dynamically at runtime ------------------------------------------- - -If you would like to have a different TTL for different -users or sessions for whatever reason, this is also possible -by passing a callback as the TTL value. The callback then has -to return an integer which will be used as TTL. - -The callback will be called right before the session is written. - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler: - arguments: - - '@Redis' - - { 'ttl': !closure '@my.ttl.handler' } - - my.ttl.handler: - class: Some\InvokableClass # some class with an __invoke() method - arguments: - # Inject whatever dependencies you need to be able to resolve a TTL for the current session - - '@security' - - .. code-block:: xml - - - - - - - - - - - - - - - - - .. code-block:: php - - // config/services.php - use Symfony\Component\HttpFoundation\Session\Storage\Handler\RedisSessionHandler; - - $services - ->set(RedisSessionHandler::class) - ->args([ - service('Redis'), - ['ttl' => closure(service('my.ttl.handler'))], - ]); - - $services - // some class with an __invoke() method - ->set('my.ttl.handler', 'Some\InvokableClass') - // Inject whatever dependencies you need to be able to resolve a TTL for the current session - ->args([service('security')]); From 981889e01f0d91c5e55b5f4ecf17b478bdf9acfe Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 7 Jan 2023 16:25:54 +0100 Subject: [PATCH 165/774] Standardize the name of the container configurator variable 6.0 --- session.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/session.rst b/session.rst index ba969dc9187..b888e5eee01 100644 --- a/session.rst +++ b/session.rst @@ -697,7 +697,7 @@ To use it, first register a new handler service with your database credentials: use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - return static function (ContainerConfigurator $containerConfiguratorConfigurator) { + return static function (ContainerConfigurator $containerConfigurator) { $services = $containerConfigurator->services(); $services->set(PdoSessionHandler::class) @@ -807,7 +807,7 @@ passed to the ``PdoSessionHandler`` service: use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler; - return static function (ContainerConfigurator $containerConfiguratorConfigurator) { + return static function (ContainerConfigurator $containerConfigurator) { $services = $containerConfigurator->services(); $services->set(PdoSessionHandler::class) @@ -980,7 +980,7 @@ the MongoDB connection as argument: use Symfony\Component\HttpFoundation\Session\Storage\Handler\MongoDbSessionHandler; - return static function (ContainerConfigurator $containerConfiguratorConfigurator) { + return static function (ContainerConfigurator $containerConfigurator) { $services = $containerConfigurator->services(); $services->set(MongoDbSessionHandler::class) From 956de74e3e9c25e693a9d3129028c88809d2cea9 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 7 Jan 2023 15:35:09 +0100 Subject: [PATCH 166/774] Merge YAML docs into one --- components/yaml.rst | 343 ++++++++++++++++++++++++++++++- components/yaml/yaml_format.rst | 344 -------------------------------- configuration.rst | 2 +- 3 files changed, 336 insertions(+), 353 deletions(-) delete mode 100644 components/yaml/yaml_format.rst diff --git a/components/yaml.rst b/components/yaml.rst index c2e38f5c2d5..e5cdc2d1ebc 100644 --- a/components/yaml.rst +++ b/components/yaml.rst @@ -23,8 +23,7 @@ the `YAML 1.2 version specification`_. .. tip:: - Learn more about the Yaml component in the - :doc:`/components/yaml/yaml_format` article. + Learn more about :ref:`YAML specifications `. Installation ------------ @@ -452,14 +451,342 @@ Add the ``--format`` option to get the output in JSON format: YAML files. This may for example be useful for recognizing deprecations of contents of YAML files during automated tests. -Learn More ----------- +.. _yaml-format: -.. toctree:: - :maxdepth: 1 - :glob: +.. index:: + single: Yaml; YAML Format + +The YAML Format +--------------- + +Scalars +~~~~~~~ + +The syntax for scalars is similar to the PHP syntax. + +Strings +....... + +Strings in YAML can be wrapped both in single and double quotes. In some cases, +they can also be unquoted: + +.. code-block:: yaml + + A string in YAML + + 'A single-quoted string in YAML' + + "A double-quoted string in YAML" + +Quoted styles are useful when a string starts or end with one or more relevant +spaces, because unquoted strings are trimmed on both end when parsing their +contents. Quotes are required when the string contains special or reserved characters. + +When using single-quoted strings, any single quote ``'`` inside its contents +must be doubled to escape it: + +.. code-block:: yaml + + 'A single quote '' inside a single-quoted string' + +Strings containing any of the following characters must be quoted. Although you +can use double quotes, for these characters it is more convenient to use single +quotes, which avoids having to escape any backslash ``\``: + +* ``:``, ``{``, ``}``, ``[``, ``]``, ``,``, ``&``, ``*``, ``#``, ``?``, ``|``, + ``-``, ``<``, ``>``, ``=``, ``!``, ``%``, ``@``, ````` + +The double-quoted style provides a way to express arbitrary strings, by +using ``\`` to escape characters and sequences. For instance, it is very useful +when you need to embed a ``\n`` or a Unicode character in a string. + +.. code-block:: yaml + + "A double-quoted string in YAML\n" + +If the string contains any of the following control characters, it must be +escaped with double quotes: + +* ``\0``, ``\x01``, ``\x02``, ``\x03``, ``\x04``, ``\x05``, ``\x06``, ``\a``, + ``\b``, ``\t``, ``\n``, ``\v``, ``\f``, ``\r``, ``\x0e``, ``\x0f``, ``\x10``, + ``\x11``, ``\x12``, ``\x13``, ``\x14``, ``\x15``, ``\x16``, ``\x17``, ``\x18``, + ``\x19``, ``\x1a``, ``\e``, ``\x1c``, ``\x1d``, ``\x1e``, ``\x1f``, ``\N``, + ``\_``, ``\L``, ``\P`` + +Finally, there are other cases when the strings must be quoted, no matter if +you're using single or double quotes: + +* When the string is ``true`` or ``false`` (otherwise, it would be treated as a + boolean value); +* When the string is ``null`` or ``~`` (otherwise, it would be considered as a + ``null`` value); +* When the string looks like a number, such as integers (e.g. ``2``, ``14``, etc.), + floats (e.g. ``2.6``, ``14.9``) and exponential numbers (e.g. ``12e7``, etc.) + (otherwise, it would be treated as a numeric value); +* When the string looks like a date (e.g. ``2014-12-31``) (otherwise it would be + automatically converted into a Unix timestamp). + +When a string contains line breaks, you can use the literal style, indicated +by the pipe (``|``), to indicate that the string will span several lines. In +literals, newlines are preserved: + +.. code-block:: yaml + + | + \/ /| |\/| | + / / | | | |__ + +Alternatively, strings can be written with the folded style, denoted by ``>``, +where each line break is replaced by a space: + +.. code-block:: yaml + + > + This is a very long sentence + that spans several lines in the YAML. + + # This will be parsed as follows: (notice the trailing \n) + # "This is a very long sentence that spans several lines in the YAML.\n" + + >- + This is a very long sentence + that spans several lines in the YAML. + + # This will be parsed as follows: (without a trailing \n) + # "This is a very long sentence that spans several lines in the YAML." + +.. note:: + + Notice the two spaces before each line in the previous examples. They + won't appear in the resulting PHP strings. + +Numbers +....... + +.. code-block:: yaml + + # an integer + 12 + +.. code-block:: yaml + + # an octal + 0o14 + +.. deprecated:: 5.1 + + In YAML 1.1, octal numbers use the notation ``0...``, whereas in YAML 1.2 + the notation changes to ``0o...``. Symfony 5.1 added support for YAML 1.2 + notation and deprecated support for YAML 1.1 notation. + +.. code-block:: yaml + + # an hexadecimal + 0xC + +.. code-block:: yaml + + # a float + 13.4 + +.. code-block:: yaml + + # an exponential number + 1.2e+34 + +.. code-block:: yaml + + # infinity + .inf + +Nulls +..... + +Nulls in YAML can be expressed with ``null`` or ``~``. + +Booleans +........ + +Booleans in YAML are expressed with ``true`` and ``false``. + +Dates +..... + +YAML uses the `ISO-8601`_ standard to express dates: + +.. code-block:: yaml + + 2001-12-14T21:59:43.10-05:00 + +.. code-block:: yaml + + # simple date + 2002-12-14 + +.. _yaml-format-collections: + +Collections +~~~~~~~~~~~ + +A YAML file is rarely used to describe a simple scalar. Most of the time, it +describes a collection. YAML collections can be a sequence (indexed arrays in PHP) +or a mapping of elements (associative arrays in PHP). + +Sequences use a dash followed by a space: + +.. code-block:: yaml + + - PHP + - Perl + - Python + +The previous YAML file is equivalent to the following PHP code:: + + ['PHP', 'Perl', 'Python']; + +Mappings use a colon followed by a space (``:`` ) to mark each key/value pair: + +.. code-block:: yaml + + PHP: 5.2 + MySQL: 5.1 + Apache: 2.2.20 + +which is equivalent to this PHP code:: + + ['PHP' => 5.2, 'MySQL' => 5.1, 'Apache' => '2.2.20']; + +.. note:: + + In a mapping, a key can be any valid scalar. + +The number of spaces between the colon and the value does not matter: + +.. code-block:: yaml + + PHP: 5.2 + MySQL: 5.1 + Apache: 2.2.20 + +YAML uses indentation with one or more spaces to describe nested collections: + +.. code-block:: yaml + + 'symfony 1.0': + PHP: 5.0 + Propel: 1.2 + 'symfony 1.2': + PHP: 5.2 + Propel: 1.3 + +The above YAML is equivalent to the following PHP code:: + + [ + 'symfony 1.0' => [ + 'PHP' => 5.0, + 'Propel' => 1.2, + ], + 'symfony 1.2' => [ + 'PHP' => 5.2, + 'Propel' => 1.3, + ], + ]; + +There is one important thing you need to remember when using indentation in a +YAML file: *Indentation must be done with one or more spaces, but never with +tabulators*. + +You can nest sequences and mappings as you like: + +.. code-block:: yaml + + 'Chapter 1': + - Introduction + - Event Types + 'Chapter 2': + - Introduction + - Helpers + +YAML can also use flow styles for collections, using explicit indicators +rather than indentation to denote scope. + +A sequence can be written as a comma separated list within square brackets +(``[]``): + +.. code-block:: yaml + + [PHP, Perl, Python] + +A mapping can be written as a comma separated list of key/values within curly +braces (``{}``): + +.. code-block:: yaml + + { PHP: 5.2, MySQL: 5.1, Apache: 2.2.20 } + +You can mix and match styles to achieve a better readability: + +.. code-block:: yaml + + 'Chapter 1': [Introduction, Event Types] + 'Chapter 2': [Introduction, Helpers] + +.. code-block:: yaml + + 'symfony 1.0': { PHP: 5.0, Propel: 1.2 } + 'symfony 1.2': { PHP: 5.2, Propel: 1.3 } + +Comments +~~~~~~~~ + +Comments can be added in YAML by prefixing them with a hash mark (``#``): + +.. code-block:: yaml + + # Comment on a line + "symfony 1.0": { PHP: 5.0, Propel: 1.2 } # Comment at the end of a line + "symfony 1.2": { PHP: 5.2, Propel: 1.3 } + +.. note:: + + Comments are ignored by the YAML parser and do not need to be indented + according to the current level of nesting in a collection. + +Explicit Typing +~~~~~~~~~~~~~~~ + +The YAML specification defines some tags to set the type of any data explicitly: + +.. code-block:: yaml - yaml/* + data: + # this value is parsed as a string (it's not transformed into a DateTime) + start_date: !!str 2002-12-14 + + # this value is parsed as a float number (it will be 3.0 instead of 3) + price: !!float 3 + + # this value is parsed as binary data encoded in base64 + picture: !!binary | + R0lGODlhDAAMAIQAAP//9/X + 17unp5WZmZgAAAOfn515eXv + Pz7Y6OjuDg4J+fn5OTk6enp + 56enmleECcgggoBADs= + +Unsupported YAML Features +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The following YAML features are not supported by the Symfony Yaml component: + +* Multi-documents (``---`` and ``...`` markers); +* Complex mapping keys and complex values starting with ``?``; +* Tagged values as keys; +* The following tags and types: ``!!set``, ``!!omap``, ``!!pairs``, ``!!seq``, + ``!!bool``, ``!!int``, ``!!merge``, ``!!null``, ``!!timestamp``, ``!!value``, ``!!yaml``; +* Tags (``TAG`` directive; example: ``%TAG ! tag:example.com,2000:app/``) + and tag references (example: ``!``); +* Using sequence-like syntax for mapping elements (example: ``{foo, bar}``; use + ``{foo: ~, bar: ~}`` instead). .. _`YAML`: https://yaml.org/ .. _`YAML 1.2 version specification`: https://yaml.org/spec/1.2/spec.html diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst deleted file mode 100644 index 94f5c96574e..00000000000 --- a/components/yaml/yaml_format.rst +++ /dev/null @@ -1,344 +0,0 @@ -.. index:: - single: Yaml; YAML Format - -The YAML Format -=============== - -According to the official `YAML website`_, YAML is "a human friendly data -serialization standard for all programming languages". The Symfony Yaml -component implements a subset of the `YAML specification`_. Specifically, it -implements the minimum set of features needed to use YAML as a configuration -file format. - -Scalars -------- - -The syntax for scalars is similar to the PHP syntax. - -Strings -~~~~~~~ - -Strings in YAML can be wrapped both in single and double quotes. In some cases, -they can also be unquoted: - -.. code-block:: yaml - - A string in YAML - - 'A single-quoted string in YAML' - - "A double-quoted string in YAML" - -Quoted styles are useful when a string starts or end with one or more relevant -spaces, because unquoted strings are trimmed on both end when parsing their -contents. Quotes are required when the string contains special or reserved characters. - -When using single-quoted strings, any single quote ``'`` inside its contents -must be doubled to escape it: - -.. code-block:: yaml - - 'A single quote '' inside a single-quoted string' - -Strings containing any of the following characters must be quoted. Although you -can use double quotes, for these characters it is more convenient to use single -quotes, which avoids having to escape any backslash ``\``: - -* ``:``, ``{``, ``}``, ``[``, ``]``, ``,``, ``&``, ``*``, ``#``, ``?``, ``|``, - ``-``, ``<``, ``>``, ``=``, ``!``, ``%``, ``@``, ````` - -The double-quoted style provides a way to express arbitrary strings, by -using ``\`` to escape characters and sequences. For instance, it is very useful -when you need to embed a ``\n`` or a Unicode character in a string. - -.. code-block:: yaml - - "A double-quoted string in YAML\n" - -If the string contains any of the following control characters, it must be -escaped with double quotes: - -* ``\0``, ``\x01``, ``\x02``, ``\x03``, ``\x04``, ``\x05``, ``\x06``, ``\a``, - ``\b``, ``\t``, ``\n``, ``\v``, ``\f``, ``\r``, ``\x0e``, ``\x0f``, ``\x10``, - ``\x11``, ``\x12``, ``\x13``, ``\x14``, ``\x15``, ``\x16``, ``\x17``, ``\x18``, - ``\x19``, ``\x1a``, ``\e``, ``\x1c``, ``\x1d``, ``\x1e``, ``\x1f``, ``\N``, - ``\_``, ``\L``, ``\P`` - -Finally, there are other cases when the strings must be quoted, no matter if -you're using single or double quotes: - -* When the string is ``true`` or ``false`` (otherwise, it would be treated as a - boolean value); -* When the string is ``null`` or ``~`` (otherwise, it would be considered as a - ``null`` value); -* When the string looks like a number, such as integers (e.g. ``2``, ``14``, etc.), - floats (e.g. ``2.6``, ``14.9``) and exponential numbers (e.g. ``12e7``, etc.) - (otherwise, it would be treated as a numeric value); -* When the string looks like a date (e.g. ``2014-12-31``) (otherwise it would be - automatically converted into a Unix timestamp). - -When a string contains line breaks, you can use the literal style, indicated -by the pipe (``|``), to indicate that the string will span several lines. In -literals, newlines are preserved: - -.. code-block:: yaml - - | - \/ /| |\/| | - / / | | | |__ - -Alternatively, strings can be written with the folded style, denoted by ``>``, -where each line break is replaced by a space: - -.. code-block:: yaml - - > - This is a very long sentence - that spans several lines in the YAML. - - # This will be parsed as follows: (notice the trailing \n) - # "This is a very long sentence that spans several lines in the YAML.\n" - - >- - This is a very long sentence - that spans several lines in the YAML. - - # This will be parsed as follows: (without a trailing \n) - # "This is a very long sentence that spans several lines in the YAML." - -.. note:: - - Notice the two spaces before each line in the previous examples. They - won't appear in the resulting PHP strings. - -Numbers -~~~~~~~ - -.. code-block:: yaml - - # an integer - 12 - -.. code-block:: yaml - - # an octal - 0o14 - -.. deprecated:: 5.1 - - In YAML 1.1, octal numbers use the notation ``0...``, whereas in YAML 1.2 - the notation changes to ``0o...``. Symfony 5.1 added support for YAML 1.2 - notation and deprecated support for YAML 1.1 notation. - -.. code-block:: yaml - - # an hexadecimal - 0xC - -.. code-block:: yaml - - # a float - 13.4 - -.. code-block:: yaml - - # an exponential number - 1.2e+34 - -.. code-block:: yaml - - # infinity - .inf - -Nulls -~~~~~ - -Nulls in YAML can be expressed with ``null`` or ``~``. - -Booleans -~~~~~~~~ - -Booleans in YAML are expressed with ``true`` and ``false``. - -Dates -~~~~~ - -YAML uses the `ISO-8601`_ standard to express dates: - -.. code-block:: yaml - - 2001-12-14T21:59:43.10-05:00 - -.. code-block:: yaml - - # simple date - 2002-12-14 - -.. _yaml-format-collections: - -Collections ------------ - -A YAML file is rarely used to describe a simple scalar. Most of the time, it -describes a collection. YAML collections can be a sequence (indexed arrays in PHP) -or a mapping of elements (associative arrays in PHP). - -Sequences use a dash followed by a space: - -.. code-block:: yaml - - - PHP - - Perl - - Python - -The previous YAML file is equivalent to the following PHP code:: - - ['PHP', 'Perl', 'Python']; - -Mappings use a colon followed by a space (``:`` ) to mark each key/value pair: - -.. code-block:: yaml - - PHP: 5.2 - MySQL: 5.1 - Apache: 2.2.20 - -which is equivalent to this PHP code:: - - ['PHP' => 5.2, 'MySQL' => 5.1, 'Apache' => '2.2.20']; - -.. note:: - - In a mapping, a key can be any valid scalar. - -The number of spaces between the colon and the value does not matter: - -.. code-block:: yaml - - PHP: 5.2 - MySQL: 5.1 - Apache: 2.2.20 - -YAML uses indentation with one or more spaces to describe nested collections: - -.. code-block:: yaml - - 'symfony 1.0': - PHP: 5.0 - Propel: 1.2 - 'symfony 1.2': - PHP: 5.2 - Propel: 1.3 - -The above YAML is equivalent to the following PHP code:: - - [ - 'symfony 1.0' => [ - 'PHP' => 5.0, - 'Propel' => 1.2, - ], - 'symfony 1.2' => [ - 'PHP' => 5.2, - 'Propel' => 1.3, - ], - ]; - -There is one important thing you need to remember when using indentation in a -YAML file: *Indentation must be done with one or more spaces, but never with -tabulators*. - -You can nest sequences and mappings as you like: - -.. code-block:: yaml - - 'Chapter 1': - - Introduction - - Event Types - 'Chapter 2': - - Introduction - - Helpers - -YAML can also use flow styles for collections, using explicit indicators -rather than indentation to denote scope. - -A sequence can be written as a comma separated list within square brackets -(``[]``): - -.. code-block:: yaml - - [PHP, Perl, Python] - -A mapping can be written as a comma separated list of key/values within curly -braces (``{}``): - -.. code-block:: yaml - - { PHP: 5.2, MySQL: 5.1, Apache: 2.2.20 } - -You can mix and match styles to achieve a better readability: - -.. code-block:: yaml - - 'Chapter 1': [Introduction, Event Types] - 'Chapter 2': [Introduction, Helpers] - -.. code-block:: yaml - - 'symfony 1.0': { PHP: 5.0, Propel: 1.2 } - 'symfony 1.2': { PHP: 5.2, Propel: 1.3 } - -Comments --------- - -Comments can be added in YAML by prefixing them with a hash mark (``#``): - -.. code-block:: yaml - - # Comment on a line - "symfony 1.0": { PHP: 5.0, Propel: 1.2 } # Comment at the end of a line - "symfony 1.2": { PHP: 5.2, Propel: 1.3 } - -.. note:: - - Comments are ignored by the YAML parser and do not need to be indented - according to the current level of nesting in a collection. - -Explicit Typing ---------------- - -The YAML specification defines some tags to set the type of any data explicitly: - -.. code-block:: yaml - - data: - # this value is parsed as a string (it's not transformed into a DateTime) - start_date: !!str 2002-12-14 - - # this value is parsed as a float number (it will be 3.0 instead of 3) - price: !!float 3 - - # this value is parsed as binary data encoded in base64 - picture: !!binary | - R0lGODlhDAAMAIQAAP//9/X - 17unp5WZmZgAAAOfn515eXv - Pz7Y6OjuDg4J+fn5OTk6enp - 56enmleECcgggoBADs= - -Unsupported YAML Features -------------------------- - -The following YAML features are not supported by the Symfony Yaml component: - -* Multi-documents (``---`` and ``...`` markers); -* Complex mapping keys and complex values starting with ``?``; -* Tagged values as keys; -* The following tags and types: ``!!set``, ``!!omap``, ``!!pairs``, ``!!seq``, - ``!!bool``, ``!!int``, ``!!merge``, ``!!null``, ``!!timestamp``, ``!!value``, ``!!yaml``; -* Tags (``TAG`` directive; example: ``%TAG ! tag:example.com,2000:app/``) - and tag references (example: ``!``); -* Using sequence-like syntax for mapping elements (example: ``{foo, bar}``; use - ``{foo: ~, bar: ~}`` instead). - -.. _`ISO-8601`: https://www.iso.org/iso-8601-date-and-time-format.html -.. _`YAML website`: https://yaml.org/ -.. _`YAML specification`: https://www.yaml.org/spec/1.2/spec.html diff --git a/configuration.rst b/configuration.rst index f1f66be2fdf..702ba9a3f59 100644 --- a/configuration.rst +++ b/configuration.rst @@ -102,7 +102,7 @@ YAML is used by default when installing packages because it's concise and very readable. These are the main advantages and disadvantages of each format: * **YAML**: simple, clean and readable, but not all IDEs support autocompletion - and validation for it. :doc:`Learn the YAML syntax `; + and validation for it. :ref:`Learn the YAML syntax `; * **XML**: autocompleted/validated by most IDEs and is parsed natively by PHP, but sometimes it generates configuration considered too verbose. `Learn the XML syntax`_; * **PHP**: very powerful and it allows you to create dynamic configuration with From 6943bc7ae2749ca610752af9d5655449d346ca21 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 7 Jan 2023 16:31:02 +0100 Subject: [PATCH 167/774] Standardize the name of the container configurator variable 6.1 --- bundles/configuration.rst | 4 ++-- bundles/extension.rst | 8 ++++---- bundles/prepend_extension.rst | 6 +++--- configuration/micro_kernel_trait.rst | 6 +++--- notifier.rst | 4 ++-- service_container/factories.rst | 4 ++-- service_container/tags.rst | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/bundles/configuration.rst b/bundles/configuration.rst index d8c7a44d7bd..2556546e2d0 100644 --- a/bundles/configuration.rst +++ b/bundles/configuration.rst @@ -352,11 +352,11 @@ add this logic to the bundle class directly:: ; } - public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void + public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $builder): void { // Contrary to the Extension class, the "$config" variable is already merged // and processed. You can use it directly to configure the service container. - $container->services() + $containerConfigurator->services() ->get('acme.social.twitter_client') ->arg(0, $config['twitter']['client_id']) ->arg(1, $config['twitter']['client_secret']) diff --git a/bundles/extension.rst b/bundles/extension.rst index de5dee53c0c..6dca67b82e2 100644 --- a/bundles/extension.rst +++ b/bundles/extension.rst @@ -131,18 +131,18 @@ method:: class AcmeHelloBundle extends AbstractBundle { - public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void + public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $builder): void { // load an XML, PHP or Yaml file - $container->import('../config/services.xml'); + $containerConfigurator->import('../config/services.xml'); // you can also add or replace parameters and services - $container->parameters() + $containerConfigurator->parameters() ->set('acme_hello.phrase', $config['phrase']) ; if ($config['scream']) { - $container->services() + $containerConfigurator->services() ->get('acme_hello.printer') ->class(ScreamingPrinter::class) ; diff --git a/bundles/prepend_extension.rst b/bundles/prepend_extension.rst index a1b559abd85..0c743a42c12 100644 --- a/bundles/prepend_extension.rst +++ b/bundles/prepend_extension.rst @@ -173,7 +173,7 @@ method:: class FooBundle extends AbstractBundle { - public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void + public function prependExtension(ContainerConfigurator $containerConfigurator, ContainerBuilder $builder): void { // prepend $builder->prependExtensionConfig('framework', [ @@ -181,12 +181,12 @@ method:: ]); // append - $container->extension('framework', [ + $containerConfigurator->extension('framework', [ 'cache' => ['prefix_seed' => 'foo/bar'], ]); // append from file - $container->import('../config/packages/cache.php'); + $containerConfigurator->import('../config/packages/cache.php'); } } diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index ec62277e224..c7755790764 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -93,10 +93,10 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: ]; } - protected function configureContainer(ContainerConfigurator $c): void + protected function configureContainer(ContainerConfigurator $containerConfigurator): void { // PHP equivalent of config/packages/framework.yaml - $c->extension('framework', [ + $containerConfigurator->extension('framework', [ 'secret' => 'S0ME_SECRET' ]); } @@ -325,7 +325,7 @@ add a service conditionally based on the ``foo`` value:: ->end(); } - public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void + public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $builder): void { if ($config['foo']) { $builder->register('foo_service', \stdClass::class); diff --git a/notifier.rst b/notifier.rst index d689d7972b2..ebe9de39054 100644 --- a/notifier.rst +++ b/notifier.rst @@ -741,8 +741,8 @@ typical alert levels, which you can implement immediately using: use Symfony\Component\Notifier\FlashMessage\BootstrapFlashMessageImportanceMapper; - return function(ContainerConfigurator $configurator) { - $configurator->services() + return function(ContainerConfigurator $containerConfigurator) { + $containerConfigurator->services() ->set('notifier.flash_message_importance_mapper', BootstrapFlashMessageImportanceMapper::class) ; }; diff --git a/service_container/factories.rst b/service_container/factories.rst index d8ca852e6e9..d5164e03b05 100644 --- a/service_container/factories.rst +++ b/service_container/factories.rst @@ -298,8 +298,8 @@ e.g. change the service based on a parameter: use App\Email\NewsletterManagerInterface; use App\Email\NewsletterManagerFactory; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(NewsletterManagerInterface::class) // use the "tracable_newsletter" service when debug is enabled, "newsletter" otherwise. diff --git a/service_container/tags.rst b/service_container/tags.rst index bf20981504f..08dbb862744 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -672,8 +672,8 @@ iterator, add the ``exclude`` option: // config/services.php namespace Symfony\Component\DependencyInjection\Loader\Configurator; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); // ... From d2cbbddb8363d6c97726de2807e957da87c615e8 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 7 Jan 2023 15:44:07 +0100 Subject: [PATCH 168/774] Merge VarDumper docs into one --- components/var_dumper.rst | 417 ++++++++++++++++++++++++++++- components/var_dumper/advanced.rst | 408 ---------------------------- 2 files changed, 410 insertions(+), 415 deletions(-) delete mode 100644 components/var_dumper/advanced.rst diff --git a/components/var_dumper.rst b/components/var_dumper.rst index 480ec326967..e7d3d381313 100644 --- a/components/var_dumper.rst +++ b/components/var_dumper.rst @@ -71,7 +71,8 @@ current PHP SAPI: .. note:: If you want to catch the dump output as a string, please read the - :doc:`advanced documentation ` which contains examples of it. + :ref:`advanced section ` which contains examples of + it. You'll also learn how to change the format or redirect the output to wherever you want. @@ -468,11 +469,413 @@ then its dump representation:: .. image:: /_images/components/var_dumper/09-cut.png -Learn More ----------- +.. _var-dumper-advanced: -.. toctree:: - :maxdepth: 1 - :glob: +.. index:: + single: VarDumper + single: Components; VarDumper + +Advanced Usage +-------------- + +The ``dump()`` function is just a thin wrapper and a more convenient way to call +:method:`VarDumper::dump() `. +You can change the behavior of this function by calling +:method:`VarDumper::setHandler($callable) `. +Calls to ``dump()`` will then be forwarded to ``$callable``. + +By adding a handler, you can customize the `Cloners`_, `Dumpers`_ and `Casters`_ +as explained below. A simple implementation of a handler function might look +like this:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\CliDumper; + use Symfony\Component\VarDumper\Dumper\HtmlDumper; + use Symfony\Component\VarDumper\VarDumper; + + VarDumper::setHandler(function ($var) { + $cloner = new VarCloner(); + $dumper = 'cli' === PHP_SAPI ? new CliDumper() : new HtmlDumper(); + + $dumper->dump($cloner->cloneVar($var)); + }); + +Cloners +~~~~~~~ + +A cloner is used to create an intermediate representation of any PHP variable. +Its output is a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` +object that wraps this representation. + +You can create a ``Data`` object this way:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + + $cloner = new VarCloner(); + $data = $cloner->cloneVar($myVar); + // this is commonly then passed to the dumper + // see the example at the top of this page + // $dumper->dump($data); + +Whatever the cloned data structure, resulting ``Data`` objects are always +serializable. + +A cloner applies limits when creating the representation, so that one +can represent only a subset of the cloned variable. +Before calling :method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::cloneVar`, +you can configure these limits: + +:method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::setMaxItems` + Configures the maximum number of items that will be cloned + *past the minimum nesting depth*. Items are counted using a breadth-first + algorithm so that lower level items have higher priority than deeply nested + items. Specifying ``-1`` removes the limit. + +:method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::setMinDepth` + Configures the minimum tree depth where we are guaranteed to clone + all the items. After this depth is reached, only ``setMaxItems`` + items will be cloned. The default value is ``1``, which is consistent + with older Symfony versions. + +:method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::setMaxString` + Configures the maximum number of characters that will be cloned before + cutting overlong strings. Specifying ``-1`` removes the limit. + +Before dumping it, you can further limit the resulting +:class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object using the following methods: + +:method:`Symfony\\Component\\VarDumper\\Cloner\\Data::withMaxDepth` + Limits dumps in the depth dimension. + +:method:`Symfony\\Component\\VarDumper\\Cloner\\Data::withMaxItemsPerDepth` + Limits the number of items per depth level. + +:method:`Symfony\\Component\\VarDumper\\Cloner\\Data::withRefHandles` + Removes internal objects' handles for sparser output (useful for tests). + +:method:`Symfony\\Component\\VarDumper\\Cloner\\Data::seek` + Selects only sub-parts of already cloned arrays, objects or resources. + +Unlike the previous limits on cloners that remove data on purpose, these can +be changed back and forth before dumping since they do not affect the +intermediate representation internally. + +.. note:: + + When no limit is applied, a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` + object is as accurate as the native :phpfunction:`serialize` function, + and thus could be used for purposes beyond debugging. + +Dumpers +~~~~~~~ + +A dumper is responsible for outputting a string representation of a PHP variable, +using a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object as input. +The destination and the formatting of this output vary with dumpers. + +This component comes with an :class:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper` +for HTML output and a :class:`Symfony\\Component\\VarDumper\\Dumper\\CliDumper` +for optionally colored command line output. + +For example, if you want to dump some ``$variable``, do:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\CliDumper; + + $cloner = new VarCloner(); + $dumper = new CliDumper(); + + $dumper->dump($cloner->cloneVar($variable)); + +By using the first argument of the constructor, you can select the output +stream where the dump will be written. By default, the ``CliDumper`` writes +on ``php://stdout`` and the ``HtmlDumper`` on ``php://output``. But any PHP +stream (resource or URL) is acceptable. + +Instead of a stream destination, you can also pass it a ``callable`` that +will be called repeatedly for each line generated by a dumper. This +callable can be configured using the first argument of a dumper's constructor, +but also using the +:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::setOutput` +method or the second argument of the +:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::dump` method. + +For example, to get a dump as a string in a variable, you can do:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\CliDumper; + + $cloner = new VarCloner(); + $dumper = new CliDumper(); + $output = ''; + + $dumper->dump( + $cloner->cloneVar($variable), + function ($line, $depth) use (&$output) { + // A negative depth means "end of dump" + if ($depth >= 0) { + // Adds a two spaces indentation to the line + $output .= str_repeat(' ', $depth).$line."\n"; + } + } + ); + + // $output is now populated with the dump representation of $variable + +Another option for doing the same could be:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\CliDumper; + + $cloner = new VarCloner(); + $dumper = new CliDumper(); + $output = fopen('php://memory', 'r+b'); + + $dumper->dump($cloner->cloneVar($variable), $output); + $output = stream_get_contents($output, -1, 0); + + // $output is now populated with the dump representation of $variable + +.. tip:: + + You can pass ``true`` to the second argument of the + :method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::dump` + method to make it return the dump as a string:: - var_dumper/* + $output = $dumper->dump($cloner->cloneVar($variable), true); + +Dumpers implement the :class:`Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface` +interface that specifies the +:method:`dump(Data $data) ` +method. They also typically implement the +:class:`Symfony\\Component\\VarDumper\\Cloner\\DumperInterface` that frees +them from re-implementing the logic required to walk through a +:class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object's internal structure. + +The :class:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper` uses a dark +theme by default. Use the :method:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper::setTheme` +method to use a light theme:: + + // ... + $htmlDumper->setTheme('light'); + +The :class:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper` limits string +length and nesting depth of the output to make it more readable. These options +can be overridden by the third optional parameter of the +:method:`dump(Data $data) ` +method:: + + use Symfony\Component\VarDumper\Dumper\HtmlDumper; + + $output = fopen('php://memory', 'r+b'); + + $dumper = new HtmlDumper(); + $dumper->dump($var, $output, [ + // 1 and 160 are the default values for these options + 'maxDepth' => 1, + 'maxStringLength' => 160, + ]); + +The output format of a dumper can be fine tuned by the two flags +``DUMP_STRING_LENGTH`` and ``DUMP_LIGHT_ARRAY`` which are passed as a bitmap +in the third constructor argument. They can also be set via environment +variables when using +:method:`assertDumpEquals($dump, $data, $filter, $message) ` +during unit testing. + +The ``$filter`` argument of ``assertDumpEquals()`` can be used to pass a +bit field of ``Caster::EXCLUDE_*`` constants and influences the expected +output produced by the different casters. + +If ``DUMP_STRING_LENGTH`` is set, then the length of a string is displayed +next to its content:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\AbstractDumper; + use Symfony\Component\VarDumper\Dumper\CliDumper; + + $varCloner = new VarCloner(); + $var = ['test']; + + $dumper = new CliDumper(); + echo $dumper->dump($varCloner->cloneVar($var), true); + + // array:1 [ + // 0 => "test" + // ] + + $dumper = new CliDumper(null, null, AbstractDumper::DUMP_STRING_LENGTH); + echo $dumper->dump($varCloner->cloneVar($var), true); + + // (added string length before the string) + // array:1 [ + // 0 => (4) "test" + // ] + +If ``DUMP_LIGHT_ARRAY`` is set, then arrays are dumped in a shortened format +similar to PHP's short array notation:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\AbstractDumper; + use Symfony\Component\VarDumper\Dumper\CliDumper; + + $varCloner = new VarCloner(); + $var = ['test']; + + $dumper = new CliDumper(); + echo $dumper->dump($varCloner->cloneVar($var), true); + + // array:1 [ + // 0 => "test" + // ] + + $dumper = new CliDumper(null, null, AbstractDumper::DUMP_LIGHT_ARRAY); + echo $dumper->dump($varCloner->cloneVar($var), true); + + // (no more array:1 prefix) + // [ + // 0 => "test" + // ] + +If you would like to use both options, then you can combine them by +using the logical OR operator ``|``:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + use Symfony\Component\VarDumper\Dumper\AbstractDumper; + use Symfony\Component\VarDumper\Dumper\CliDumper; + + $varCloner = new VarCloner(); + $var = ['test']; + + $dumper = new CliDumper(null, null, AbstractDumper::DUMP_STRING_LENGTH | AbstractDumper::DUMP_LIGHT_ARRAY); + echo $dumper->dump($varCloner->cloneVar($var), true); + + // [ + // 0 => (4) "test" + // ] + +Casters +~~~~~~~ + +Objects and resources nested in a PHP variable are "cast" to arrays in the +intermediate :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` +representation. You can customize the array representation for each object/resource +by hooking a Caster into this process. The component already includes many +casters for base PHP classes and other common classes. + +If you want to build your own Caster, you can register one before cloning +a PHP variable. Casters are registered using either a Cloner's constructor +or its ``addCasters()`` method:: + + use Symfony\Component\VarDumper\Cloner\VarCloner; + + $myCasters = [...]; + $cloner = new VarCloner($myCasters); + + // or + + $cloner->addCasters($myCasters); + +The provided ``$myCasters`` argument is an array that maps a class, +an interface or a resource type to a callable:: + + $myCasters = [ + 'FooClass' => $myFooClassCallableCaster, + ':bar resource' => $myBarResourceCallableCaster, + ]; + +As you can notice, resource types are prefixed by a ``:`` to prevent +colliding with a class name. + +Because an object has one main class and potentially many parent classes +or interfaces, many casters can be applied to one object. In this case, +casters are called one after the other, starting from casters bound to the +interfaces, the parents classes and then the main class. Several casters +can also be registered for the same resource type/class/interface. +They are called in registration order. + +Casters are responsible for returning the properties of the object or resource +being cloned in an array. They are callables that accept five arguments: + +* the object or resource being casted; +* an array modeled for objects after PHP's native ``(array)`` cast operator; +* a :class:`Symfony\\Component\\VarDumper\\Cloner\\Stub` object + representing the main properties of the object (class, type, etc.); +* true/false when the caster is called nested in a structure or not; +* A bit field of :class:`Symfony\\Component\\VarDumper\\Caster\\Caster` ``::EXCLUDE_*`` + constants. + +Here is a simple caster not doing anything:: + + use Symfony\Component\VarDumper\Cloner\Stub; + + function myCaster($object, $array, Stub $stub, $isNested, $filter) + { + // ... populate/alter $array to your needs + + return $array; + } + +For objects, the ``$array`` parameter comes pre-populated using PHP's native +``(array)`` casting operator or with the return value of ``$object->__debugInfo()`` +if the magic method exists. Then, the return value of one Caster is given +as the array argument to the next Caster in the chain. + +When casting with the ``(array)`` operator, PHP prefixes protected properties +with a ``\0*\0`` and private ones with the class owning the property. For example, +``\0Foobar\0`` will be the prefix for all private properties of objects of +type Foobar. Casters follow this convention and add two more prefixes: ``\0~\0`` +is used for virtual properties and ``\0+\0`` for dynamic ones (runtime added +properties not in the class declaration). + +.. note:: + + Although you can, it is advised to not alter the state of an object + while casting it in a Caster. + +.. tip:: + + Before writing your own casters, you should check the existing ones. + +Adding Semantics with Metadata +.............................. + +Since casters are hooked on specific classes or interfaces, they know about the +objects they manipulate. By altering the ``$stub`` object (the third argument of +any caster), one can transfer this knowledge to the resulting ``Data`` object, +thus to dumpers. To help you do this (see the source code for how it works), +the component comes with a set of wrappers for common additional semantics. You +can use: + +* :class:`Symfony\\Component\\VarDumper\\Caster\\ConstStub` to wrap a value that is + best represented by a PHP constant; +* :class:`Symfony\\Component\\VarDumper\\Caster\\ClassStub` to wrap a PHP identifier + (*i.e.* a class name, a method name, an interface, *etc.*); +* :class:`Symfony\\Component\\VarDumper\\Caster\\CutStub` to replace big noisy + objects/strings/*etc.* by ellipses; +* :class:`Symfony\\Component\\VarDumper\\Caster\\CutArrayStub` to keep only some + useful keys of an array; +* :class:`Symfony\\Component\\VarDumper\\Caster\\ImgStub` to wrap an image; +* :class:`Symfony\\Component\\VarDumper\\Caster\\EnumStub` to wrap a set of virtual + values (*i.e.* values that do not exist as properties in the original PHP data + structure, but are worth listing alongside with real ones); +* :class:`Symfony\\Component\\VarDumper\\Caster\\LinkStub` to wrap strings that can + be turned into links by dumpers; +* :class:`Symfony\\Component\\VarDumper\\Caster\\TraceStub` and their +* :class:`Symfony\\Component\\VarDumper\\Caster\\FrameStub` and +* :class:`Symfony\\Component\\VarDumper\\Caster\\ArgsStub` relatives to wrap PHP + traces (used by :class:`Symfony\\Component\\VarDumper\\Caster\\ExceptionCaster`). + +For example, if you know that your ``Product`` objects have a ``brochure`` property +that holds a file name or a URL, you can wrap them in a ``LinkStub`` to tell +``HtmlDumper`` to make them clickable:: + + use Symfony\Component\VarDumper\Caster\LinkStub; + use Symfony\Component\VarDumper\Cloner\Stub; + + function ProductCaster(Product $object, $array, Stub $stub, $isNested, $filter = 0) + { + $array['brochure'] = new LinkStub($array['brochure']); + + return $array; + } diff --git a/components/var_dumper/advanced.rst b/components/var_dumper/advanced.rst deleted file mode 100644 index ded04cca902..00000000000 --- a/components/var_dumper/advanced.rst +++ /dev/null @@ -1,408 +0,0 @@ -.. index:: - single: VarDumper - single: Components; VarDumper - -Advanced Usage of the VarDumper Component -========================================= - -The ``dump()`` function is just a thin wrapper and a more convenient way to call -:method:`VarDumper::dump() `. -You can change the behavior of this function by calling -:method:`VarDumper::setHandler($callable) `. -Calls to ``dump()`` will then be forwarded to ``$callable``. - -By adding a handler, you can customize the `Cloners`_, `Dumpers`_ and `Casters`_ -as explained below. A simple implementation of a handler function might look -like this:: - - use Symfony\Component\VarDumper\Cloner\VarCloner; - use Symfony\Component\VarDumper\Dumper\CliDumper; - use Symfony\Component\VarDumper\Dumper\HtmlDumper; - use Symfony\Component\VarDumper\VarDumper; - - VarDumper::setHandler(function ($var) { - $cloner = new VarCloner(); - $dumper = 'cli' === PHP_SAPI ? new CliDumper() : new HtmlDumper(); - - $dumper->dump($cloner->cloneVar($var)); - }); - -Cloners -------- - -A cloner is used to create an intermediate representation of any PHP variable. -Its output is a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` -object that wraps this representation. - -You can create a ``Data`` object this way:: - - use Symfony\Component\VarDumper\Cloner\VarCloner; - - $cloner = new VarCloner(); - $data = $cloner->cloneVar($myVar); - // this is commonly then passed to the dumper - // see the example at the top of this page - // $dumper->dump($data); - -Whatever the cloned data structure, resulting ``Data`` objects are always -serializable. - -A cloner applies limits when creating the representation, so that one -can represent only a subset of the cloned variable. -Before calling :method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::cloneVar`, -you can configure these limits: - -:method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::setMaxItems` - Configures the maximum number of items that will be cloned - *past the minimum nesting depth*. Items are counted using a breadth-first - algorithm so that lower level items have higher priority than deeply nested - items. Specifying ``-1`` removes the limit. - -:method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::setMinDepth` - Configures the minimum tree depth where we are guaranteed to clone - all the items. After this depth is reached, only ``setMaxItems`` - items will be cloned. The default value is ``1``, which is consistent - with older Symfony versions. - -:method:`Symfony\\Component\\VarDumper\\Cloner\\VarCloner::setMaxString` - Configures the maximum number of characters that will be cloned before - cutting overlong strings. Specifying ``-1`` removes the limit. - -Before dumping it, you can further limit the resulting -:class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object using the following methods: - -:method:`Symfony\\Component\\VarDumper\\Cloner\\Data::withMaxDepth` - Limits dumps in the depth dimension. - -:method:`Symfony\\Component\\VarDumper\\Cloner\\Data::withMaxItemsPerDepth` - Limits the number of items per depth level. - -:method:`Symfony\\Component\\VarDumper\\Cloner\\Data::withRefHandles` - Removes internal objects' handles for sparser output (useful for tests). - -:method:`Symfony\\Component\\VarDumper\\Cloner\\Data::seek` - Selects only sub-parts of already cloned arrays, objects or resources. - -Unlike the previous limits on cloners that remove data on purpose, these can -be changed back and forth before dumping since they do not affect the -intermediate representation internally. - -.. note:: - - When no limit is applied, a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` - object is as accurate as the native :phpfunction:`serialize` function, - and thus could be used for purposes beyond debugging. - -Dumpers -------- - -A dumper is responsible for outputting a string representation of a PHP variable, -using a :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object as input. -The destination and the formatting of this output vary with dumpers. - -This component comes with an :class:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper` -for HTML output and a :class:`Symfony\\Component\\VarDumper\\Dumper\\CliDumper` -for optionally colored command line output. - -For example, if you want to dump some ``$variable``, do:: - - use Symfony\Component\VarDumper\Cloner\VarCloner; - use Symfony\Component\VarDumper\Dumper\CliDumper; - - $cloner = new VarCloner(); - $dumper = new CliDumper(); - - $dumper->dump($cloner->cloneVar($variable)); - -By using the first argument of the constructor, you can select the output -stream where the dump will be written. By default, the ``CliDumper`` writes -on ``php://stdout`` and the ``HtmlDumper`` on ``php://output``. But any PHP -stream (resource or URL) is acceptable. - -Instead of a stream destination, you can also pass it a ``callable`` that -will be called repeatedly for each line generated by a dumper. This -callable can be configured using the first argument of a dumper's constructor, -but also using the -:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::setOutput` -method or the second argument of the -:method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::dump` method. - -For example, to get a dump as a string in a variable, you can do:: - - use Symfony\Component\VarDumper\Cloner\VarCloner; - use Symfony\Component\VarDumper\Dumper\CliDumper; - - $cloner = new VarCloner(); - $dumper = new CliDumper(); - $output = ''; - - $dumper->dump( - $cloner->cloneVar($variable), - function ($line, $depth) use (&$output) { - // A negative depth means "end of dump" - if ($depth >= 0) { - // Adds a two spaces indentation to the line - $output .= str_repeat(' ', $depth).$line."\n"; - } - } - ); - - // $output is now populated with the dump representation of $variable - -Another option for doing the same could be:: - - use Symfony\Component\VarDumper\Cloner\VarCloner; - use Symfony\Component\VarDumper\Dumper\CliDumper; - - $cloner = new VarCloner(); - $dumper = new CliDumper(); - $output = fopen('php://memory', 'r+b'); - - $dumper->dump($cloner->cloneVar($variable), $output); - $output = stream_get_contents($output, -1, 0); - - // $output is now populated with the dump representation of $variable - -.. tip:: - - You can pass ``true`` to the second argument of the - :method:`Symfony\\Component\\VarDumper\\Dumper\\AbstractDumper::dump` - method to make it return the dump as a string:: - - $output = $dumper->dump($cloner->cloneVar($variable), true); - -Dumpers implement the :class:`Symfony\\Component\\VarDumper\\Dumper\\DataDumperInterface` -interface that specifies the -:method:`dump(Data $data) ` -method. They also typically implement the -:class:`Symfony\\Component\\VarDumper\\Cloner\\DumperInterface` that frees -them from re-implementing the logic required to walk through a -:class:`Symfony\\Component\\VarDumper\\Cloner\\Data` object's internal structure. - -The :class:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper` uses a dark -theme by default. Use the :method:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper::setTheme` -method to use a light theme:: - - // ... - $htmlDumper->setTheme('light'); - -The :class:`Symfony\\Component\\VarDumper\\Dumper\\HtmlDumper` limits string -length and nesting depth of the output to make it more readable. These options -can be overridden by the third optional parameter of the -:method:`dump(Data $data) ` -method:: - - use Symfony\Component\VarDumper\Dumper\HtmlDumper; - - $output = fopen('php://memory', 'r+b'); - - $dumper = new HtmlDumper(); - $dumper->dump($var, $output, [ - // 1 and 160 are the default values for these options - 'maxDepth' => 1, - 'maxStringLength' => 160, - ]); - -The output format of a dumper can be fine tuned by the two flags -``DUMP_STRING_LENGTH`` and ``DUMP_LIGHT_ARRAY`` which are passed as a bitmap -in the third constructor argument. They can also be set via environment -variables when using -:method:`assertDumpEquals($dump, $data, $filter, $message) ` -during unit testing. - -The ``$filter`` argument of ``assertDumpEquals()`` can be used to pass a -bit field of ``Caster::EXCLUDE_*`` constants and influences the expected -output produced by the different casters. - -If ``DUMP_STRING_LENGTH`` is set, then the length of a string is displayed -next to its content:: - - use Symfony\Component\VarDumper\Cloner\VarCloner; - use Symfony\Component\VarDumper\Dumper\AbstractDumper; - use Symfony\Component\VarDumper\Dumper\CliDumper; - - $varCloner = new VarCloner(); - $var = ['test']; - - $dumper = new CliDumper(); - echo $dumper->dump($varCloner->cloneVar($var), true); - - // array:1 [ - // 0 => "test" - // ] - - $dumper = new CliDumper(null, null, AbstractDumper::DUMP_STRING_LENGTH); - echo $dumper->dump($varCloner->cloneVar($var), true); - - // (added string length before the string) - // array:1 [ - // 0 => (4) "test" - // ] - -If ``DUMP_LIGHT_ARRAY`` is set, then arrays are dumped in a shortened format -similar to PHP's short array notation:: - - use Symfony\Component\VarDumper\Cloner\VarCloner; - use Symfony\Component\VarDumper\Dumper\AbstractDumper; - use Symfony\Component\VarDumper\Dumper\CliDumper; - - $varCloner = new VarCloner(); - $var = ['test']; - - $dumper = new CliDumper(); - echo $dumper->dump($varCloner->cloneVar($var), true); - - // array:1 [ - // 0 => "test" - // ] - - $dumper = new CliDumper(null, null, AbstractDumper::DUMP_LIGHT_ARRAY); - echo $dumper->dump($varCloner->cloneVar($var), true); - - // (no more array:1 prefix) - // [ - // 0 => "test" - // ] - -If you would like to use both options, then you can combine them by -using the logical OR operator ``|``:: - - use Symfony\Component\VarDumper\Cloner\VarCloner; - use Symfony\Component\VarDumper\Dumper\AbstractDumper; - use Symfony\Component\VarDumper\Dumper\CliDumper; - - $varCloner = new VarCloner(); - $var = ['test']; - - $dumper = new CliDumper(null, null, AbstractDumper::DUMP_STRING_LENGTH | AbstractDumper::DUMP_LIGHT_ARRAY); - echo $dumper->dump($varCloner->cloneVar($var), true); - - // [ - // 0 => (4) "test" - // ] - -Casters -------- - -Objects and resources nested in a PHP variable are "cast" to arrays in the -intermediate :class:`Symfony\\Component\\VarDumper\\Cloner\\Data` -representation. You can customize the array representation for each object/resource -by hooking a Caster into this process. The component already includes many -casters for base PHP classes and other common classes. - -If you want to build your own Caster, you can register one before cloning -a PHP variable. Casters are registered using either a Cloner's constructor -or its ``addCasters()`` method:: - - use Symfony\Component\VarDumper\Cloner\VarCloner; - - $myCasters = [...]; - $cloner = new VarCloner($myCasters); - - // or - - $cloner->addCasters($myCasters); - -The provided ``$myCasters`` argument is an array that maps a class, -an interface or a resource type to a callable:: - - $myCasters = [ - 'FooClass' => $myFooClassCallableCaster, - ':bar resource' => $myBarResourceCallableCaster, - ]; - -As you can notice, resource types are prefixed by a ``:`` to prevent -colliding with a class name. - -Because an object has one main class and potentially many parent classes -or interfaces, many casters can be applied to one object. In this case, -casters are called one after the other, starting from casters bound to the -interfaces, the parents classes and then the main class. Several casters -can also be registered for the same resource type/class/interface. -They are called in registration order. - -Casters are responsible for returning the properties of the object or resource -being cloned in an array. They are callables that accept five arguments: - -* the object or resource being casted; -* an array modeled for objects after PHP's native ``(array)`` cast operator; -* a :class:`Symfony\\Component\\VarDumper\\Cloner\\Stub` object - representing the main properties of the object (class, type, etc.); -* true/false when the caster is called nested in a structure or not; -* A bit field of :class:`Symfony\\Component\\VarDumper\\Caster\\Caster` ``::EXCLUDE_*`` - constants. - -Here is a simple caster not doing anything:: - - use Symfony\Component\VarDumper\Cloner\Stub; - - function myCaster($object, $array, Stub $stub, $isNested, $filter) - { - // ... populate/alter $array to your needs - - return $array; - } - -For objects, the ``$array`` parameter comes pre-populated using PHP's native -``(array)`` casting operator or with the return value of ``$object->__debugInfo()`` -if the magic method exists. Then, the return value of one Caster is given -as the array argument to the next Caster in the chain. - -When casting with the ``(array)`` operator, PHP prefixes protected properties -with a ``\0*\0`` and private ones with the class owning the property. For example, -``\0Foobar\0`` will be the prefix for all private properties of objects of -type Foobar. Casters follow this convention and add two more prefixes: ``\0~\0`` -is used for virtual properties and ``\0+\0`` for dynamic ones (runtime added -properties not in the class declaration). - -.. note:: - - Although you can, it is advised to not alter the state of an object - while casting it in a Caster. - -.. tip:: - - Before writing your own casters, you should check the existing ones. - -Adding Semantics with Metadata -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Since casters are hooked on specific classes or interfaces, they know about the -objects they manipulate. By altering the ``$stub`` object (the third argument of -any caster), one can transfer this knowledge to the resulting ``Data`` object, -thus to dumpers. To help you do this (see the source code for how it works), -the component comes with a set of wrappers for common additional semantics. You -can use: - -* :class:`Symfony\\Component\\VarDumper\\Caster\\ConstStub` to wrap a value that is - best represented by a PHP constant; -* :class:`Symfony\\Component\\VarDumper\\Caster\\ClassStub` to wrap a PHP identifier - (*i.e.* a class name, a method name, an interface, *etc.*); -* :class:`Symfony\\Component\\VarDumper\\Caster\\CutStub` to replace big noisy - objects/strings/*etc.* by ellipses; -* :class:`Symfony\\Component\\VarDumper\\Caster\\CutArrayStub` to keep only some - useful keys of an array; -* :class:`Symfony\\Component\\VarDumper\\Caster\\ImgStub` to wrap an image; -* :class:`Symfony\\Component\\VarDumper\\Caster\\EnumStub` to wrap a set of virtual - values (*i.e.* values that do not exist as properties in the original PHP data - structure, but are worth listing alongside with real ones); -* :class:`Symfony\\Component\\VarDumper\\Caster\\LinkStub` to wrap strings that can - be turned into links by dumpers; -* :class:`Symfony\\Component\\VarDumper\\Caster\\TraceStub` and their -* :class:`Symfony\\Component\\VarDumper\\Caster\\FrameStub` and -* :class:`Symfony\\Component\\VarDumper\\Caster\\ArgsStub` relatives to wrap PHP - traces (used by :class:`Symfony\\Component\\VarDumper\\Caster\\ExceptionCaster`). - -For example, if you know that your ``Product`` objects have a ``brochure`` property -that holds a file name or a URL, you can wrap them in a ``LinkStub`` to tell -``HtmlDumper`` to make them clickable:: - - use Symfony\Component\VarDumper\Caster\LinkStub; - use Symfony\Component\VarDumper\Cloner\Stub; - - function ProductCaster(Product $object, $array, Stub $stub, $isNested, $filter = 0) - { - $array['brochure'] = new LinkStub($array['brochure']); - - return $array; - } From ff9ce3da247646760f74b07401dd8f1f5fc51811 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 7 Jan 2023 16:48:58 +0100 Subject: [PATCH 169/774] Standardize the name of the container configurator variable 6.2 --- configuration/env_var_processors.rst | 4 ++-- security/user_checkers.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/configuration/env_var_processors.rst b/configuration/env_var_processors.rst index 658a05163df..c515c9f60c5 100644 --- a/configuration/env_var_processors.rst +++ b/configuration/env_var_processors.rst @@ -423,8 +423,8 @@ Symfony provides the following env var processors: // config/services.php use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; - return static function (ContainerConfigurator $configurator): void { - $container = $configurator->services() + return static function (ContainerConfigurator $containerConfigurator): void { + $container = $containerConfigurator->services() ->set(\RedisCluster::class, \RedisCluster::class)->args([null, '%env(shuffle:csv:REDIS_NODES)%']); }; diff --git a/security/user_checkers.rst b/security/user_checkers.rst index 3c13a57e239..4fd55a23631 100644 --- a/security/user_checkers.rst +++ b/security/user_checkers.rst @@ -183,8 +183,8 @@ order in which user checkers are called:: use App\Security\AccountEnabledUserChecker; use App\Security\APIAccessAllowedUserChecker; - return function(ContainerConfigurator $configurator) { - $services = $configurator->services(); + return function(ContainerConfigurator $containerConfigurator) { + $services = $containerConfigurator->services(); $services->set(AccountEnabledUserChecker::class) ->tag('security.user_checker.api', ['priority' => 10]) From f93dc8ef25562ee6bec95d06d454178f4a9a2368 Mon Sep 17 00:00:00 2001 From: miqrogroove <1371835+miqrogroove@users.noreply.github.com> Date: Sat, 7 Jan 2023 10:21:43 -0500 Subject: [PATCH 170/774] Include % as a Special Character in URIs Docs for Doctrine customization should explain that a `%` char is special in urlencoded string input. --- doctrine.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doctrine.rst b/doctrine.rst index 04cbe6710ca..88b5237e6e1 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -61,7 +61,7 @@ The database connection information is stored as an environment variable called .. caution:: If the username, password, host or database name contain any character considered - special in a URI (such as ``+``, ``@``, ``$``, ``#``, ``/``, ``:``, ``*``, ``!``), + special in a URI (such as ``+``, ``@``, ``$``, ``#``, ``/``, ``:``, ``*``, ``!``, ``%``), you must encode them. See `RFC 3986`_ for the full list of reserved characters or use the :phpfunction:`urlencode` function to encode them. In this case you need to remove the ``resolve:`` prefix in ``config/packages/doctrine.yaml`` to avoid errors: From 745f9dbef5e5c299f7db0a21b621c0efcab16fd8 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 7 Jan 2023 17:09:39 +0100 Subject: [PATCH 171/774] Standardize the name of the container builder variable 5.4 --- bundles/configuration.rst | 12 +++++----- bundles/extension.rst | 8 +++---- bundles/prepend_extension.rst | 14 +++++------ cache.rst | 4 ++-- .../dependency_injection/compilation.rst | 24 +++++++++---------- configuration/env_var_processors.rst | 20 ++++++++-------- configuration/using_parameters_in_dic.rst | 4 ++-- event_dispatcher.rst | 4 ++-- security.rst | 4 ++-- security/access_control.rst | 4 ++-- service_container/compiler_passes.rst | 16 ++++++------- .../service_subscribers_locators.rst | 6 ++--- service_container/tags.rst | 22 ++++++++--------- 13 files changed, 71 insertions(+), 71 deletions(-) diff --git a/bundles/configuration.rst b/bundles/configuration.rst index 2f6919a7347..c49e53e9987 100644 --- a/bundles/configuration.rst +++ b/bundles/configuration.rst @@ -221,7 +221,7 @@ force validation (e.g. if an additional option was passed, an exception will be thrown):: // src/Acme/SocialBundle/DependencyInjection/AcmeSocialExtension.php - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { $configuration = new Configuration(); @@ -263,15 +263,15 @@ In your extension, you can load this and dynamically set its arguments:: use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { - $loader = new XmlFileLoader($container, new FileLocator(dirname(__DIR__).'/Resources/config')); + $loader = new XmlFileLoader($containerBuilder, new FileLocator(dirname(__DIR__).'/Resources/config')); $loader->load('services.xml'); $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - $definition = $container->getDefinition('acme.social.twitter_client'); + $definition = $containerBuilder->getDefinition('acme.social.twitter_client'); $definition->replaceArgument(0, $config['twitter']['client_id']); $definition->replaceArgument(1, $config['twitter']['client_secret']); } @@ -292,7 +292,7 @@ In your extension, you can load this and dynamically set its arguments:: class AcmeHelloExtension extends ConfigurableExtension { // note that this method is called loadInternal and not load - protected function loadInternal(array $mergedConfig, ContainerBuilder $container) + protected function loadInternal(array $mergedConfig, ContainerBuilder $containerBuilder) { // ... } @@ -308,7 +308,7 @@ In your extension, you can load this and dynamically set its arguments:: (e.g. by overriding configurations and using :phpfunction:`isset` to check for the existence of a value). Be aware that it'll be very hard to support XML:: - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { $config = []; // let resources override the previous set value diff --git a/bundles/extension.rst b/bundles/extension.rst index edbcb5cd270..eadd0ab864a 100644 --- a/bundles/extension.rst +++ b/bundles/extension.rst @@ -38,7 +38,7 @@ This is how the extension of an AcmeHelloBundle should look like:: class AcmeHelloExtension extends Extension { - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { // ... you'll load the files here later } @@ -93,10 +93,10 @@ For instance, assume you have a file called ``services.xml`` in the use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; // ... - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { $loader = new XmlFileLoader( - $container, + $containerBuilder, new FileLocator(__DIR__.'/../Resources/config') ); $loader->load('services.xml'); @@ -119,7 +119,7 @@ they are compiled when generating the application cache to improve the overall performance. Define the list of annotated classes to compile in the ``addAnnotatedClassesToCompile()`` method:: - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { // ... diff --git a/bundles/prepend_extension.rst b/bundles/prepend_extension.rst index 9478f045f46..53f0fed9da9 100644 --- a/bundles/prepend_extension.rst +++ b/bundles/prepend_extension.rst @@ -35,7 +35,7 @@ To give an Extension the power to do this, it needs to implement { // ... - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $containerBuilder) { // ... } @@ -56,15 +56,15 @@ a configuration setting in multiple bundles as well as disable a flag in multipl in case a specific other bundle is not registered:: // src/Acme/HelloBundle/DependencyInjection/AcmeHelloExtension.php - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $containerBuilder) { // get all bundles - $bundles = $container->getParameter('kernel.bundles'); + $bundles = $containerBuilder->getParameter('kernel.bundles'); // determine if AcmeGoodbyeBundle is registered if (!isset($bundles['AcmeGoodbyeBundle'])) { // disable AcmeGoodbyeBundle in bundles $config = ['use_acme_goodbye' => false]; - foreach ($container->getExtensions() as $name => $extension) { + foreach ($containerBuilder->getExtensions() as $name => $extension) { switch ($name) { case 'acme_something': case 'acme_other': @@ -74,21 +74,21 @@ in case a specific other bundle is not registered:: // note that if the user manually configured // use_acme_goodbye to true in config/services.yaml // then the setting would in the end be true and not false - $container->prependExtensionConfig($name, $config); + $containerBuilder->prependExtensionConfig($name, $config); break; } } } // get the configuration of AcmeHelloExtension (it's a list of configuration) - $configs = $container->getExtensionConfig($this->getAlias()); + $configs = $containerBuilder->getExtensionConfig($this->getAlias()); // iterate in reverse to preserve the original order after prepending the config foreach (array_reverse($configs) as $config) { // check if entity_manager_name is set in the "acme_hello" configuration if (isset($config['entity_manager_name'])) { // prepend the acme_something settings with the entity_manager_name - $container->prependExtensionConfig('acme_something', [ + $containerBuilder->prependExtensionConfig('acme_something', [ 'entity_manager_name' => $config['entity_manager_name'], ]); } diff --git a/cache.rst b/cache.rst index 1676fc0773c..e9ff5d41de2 100644 --- a/cache.rst +++ b/cache.rst @@ -468,14 +468,14 @@ and use that when configuring the pool. use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Config\FrameworkConfig; - return static function (ContainerBuilder $container, FrameworkConfig $framework) { + return static function (ContainerBuilder $containerBuilder, FrameworkConfig $framework) { $framework->cache() ->pool('cache.my_redis') ->adapters(['cache.adapter.redis']) ->provider('app.my_custom_redis_provider'); - $container->register('app.my_custom_redis_provider', \Redis::class) + $containerBuilder->register('app.my_custom_redis_provider', \Redis::class) ->setFactory([RedisAdapter::class, 'createConnection']) ->addArgument('redis://localhost') ->addArgument([ diff --git a/components/dependency_injection/compilation.rst b/components/dependency_injection/compilation.rst index 4d8fb3e54e3..2d471177c58 100644 --- a/components/dependency_injection/compilation.rst +++ b/components/dependency_injection/compilation.rst @@ -64,10 +64,10 @@ A very simple extension may just load configuration files into the container:: class AcmeDemoExtension implements ExtensionInterface { - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { $loader = new XmlFileLoader( - $container, + $containerBuilder, new FileLocator(__DIR__.'/../Resources/config') ); $loader->load('services.xml'); @@ -135,7 +135,7 @@ are loaded:: The values from those sections of the config files are passed into the first argument of the ``load()`` method of the extension:: - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { $foo = $configs[0]['foo']; //fooValue $bar = $configs[0]['bar']; //barValue @@ -161,7 +161,7 @@ you could access the config value this way:: use Symfony\Component\Config\Definition\Processor; // ... - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { $configuration = new Configuration(); $processor = new Processor(); @@ -222,13 +222,13 @@ The processed config value can now be added as container parameters as if it were listed in a ``parameters`` section of the config file but with the additional benefit of merging multiple files and validation of the configuration:: - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { $configuration = new Configuration(); $processor = new Processor(); $config = $processor->processConfiguration($configuration, $configs); - $container->setParameter('acme_demo.FOO', $config['foo']); + $containerBuilder->setParameter('acme_demo.FOO', $config['foo']); // ... } @@ -237,14 +237,14 @@ More complex configuration requirements can be catered for in the Extension classes. For example, you may choose to load a main service configuration file but also load a secondary one only if a certain parameter is set:: - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $containerBuilder) { $configuration = new Configuration(); $processor = new Processor(); $config = $processor->processConfiguration($configuration, $configs); $loader = new XmlFileLoader( - $container, + $containerBuilder, new FileLocator(__DIR__.'/../Resources/config') ); $loader->load('services.xml'); @@ -295,11 +295,11 @@ method is called by implementing { // ... - public function prepend(ContainerBuilder $container) + public function prepend(ContainerBuilder $containerBuilder) { // ... - $container->prependExtensionConfig($name, $config); + $containerBuilder->prependExtensionConfig($name, $config); // ... } @@ -326,7 +326,7 @@ compilation:: class AcmeDemoExtension implements ExtensionInterface, CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $containerBuilder) { // ... do something during the compilation } @@ -380,7 +380,7 @@ class implementing the ``CompilerPassInterface``:: class CustomPass implements CompilerPassInterface { - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $containerBuilder) { // ... do something during the compilation } diff --git a/configuration/env_var_processors.rst b/configuration/env_var_processors.rst index 4d2615fc3b6..2739433d9a9 100644 --- a/configuration/env_var_processors.rst +++ b/configuration/env_var_processors.rst @@ -107,8 +107,8 @@ Symfony provides the following env var processors: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Config\FrameworkConfig; - return static function (ContainerBuilder $container, FrameworkConfig $framework) { - $container->setParameter('env(SECRET)', 'some_secret'); + return static function (ContainerBuilder $containerBuilder, FrameworkConfig $framework) { + $containerBuilder->setParameter('env(SECRET)', 'some_secret'); $framework->secret(env('SECRET')->string()); }; @@ -153,8 +153,8 @@ Symfony provides the following env var processors: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Config\FrameworkConfig; - return static function (ContainerBuilder $container, FrameworkConfig $framework) { - $container->setParameter('env(HTTP_METHOD_OVERRIDE)', 'true'); + return static function (ContainerBuilder $containerBuilder, FrameworkConfig $framework) { + $containerBuilder->setParameter('env(HTTP_METHOD_OVERRIDE)', 'true'); $framework->httpMethodOverride(env('HTTP_METHOD_OVERRIDE')->bool()); }; @@ -245,8 +245,8 @@ Symfony provides the following env var processors: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Config\SecurityConfig; - return static function (ContainerBuilder $container, SecurityConfig $security) { - $container->setParameter('env(HEALTH_CHECK_METHOD)', 'Symfony\Component\HttpFoundation\Request::METHOD_HEAD'); + return static function (ContainerBuilder $containerBuilder, SecurityConfig $security) { + $containerBuilder->setParameter('env(HEALTH_CHECK_METHOD)', 'Symfony\Component\HttpFoundation\Request::METHOD_HEAD'); $security->accessControl() ->path('^/health-check$') ->methods([env('HEALTH_CHECK_METHOD')->const()]); @@ -296,8 +296,8 @@ Symfony provides the following env var processors: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Config\FrameworkConfig; - return static function (ContainerBuilder $container, FrameworkConfig $framework) { - $container->setParameter('env(TRUSTED_HOSTS)', '["10.0.0.1", "10.0.0.2"]'); + return static function (ContainerBuilder $containerBuilder, FrameworkConfig $framework) { + $containerBuilder->setParameter('env(TRUSTED_HOSTS)', '["10.0.0.1", "10.0.0.2"]'); $framework->trustedHosts(env('TRUSTED_HOSTS')->json()); }; @@ -385,8 +385,8 @@ Symfony provides the following env var processors: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Config\FrameworkConfig; - return static function (ContainerBuilder $container, FrameworkConfig $framework) { - $container->setParameter('env(TRUSTED_HOSTS)', '10.0.0.1,10.0.0.2'); + return static function (ContainerBuilder $containerBuilder, FrameworkConfig $framework) { + $containerBuilder->setParameter('env(TRUSTED_HOSTS)', '10.0.0.1,10.0.0.2'); $framework->trustedHosts(env('TRUSTED_HOSTS')->csv()); }; diff --git a/configuration/using_parameters_in_dic.rst b/configuration/using_parameters_in_dic.rst index 6bdf07ff886..1cc51dcfd9f 100644 --- a/configuration/using_parameters_in_dic.rst +++ b/configuration/using_parameters_in_dic.rst @@ -138,9 +138,9 @@ And set it in the constructor of ``Configuration`` via the ``Extension`` class:: { // ... - public function getConfiguration(array $config, ContainerBuilder $container) + public function getConfiguration(array $config, ContainerBuilder $containerBuilder) { - return new Configuration($container->getParameter('kernel.debug')); + return new Configuration($containerBuilder->getParameter('kernel.debug')); } } diff --git a/event_dispatcher.rst b/event_dispatcher.rst index f10a93bc90f..9fa30f9ab74 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -343,9 +343,9 @@ compiler pass ``AddEventAliasesPass``:: class Kernel extends BaseKernel { - protected function build(ContainerBuilder $container) + protected function build(ContainerBuilder $containerBuilder) { - $container->addCompilerPass(new AddEventAliasesPass([ + $containerBuilder->addCompilerPass(new AddEventAliasesPass([ MyCustomEvent::class => 'my_custom_event', ])); } diff --git a/security.rst b/security.rst index 13743996749..924bbecd58e 100644 --- a/security.rst +++ b/security.rst @@ -1586,7 +1586,7 @@ and set the ``limiter`` option to its service ID: use Symfony\Config\FrameworkConfig; use Symfony\Config\SecurityConfig; - return static function (ContainerBuilder $container, FrameworkConfig $framework, SecurityConfig $security) { + return static function (ContainerBuilder $containerBuilder, FrameworkConfig $framework, SecurityConfig $security) { $framework->rateLimiter() ->limiter('username_ip_login') ->policy('token_bucket') @@ -1602,7 +1602,7 @@ and set the ``limiter`` option to its service ID: ->interval('15 minutes') ; - $container->register('app.login_rate_limiter', DefaultLoginRateLimiter::class) + $containerBuilder->register('app.login_rate_limiter', DefaultLoginRateLimiter::class) ->setArguments([ // 1st argument is the limiter for IP new Reference('limiter.ip_login'), diff --git a/security/access_control.rst b/security/access_control.rst index df9536fef2c..948825e64fd 100644 --- a/security/access_control.rst +++ b/security/access_control.rst @@ -91,8 +91,8 @@ Take the following ``access_control`` entries as an example: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Config\SecurityConfig; - return static function (ContainerBuilder $container, SecurityConfig $security) { - $container->setParameter('env(TRUSTED_IPS)', '10.0.0.1, 10.0.0.2'); + return static function (ContainerBuilder $containerBuilder, SecurityConfig $security) { + $containerBuilder->setParameter('env(TRUSTED_IPS)', '10.0.0.1, 10.0.0.2'); // ... $security->accessControl() diff --git a/service_container/compiler_passes.rst b/service_container/compiler_passes.rst index d0e55c1f51e..462c5942824 100644 --- a/service_container/compiler_passes.rst +++ b/service_container/compiler_passes.rst @@ -26,9 +26,9 @@ Compiler passes are registered in the ``build()`` method of the application kern // ... - protected function build(ContainerBuilder $container): void + protected function build(ContainerBuilder $containerBuilder): void { - $container->addCompilerPass(new CustomPass()); + $containerBuilder->addCompilerPass(new CustomPass()); } } @@ -54,14 +54,14 @@ and process the services inside the ``process()`` method:: // ... - public function process(ContainerBuilder $container): void + public function process(ContainerBuilder $containerBuilder): void { // in this method you can manipulate the service container: // for example, changing some container service: - $container->getDefinition('app.some_private_service')->setPublic(true); + $containerBuilder->getDefinition('app.some_private_service')->setPublic(true); // or processing tagged services: - foreach ($container->findTaggedServiceIds('some_tag') as $id => $tags) { + foreach ($containerBuilder->findTaggedServiceIds('some_tag') as $id => $tags) { // ... } } @@ -83,11 +83,11 @@ method in the extension):: class MyBundle extends Bundle { - public function build(ContainerBuilder $container): void + public function build(ContainerBuilder $containerBuilder): void { - parent::build($container); + parent::build($containerBuilder); - $container->addCompilerPass(new CustomPass()); + $containerBuilder->addCompilerPass(new CustomPass()); } } diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index 19c4ec8862c..3785975549e 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -490,7 +490,7 @@ will share identical locators among all the services referencing them:: use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; - public function process(ContainerBuilder $container): void + public function process(ContainerBuilder $containerBuilder): void { // ... @@ -499,9 +499,9 @@ will share identical locators among all the services referencing them:: 'logger' => new Reference('logger'), ]; - $myService = $container->findDefinition(MyService::class); + $myService = $containerBuilder->findDefinition(MyService::class); - $myService->addArgument(ServiceLocatorTagPass::register($container, $locateableServices)); + $myService->addArgument(ServiceLocatorTagPass::register($containerBuilder, $locateableServices)); } Indexing the Collection of Services diff --git a/service_container/tags.rst b/service_container/tags.rst index 2874fb103f2..9de74dc922c 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -128,9 +128,9 @@ In a Symfony application, call this method in your kernel class:: { // ... - protected function build(ContainerBuilder $container): void + protected function build(ContainerBuilder $containerBuilder): void { - $container->registerForAutoconfiguration(CustomInterface::class) + $containerBuilder->registerForAutoconfiguration(CustomInterface::class) ->addTag('app.custom_tag') ; } @@ -144,9 +144,9 @@ In a Symfony bundle, call this method in the ``load()`` method of the { // ... - public function load(array $configs, ContainerBuilder $container): void + public function load(array $configs, ContainerBuilder $containerBuilder): void { - $container->registerForAutoconfiguration(CustomInterface::class) + $containerBuilder->registerForAutoconfiguration(CustomInterface::class) ->addTag('app.custom_tag') ; } @@ -307,17 +307,17 @@ container for any services with the ``app.mail_transport`` tag:: class MailTransportPass implements CompilerPassInterface { - public function process(ContainerBuilder $container): void + public function process(ContainerBuilder $containerBuilder): void { // always first check if the primary service is defined - if (!$container->has(TransportChain::class)) { + if (!$containerBuilder->has(TransportChain::class)) { return; } - $definition = $container->findDefinition(TransportChain::class); + $definition = $containerBuilder->findDefinition(TransportChain::class); // find all service IDs with the app.mail_transport tag - $taggedServices = $container->findTaggedServiceIds('app.mail_transport'); + $taggedServices = $containerBuilder->findTaggedServiceIds('app.mail_transport'); foreach ($taggedServices as $id => $tags) { // add the transport service to the TransportChain service @@ -344,9 +344,9 @@ or from your kernel:: { // ... - protected function build(ContainerBuilder $container): void + protected function build(ContainerBuilder $containerBuilder): void { - $container->addCompilerPass(new MailTransportPass()); + $containerBuilder->addCompilerPass(new MailTransportPass()); } } @@ -482,7 +482,7 @@ use this, update the compiler:: class TransportCompilerPass implements CompilerPassInterface { - public function process(ContainerBuilder $container): void + public function process(ContainerBuilder $containerBuilder): void { // ... From 68065431184f16a530a45bbf13db4586a4bd4a2d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sat, 7 Jan 2023 17:23:18 +0100 Subject: [PATCH 172/774] Add redirection for removed article --- _build/redirection_map | 1 + 1 file changed, 1 insertion(+) diff --git a/_build/redirection_map b/_build/redirection_map index 9175f68d5e3..315b95fda93 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -536,3 +536,4 @@ /components/security/firewall /security#the-firewall /components/security/secure_tools /security/passwords /components/security /security +/components/var_dumper/advanced /components/var_dumper From bfa6910707210c52ee448716f1e2b71cdda11d33 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 7 Jan 2023 19:20:51 +0100 Subject: [PATCH 173/774] Standardize the name of the container builder variable 6.1 --- bundles/configuration.rst | 2 +- bundles/extension.rst | 2 +- bundles/prepend_extension.rst | 4 ++-- configuration/micro_kernel_trait.rst | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bundles/configuration.rst b/bundles/configuration.rst index 35379d6d0d9..738c2affefb 100644 --- a/bundles/configuration.rst +++ b/bundles/configuration.rst @@ -352,7 +352,7 @@ add this logic to the bundle class directly:: ; } - public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $builder): void + public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void { // Contrary to the Extension class, the "$config" variable is already merged // and processed. You can use it directly to configure the service container. diff --git a/bundles/extension.rst b/bundles/extension.rst index 474de1dd3b2..9e5da5632be 100644 --- a/bundles/extension.rst +++ b/bundles/extension.rst @@ -131,7 +131,7 @@ method:: class AcmeHelloBundle extends AbstractBundle { - public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $builder): void + public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void { // load an XML, PHP or Yaml file $containerConfigurator->import('../config/services.xml'); diff --git a/bundles/prepend_extension.rst b/bundles/prepend_extension.rst index 52bf9d0e5f2..bc3c8342b58 100644 --- a/bundles/prepend_extension.rst +++ b/bundles/prepend_extension.rst @@ -173,10 +173,10 @@ method:: class FooBundle extends AbstractBundle { - public function prependExtension(ContainerConfigurator $containerConfigurator, ContainerBuilder $builder): void + public function prependExtension(ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void { // prepend - $builder->prependExtensionConfig('framework', [ + $containerBuilder->prependExtensionConfig('framework', [ 'cache' => ['prefix_seed' => 'foo/bar'], ]); diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index c7755790764..fca778f2e6a 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -247,9 +247,9 @@ Now it looks like this:: return $bundles; } - protected function build(ContainerBuilder $container) + protected function build(ContainerBuilder $containerBuilder) { - $container->registerExtension(new AppExtension()); + $containerBuilder->registerExtension(new AppExtension()); } protected function configureContainer(ContainerConfigurator $containerConfigurator): void @@ -325,10 +325,10 @@ add a service conditionally based on the ``foo`` value:: ->end(); } - public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $builder): void + public function loadExtension(array $config, ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void { if ($config['foo']) { - $builder->register('foo_service', \stdClass::class); + $containerBuilder->register('foo_service', \stdClass::class); } } } From 1fdd76780f4d01011aa7fe82ebaa2f75fe736f55 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sun, 8 Jan 2023 12:48:37 +0100 Subject: [PATCH 174/774] Update caution resolve doctrine example --- doctrine.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doctrine.rst b/doctrine.rst index 88b5237e6e1..4b205fe73c4 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -65,7 +65,7 @@ The database connection information is stored as an environment variable called you must encode them. See `RFC 3986`_ for the full list of reserved characters or use the :phpfunction:`urlencode` function to encode them. In this case you need to remove the ``resolve:`` prefix in ``config/packages/doctrine.yaml`` to avoid errors: - ``url: '%env(resolve:DATABASE_URL)%'`` + ``url: '%env(DATABASE_URL)%'`` Now that your connection parameters are setup, Doctrine can create the ``db_name`` database for you: From de628c81a92edf33d24038671b3a74fcbb143ab1 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Sun, 8 Jan 2023 14:56:01 +0100 Subject: [PATCH 175/774] [Uid] Add `UuidFactory` and `UlidFactory` mentions --- components/uid.rst | 126 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/components/uid.rst b/components/uid.rst index c2a0c79315b..b2d35f4154f 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -87,6 +87,103 @@ following methods to create a ``Uuid`` object from it:: The ``fromBinary()``, ``fromBase32()``, ``fromBase58()`` and ``fromRfc4122()`` methods were introduced in Symfony 5.3. +You can also use the ``UuidFactory`` to generate UUIDs. First, you may +configure the behavior of the factory using configuration files:: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/uid.yaml + framework: + uid: + default_uuid_version: 6 + name_based_uuid_version: 5 + name_based_uuid_namespace: ~ + time_based_uuid_version: 6 + time_based_uuid_node: ~ + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/packages/uid.php + services() + ->defaults() + ->autowire() + ->autoconfigure(); + + $configurator->extension('framework', [ + 'uid' => [ + 'default_uuid_version' => 6, + 'name_based_uuid_version' => 5, + 'name_based_uuid_namespace' => '', + 'time_based_uuid_version' => 6, + 'time_based_uuid_node' => '', + ], + ]); + }; + +Then, you can inject the factory in your services and use it to generate UUIDs based +on the configuration you defined:: + + uuidFactory = $uuidFactory; + } + + public function generate(): void + { + // This creates a UUID of the version given in the configuration file (v6 by default) + $uuid = $this->uuidFactory->create(); + + $nameBasedUuid = $this->uuidFactory->nameBased(/** ... */); + $randomBasedUuid = $this->uuidFactory->randomBased(); + $timestampBased = $this->uuidFactory->timeBased(); + + // ... + } + } + +.. versionadded:: 5.3 + + The ``UuidFactory`` was introduced in Symfony 5.3. + Converting UUIDs ~~~~~~~~~~~~~~~~ @@ -268,6 +365,35 @@ following methods to create a ``Ulid`` object from it:: The ``fromBinary()``, ``fromBase32()``, ``fromBase58()`` and ``fromRfc4122()`` methods were introduced in Symfony 5.3. +Like UUIDs, ULIDs have their own factory, ``UlidFactory``, that can be used to generate them:: + + ulidFactory = $ulidFactory; + } + + public function generate(): void + { + $ulid = $this->ulidFactory->create(); + + // ... + } + } + +.. versionadded:: 5.3 + + The ``UlidFactory`` was introduced in Symfony 5.3. + There's also a special ``NilUlid`` class to represent ULID ``null`` values:: use Symfony\Component\Uid\NilUlid; From 49e2405e6b5e3639e345709634a99e54a0c374b0 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Sun, 8 Jan 2023 20:05:48 +0100 Subject: [PATCH 176/774] [DependencyInjection] Add #[AsTaggedItem] documentation --- service_container/tags.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/service_container/tags.rst b/service_container/tags.rst index 9de74dc922c..7196d5fea93 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -897,3 +897,25 @@ array element. For example, to retrieve the ``handler_two`` handler:: ]) ; }; + +The #[AsTaggedItem] attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is possible to define both the priority and the index of a tagged +item thanks to the ``#[AsTaggedItem]`` attribute. This attribute must +be used directly on the class of the service you want to configure:: + + // src/Handler/One.php + namespace App\Handler; + + use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem; + + #[AsTaggedItem(index: 'handler_one', priority: 10)] + class One + { + // ... + } + +.. versionadded:: 5.3 + + The ``#[AsTaggedItem]`` attribute was introduced in Symfony 5.3 and requires PHP 8. From ce1630167183e24043c8068acf1a593ca480ef25 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Sun, 8 Jan 2023 20:24:09 +0100 Subject: [PATCH 177/774] [DependencyInjection] Add #[Autoconfigure] and #[AutoconfigureTag] documentation --- service_container/tags.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/service_container/tags.rst b/service_container/tags.rst index 9de74dc922c..f840059afeb 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -117,6 +117,29 @@ If you want to apply tags automatically for your own services, use the ->tag('app.custom_tag'); }; +It is also possible to use the ``#[AutoconfigureTag]`` attribute directly on the +base class or interface:: + + // src/Security/CustomInterface.php + namespace App\Security; + + use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; + + #[AutoconfigureTag('app.custom_tag')] + interface CustomInterface + { + // ... + } + +.. tip:: + + If you need more capabilities to autoconfigure instances of your base class + like their laziness, their bindings or their calls for example, you may rely + on the :class:`Symfony\\Component\\DependencyInjection\\Attribute\\Autoconfigure` attribute. + +.. versionadded:: 5.3 + + The ``#[Autoconfigure]`` and ``#[AutoconfigureTag]`` attributes were introduced in Symfony 5.3. For more advanced needs, you can define the automatic tags using the :method:`Symfony\\Component\\DependencyInjection\\ContainerBuilder::registerForAutoconfiguration` method. From ce8739e680c5e4e8431c056f581eac0fca2c49a4 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Sun, 8 Jan 2023 20:45:30 +0100 Subject: [PATCH 178/774] [DependencyInjection] Add #[Target] documentation --- service_container/autowiring.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/service_container/autowiring.rst b/service_container/autowiring.rst index d74b445a054..084f37587a2 100644 --- a/service_container/autowiring.rst +++ b/service_container/autowiring.rst @@ -531,6 +531,36 @@ If the argument is named ``$shoutyTransformer``, But, you can also manually wire any *other* service by specifying the argument under the arguments key. +Another possibility is to use the ``#[Target]`` attribute. By using this attribute +on the argument you want to autowire, you can define exactly which service to inject +by using its alias. Thanks to this, you're able to have multiple services implementing +the same interface and keep the argument name decorrelated of any implementation name +(like shown in the example above). + +Let's say you defined the ``app.uppercase_transformer`` alias for the +``App\Util\UppercaseTransformer`` service. You would be able to use the ``#[Target]`` +attribute like this:: + + // src/Service/MastodonClient.php + namespace App\Service; + + use App\Util\TransformerInterface; + use Symfony\Component\DependencyInjection\Attribute\Target; + + class MastodonClient + { + private $transformer; + + public function __construct(#[Target('app.uppercase_transformer')] TransformerInterface $transformer) + { + $this->transformer = $transformer; + } + } + +.. versionadded:: 5.3 + + The ``#[Target]`` attribute was introduced in Symfony 5.3. + Fixing Non-Autowireable Arguments --------------------------------- From 5232e0d8b24edc5b7b7a284151f167c6c664e8ff Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 9 Jan 2023 18:33:13 +0100 Subject: [PATCH 179/774] [Uid] Use constructor property promotion for UuidFactory and UlidFactory examples --- components/uid.rst | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/components/uid.rst b/components/uid.rst index 0af55566165..332d523eae2 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -146,11 +146,8 @@ on the configuration you defined:: class FooService { - private UuidFactory $uuidFactory; - - public function __construct(UuidFactory $uuidFactory) + public function __construct(private UuidFactory $uuidFactory) { - $this->uuidFactory = $uuidFactory; } public function generate(): void @@ -337,11 +334,8 @@ Like UUIDs, ULIDs have their own factory, ``UlidFactory``, that can be used to g class FooService { - private UlidFactory $ulidFactory; - - public function __construct(UlidFactory $ulidFactory) + public function __construct(private UlidFactory $ulidFactory) { - $this->ulidFactory = $ulidFactory; } public function generate(): void From 8e427e4e021a0803606e85c48ce91e2c5aafe33b Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 9 Jan 2023 19:48:45 +0100 Subject: [PATCH 180/774] Remove commented rule --- .doctor-rst.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml index e440e026fa9..5b47aa51c28 100644 --- a/.doctor-rst.yaml +++ b/.doctor-rst.yaml @@ -49,7 +49,6 @@ rules: versionadded_directive_should_have_version: ~ yaml_instead_of_yml_suffix: ~ yarn_dev_option_at_the_end: ~ -# no_app_bundle: ~ # master versionadded_directive_major_version: From 70465944e0c4b62a2a562099d8945a835957bcaf Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 9 Jan 2023 23:12:32 +0100 Subject: [PATCH 181/774] Enable ensure_link_definition_contains_valid_url doctor-rst rule --- .doctor-rst.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml index 5b47aa51c28..3c671b143db 100644 --- a/.doctor-rst.yaml +++ b/.doctor-rst.yaml @@ -7,6 +7,7 @@ rules: composer_dev_option_not_at_the_end: ~ correct_code_block_directive_based_on_the_content: ~ deprecated_directive_should_have_version: ~ + ensure_exactly_one_space_before_directive_type: ~ ensure_exactly_one_space_between_link_definition_and_link: ~ ensure_link_definition_contains_valid_url: ~ ensure_order_of_code_blocks_in_configuration_block: ~ From 17b880b64dc34299023248b61ec93f7c2421e9e9 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 10 Jan 2023 09:20:22 +0100 Subject: [PATCH 182/774] Use CPP --- service_container/autowiring.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/service_container/autowiring.rst b/service_container/autowiring.rst index f6064970e2c..103d92aae84 100644 --- a/service_container/autowiring.rst +++ b/service_container/autowiring.rst @@ -547,11 +547,10 @@ attribute like this:: class MastodonClient { - private $transformer; - - public function __construct(#[Target('app.uppercase_transformer')] TransformerInterface $transformer) - { - $this->transformer = $transformer; + public function __construct( + #[Target('app.uppercase_transformer')] + private TransformerInterface $transformer + ){ } } From 4b6ee9ded1f4225861a37cfc5e459768df537591 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 10 Jan 2023 09:22:43 +0100 Subject: [PATCH 183/774] Better highlighting --- service_container/tags.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index 7196d5fea93..0e376d46a4b 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -898,8 +898,8 @@ array element. For example, to retrieve the ``handler_two`` handler:: ; }; -The #[AsTaggedItem] attribute -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The ``#[AsTaggedItem]`` attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is possible to define both the priority and the index of a tagged item thanks to the ``#[AsTaggedItem]`` attribute. This attribute must From 2e6cbc2191905f8844625ee738cccca6cd55d9b4 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 10 Jan 2023 09:22:59 +0100 Subject: [PATCH 184/774] Minor --- service_container/tags.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index 0e376d46a4b..53e0374b4c8 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -918,4 +918,4 @@ be used directly on the class of the service you want to configure:: .. versionadded:: 5.3 - The ``#[AsTaggedItem]`` attribute was introduced in Symfony 5.3 and requires PHP 8. + The ``#[AsTaggedItem]`` attribute was introduced in Symfony 5.3. From a21f9c891442116ab530a13ea77ed825e0237633 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 10 Jan 2023 09:23:34 +0100 Subject: [PATCH 185/774] Remove obsolete versionadded directive --- service_container/tags.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index 751ae20c86c..a69b9b1b754 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -911,7 +911,3 @@ be used directly on the class of the service you want to configure:: { // ... } - -.. versionadded:: 5.3 - - The ``#[AsTaggedItem]`` attribute was introduced in Symfony 5.3. From c6ce66cb240eaa0e1f0bdfb02f15bcc57ebf122c Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 10 Jan 2023 09:24:28 +0100 Subject: [PATCH 186/774] Remove obsolete versionadded directive --- service_container/autowiring.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/service_container/autowiring.rst b/service_container/autowiring.rst index 103d92aae84..328d01fe648 100644 --- a/service_container/autowiring.rst +++ b/service_container/autowiring.rst @@ -554,10 +554,6 @@ attribute like this:: } } -.. versionadded:: 5.3 - - The ``#[Target]`` attribute was introduced in Symfony 5.3. - Fixing Non-Autowireable Arguments --------------------------------- From b93967ad41fd1baac5ea77100bbf5773aa992ea2 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 10 Jan 2023 09:55:00 +0100 Subject: [PATCH 187/774] Fix: Typos --- service_container/service_subscribers_locators.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index 3785975549e..c3cf4c6a744 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -307,8 +307,8 @@ or directly via PHP attributes: - - + + From 4150435c75fcb40dc74a6b6047c3717aabbd9bcd Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 10 Jan 2023 14:18:21 +0100 Subject: [PATCH 188/774] Remove obsolete versionadded directive --- service_container/tags.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index 668fafaabc5..913dd417ee4 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -137,10 +137,6 @@ base class or interface:: like their laziness, their bindings or their calls for example, you may rely on the :class:`Symfony\\Component\\DependencyInjection\\Attribute\\Autoconfigure` attribute. -.. versionadded:: 5.3 - - The ``#[Autoconfigure]`` and ``#[AutoconfigureTag]`` attributes were introduced in Symfony 5.3. - For more advanced needs, you can define the automatic tags using the :method:`Symfony\\Component\\DependencyInjection\\ContainerBuilder::registerForAutoconfiguration` method. From b8350735a0581bbb138a3843b2353ebe2f7c6ac2 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 10 Jan 2023 17:49:37 +0100 Subject: [PATCH 189/774] Add missing redirection --- _build/redirection_map | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_build/redirection_map b/_build/redirection_map index 315b95fda93..064f9c8261a 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -536,4 +536,5 @@ /components/security/firewall /security#the-firewall /components/security/secure_tools /security/passwords /components/security /security -/components/var_dumper/advanced /components/var_dumper +/components/var_dumper/advanced /components/var_dumper#advanced-usage +/components/yaml/yaml_format /components/yaml#yaml-format From cc078fdcaf8f3e7dcffc160e959f5698f402064c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 10 Jan 2023 19:30:17 +0100 Subject: [PATCH 190/774] Tweaks --- configuration.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/configuration.rst b/configuration.rst index ca62ee96df6..fbdae492184 100644 --- a/configuration.rst +++ b/configuration.rst @@ -20,12 +20,12 @@ directory, which has this default structure: │ └─ services.yaml * The ``routes.yaml`` file defines the :doc:`routing configuration `; -* the ``services.yaml`` file configures the services of the +* The ``services.yaml`` file configures the services of the :doc:`service container `; -* the ``bundles.php`` file enables/disables packages in your application. +* The ``bundles.php`` file enables/disables packages in your application; +* The ``config/packages/`` directory stores the configuration of every package + installed in your application. -The ``config/packages/`` directory -stores the configuration of every package installed in your application. Packages (also called "bundles" in Symfony and "plugins/modules" in other projects) add ready-to-use features to your projects. From 62f516596b9e5efc595bbdb0672400231a3810c7 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 7 Jan 2023 18:02:00 +0100 Subject: [PATCH 191/774] [EventDispatcher] Allow to omit the event name when registering listeners --- event_dispatcher.rst | 45 ++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/event_dispatcher.rst b/event_dispatcher.rst index f10a93bc90f..17e4e02019c 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -32,7 +32,7 @@ The most common way to listen to an event is to register an **event listener**:: class ExceptionListener { - public function onKernelException(ExceptionEvent $event) + public function __invoke(ExceptionEvent $event): void { // You get the exception object from the received event $exception = $event->getThrowable(); @@ -60,16 +60,8 @@ The most common way to listen to an event is to register an **event listener**:: } } -.. tip:: - - Each event receives a slightly different type of ``$event`` object. For - the ``kernel.exception`` event, it is :class:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent`. - Check out the :doc:`Symfony events reference ` to see - what type of object each event provides. - Now that the class is created, you need to register it as a service and -notify Symfony that it is a "listener" on the ``kernel.exception`` event by -using a special "tag": +notify Symfony that it is a event listener by using a special "tag": .. configuration-block:: @@ -78,8 +70,7 @@ using a special "tag": # config/services.yaml services: App\EventListener\ExceptionListener: - tags: - - { name: kernel.event_listener, event: kernel.exception } + tags: [kernel.event_listener] .. code-block:: xml @@ -92,7 +83,7 @@ using a special "tag": - + @@ -108,7 +99,7 @@ using a special "tag": $services = $containerConfigurator->services(); $services->set(ExceptionListener::class) - ->tag('kernel.event_listener', ['event' => 'kernel.exception']) + ->tag('kernel.event_listener') ; }; @@ -117,10 +108,7 @@ listener class: #. If the ``kernel.event_listener`` tag defines the ``method`` attribute, that's the name of the method to be called; -#. If no ``method`` attribute is defined, try to call the method whose name - is ``on`` + "PascalCased event name" (e.g. ``onKernelException()`` method for - the ``kernel.exception`` event); -#. If that method is not defined either, try to call the ``__invoke()`` magic +#. If no ``method`` attribute is defined, try to call the ``__invoke()`` magic method (which makes event listeners invokable); #. If the ``__invoke()`` method is not defined either, throw an exception. @@ -134,6 +122,27 @@ listener class: internal Symfony listeners usually range from ``-256`` to ``256`` but your own listeners can use any positive or negative integer. +.. note:: + + There is an optional attribute for the ``kernel.event_listener`` tag called + ``event`` which is useful when listener ``$event`` argument is not typed. + If you configure it, it will change type of ``$event`` object. + For the ``kernel.exception`` event, it is :class:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent`. + Check out the :doc:`Symfony events reference ` to see + what type of object each event provides. + + With this attribute, Symfony follows this logic to decide which method to call + inside the event listener class: + + #. If the ``kernel.event_listener`` tag defines the ``method`` attribute, that's + the name of the method to be called; + #. If no ``method`` attribute is defined, try to call the method whose name + is ``on`` + "PascalCased event name" (e.g. ``onKernelException()`` method for + the ``kernel.exception`` event); + #. If that method is not defined either, try to call the ``__invoke()`` magic + method (which makes event listeners invokable); + #. If the ``__invoke()`` method is not defined either, throw an exception. + Defining Event Listeners with PHP Attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From f068e3cff0879ac0a9c09416900f35376fff31f3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 10 Jan 2023 11:58:03 +0100 Subject: [PATCH 192/774] Add a section about how to add an argument to a method without breaking BC --- contributing/code/bc.rst | 379 +++++++++++++++++++++++---------------- 1 file changed, 222 insertions(+), 157 deletions(-) diff --git a/contributing/code/bc.rst b/contributing/code/bc.rst index 3f1e6164087..ae97007117e 100644 --- a/contributing/code/bc.rst +++ b/contributing/code/bc.rst @@ -231,102 +231,102 @@ Changing Classes This table tells you which changes you are allowed to do when working on Symfony's classes: -================================================== ============== =============== -Type of Change Change Allowed Notes -================================================== ============== =============== -Remove entirely No -Make final No :ref:`[6] ` -Make abstract No -Change name or namespace No -Change parent class Yes :ref:`[4] ` -Add interface Yes -Remove interface No +======================================================================== ============== =============== +Type of Change Change Allowed Notes +======================================================================== ============== =============== +Remove entirely No +Make final No :ref:`[6] ` +Make abstract No +Change name or namespace No +Change parent class Yes :ref:`[4] ` +Add interface Yes +Remove interface No **Public Properties** -Add public property Yes -Remove public property No -Reduce visibility No -Move to parent class Yes +Add public property Yes +Remove public property No +Reduce visibility No +Move to parent class Yes **Protected Properties** -Add protected property Yes -Remove protected property No :ref:`[7] ` -Reduce visibility No :ref:`[7] ` -Make public No :ref:`[7] ` -Move to parent class Yes +Add protected property Yes +Remove protected property No :ref:`[7] ` +Reduce visibility No :ref:`[7] ` +Make public No :ref:`[7] ` +Move to parent class Yes **Private Properties** -Add private property Yes -Make public or protected Yes -Remove private property Yes +Add private property Yes +Make public or protected Yes +Remove private property Yes **Constructors** -Add constructor without mandatory arguments Yes :ref:`[1] ` -Remove constructor No -Reduce visibility of a public constructor No -Reduce visibility of a protected constructor No :ref:`[7] ` -Move to parent class Yes +Add constructor without mandatory arguments Yes :ref:`[1] ` +Remove constructor No +Reduce visibility of a public constructor No +Reduce visibility of a protected constructor No :ref:`[7] ` +Move to parent class Yes **Destructors** -Add destructor Yes -Remove destructor No -Move to parent class Yes +Add destructor Yes +Remove destructor No +Move to parent class Yes **Public Methods** -Add public method Yes -Remove public method No -Change name No -Reduce visibility No -Make final No :ref:`[6] ` -Move to parent class Yes -Add argument without a default value No -Add argument with a default value No :ref:`[7] ` :ref:`[8] ` -Remove argument No :ref:`[3] ` -Add default value to an argument No :ref:`[7] ` :ref:`[8] ` -Remove default value of an argument No -Add type hint to an argument No :ref:`[7] ` :ref:`[8] ` -Remove type hint of an argument No :ref:`[7] ` :ref:`[8] ` -Change argument type No :ref:`[7] ` :ref:`[8] ` -Add return type No :ref:`[7] ` :ref:`[8] ` -Remove return type No :ref:`[7] ` :ref:`[8] ` :ref:`[9] ` -Change return type No :ref:`[7] ` :ref:`[8] ` +Add public method Yes +Remove public method No +Change name No +Reduce visibility No +Make final No :ref:`[6] ` +Move to parent class Yes +:ref:`Add argument without a default value ` No +:ref:`Add argument with a default value ` No :ref:`[7] ` :ref:`[8] ` +Remove argument No :ref:`[3] ` +Add default value to an argument No :ref:`[7] ` :ref:`[8] ` +Remove default value of an argument No +Add type hint to an argument No :ref:`[7] ` :ref:`[8] ` +Remove type hint of an argument No :ref:`[7] ` :ref:`[8] ` +Change argument type No :ref:`[7] ` :ref:`[8] ` +Add return type No :ref:`[7] ` :ref:`[8] ` +Remove return type No :ref:`[7] ` :ref:`[8] ` :ref:`[9] ` +Change return type No :ref:`[7] ` :ref:`[8] ` **Protected Methods** -Add protected method Yes -Remove protected method No :ref:`[7] ` -Change name No :ref:`[7] ` -Reduce visibility No :ref:`[7] ` -Make final No :ref:`[6] ` -Make public No :ref:`[7] ` :ref:`[8] ` -Move to parent class Yes -Add argument without a default value No :ref:`[7] ` -Add argument with a default value No :ref:`[7] ` :ref:`[8] ` -Remove argument No :ref:`[3] ` -Add default value to an argument No :ref:`[7] ` :ref:`[8] ` -Remove default value of an argument No :ref:`[7] ` -Add type hint to an argument No :ref:`[7] ` :ref:`[8] ` -Remove type hint of an argument No :ref:`[7] ` :ref:`[8] ` -Change argument type No :ref:`[7] ` :ref:`[8] ` -Add return type No :ref:`[7] ` :ref:`[8] ` -Remove return type No :ref:`[7] ` :ref:`[8] ` :ref:`[9] ` -Change return type No :ref:`[7] ` :ref:`[8] ` +Add protected method Yes +Remove protected method No :ref:`[7] ` +Change name No :ref:`[7] ` +Reduce visibility No :ref:`[7] ` +Make final No :ref:`[6] ` +Make public No :ref:`[7] ` :ref:`[8] ` +Move to parent class Yes +:ref:`Add argument without a default value ` No +:ref:`Add argument with a default value ` No :ref:`[7] ` :ref:`[8] ` +Remove argument No :ref:`[3] ` +Add default value to an argument No :ref:`[7] ` :ref:`[8] ` +Remove default value of an argument No :ref:`[7] ` +Add type hint to an argument No :ref:`[7] ` :ref:`[8] ` +Remove type hint of an argument No :ref:`[7] ` :ref:`[8] ` +Change argument type No :ref:`[7] ` :ref:`[8] ` +Add return type No :ref:`[7] ` :ref:`[8] ` +Remove return type No :ref:`[7] ` :ref:`[8] ` :ref:`[9] ` +Change return type No :ref:`[7] ` :ref:`[8] ` **Private Methods** -Add private method Yes -Remove private method Yes -Change name Yes -Make public or protected Yes -Add argument without a default value Yes -Add argument with a default value Yes -Remove argument Yes -Add default value to an argument Yes -Remove default value of an argument Yes -Add type hint to an argument Yes -Remove type hint of an argument Yes -Change argument type Yes -Add return type Yes -Remove return type Yes -Change return type Yes +Add private method Yes +Remove private method Yes +Change name Yes +Make public or protected Yes +Add argument without a default value Yes +Add argument with a default value Yes +Remove argument Yes +Add default value to an argument Yes +Remove default value of an argument Yes +Add type hint to an argument Yes +Remove type hint of an argument Yes +Change argument type Yes +Add return type Yes +Remove return type Yes +Change return type Yes **Static Methods and Properties** -Turn non static into static No :ref:`[7] ` :ref:`[8] ` -Turn static into non static No +Turn non static into static No :ref:`[7] ` :ref:`[8] ` +Turn static into non static No **Constants** -Add constant Yes -Remove constant No -Change value of a constant Yes :ref:`[1] ` :ref:`[5] ` -================================================== ============== =============== +Add constant Yes +Remove constant No +Change value of a constant Yes :ref:`[1] ` :ref:`[5] ` +======================================================================== ============== =============== Changing Traits ~~~~~~~~~~~~~~~ @@ -334,84 +334,84 @@ Changing Traits This table tells you which changes you are allowed to do when working on Symfony's traits: -================================================== ============== =============== -Type of Change Change Allowed Notes -================================================== ============== =============== -Remove entirely No -Change name or namespace No -Use another trait Yes +=============================================================================== ============== =============== +Type of Change Change Allowed Notes +=============================================================================== ============== =============== +Remove entirely No +Change name or namespace No +Use another trait Yes **Public Properties** -Add public property Yes -Remove public property No -Reduce visibility No -Move to a used trait Yes +Add public property Yes +Remove public property No +Reduce visibility No +Move to a used trait Yes **Protected Properties** -Add protected property Yes -Remove protected property No -Reduce visibility No -Make public No -Move to a used trait Yes +Add protected property Yes +Remove protected property No +Reduce visibility No +Make public No +Move to a used trait Yes **Private Properties** -Add private property Yes -Remove private property No -Make public or protected Yes -Move to a used trait Yes +Add private property Yes +Remove private property No +Make public or protected Yes +Move to a used trait Yes **Constructors and destructors** -Have constructor or destructor No +Have constructor or destructor No **Public Methods** -Add public method Yes -Remove public method No -Change name No -Reduce visibility No -Make final No :ref:`[6] ` -Move to used trait Yes -Add argument without a default value No -Add argument with a default value No -Remove argument No -Add default value to an argument No -Remove default value of an argument No -Add type hint to an argument No -Remove type hint of an argument No -Change argument type No -Change return type No +Add public method Yes +Remove public method No +Change name No +Reduce visibility No +Make final No :ref:`[6] ` +Move to used trait Yes +:ref:`Add argument without a default value ` No +:ref:`Add argument with a default value ` No +Remove argument No +Add default value to an argument No +Remove default value of an argument No +Add type hint to an argument No +Remove type hint of an argument No +Change argument type No +Change return type No **Protected Methods** -Add protected method Yes -Remove protected method No -Change name No -Reduce visibility No -Make final No :ref:`[6] ` -Make public No :ref:`[8] ` -Move to used trait Yes -Add argument without a default value No -Add argument with a default value No -Remove argument No -Add default value to an argument No -Remove default value of an argument No -Add type hint to an argument No -Remove type hint of an argument No -Change argument type No -Change return type No +Add protected method Yes +Remove protected method No +Change name No +Reduce visibility No +Make final No :ref:`[6] ` +Make public No :ref:`[8] ` +Move to used trait Yes +:ref:`Add argument without a default value ` No +:ref:`Add argument with a default value ` No +Remove argument No +Add default value to an argument No +Remove default value of an argument No +Add type hint to an argument No +Remove type hint of an argument No +Change argument type No +Change return type No **Private Methods** -Add private method Yes -Remove private method No -Change name No -Make public or protected Yes -Move to used trait Yes -Add argument without a default value No -Add argument with a default value No -Remove argument No -Add default value to an argument No -Remove default value of an argument No -Add type hint to an argument No -Remove type hint of an argument No -Change argument type No -Add return type No -Remove return type No -Change return type No -**Static Methods and Properties** -Turn non static into static No -Turn static into non static No -================================================== ============== =============== +Add private method Yes +Remove private method No +Change name No +Make public or protected Yes +Move to used trait Yes +Add argument without a default value No +Add argument with a default value No +Remove argument No +Add default value to an argument No +Remove default value of an argument No +Add type hint to an argument No +Remove type hint of an argument No +Change argument type No +Add return type No +Remove return type No +Change return type No +**Static Methods and Properties** +Turn non static into static No +Turn static into non static No +=============================================================================== ============== =============== Notes ~~~~~ @@ -473,4 +473,69 @@ a return type is only possible with a child type. constructors of Attribute classes. Using PHP named arguments might break your code when upgrading to newer Symfony versions. +Making Code Changes in a Backward Compatible Way +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +As you read above, many changes are not allowed because they would represent a +backward compability break. However, we want to be able to improve the code and +its features over time and that can be done thanks to some strategies that +allow to still do some unallowed changes in several steps that ensure backward +compability and a smooth upgrade path. Some of them are described in the next +sections. + +.. _add-argument-public-method: + +Adding an Argument to a Public Method +..................................... + +Adding a new argument to a public method is possible only if this is the last +argument of the method. + +If that's the case, here is how to do it properly in a minor version: + +#. Add the argument as a comment in the signature:: + + // the new argument can be optional + public function say(string $text, /* bool $stripWithespace = true */): void + { + } + + // or required + public function say(string $text, /* bool $stripWithespace */): void + { + } + +#. Document the new argument in a PHPDoc:: + + /** + * @param bool $stripWithespace + */ + +#. Use ``func_num_args`` and ``func_get_arg`` to retrieve the argument in the + method:: + + $stripWithespace = 2 <= \func_num_args() ? func_get_arg(1) : false; + + Note that the default value is ``false`` to keep the current behavior. + +#. If the argument has a default value that will change the current behavior, + warn the user:: + + trigger_deprecation('symfony/COMPONENT', 'X.Y', 'Not passing the "bool $stripWithespace" argument explicitly is deprecated, its default value will change to X in Z.0.'); + +#. If the argument has no default value, warn the user that is going to be + required in the next major version:: + + if (\func_num_args() < 2) { + trigger_deprecation('symfony/COMPONENT', 'X.Y', 'The "%s()" method will have a new "bool $stripWithespace" argument in version Z.0, not defining it is deprecated.', __METHOD__); + + $stripWithespace = false; + } else { + $stripWithespace = func_get_arg(1); + } + +#. In the next major version (``X.0``), uncomment the argument, remove the + PHPDoc if there is no need for a description, and remove the + ``func_get_arg`` code and the warning if any. + .. _`Semantic Versioning`: https://semver.org/ From 12cc8a28adc7522a1d36727fef78aff56349782d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 11 Jan 2023 08:50:42 +0100 Subject: [PATCH 193/774] Fix Symfony versions when needed --- README.markdown | 4 +-- contributing/code/pull_requests.rst | 40 ++++++++++++++--------------- contributing/code/tests.rst | 2 +- contributing/community/releases.rst | 20 +++++++-------- 4 files changed, 33 insertions(+), 33 deletions(-) diff --git a/README.markdown b/README.markdown index b8c8f863b4d..8424f980f7e 100644 --- a/README.markdown +++ b/README.markdown @@ -26,8 +26,8 @@ Contributing We love contributors! For more information on how you can contribute, please read the [Symfony Docs Contributing Guide](https://symfony.com/doc/current/contributing/documentation/overview.html). -**Important**: use `4.4` branch as the base of your pull requests, unless you are -documenting a feature that was introduced *after* Symfony 4.4 (e.g. in Symfony 5.4). +**Important**: use `5.4` branch as the base of your pull requests, unless you are +documenting a feature that was introduced *after* Symfony 5.4 (e.g. in Symfony 6.2). Build Documentation Locally --------------------------- diff --git a/contributing/code/pull_requests.rst b/contributing/code/pull_requests.rst index fa2306f613c..5255613c000 100644 --- a/contributing/code/pull_requests.rst +++ b/contributing/code/pull_requests.rst @@ -132,20 +132,20 @@ work: branch (you can find them on the `Symfony releases page`_). E.g. if you found a bug introduced in ``v5.1.10``, you need to work on ``5.4``. -* ``6.2``, if you are adding a new feature. +* ``6.3``, if you are adding a new feature. The only exception is when a new :doc:`major Symfony version ` (5.0, 6.0, etc.) comes out every two years. Because of the :ref:`special development process ` of those versions, - you need to use the previous minor version for the features (e.g. use ``4.4`` - instead of ``5.0``, use ``5.4`` instead of ``6.0``, etc.) + you need to use the previous minor version for the features (e.g. use ``5.4`` + instead of ``6.0``, use ``6.4`` instead of ``7.0``, etc.) .. note:: All bug fixes merged into maintenance branches are also merged into more recent branches on a regular basis. For instance, if you submit a PR - for the ``4.4`` branch, the PR will also be applied by the core team on - the ``5.x`` and ``6.x`` branches. + for the ``5.4`` branch, the PR will also be applied by the core team on + all the ``6.x`` branches that are still maintained. Create a Topic Branch ~~~~~~~~~~~~~~~~~~~~~ @@ -157,18 +157,18 @@ topic branch: $ git checkout -b BRANCH_NAME 5.x -Or, if you want to provide a bug fix for the ``4.4`` branch, first track the remote -``4.4`` branch locally: +Or, if you want to provide a bug fix for the ``5.4`` branch, first track the remote +``5.4`` branch locally: .. code-block:: terminal - $ git checkout --track origin/4.4 + $ git checkout --track origin/5.4 -Then create a new branch off the ``4.4`` branch to work on the bug fix: +Then create a new branch off the ``5.4`` branch to work on the bug fix: .. code-block:: terminal - $ git checkout -b BRANCH_NAME 4.4 + $ git checkout -b BRANCH_NAME 5.4 .. tip:: @@ -284,15 +284,15 @@ while to finish your changes): .. code-block:: terminal - $ git checkout 5.x + $ git checkout 6.x $ git fetch upstream - $ git merge upstream/5.x + $ git merge upstream/6.x $ git checkout BRANCH_NAME - $ git rebase 5.x + $ git rebase 6.x .. tip:: - Replace ``5.x`` with the branch you selected previously (e.g. ``4.4``) + Replace ``6.x`` with the branch you selected previously (e.g. ``5.4``) if you are working on a bug fix. When doing the ``rebase`` command, you might have to fix merge conflicts. @@ -319,8 +319,8 @@ You can now make a pull request on the ``symfony/symfony`` GitHub repository. .. tip:: - Take care to point your pull request towards ``symfony:4.4`` if you want - the core team to pull a bug fix based on the ``4.4`` branch. + Take care to point your pull request towards ``symfony:5.4`` if you want + the core team to pull a bug fix based on the ``5.4`` branch. To ease the core team work, always include the modified components in your pull request message, like in: @@ -461,7 +461,7 @@ test scenarios run on each change: This job also runs relevant packages using a "flipped" test (indicated by a ``^`` suffix in the package name). These tests checkout the - previous major release (e.g. ``4.4`` for a pull requests on ``5.4``) + previous major release (e.g. ``5.4`` for a pull requests on ``6.3``) and run the tests with your branch as dependency. A failure in these flipped tests indicate a backwards compatibility @@ -500,12 +500,12 @@ Rework your Pull Request ~~~~~~~~~~~~~~~~~~~~~~~~ Based on the feedback on the pull request, you might need to rework your -PR. Before re-submitting the PR, rebase with ``upstream/5.x`` or -``upstream/4.4``, don't merge; and force the push to the origin: +PR. Before re-submitting the PR, rebase with ``upstream/6.x`` or +``upstream/5.4``, don't merge; and force the push to the origin: .. code-block:: terminal - $ git rebase -f upstream/5.x + $ git rebase -f upstream/6.x $ git push --force origin BRANCH_NAME .. note:: diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst index 376792f879f..4c0c65ae09c 100644 --- a/contributing/code/tests.rst +++ b/contributing/code/tests.rst @@ -32,7 +32,7 @@ tests, such as Doctrine, Twig and Monolog. To do so, .. code-block:: terminal - $ COMPOSER_ROOT_VERSION=4.4.x-dev composer update + $ COMPOSER_ROOT_VERSION=5.4.x-dev composer update .. _running: diff --git a/contributing/community/releases.rst b/contributing/community/releases.rst index 0ecaa641f9b..fa452b67dfc 100644 --- a/contributing/community/releases.rst +++ b/contributing/community/releases.rst @@ -7,9 +7,9 @@ release and maintain its different versions. Symfony releases follow the `semantic versioning`_ strategy and they are published through a *time-based model*: -* A new **Symfony patch version** (e.g. 4.4.12, 5.1.9) comes out roughly every +* A new **Symfony patch version** (e.g. 5.4.12, 6.1.9) comes out roughly every month. It only contains bug fixes, so you can safely upgrade your applications; -* A new **Symfony minor version** (e.g. 4.4, 5.0, 5.1) comes out every *six months*: +* A new **Symfony minor version** (e.g. 5.4, 6.0, 6.1) comes out every *six months*: one in *May* and one in *November*. It contains bug fixes and new features, can contain new deprecations but it doesn't include any breaking change, so you can safely upgrade your applications; @@ -54,7 +54,7 @@ Maintenance Starting from the Symfony 3.x branch, the number of minor versions is limited to five per branch (X.0, X.1, X.2, X.3 and X.4). The last minor version of a branch -(e.g. 4.4, 5.4) is considered a **long-term support version** and the other +(e.g. 5.4, 6.4) is considered a **long-term support version** and the other ones are considered **standard versions**: ======================= ===================== ================================ @@ -88,17 +88,17 @@ learn more about how deprecations are handled in Symfony. .. _major-version-development: This deprecation policy also requires a custom development process for major -versions (5.0, 6.0, etc.) In those cases, Symfony develops at the same time -two versions: the new major one (e.g. 5.0) and the latest version of the -previous branch (e.g. 4.4). +versions (6.0, 7.0, etc.) In those cases, Symfony develops at the same time +two versions: the new major one (e.g. 6.0) and the latest version of the +previous branch (e.g. 5.4). Both versions have the same new features, but they differ in the deprecated -features. The oldest version (4.4 in this example) contains all the deprecated -features whereas the new version (5.0 in this example) removes all of them. +features. The oldest version (5.4 in this example) contains all the deprecated +features whereas the new version (6.0 in this example) removes all of them. -This allows you to upgrade your projects to the latest minor version (e.g. 4.4), +This allows you to upgrade your projects to the latest minor version (e.g. 5.4), see all the deprecation messages and fix them. Once you have fixed all those -deprecations, you can upgrade to the new major version (e.g. 5.0) without +deprecations, you can upgrade to the new major version (e.g. 6.0) without effort, because it contains the same features (the only difference are the deprecated features, which your project no longer uses). From 384046125741795282451f7d898f8a088d521da6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 16 Dec 2022 17:06:28 +0100 Subject: [PATCH 194/774] Deprecate the proxy-manager package --- service_container/lazy_services.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service_container/lazy_services.rst b/service_container/lazy_services.rst index 6f92f5b0d5d..b5190c0d159 100644 --- a/service_container/lazy_services.rst +++ b/service_container/lazy_services.rst @@ -33,8 +33,8 @@ until you interact with the proxy in some way. .. versionadded:: 6.2 - Starting from Symfony 6.2, you don't have to install any package (e.g. - ``symfony/proxy-manager-bridge``) in order to use the lazy service instantiation. + Starting from Symfony 6.2, service laziness is supported out of the box + without having to install any additional package. Configuration ------------- From 32b12df56980c6f0d9c3f40d463b9077626b9f65 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 11 Jan 2023 08:58:57 +0100 Subject: [PATCH 195/774] Remove obsolete comments --- components/form.rst | 4 ---- reference/configuration/twig.rst | 8 ++++---- templating/twig_extension.rst | 4 ---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/components/form.rst b/components/form.rst index d4fdab0a7ec..7e7e92b3b48 100644 --- a/components/form.rst +++ b/components/form.rst @@ -223,10 +223,6 @@ to bootstrap or access Twig and add the :class:`Symfony\\Bridge\\Twig\\Extension // ... ->getFormFactory(); -.. versionadded:: 1.30 - - The ``Twig\RuntimeLoader\FactoryRuntimeLoader`` was introduced in Twig 1.30. - The exact details of your `Twig Configuration`_ will vary, but the goal is always to add the :class:`Symfony\\Bridge\\Twig\\Extension\\FormExtension` to Twig, which gives you access to the Twig functions for rendering forms. diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index 8ac4068517e..29b22fb3e28 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -67,10 +67,10 @@ autoescape_service **type**: ``string`` **default**: ``null`` -As of Twig 1.17, the escaping strategy applied by default to the template is -determined during compilation time based on the filename of the template. This -means for example that the contents of a ``*.html.twig`` template are escaped -for HTML and the contents of ``*.js.twig`` are escaped for JavaScript. +The escaping strategy applied by default to the template is determined during +compilation time based on the filename of the template. This means for example +that the contents of a ``*.html.twig`` template are escaped for HTML and the +contents of ``*.js.twig`` are escaped for JavaScript. This option allows to define the Symfony service which will be used to determine the default escaping applied to the template. diff --git a/templating/twig_extension.rst b/templating/twig_extension.rst index 64f2c7519cb..3654fab4f6c 100644 --- a/templating/twig_extension.rst +++ b/templating/twig_extension.rst @@ -103,10 +103,6 @@ this command to confirm that your new filter was successfully registered: Creating Lazy-Loaded Twig Extensions ------------------------------------ -.. versionadded:: 1.35 - - Support for lazy-loaded extensions was introduced in Twig 1.35.0 and 2.4.4. - Including the code of the custom filters/functions in the Twig extension class is the simplest way to create extensions. However, Twig must initialize all extensions before rendering any template, even if the template doesn't use an From dc906476dd916a78d80c236e5f4967a778e89574 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 11 Jan 2023 09:04:22 +0100 Subject: [PATCH 196/774] Remove obsolete blacklist entries Follows * https://github.com/symfony/symfony-docs/pull/17711 --- .doctor-rst.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml index 3c671b143db..2833840d86f 100644 --- a/.doctor-rst.yaml +++ b/.doctor-rst.yaml @@ -83,8 +83,6 @@ whitelist: - '.. versionadded:: 1.9.0' # Encore - '.. versionadded:: 0.28.4' # Encore - '.. versionadded:: 2.4.0' # SwiftMailer - - '.. versionadded:: 1.30' # Twig - - '.. versionadded:: 1.35' # Twig - '.. versionadded:: 1.11' # Messenger (Middleware / DoctrineBundle) - '.. versionadded:: 1.18' # Flex in setup/upgrade_minor.rst - '.. versionadded:: 1.0.0' # Encore From b07c069ce9cd973977d7d600ff40c6a25ebd46ac Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 11 Jan 2023 09:04:35 +0100 Subject: [PATCH 197/774] Bump versions on the build script --- _build/build.php | 2 +- _build/composer.json | 8 +- _build/composer.lock | 728 ++++++++++++++++++++----------------------- 3 files changed, 342 insertions(+), 396 deletions(-) diff --git a/_build/build.php b/_build/build.php index 989eaea7cbe..897fd8dac20 100755 --- a/_build/build.php +++ b/_build/build.php @@ -20,7 +20,7 @@ $outputDir = __DIR__.'/output'; $buildConfig = (new BuildConfig()) - ->setSymfonyVersion('4.4') + ->setSymfonyVersion('5.4') ->setContentDir(__DIR__.'/..') ->setOutputDir($outputDir) ->setImagesDir(__DIR__.'/output/_images') diff --git a/_build/composer.json b/_build/composer.json index 57b77fa5808..1f070475062 100644 --- a/_build/composer.json +++ b/_build/composer.json @@ -3,7 +3,7 @@ "prefer-stable": true, "config": { "platform": { - "php": "7.4.14" + "php": "8.1.0" }, "preferred-install": { "*": "dist" @@ -14,9 +14,9 @@ } }, "require": { - "php": ">=7.4", - "symfony/console": "^5.4", - "symfony/process": "^5.4", + "php": ">=8.1", + "symfony/console": "^6.2", + "symfony/process": "^6.2", "symfony-tools/docs-builder": "^0.18" } } diff --git a/_build/composer.lock b/_build/composer.lock index 503bfab012b..fa23243ea98 100644 --- a/_build/composer.lock +++ b/_build/composer.lock @@ -4,41 +4,82 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4cd8dc9a70f9ccfb279a426fffbcf2bc", + "content-hash": "1243668a34d12e1bfc2a367bb87dd2c9", "packages": [ + { + "name": "doctrine/deprecations", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", + "shasum": "" + }, + "require": { + "php": "^7.1|^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5|^8.5|^9.5", + "psr/log": "^1|^2|^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" + }, + "time": "2022-05-02T15:47:09+00:00" + }, { "name": "doctrine/event-manager", - "version": "1.1.1", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f" + "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f", - "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", + "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520", "shasum": "" }, "require": { + "doctrine/deprecations": "^0.5.3 || ^1", "php": "^7.1 || ^8.0" }, "conflict": { - "doctrine/common": "<2.9@dev" + "doctrine/common": "<2.9" }, "require-dev": { - "doctrine/coding-standard": "^6.0", - "phpunit/phpunit": "^7.0" + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "~1.4.10 || ^1.8.8", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.24" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { - "Doctrine\\Common\\": "lib/Doctrine/Common" + "Doctrine\\Common\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -82,7 +123,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.1.x" + "source": "https://github.com/doctrine/event-manager/tree/1.2.0" }, "funding": [ { @@ -98,20 +139,20 @@ "type": "tidelift" } ], - "time": "2020-05-29T18:28:51+00:00" + "time": "2022-10-12T20:51:15+00:00" }, { "name": "doctrine/rst-parser", - "version": "0.5.2", + "version": "0.5.3", "source": { "type": "git", "url": "https://github.com/doctrine/rst-parser.git", - "reference": "3b914d5eb8f6a91afc7462ea7794b0e05b884a35" + "reference": "0b1d413d6bb27699ccec1151da6f617554d02c13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/rst-parser/zipball/3b914d5eb8f6a91afc7462ea7794b0e05b884a35", - "reference": "3b914d5eb8f6a91afc7462ea7794b0e05b884a35", + "url": "https://api.github.com/repos/doctrine/rst-parser/zipball/0b1d413d6bb27699ccec1151da6f617554d02c13", + "reference": "0b1d413d6bb27699ccec1151da6f617554d02c13", "shasum": "" }, "require": { @@ -125,12 +166,12 @@ "twig/twig": "^2.9 || ^3.3" }, "require-dev": { - "doctrine/coding-standard": "^8.0", + "doctrine/coding-standard": "^10.0", "gajus/dindent": "^2.0.2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-deprecation-rules": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.2", + "phpstan/phpstan-strict-rules": "^1.4", "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0", "symfony/css-selector": "4.4 || ^5.2 || ^6.0", "symfony/dom-crawler": "4.4 || ^5.2 || ^6.0" @@ -169,28 +210,102 @@ ], "support": { "issues": "https://github.com/doctrine/rst-parser/issues", - "source": "https://github.com/doctrine/rst-parser/tree/0.5.2" + "source": "https://github.com/doctrine/rst-parser/tree/0.5.3" + }, + "time": "2022-12-29T16:24:52+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.7.6", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "897eb517a343a2281f11bc5556d6548db7d93947" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/897eb517a343a2281f11bc5556d6548db7d93947", + "reference": "897eb517a343a2281f11bc5556d6548db7d93947", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-dom": "*", + "ext-libxml": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.7.6" }, - "time": "2022-03-22T13:52:20+00:00" + "time": "2022-08-18T16:18:26+00:00" }, { "name": "psr/container", - "version": "1.1.2", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { "php": ">=7.4.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -217,36 +332,36 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { "name": "psr/log", - "version": "1.1.4", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "Psr\\Log\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -267,22 +382,22 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "source": "https://github.com/php-fig/log/tree/3.0.0" }, - "time": "2021-05-03T11:20:27+00:00" + "time": "2021-07-14T16:46:02+00:00" }, { "name": "scrivo/highlight.php", - "version": "v9.18.1.9", + "version": "v9.18.1.10", "source": { "type": "git", "url": "https://github.com/scrivo/highlight.php.git", - "reference": "d45585504777e6194a91dffc7270ca39833787f8" + "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/d45585504777e6194a91dffc7270ca39833787f8", - "reference": "d45585504777e6194a91dffc7270ca39833787f8", + "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/850f4b44697a2552e892ffe71490ba2733c2fc6e", + "reference": "850f4b44697a2552e892ffe71490ba2733c2fc6e", "shasum": "" }, "require": { @@ -292,8 +407,8 @@ "require-dev": { "phpunit/phpunit": "^4.8|^5.7", "sabberworm/php-css-parser": "^8.3", - "symfony/finder": "^2.8|^3.4", - "symfony/var-dumper": "^2.8|^3.4" + "symfony/finder": "^2.8|^3.4|^5.4", + "symfony/var-dumper": "^2.8|^3.4|^5.4" }, "suggest": { "ext-mbstring": "Allows highlighting code with unicode characters and supports language with unicode keywords" @@ -347,20 +462,20 @@ "type": "github" } ], - "time": "2021-12-03T06:45:28+00:00" + "time": "2022-12-17T21:53:22+00:00" }, { "name": "symfony-tools/docs-builder", - "version": "v0.18.9", + "version": "v0.18.10", "source": { "type": "git", "url": "https://github.com/symfony-tools/docs-builder.git", - "reference": "1bc91f91887b115d78e7d2c8879c19af515b36ae" + "reference": "8420c687cff102ee30288380ab682ecf539dbf3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony-tools/docs-builder/zipball/1bc91f91887b115d78e7d2c8879c19af515b36ae", - "reference": "1bc91f91887b115d78e7d2c8879c19af515b36ae", + "url": "https://api.github.com/repos/symfony-tools/docs-builder/zipball/8420c687cff102ee30288380ab682ecf539dbf3b", + "reference": "8420c687cff102ee30288380ab682ecf539dbf3b", "shasum": "" }, "require": { @@ -398,52 +513,49 @@ "description": "The build system for Symfony's documentation", "support": { "issues": "https://github.com/symfony-tools/docs-builder/issues", - "source": "https://github.com/symfony-tools/docs-builder/tree/v0.18.9" + "source": "https://github.com/symfony-tools/docs-builder/tree/v0.18.10" }, - "time": "2022-03-22T14:32:49+00:00" + "time": "2022-12-30T15:11:58+00:00" }, { "name": "symfony/console", - "version": "v5.4.8", + "version": "v6.2.3", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "ffe3aed36c4d60da2cf1b0a1cee6b8f2e5fa881b" + "reference": "0f579613e771dba2dbb8211c382342a641f5da06" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/ffe3aed36c4d60da2cf1b0a1cee6b8f2e5fa881b", - "reference": "ffe3aed36c4d60da2cf1b0a1cee6b8f2e5fa881b", + "url": "https://api.github.com/repos/symfony/console/zipball/0f579613e771dba2dbb8211c382342a641f5da06", + "reference": "0f579613e771dba2dbb8211c382342a641f5da06", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" + "symfony/string": "^5.4|^6.0" }, "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" }, "suggest": { "psr/log": "For using the console logger", @@ -483,7 +595,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.8" + "source": "https://github.com/symfony/console/tree/v6.2.3" }, "funding": [ { @@ -499,25 +611,24 @@ "type": "tidelift" } ], - "time": "2022-04-12T16:02:29+00:00" + "time": "2022-12-28T14:26:22+00:00" }, { "name": "symfony/css-selector", - "version": "v5.4.3", + "version": "v6.2.3", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "b0a190285cd95cb019237851205b8140ef6e368e" + "reference": "ab1df4ba3ded7b724766ba3a6e0eca0418e74f80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/b0a190285cd95cb019237851205b8140ef6e368e", - "reference": "b0a190285cd95cb019237851205b8140ef6e368e", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/ab1df4ba3ded7b724766ba3a6e0eca0418e74f80", + "reference": "ab1df4ba3ded7b724766ba3a6e0eca0418e74f80", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -549,7 +660,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.4.3" + "source": "https://github.com/symfony/css-selector/tree/v6.2.3" }, "funding": [ { @@ -565,29 +676,29 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-12-28T14:26:22+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3", + "reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -616,7 +727,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0" }, "funding": [ { @@ -632,35 +743,30 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/dom-crawler", - "version": "v5.4.6", + "version": "v6.2.3", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "c0bda97480d96337bd3866026159a8b358665457" + "reference": "f2743e033dd05a62978ced0ad368022e82c9fab2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/c0bda97480d96337bd3866026159a8b358665457", - "reference": "c0bda97480d96337bd3866026159a8b358665457", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/f2743e033dd05a62978ced0ad368022e82c9fab2", + "reference": "f2743e033dd05a62978ced0ad368022e82c9fab2", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "masterminds/html5": "^2.6", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16" - }, - "conflict": { - "masterminds/html5": "<2.6" + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "masterminds/html5": "^2.6", - "symfony/css-selector": "^4.4|^5.0|^6.0" + "symfony/css-selector": "^5.4|^6.0" }, "suggest": { "symfony/css-selector": "" @@ -691,7 +797,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v5.4.6" + "source": "https://github.com/symfony/dom-crawler/tree/v6.2.3" }, "funding": [ { @@ -707,27 +813,26 @@ "type": "tidelift" } ], - "time": "2022-03-02T12:42:23+00:00" + "time": "2022-12-22T17:55:15+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.7", + "version": "v6.2.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3a4442138d80c9f7b600fb297534ac718b61d37f" + "reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3a4442138d80c9f7b600fb297534ac718b61d37f", - "reference": "3a4442138d80c9f7b600fb297534ac718b61d37f", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/50b2523c874605cf3d4acf7a9e2b30b6a440a016", + "reference": "50b2523c874605cf3d4acf7a9e2b30b6a440a016", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-mbstring": "~1.8" }, "type": "library", "autoload": { @@ -755,7 +860,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.7" + "source": "https://github.com/symfony/filesystem/tree/v6.2.0" }, "funding": [ { @@ -771,26 +876,27 @@ "type": "tidelift" } ], - "time": "2022-04-01T12:33:59+00:00" + "time": "2022-11-20T13:01:27+00:00" }, { "name": "symfony/finder", - "version": "v5.4.8", + "version": "v6.2.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9" + "reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/9b630f3427f3ebe7cd346c277a1408b00249dad9", - "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9", + "url": "https://api.github.com/repos/symfony/finder/zipball/81eefbddfde282ee33b437ba5e13d7753211ae8e", + "reference": "81eefbddfde282ee33b437ba5e13d7753211ae8e", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" }, "type": "library", "autoload": { @@ -818,7 +924,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.8" + "source": "https://github.com/symfony/finder/tree/v6.2.3" }, "funding": [ { @@ -834,36 +940,34 @@ "type": "tidelift" } ], - "time": "2022-04-15T08:07:45+00:00" + "time": "2022-12-22T17:55:15+00:00" }, { "name": "symfony/http-client", - "version": "v5.4.8", + "version": "v6.2.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "0dabec4e3898d3e00451dd47b5ef839168f9bbf5" + "reference": "7054ad466f836309aef511789b9c697bc986d8ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/0dabec4e3898d3e00451dd47b5ef839168f9bbf5", - "reference": "0dabec4e3898d3e00451dd47b5ef839168f9bbf5", + "url": "https://api.github.com/repos/symfony/http-client/zipball/7054ad466f836309aef511789b9c697bc986d8ce", + "reference": "7054ad466f836309aef511789b9c697bc986d8ce", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-client-contracts": "^2.4", - "symfony/polyfill-php73": "^1.11", - "symfony/polyfill-php80": "^1.16", + "symfony/http-client-contracts": "^3", "symfony/service-contracts": "^1.0|^2|^3" }, "provide": { "php-http/async-client-implementation": "*", "php-http/client-implementation": "*", "psr/http-client-implementation": "1.0", - "symfony/http-client-implementation": "2.4" + "symfony/http-client-implementation": "3.0" }, "require-dev": { "amphp/amp": "^2.5", @@ -874,10 +978,10 @@ "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/http-kernel": "^4.4.13|^5.1.5|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/stopwatch": "^4.4|^5.0|^6.0" + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0" }, "type": "library", "autoload": { @@ -905,7 +1009,7 @@ "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-client/tree/v5.4.8" + "source": "https://github.com/symfony/http-client/tree/v6.2.2" }, "funding": [ { @@ -921,24 +1025,24 @@ "type": "tidelift" } ], - "time": "2022-04-12T16:02:29+00:00" + "time": "2022-12-14T16:11:27+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v2.5.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5" + "reference": "c5f587eb445224ddfeb05b5ee703476742d730bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1a4f708e4e87f335d1b1be6148060739152f0bd5", - "reference": "1a4f708e4e87f335d1b1be6148060739152f0bd5", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/c5f587eb445224ddfeb05b5ee703476742d730bf", + "reference": "c5f587eb445224ddfeb05b5ee703476742d730bf", "shasum": "" }, "require": { - "php": ">=7.2.5" + "php": ">=8.1" }, "suggest": { "symfony/http-client-implementation": "" @@ -946,7 +1050,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -956,7 +1060,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\HttpClient\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -983,7 +1090,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.2.0" }, "funding": [ { @@ -999,20 +1106,20 @@ "type": "tidelift" } ], - "time": "2022-03-13T20:07:29+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "30885182c981ab175d4d034db0f6f469898070ab" + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", - "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", + "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", "shasum": "" }, "require": { @@ -1027,7 +1134,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1065,7 +1172,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" }, "funding": [ { @@ -1081,20 +1188,20 @@ "type": "tidelift" } ], - "time": "2021-10-20T20:35:02+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783" + "reference": "511a08c03c1960e08a883f4cffcacd219b758354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783", - "reference": "81b86b50cf841a64252b439e738e97f4a34e2783", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", + "reference": "511a08c03c1960e08a883f4cffcacd219b758354", "shasum": "" }, "require": { @@ -1106,7 +1213,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1146,7 +1253,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" }, "funding": [ { @@ -1162,20 +1269,20 @@ "type": "tidelift" } ], - "time": "2021-11-23T21:10:46+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8" + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8", - "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", "shasum": "" }, "require": { @@ -1187,7 +1294,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1230,7 +1337,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" }, "funding": [ { @@ -1246,20 +1353,20 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.25.0", + "version": "v1.27.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", - "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", "shasum": "" }, "require": { @@ -1274,7 +1381,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.23-dev" + "dev-main": "1.27-dev" }, "thanks": { "name": "symfony/polyfill", @@ -1313,7 +1420,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -1329,187 +1436,24 @@ "type": "tidelift" } ], - "time": "2021-11-30T18:21:41+00:00" - }, - { - "name": "symfony/polyfill-php73", - "version": "v1.25.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5", - "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2021-06-05T21:20:04+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.25.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", - "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", - "shasum": "" - }, - "require": { - "php": ">=7.1" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.23-dev" - }, - "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2022-03-04T08:16:47+00:00" + "time": "2022-11-03T14:55:06+00:00" }, { "name": "symfony/process", - "version": "v5.4.8", + "version": "v6.2.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3" + "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/597f3fff8e3e91836bb0bd38f5718b56ddbde2f3", - "reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3", + "url": "https://api.github.com/repos/symfony/process/zipball/ba6e55359f8f755fe996c58a81e00eaa67a35877", + "reference": "ba6e55359f8f755fe996c58a81e00eaa67a35877", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -1537,7 +1481,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.8" + "source": "https://github.com/symfony/process/tree/v6.2.0" }, "funding": [ { @@ -1553,26 +1497,25 @@ "type": "tidelift" } ], - "time": "2022-04-08T05:07:18+00:00" + "time": "2022-11-02T09:08:04+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.1", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c" + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c", - "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/aac98028c69df04ee77eb69b96b86ee51fbf4b75", + "reference": "aac98028c69df04ee77eb69b96b86ee51fbf4b75", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.1", + "psr/container": "^2.0" }, "conflict": { "ext-psr": "<1.1|>=2" @@ -1583,7 +1526,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.5-dev" + "dev-main": "3.3-dev" }, "thanks": { "name": "symfony/contracts", @@ -1593,7 +1536,10 @@ "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1620,7 +1566,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.2.0" }, "funding": [ { @@ -1636,38 +1582,38 @@ "type": "tidelift" } ], - "time": "2022-03-13T20:07:29+00:00" + "time": "2022-11-25T10:21:52+00:00" }, { "name": "symfony/string", - "version": "v5.4.8", + "version": "v6.2.2", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "3c061a76bff6d6ea427d85e12ad1bb8ed8cd43e8" + "reference": "863219fd713fa41cbcd285a79723f94672faff4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/3c061a76bff6d6ea427d85e12ad1bb8ed8cd43e8", - "reference": "3c061a76bff6d6ea427d85e12ad1bb8ed8cd43e8", + "url": "https://api.github.com/repos/symfony/string/zipball/863219fd713fa41cbcd285a79723f94672faff4d", + "reference": "863219fd713fa41cbcd285a79723f94672faff4d", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": ">=3.0" + "symfony/translation-contracts": "<2.0" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" }, "type": "library", "autoload": { @@ -1706,7 +1652,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.8" + "source": "https://github.com/symfony/string/tree/v6.2.2" }, "funding": [ { @@ -1722,20 +1668,20 @@ "type": "tidelift" } ], - "time": "2022-04-19T10:40:37+00:00" + "time": "2022-12-14T16:11:27+00:00" }, { "name": "symfony/translation-contracts", - "version": "v2.5.1", + "version": "v2.5.2", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "1211df0afa701e45a04253110e959d4af4ef0f07" + "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1211df0afa701e45a04253110e959d4af4ef0f07", - "reference": "1211df0afa701e45a04253110e959d4af4ef0f07", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe", + "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe", "shasum": "" }, "require": { @@ -1784,7 +1730,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v2.5.1" + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2" }, "funding": [ { @@ -1800,20 +1746,20 @@ "type": "tidelift" } ], - "time": "2022-01-02T09:53:40+00:00" + "time": "2022-06-27T16:58:25+00:00" }, { "name": "twig/twig", - "version": "v3.3.10", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "8442df056c51b706793adf80a9fd363406dd3674" + "reference": "3ffcf4b7d890770466da3b2666f82ac054e7ec72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/8442df056c51b706793adf80a9fd363406dd3674", - "reference": "8442df056c51b706793adf80a9fd363406dd3674", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3ffcf4b7d890770466da3b2666f82ac054e7ec72", + "reference": "3ffcf4b7d890770466da3b2666f82ac054e7ec72", "shasum": "" }, "require": { @@ -1828,7 +1774,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3-dev" + "dev-master": "3.5-dev" } }, "autoload": { @@ -1864,7 +1810,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.3.10" + "source": "https://github.com/twigphp/Twig/tree/v3.5.0" }, "funding": [ { @@ -1876,7 +1822,7 @@ "type": "tidelift" } ], - "time": "2022-04-06T06:47:41+00:00" + "time": "2022-12-27T12:28:18+00:00" } ], "packages-dev": [], @@ -1886,11 +1832,11 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=7.4" + "php": ">=8.1" }, "platform-dev": [], "platform-overrides": { - "php": "7.4.14" + "php": "8.1.0" }, - "plugin-api-version": "2.2.0" + "plugin-api-version": "2.3.0" } From 28c5ce432f4cae676be17eaf3dfe2ed7bc748875 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 11 Jan 2023 09:13:27 +0100 Subject: [PATCH 198/774] Remove obsolete whitelist entries --- .doctor-rst.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml index 2833840d86f..1afa4342584 100644 --- a/.doctor-rst.yaml +++ b/.doctor-rst.yaml @@ -70,7 +70,6 @@ whitelist: - '/FOSUserBundle(.*)\.yml/' - '/``.yml``/' - '/(.*)\.orm\.yml/' # currently DoctrineBundle only supports .yml - - '/rst-class/' - /docker-compose\.yml/ lines: - 'in config files, so the old ``app/config/config_dev.yml`` goes to' @@ -78,11 +77,9 @@ whitelist: - 'code in production without a proxy, it becomes trivially easy to abuse your' - '.. _`EasyDeployBundle`: https://github.com/EasyCorp/easy-deploy-bundle' - 'The bin/console Command' - - '# username is your full Gmail or Google Apps email address' - '.. _`LDAP injection`: http://projects.webappsec.org/w/page/13246947/LDAP%20Injection' - '.. versionadded:: 1.9.0' # Encore - '.. versionadded:: 0.28.4' # Encore - - '.. versionadded:: 2.4.0' # SwiftMailer - '.. versionadded:: 1.11' # Messenger (Middleware / DoctrineBundle) - '.. versionadded:: 1.18' # Flex in setup/upgrade_minor.rst - '.. versionadded:: 1.0.0' # Encore @@ -92,8 +89,6 @@ whitelist: - '123,' # assertion for var_dumper - components/var_dumper.rst - '"foo",' # assertion for var_dumper - components/var_dumper.rst - '$var .= "Because of this `\xE9` octet (\\xE9),\n";' - - "`Deploying Symfony 4 Apps on Heroku`_." - - ".. _`Deploying Symfony 4 Apps on Heroku`: https://devcenter.heroku.com/articles/deploying-symfony4" - "// 224, 165, 141, 224, 164, 164, 224, 165, 135])" - '.. versionadded:: 0.2' # MercureBundle - 'provides a ``loginUser()`` method to simulate logging in in your functional' From 7a307dc2d2786ae4b8c720cbe019ebf40eabe495 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Wed, 11 Jan 2023 15:50:43 +0100 Subject: [PATCH 199/774] Use when@dev insteadof dev/mailer.yaml --- mailer.rst | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/mailer.rst b/mailer.rst index 0cf1dc2a34c..b1850ce55c5 100644 --- a/mailer.rst +++ b/mailer.rst @@ -565,7 +565,7 @@ and headers. .. code-block:: yaml - # config/packages/dev/mailer.yaml + # config/packages/mailer.yaml framework: mailer: envelope: @@ -1442,10 +1442,11 @@ the mailer configuration file (e.g. in the ``dev`` or ``test`` environments): .. code-block:: yaml - # config/packages/dev/mailer.yaml - framework: - mailer: - dsn: 'null://null' + # config/packages/mailer.yaml + when@dev: + framework: + mailer: + dsn: 'null://null' .. code-block:: xml @@ -1490,11 +1491,12 @@ a specific address, instead of the *real* address: .. code-block:: yaml - # config/packages/dev/mailer.yaml - framework: - mailer: - envelope: - recipients: ['youremail@example.com'] + # config/packages/mailer.yaml + when@dev: + framework: + mailer: + envelope: + recipients: ['youremail@example.com'] .. code-block:: xml From 13f9766dd87758abad37a376faab84d35555b66f Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 11 Jan 2023 18:26:40 +0100 Subject: [PATCH 200/774] [Serializer] Fix default XmlVersion used by XmlEncoder --- components/serializer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/serializer.rst b/components/serializer.rst index adeb1328c2b..347d04dd72f 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -1181,7 +1181,7 @@ Option Description ============================== ================================================= ========================== ``xml_format_output`` If set to true, formats the generated XML with ``false`` line breaks and indentation -``xml_version`` Sets the XML version attribute ``1.1`` +``xml_version`` Sets the XML version attribute ``1.0`` ``xml_encoding`` Sets the XML encoding attribute ``utf-8`` ``xml_standalone`` Adds standalone attribute in the generated XML ``true`` ``xml_type_cast_attributes`` This provides the ability to forget the attribute ``true`` From 3ecb2820a059a501ccba7f26849fa80990341cfb Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 11 Jan 2023 20:40:28 +0100 Subject: [PATCH 201/774] [ExpressionLanguage] Merge docs about ExpresssionLanguage --- _build/redirection_map | 2 + components/expression_language.rst | 578 ++++++++++++++++++- components/expression_language/ast.rst | 49 -- components/expression_language/caching.rst | 70 --- components/expression_language/extending.rst | 136 ----- components/expression_language/syntax.rst | 320 ---------- reference/constraints/Expression.rst | 4 +- routing.rst | 6 +- security/expressions.rst | 2 +- service_container/expression_language.rst | 2 +- 10 files changed, 571 insertions(+), 598 deletions(-) delete mode 100644 components/expression_language/ast.rst delete mode 100644 components/expression_language/caching.rst delete mode 100644 components/expression_language/extending.rst delete mode 100644 components/expression_language/syntax.rst diff --git a/_build/redirection_map b/_build/redirection_map index 064f9c8261a..764f690c213 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -538,3 +538,5 @@ /components/security /security /components/var_dumper/advanced /components/var_dumper#advanced-usage /components/yaml/yaml_format /components/yaml#yaml-format +/components/expression_language/syntax /components/expression_language#expression-language-syntax +/components/expression_language/extending /components/expression_language#expression-language-extending diff --git a/components/expression_language.rst b/components/expression_language.rst index 988bda75884..be84457995f 100644 --- a/components/expression_language.rst +++ b/components/expression_language.rst @@ -73,11 +73,317 @@ The main class of the component is var_dump($expressionLanguage->compile('1 + 2')); // displays (1 + 2) +.. _expression-language-syntax: + +.. index:: + single: Syntax; ExpressionLanguage + Expression Syntax ----------------- -See :doc:`/components/expression_language/syntax` to learn the syntax of the -ExpressionLanguage component. +The ExpressionLanguage component uses a specific syntax which is based on the +expression syntax of Twig. In this document, you can find all supported +syntaxes. + +Supported Literals +~~~~~~~~~~~~~~~~~~ + +The component supports: + +* **strings** - single and double quotes (e.g. ``'hello'``) +* **numbers** - e.g. ``103`` +* **arrays** - using JSON-like notation (e.g. ``[1, 2]``) +* **hashes** - using JSON-like notation (e.g. ``{ foo: 'bar' }``) +* **booleans** - ``true`` and ``false`` +* **null** - ``null`` +* **exponential** - also known as scientific (e.g. ``1.99E+3`` or ``1e-2``) + +.. caution:: + + A backslash (``\``) must be escaped by 4 backslashes (``\\\\``) in a string + and 8 backslashes (``\\\\\\\\``) in a regex:: + + echo $expressionLanguage->evaluate('"\\\\"'); // prints \ + $expressionLanguage->evaluate('"a\\\\b" matches "/^a\\\\\\\\b$/"'); // returns true + + Control characters (e.g. ``\n``) in expressions are replaced with + whitespace. To avoid this, escape the sequence with a single backslash + (e.g. ``\\n``). + +.. _component-expression-objects: + +Working with Objects +~~~~~~~~~~~~~~~~~~~~ + +When passing objects into an expression, you can use different syntaxes to +access properties and call methods on the object. + +Accessing Public Properties +........................... + +Public properties on objects can be accessed by using the ``.`` syntax, similar +to JavaScript:: + + class Apple + { + public $variety; + } + + $apple = new Apple(); + $apple->variety = 'Honeycrisp'; + + var_dump($expressionLanguage->evaluate( + 'fruit.variety', + [ + 'fruit' => $apple, + ] + )); + +This will print out ``Honeycrisp``. + +Calling Methods +............... + +The ``.`` syntax can also be used to call methods on an object, similar to +JavaScript:: + + class Robot + { + public function sayHi($times) + { + $greetings = []; + for ($i = 0; $i < $times; $i++) { + $greetings[] = 'Hi'; + } + + return implode(' ', $greetings).'!'; + } + } + + $robot = new Robot(); + + var_dump($expressionLanguage->evaluate( + 'robot.sayHi(3)', + [ + 'robot' => $robot, + ] + )); + +This will print out ``Hi Hi Hi!``. + +.. _component-expression-functions: + +Working with Functions +~~~~~~~~~~~~~~~~~~~~~~ + +You can also use registered functions in the expression by using the same +syntax as PHP and JavaScript. The ExpressionLanguage component comes with one +function by default: ``constant()``, which will return the value of the PHP +constant:: + + define('DB_USER', 'root'); + + var_dump($expressionLanguage->evaluate( + 'constant("DB_USER")' + )); + +This will print out ``root``. + +.. tip:: + + To read how to register your own functions to use in an expression, see + ":ref:`expression-language-extending`". + +.. _component-expression-arrays: + +Working with Arrays +~~~~~~~~~~~~~~~~~~~ + +If you pass an array into an expression, use the ``[]`` syntax to access +array keys, similar to JavaScript:: + + $data = ['life' => 10, 'universe' => 10, 'everything' => 22]; + + var_dump($expressionLanguage->evaluate( + 'data["life"] + data["universe"] + data["everything"]', + [ + 'data' => $data, + ] + )); + +This will print out ``42``. + +Supported Operators +~~~~~~~~~~~~~~~~~~~ + +The component comes with a lot of operators: + +Arithmetic Operators +.................... + +* ``+`` (addition) +* ``-`` (subtraction) +* ``*`` (multiplication) +* ``/`` (division) +* ``%`` (modulus) +* ``**`` (pow) + +For example:: + + var_dump($expressionLanguage->evaluate( + 'life + universe + everything', + [ + 'life' => 10, + 'universe' => 10, + 'everything' => 22, + ] + )); + +This will print out ``42``. + +Bitwise Operators +................. + +* ``&`` (and) +* ``|`` (or) +* ``^`` (xor) + +Comparison Operators +.................... + +* ``==`` (equal) +* ``===`` (identical) +* ``!=`` (not equal) +* ``!==`` (not identical) +* ``<`` (less than) +* ``>`` (greater than) +* ``<=`` (less than or equal to) +* ``>=`` (greater than or equal to) +* ``matches`` (regex match) + +.. tip:: + + To test if a string does *not* match a regex, use the logical ``not`` + operator in combination with the ``matches`` operator:: + + $expressionLanguage->evaluate('not ("foo" matches "/bar/")'); // returns true + + You must use parentheses because the unary operator ``not`` has precedence + over the binary operator ``matches``. + +Examples:: + + $ret1 = $expressionLanguage->evaluate( + 'life == everything', + [ + 'life' => 10, + 'everything' => 22, + ] + ); + + $ret2 = $expressionLanguage->evaluate( + 'life > everything', + [ + 'life' => 10, + 'everything' => 22, + ] + ); + +Both variables would be set to ``false``. + +Logical Operators +................. + +* ``not`` or ``!`` +* ``and`` or ``&&`` +* ``or`` or ``||`` + +For example:: + + $ret = $expressionLanguage->evaluate( + 'life < universe or life < everything', + [ + 'life' => 10, + 'universe' => 10, + 'everything' => 22, + ] + ); + +This ``$ret`` variable will be set to ``true``. + +String Operators +................ + +* ``~`` (concatenation) + +For example:: + + var_dump($expressionLanguage->evaluate( + 'firstName~" "~lastName', + [ + 'firstName' => 'Arthur', + 'lastName' => 'Dent', + ] + )); + +This would print out ``Arthur Dent``. + +Array Operators +............... + +* ``in`` (contain) +* ``not in`` (does not contain) + +For example:: + + class User + { + public $group; + } + + $user = new User(); + $user->group = 'human_resources'; + + $inGroup = $expressionLanguage->evaluate( + 'user.group in ["human_resources", "marketing"]', + [ + 'user' => $user, + ] + ); + +The ``$inGroup`` would evaluate to ``true``. + +Numeric Operators +................. + +* ``..`` (range) + +For example:: + + class User + { + public $age; + } + + $user = new User(); + $user->age = 34; + + $expressionLanguage->evaluate( + 'user.age in 18..45', + [ + 'user' => $user, + ] + ); + +This will evaluate to ``true``, because ``user.age`` is in the range from +``18`` to ``45``. + +Ternary Operators +................. + +* ``foo ? 'yes' : 'no'`` +* ``foo ?: 'no'`` (equal to ``foo ? foo : 'no'``) +* ``foo ? 'yes'`` (equal to ``foo ? 'yes' : ''``) Passing in Variables -------------------- @@ -104,8 +410,13 @@ PHP type (including objects):: ] )); // displays "Honeycrisp" -For more information, see the :doc:`/components/expression_language/syntax` -entry, especially :ref:`Working with Objects ` and :ref:`Working with Arrays `. +When using this component inside a Symfony application, certain objects and +variables are automatically injected by Symfony so you can use them in your +expressions (e.g. the request, the current user, etc.): + +* :doc:`Variables available in security expressions `; +* :doc:`Variables available in service container expressions `; +* :ref:`Variables available in routing expressions `. .. caution:: @@ -114,25 +425,260 @@ entry, especially :ref:`Working with Objects ` and characters in untrusted data to prevent malicious users from injecting control characters and altering the expression. +.. index:: + single: Caching; ExpressionLanguage + Caching ------- -The component provides some different caching strategies, read more about them -in :doc:`/components/expression_language/caching`. +The ExpressionLanguage component provides a +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::compile` +method to be able to cache the expressions in plain PHP. But internally, the +component also caches the parsed expressions, so duplicated expressions can be +compiled/evaluated quicker. + +The Workflow +~~~~~~~~~~~~ + +Both :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::evaluate` +and ``compile()`` need to do some things before each can provide the return +values. For ``evaluate()``, this overhead is even bigger. + +Both methods need to tokenize and parse the expression. This is done by the +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::parse` +method. It returns a :class:`Symfony\\Component\\ExpressionLanguage\\ParsedExpression`. +Now, the ``compile()`` method just returns the string conversion of this object. +The ``evaluate()`` method needs to loop through the "nodes" (pieces of an +expression saved in the ``ParsedExpression``) and evaluate them on the fly. + +To save time, the ``ExpressionLanguage`` caches the ``ParsedExpression`` so +it can skip the tokenization and parsing steps with duplicate expressions. The +caching is done by a PSR-6 `CacheItemPoolInterface`_ instance (by default, it +uses an :class:`Symfony\\Component\\Cache\\Adapter\\ArrayAdapter`). You can +customize this by creating a custom cache pool or using one of the available +ones and injecting this using the constructor:: + + use Symfony\Component\Cache\Adapter\RedisAdapter; + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + $cache = new RedisAdapter(...); + $expressionLanguage = new ExpressionLanguage($cache); + +.. seealso:: + + See the :doc:`/components/cache` documentation for more information about + available cache adapters. + +Using Parsed and Serialized Expressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and +``SerializedParsedExpression``:: + + // ... + + // the parse() method returns a ParsedExpression + $expression = $expressionLanguage->parse('1 + 4', []); + + var_dump($expressionLanguage->evaluate($expression)); // prints 5 + +.. code-block:: php + + use Symfony\Component\ExpressionLanguage\SerializedParsedExpression; + // ... + + $expression = new SerializedParsedExpression( + '1 + 4', + serialize($expressionLanguage->parse('1 + 4', [])->getNodes()) + ); + + var_dump($expressionLanguage->evaluate($expression)); // prints 5 + +.. index:: + single: AST; ExpressionLanguage + single: AST; Abstract Syntax Tree AST Dumping and Editing ----------------------- -The AST (*Abstract Syntax Tree*) of expressions can be dumped and manipulated -as explained in :doc:`/components/expression_language/ast`. +It's difficult to manipulate or inspect the expressions created with the ExpressionLanguage +component, because the expressions are plain strings. A better approach is to +turn those expressions into an AST. In computer science, `AST`_ (*Abstract +Syntax Tree*) is *"a tree representation of the structure of source code written +in a programming language"*. In Symfony, a ExpressionLanguage AST is a set of +nodes that contain PHP classes representing the given expression. + +Dumping the AST +~~~~~~~~~~~~~~~ + +Call the :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::getNodes` +method after parsing any expression to get its AST:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + $ast = (new ExpressionLanguage()) + ->parse('1 + 2', []) + ->getNodes() + ; + + // dump the AST nodes for inspection + var_dump($ast); + + // dump the AST nodes as a string representation + $astAsString = $ast->dump(); + +Manipulating the AST +~~~~~~~~~~~~~~~~~~~~ + +The nodes of the AST can also be dumped into a PHP array of nodes to allow +manipulating them. Call the :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::toArray` +method to turn the AST into an array:: + + // ... + + $astAsArray = (new ExpressionLanguage()) + ->parse('1 + 2', []) + ->getNodes() + ->toArray() + ; + +.. _expression-language-extending: + +.. index:: + single: Extending; ExpressionLanguage + +Extending the ExpressionLanguage +-------------------------------- + +The ExpressionLanguage can be extended by adding custom functions. For +instance, in the Symfony Framework, the security has custom functions to check +the user's role. + +.. note:: + + If you want to learn how to use functions in an expression, read + ":ref:`component-expression-functions`". + +Registering Functions +~~~~~~~~~~~~~~~~~~~~~ + +Functions are registered on each specific ``ExpressionLanguage`` instance. +That means the functions can be used in any expression executed by that +instance. + +To register a function, use +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::register`. +This method has 3 arguments: + +* **name** - The name of the function in an expression; +* **compiler** - A function executed when compiling an expression using the + function; +* **evaluator** - A function executed when the expression is evaluated. + +Example:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + $expressionLanguage = new ExpressionLanguage(); + $expressionLanguage->register('lowercase', function ($str) { + return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); + }, function ($arguments, $str) { + if (!is_string($str)) { + return $str; + } + + return strtolower($str); + }); + + var_dump($expressionLanguage->evaluate('lowercase("HELLO")')); + // this will print: hello + +In addition to the custom function arguments, the **evaluator** is passed an +``arguments`` variable as its first argument, which is equal to the second +argument of ``evaluate()`` (e.g. the "values" when evaluating an expression). + +.. _components-expression-language-provider: + +Using Expression Providers +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you use the ``ExpressionLanguage`` class in your library, you often want +to add custom functions. To do so, you can create a new expression provider by +creating a class that implements +:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface`. + +This interface requires one method: +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface::getFunctions`, +which returns an array of expression functions (instances of +:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction`) to +register:: + + use Symfony\Component\ExpressionLanguage\ExpressionFunction; + use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; + + class StringExpressionLanguageProvider implements ExpressionFunctionProviderInterface + { + public function getFunctions() + { + return [ + new ExpressionFunction('lowercase', function ($str) { + return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); + }, function ($arguments, $str) { + if (!is_string($str)) { + return $str; + } + + return strtolower($str); + }), + ]; + } + } + +.. tip:: + + To create an expression function from a PHP function with the + :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction::fromPhp` static method:: + + ExpressionFunction::fromPhp('strtoupper'); + + Namespaced functions are supported, but they require a second argument to + define the name of the expression:: + + ExpressionFunction::fromPhp('My\strtoupper', 'my_strtoupper'); + +You can register providers using +:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::registerProvider` +or by using the second argument of the constructor:: + + use Symfony\Component\ExpressionLanguage\ExpressionLanguage; + + // using the constructor + $expressionLanguage = new ExpressionLanguage(null, [ + new StringExpressionLanguageProvider(), + // ... + ]); + + // using registerProvider() + $expressionLanguage->registerProvider(new StringExpressionLanguageProvider()); + +.. tip:: + + It is recommended to create your own ``ExpressionLanguage`` class in your + library. Now you can add the extension by overriding the constructor:: + + use Psr\Cache\CacheItemPoolInterface; + use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; -Learn More ----------- + class ExpressionLanguage extends BaseExpressionLanguage + { + public function __construct(CacheItemPoolInterface $cache = null, array $providers = []) + { + // prepends the default provider to let users override it + array_unshift($providers, new StringExpressionLanguageProvider()); -.. toctree:: - :maxdepth: 1 - :glob: + parent::__construct($cache, $providers); + } + } - /components/expression_language/* - /service_container/expression_language - /reference/constraints/Expression +.. _`AST`: https://en.wikipedia.org/wiki/Abstract_syntax_tree +.. _`CacheItemPoolInterface`: https://github.com/php-fig/cache/blob/master/src/CacheItemPoolInterface.php diff --git a/components/expression_language/ast.rst b/components/expression_language/ast.rst deleted file mode 100644 index 2bd2bf80023..00000000000 --- a/components/expression_language/ast.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. index:: - single: AST; ExpressionLanguage - single: AST; Abstract Syntax Tree - -Dumping and Manipulating the AST of Expressions -=============================================== - -It’s difficult to manipulate or inspect the expressions created with the ExpressionLanguage -component, because the expressions are plain strings. A better approach is to -turn those expressions into an AST. In computer science, `AST`_ (*Abstract -Syntax Tree*) is *"a tree representation of the structure of source code written -in a programming language"*. In Symfony, a ExpressionLanguage AST is a set of -nodes that contain PHP classes representing the given expression. - -Dumping the AST ---------------- - -Call the :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::getNodes` -method after parsing any expression to get its AST:: - - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; - - $ast = (new ExpressionLanguage()) - ->parse('1 + 2', []) - ->getNodes() - ; - - // dump the AST nodes for inspection - var_dump($ast); - - // dump the AST nodes as a string representation - $astAsString = $ast->dump(); - -Manipulating the AST --------------------- - -The nodes of the AST can also be dumped into a PHP array of nodes to allow -manipulating them. Call the :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::toArray` -method to turn the AST into an array:: - - // ... - - $astAsArray = (new ExpressionLanguage()) - ->parse('1 + 2', []) - ->getNodes() - ->toArray() - ; - -.. _`AST`: https://en.wikipedia.org/wiki/Abstract_syntax_tree diff --git a/components/expression_language/caching.rst b/components/expression_language/caching.rst deleted file mode 100644 index 29e1e0116f7..00000000000 --- a/components/expression_language/caching.rst +++ /dev/null @@ -1,70 +0,0 @@ -.. index:: - single: Caching; ExpressionLanguage - -Caching Expressions Using Parser Caches -======================================= - -The ExpressionLanguage component already provides a -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::compile` -method to be able to cache the expressions in plain PHP. But internally, the -component also caches the parsed expressions, so duplicated expressions can be -compiled/evaluated quicker. - -The Workflow ------------- - -Both :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::evaluate` -and ``compile()`` need to do some things before each can provide the return -values. For ``evaluate()``, this overhead is even bigger. - -Both methods need to tokenize and parse the expression. This is done by the -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::parse` -method. It returns a :class:`Symfony\\Component\\ExpressionLanguage\\ParsedExpression`. -Now, the ``compile()`` method just returns the string conversion of this object. -The ``evaluate()`` method needs to loop through the "nodes" (pieces of an -expression saved in the ``ParsedExpression``) and evaluate them on the fly. - -To save time, the ``ExpressionLanguage`` caches the ``ParsedExpression`` so -it can skip the tokenization and parsing steps with duplicate expressions. The -caching is done by a PSR-6 `CacheItemPoolInterface`_ instance (by default, it -uses an :class:`Symfony\\Component\\Cache\\Adapter\\ArrayAdapter`). You can -customize this by creating a custom cache pool or using one of the available -ones and injecting this using the constructor:: - - use Symfony\Component\Cache\Adapter\RedisAdapter; - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; - - $cache = new RedisAdapter(...); - $expressionLanguage = new ExpressionLanguage($cache); - -.. seealso:: - - See the :doc:`/components/cache` documentation for more information about - available cache adapters. - -Using Parsed and Serialized Expressions ---------------------------------------- - -Both ``evaluate()`` and ``compile()`` can handle ``ParsedExpression`` and -``SerializedParsedExpression``:: - - // ... - - // the parse() method returns a ParsedExpression - $expression = $expressionLanguage->parse('1 + 4', []); - - var_dump($expressionLanguage->evaluate($expression)); // prints 5 - -.. code-block:: php - - use Symfony\Component\ExpressionLanguage\SerializedParsedExpression; - // ... - - $expression = new SerializedParsedExpression( - '1 + 4', - serialize($expressionLanguage->parse('1 + 4', [])->getNodes()) - ); - - var_dump($expressionLanguage->evaluate($expression)); // prints 5 - -.. _`CacheItemPoolInterface`: https://github.com/php-fig/cache/blob/master/src/CacheItemPoolInterface.php diff --git a/components/expression_language/extending.rst b/components/expression_language/extending.rst deleted file mode 100644 index 787d0f61d31..00000000000 --- a/components/expression_language/extending.rst +++ /dev/null @@ -1,136 +0,0 @@ -.. index:: - single: Extending; ExpressionLanguage - -Extending the ExpressionLanguage -================================ - -The ExpressionLanguage can be extended by adding custom functions. For -instance, in the Symfony Framework, the security has custom functions to check -the user's role. - -.. note:: - - If you want to learn how to use functions in an expression, read - ":ref:`component-expression-functions`". - -Registering Functions ---------------------- - -Functions are registered on each specific ``ExpressionLanguage`` instance. -That means the functions can be used in any expression executed by that -instance. - -To register a function, use -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::register`. -This method has 3 arguments: - -* **name** - The name of the function in an expression; -* **compiler** - A function executed when compiling an expression using the - function; -* **evaluator** - A function executed when the expression is evaluated. - -Example:: - - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; - - $expressionLanguage = new ExpressionLanguage(); - $expressionLanguage->register('lowercase', function ($str) { - return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); - }, function ($arguments, $str) { - if (!is_string($str)) { - return $str; - } - - return strtolower($str); - }); - - var_dump($expressionLanguage->evaluate('lowercase("HELLO")')); - // this will print: hello - -In addition to the custom function arguments, the **evaluator** is passed an -``arguments`` variable as its first argument, which is equal to the second -argument of ``evaluate()`` (e.g. the "values" when evaluating an expression). - -.. _components-expression-language-provider: - -Using Expression Providers --------------------------- - -When you use the ``ExpressionLanguage`` class in your library, you often want -to add custom functions. To do so, you can create a new expression provider by -creating a class that implements -:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface`. - -This interface requires one method: -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunctionProviderInterface::getFunctions`, -which returns an array of expression functions (instances of -:class:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction`) to -register:: - - use Symfony\Component\ExpressionLanguage\ExpressionFunction; - use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; - - class StringExpressionLanguageProvider implements ExpressionFunctionProviderInterface - { - public function getFunctions() - { - return [ - new ExpressionFunction('lowercase', function ($str) { - return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str); - }, function ($arguments, $str) { - if (!is_string($str)) { - return $str; - } - - return strtolower($str); - }), - ]; - } - } - -.. tip:: - - To create an expression function from a PHP function with the - :method:`Symfony\\Component\\ExpressionLanguage\\ExpressionFunction::fromPhp` static method:: - - ExpressionFunction::fromPhp('strtoupper'); - - Namespaced functions are supported, but they require a second argument to - define the name of the expression:: - - ExpressionFunction::fromPhp('My\strtoupper', 'my_strtoupper'); - -You can register providers using -:method:`Symfony\\Component\\ExpressionLanguage\\ExpressionLanguage::registerProvider` -or by using the second argument of the constructor:: - - use Symfony\Component\ExpressionLanguage\ExpressionLanguage; - - // using the constructor - $expressionLanguage = new ExpressionLanguage(null, [ - new StringExpressionLanguageProvider(), - // ... - ]); - - // using registerProvider() - $expressionLanguage->registerProvider(new StringExpressionLanguageProvider()); - -.. tip:: - - It is recommended to create your own ``ExpressionLanguage`` class in your - library. Now you can add the extension by overriding the constructor:: - - use Psr\Cache\CacheItemPoolInterface; - use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; - - class ExpressionLanguage extends BaseExpressionLanguage - { - public function __construct(CacheItemPoolInterface $cache = null, array $providers = []) - { - // prepends the default provider to let users override it - array_unshift($providers, new StringExpressionLanguageProvider()); - - parent::__construct($cache, $providers); - } - } - diff --git a/components/expression_language/syntax.rst b/components/expression_language/syntax.rst deleted file mode 100644 index a4c17f81a27..00000000000 --- a/components/expression_language/syntax.rst +++ /dev/null @@ -1,320 +0,0 @@ -.. index:: - single: Syntax; ExpressionLanguage - -The Expression Syntax -===================== - -The ExpressionLanguage component uses a specific syntax which is based on the -expression syntax of Twig. In this document, you can find all supported -syntaxes. - -Supported Literals ------------------- - -The component supports: - -* **strings** - single and double quotes (e.g. ``'hello'``) -* **numbers** - e.g. ``103`` -* **arrays** - using JSON-like notation (e.g. ``[1, 2]``) -* **hashes** - using JSON-like notation (e.g. ``{ foo: 'bar' }``) -* **booleans** - ``true`` and ``false`` -* **null** - ``null`` -* **exponential** - also known as scientific (e.g. ``1.99E+3`` or ``1e-2``) - -.. caution:: - - A backslash (``\``) must be escaped by 4 backslashes (``\\\\``) in a string - and 8 backslashes (``\\\\\\\\``) in a regex:: - - echo $expressionLanguage->evaluate('"\\\\"'); // prints \ - $expressionLanguage->evaluate('"a\\\\b" matches "/^a\\\\\\\\b$/"'); // returns true - - Control characters (e.g. ``\n``) in expressions are replaced with - whitespace. To avoid this, escape the sequence with a single backslash - (e.g. ``\\n``). - -.. _component-expression-objects: - -Working with Objects --------------------- - -When passing objects into an expression, you can use different syntaxes to -access properties and call methods on the object. - -Accessing Public Properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Public properties on objects can be accessed by using the ``.`` syntax, similar -to JavaScript:: - - class Apple - { - public $variety; - } - - $apple = new Apple(); - $apple->variety = 'Honeycrisp'; - - var_dump($expressionLanguage->evaluate( - 'fruit.variety', - [ - 'fruit' => $apple, - ] - )); - -This will print out ``Honeycrisp``. - -Calling Methods -~~~~~~~~~~~~~~~ - -The ``.`` syntax can also be used to call methods on an object, similar to -JavaScript:: - - class Robot - { - public function sayHi($times) - { - $greetings = []; - for ($i = 0; $i < $times; $i++) { - $greetings[] = 'Hi'; - } - - return implode(' ', $greetings).'!'; - } - } - - $robot = new Robot(); - - var_dump($expressionLanguage->evaluate( - 'robot.sayHi(3)', - [ - 'robot' => $robot, - ] - )); - -This will print out ``Hi Hi Hi!``. - -.. _component-expression-functions: - -Working with Functions ----------------------- - -You can also use registered functions in the expression by using the same -syntax as PHP and JavaScript. The ExpressionLanguage component comes with one -function by default: ``constant()``, which will return the value of the PHP -constant:: - - define('DB_USER', 'root'); - - var_dump($expressionLanguage->evaluate( - 'constant("DB_USER")' - )); - -This will print out ``root``. - -.. tip:: - - To read how to register your own functions to use in an expression, see - ":doc:`/components/expression_language/extending`". - -.. _component-expression-arrays: - -Working with Arrays -------------------- - -If you pass an array into an expression, use the ``[]`` syntax to access -array keys, similar to JavaScript:: - - $data = ['life' => 10, 'universe' => 10, 'everything' => 22]; - - var_dump($expressionLanguage->evaluate( - 'data["life"] + data["universe"] + data["everything"]', - [ - 'data' => $data, - ] - )); - -This will print out ``42``. - -Supported Operators -------------------- - -The component comes with a lot of operators: - -Arithmetic Operators -~~~~~~~~~~~~~~~~~~~~ - -* ``+`` (addition) -* ``-`` (subtraction) -* ``*`` (multiplication) -* ``/`` (division) -* ``%`` (modulus) -* ``**`` (pow) - -For example:: - - var_dump($expressionLanguage->evaluate( - 'life + universe + everything', - [ - 'life' => 10, - 'universe' => 10, - 'everything' => 22, - ] - )); - -This will print out ``42``. - -Bitwise Operators -~~~~~~~~~~~~~~~~~ - -* ``&`` (and) -* ``|`` (or) -* ``^`` (xor) - -Comparison Operators -~~~~~~~~~~~~~~~~~~~~ - -* ``==`` (equal) -* ``===`` (identical) -* ``!=`` (not equal) -* ``!==`` (not identical) -* ``<`` (less than) -* ``>`` (greater than) -* ``<=`` (less than or equal to) -* ``>=`` (greater than or equal to) -* ``matches`` (regex match) - -.. tip:: - - To test if a string does *not* match a regex, use the logical ``not`` - operator in combination with the ``matches`` operator:: - - $expressionLanguage->evaluate('not ("foo" matches "/bar/")'); // returns true - - You must use parentheses because the unary operator ``not`` has precedence - over the binary operator ``matches``. - -Examples:: - - $ret1 = $expressionLanguage->evaluate( - 'life == everything', - [ - 'life' => 10, - 'everything' => 22, - ] - ); - - $ret2 = $expressionLanguage->evaluate( - 'life > everything', - [ - 'life' => 10, - 'everything' => 22, - ] - ); - -Both variables would be set to ``false``. - -Logical Operators -~~~~~~~~~~~~~~~~~ - -* ``not`` or ``!`` -* ``and`` or ``&&`` -* ``or`` or ``||`` - -For example:: - - $ret = $expressionLanguage->evaluate( - 'life < universe or life < everything', - [ - 'life' => 10, - 'universe' => 10, - 'everything' => 22, - ] - ); - -This ``$ret`` variable will be set to ``true``. - -String Operators -~~~~~~~~~~~~~~~~ - -* ``~`` (concatenation) - -For example:: - - var_dump($expressionLanguage->evaluate( - 'firstName~" "~lastName', - [ - 'firstName' => 'Arthur', - 'lastName' => 'Dent', - ] - )); - -This would print out ``Arthur Dent``. - -Array Operators -~~~~~~~~~~~~~~~ - -* ``in`` (contain) -* ``not in`` (does not contain) - -For example:: - - class User - { - public $group; - } - - $user = new User(); - $user->group = 'human_resources'; - - $inGroup = $expressionLanguage->evaluate( - 'user.group in ["human_resources", "marketing"]', - [ - 'user' => $user, - ] - ); - -The ``$inGroup`` would evaluate to ``true``. - -Numeric Operators -~~~~~~~~~~~~~~~~~ - -* ``..`` (range) - -For example:: - - class User - { - public $age; - } - - $user = new User(); - $user->age = 34; - - $expressionLanguage->evaluate( - 'user.age in 18..45', - [ - 'user' => $user, - ] - ); - -This will evaluate to ``true``, because ``user.age`` is in the range from -``18`` to ``45``. - -Ternary Operators -~~~~~~~~~~~~~~~~~ - -* ``foo ? 'yes' : 'no'`` -* ``foo ?: 'no'`` (equal to ``foo ? foo : 'no'``) -* ``foo ? 'yes'`` (equal to ``foo ? 'yes' : ''``) - -Built-in Objects and Variables ------------------------------- - -When using this component inside a Symfony application, certain objects and -variables are automatically injected by Symfony so you can use them in your -expressions (e.g. the request, the current user, etc.): - -* :doc:`Variables available in security expressions `; -* :doc:`Variables available in service container expressions `; -* :ref:`Variables available in routing expressions `. diff --git a/reference/constraints/Expression.rst b/reference/constraints/Expression.rst index 65a38efb415..3a75b98bb40 100644 --- a/reference/constraints/Expression.rst +++ b/reference/constraints/Expression.rst @@ -141,7 +141,7 @@ One way to accomplish this is with the Expression constraint: The :ref:`expression ` option is the expression that must return true in order for validation to pass. To learn more about the expression language syntax, see -:doc:`/components/expression_language/syntax`. +:ref:`expression-language-syntax`. .. sidebar:: Mapping the Error to a Specific Field @@ -265,7 +265,7 @@ The expression that will be evaluated. If the expression evaluates to a false value (using ``==``, not ``===``), validation will fail. To learn more about the expression language syntax, see -:doc:`/components/expression_language/syntax`. +:ref:`expression-language-syntax`. Inside of the expression, you have access to up to 2 variables: diff --git a/routing.rst b/routing.rst index e65e7dde27f..560d61040cb 100644 --- a/routing.rst +++ b/routing.rst @@ -414,9 +414,9 @@ arbitrary matching logic: ; }; -The value of the ``condition`` option is any valid -:doc:`ExpressionLanguage expression ` -and can use any of these variables created by Symfony: +The value of the ``condition`` option is any valid :ref:`ExpressionLanguage +expression ` and can use any of these variables +created by Symfony: ``context`` An instance of :class:`Symfony\\Component\\Routing\\RequestContext`, diff --git a/security/expressions.rst b/security/expressions.rst index cad80f6365f..85767ff6a60 100644 --- a/security/expressions.rst +++ b/security/expressions.rst @@ -37,7 +37,7 @@ be granted (note: your User object may not have an ``isSuperAdmin()`` method, that method is invented for this example). This uses an expression and you can learn more about the expression language -syntax, see :doc:`/components/expression_language/syntax`. +syntax, see :ref:`expression-language-syntax`. .. _security-expression-variables: diff --git a/service_container/expression_language.rst b/service_container/expression_language.rst index f755057e240..b70e27ad0dc 100644 --- a/service_container/expression_language.rst +++ b/service_container/expression_language.rst @@ -71,7 +71,7 @@ to another service: ``App\Mailer``. One way to do this is with an expression: ->args([expr("service('App\\\\Mail\\\\MailerConfiguration').getMailerMethod()")]); }; -To learn more about the expression language syntax, see :doc:`/components/expression_language/syntax`. +To learn more about the expression language syntax, see :ref:`expression-language-syntax`. In this context, you have access to 2 functions: From 3062a153b640ce6fbd1966ff7a064830e1609621 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 12 Jan 2023 10:42:38 +0100 Subject: [PATCH 202/774] Minor tweaks --- reference/constraints/Expression.rst | 15 +++++---------- routing.rst | 6 +++--- security/expressions.rst | 6 ++---- service_container/expression_language.rst | 2 +- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/reference/constraints/Expression.rst b/reference/constraints/Expression.rst index 3a75b98bb40..9af9b3ff0ea 100644 --- a/reference/constraints/Expression.rst +++ b/reference/constraints/Expression.rst @@ -139,9 +139,8 @@ One way to accomplish this is with the Expression constraint: } The :ref:`expression ` option is the -expression that must return true in order for validation to pass. To learn -more about the expression language syntax, see -:ref:`expression-language-syntax`. +expression that must return true in order for validation to pass. Learn more +about the :ref:`expression language syntax `. .. sidebar:: Mapping the Error to a Specific Field @@ -262,14 +261,10 @@ Options **type**: ``string`` [:ref:`default option `] The expression that will be evaluated. If the expression evaluates to a false -value (using ``==``, not ``===``), validation will fail. +value (using ``==``, not ``===``), validation will fail. Learn more about the +:ref:`expression language syntax `. -To learn more about the expression language syntax, see -:ref:`expression-language-syntax`. - -Inside of the expression, you have access to up to 2 variables: - -Depending on how you use the constraint, you have access to 1 or 2 variables +Depending on how you use the constraint, you have access to different variables in your expression: * ``this``: The object being validated (e.g. an instance of BlogPost); diff --git a/routing.rst b/routing.rst index 560d61040cb..cd3ec6d2c76 100644 --- a/routing.rst +++ b/routing.rst @@ -414,9 +414,9 @@ arbitrary matching logic: ; }; -The value of the ``condition`` option is any valid :ref:`ExpressionLanguage -expression ` and can use any of these variables -created by Symfony: +The value of the ``condition`` option is an expression using any valid +:ref:`expression language syntax ` and can use any +of these variables created by Symfony: ``context`` An instance of :class:`Symfony\\Component\\Routing\\RequestContext`, diff --git a/security/expressions.rst b/security/expressions.rst index 85767ff6a60..afec9ac30b1 100644 --- a/security/expressions.rst +++ b/security/expressions.rst @@ -36,12 +36,10 @@ user object's ``isSuperAdmin()`` method returns ``true``, then access will be granted (note: your User object may not have an ``isSuperAdmin()`` method, that method is invented for this example). -This uses an expression and you can learn more about the expression language -syntax, see :ref:`expression-language-syntax`. - .. _security-expression-variables: -Inside the expression, you have access to a number of variables: +The security expression must use any valid :ref:`expression language syntax ` +and can use any of these variables created by Symfony: ``user`` The user object (or the string ``anon`` if you're not authenticated). diff --git a/service_container/expression_language.rst b/service_container/expression_language.rst index b70e27ad0dc..53b1524b762 100644 --- a/service_container/expression_language.rst +++ b/service_container/expression_language.rst @@ -71,7 +71,7 @@ to another service: ``App\Mailer``. One way to do this is with an expression: ->args([expr("service('App\\\\Mail\\\\MailerConfiguration').getMailerMethod()")]); }; -To learn more about the expression language syntax, see :ref:`expression-language-syntax`. +Learn more about the :ref:`expression language syntax `. In this context, you have access to 2 functions: From 8583932e0080744be1ead4141a0e9fccb6299b15 Mon Sep 17 00:00:00 2001 From: MrYamous Date: Wed, 11 Jan 2023 18:23:40 +0100 Subject: [PATCH 203/774] [Notifier] add notification assertion in notifier doc --- notifier.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/notifier.rst b/notifier.rst index bff9af3d7c6..a2980ff5be5 100644 --- a/notifier.rst +++ b/notifier.rst @@ -759,6 +759,19 @@ typical alert levels, which you can implement immediately using: ; }; +Testing Notifier +---------------- + +Symfony provides a :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\NotificationAssertionsTrait` +which provide useful methods for testing your Notifier implementation. +You can benefit from this class by using it directly or extending the :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase`. + +See :ref:`testing documentation ` for the list of available assertions. + +.. versionadded:: 6.2 + + The :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\NotificationAssertionsTrait` was introduced in Symfony 6.2. + Disabling Delivery ------------------ From 3e960bc0b7d267164ecf8dff749ee7b1ba4d71b1 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 12 Jan 2023 13:33:30 +0100 Subject: [PATCH 204/774] Minor --- notifier.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/notifier.rst b/notifier.rst index a2980ff5be5..588b8e408e1 100644 --- a/notifier.rst +++ b/notifier.rst @@ -764,13 +764,15 @@ Testing Notifier Symfony provides a :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\NotificationAssertionsTrait` which provide useful methods for testing your Notifier implementation. -You can benefit from this class by using it directly or extending the :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase`. +You can benefit from this class by using it directly or extending the +:class:`Symfony\\Bundle\\FrameworkBundle\\Test\\KernelTestCase`. See :ref:`testing documentation ` for the list of available assertions. .. versionadded:: 6.2 - The :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\NotificationAssertionsTrait` was introduced in Symfony 6.2. + The :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\NotificationAssertionsTrait` + was introduced in Symfony 6.2. Disabling Delivery ------------------ From 96ff886797f447673551fb47b4563cc4d69ced69 Mon Sep 17 00:00:00 2001 From: MrYamous Date: Thu, 12 Jan 2023 17:34:53 +0100 Subject: [PATCH 205/774] update mailer testing documentation --- mailer.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mailer.rst b/mailer.rst index 0cf1dc2a34c..cf838c8af8f 100644 --- a/mailer.rst +++ b/mailer.rst @@ -1534,21 +1534,21 @@ Write a Functional Test ~~~~~~~~~~~~~~~~~~~~~~~ To functionally test that an email was sent, and even assert the email content or headers, -you can use the built in assertions:: +you can use the built in assertions provided by :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\MailerAssertionsTrait`. + +See :ref:`testing documentation ` for the list of available assertions.:: // tests/Controller/MailControllerTest.php namespace App\Tests\Controller; - use Symfony\Bundle\FrameworkBundle\Test\MailerAssertionsTrait; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; class MailControllerTest extends WebTestCase { - use MailerAssertionsTrait; public function testMailIsSentAndContentIsOk() { - $client = $this->createClient(); + $client = static::createClient(); $client->request('GET', '/mail/send'); $this->assertResponseIsSuccessful(); From 32e93ba84dfcd3c5537855c1c567ac8638b8f200 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 10 Jan 2023 20:31:50 +0100 Subject: [PATCH 206/774] [Form] Add `group_by` option for EnumType --- reference/forms/types/enum.rst | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/reference/forms/types/enum.rst b/reference/forms/types/enum.rst index 63bca396c4b..c8bd18d2c04 100644 --- a/reference/forms/types/enum.rst +++ b/reference/forms/types/enum.rst @@ -92,6 +92,59 @@ These options inherit from the :doc:`ChoiceType ` .. include:: /reference/forms/types/options/expanded.rst.inc +``group_by`` +~~~~~~~~~~~~ + +**type**: ``string`` or ``callable`` or :class:`Symfony\\Component\\PropertyAccess\\PropertyPath` **default**: ``null`` + +You can group the ``