From 6ae48a88672d49b781535a02f73ad971d4ca87b7 Mon Sep 17 00:00:00 2001 From: Jesse Rushlow Date: Sun, 21 Apr 2024 04:11:06 -0400 Subject: [PATCH 001/642] [security] `make:security:custom` Since MakerBundle `v1.59.0` - you can call `make:security:custom` to generate a simple, no-frills, custom authenticator based off the example in the docs. --- security/custom_authenticator.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/custom_authenticator.rst b/security/custom_authenticator.rst index c57a2d84c23..3ec158d4e76 100644 --- a/security/custom_authenticator.rst +++ b/security/custom_authenticator.rst @@ -5,7 +5,8 @@ Symfony comes with :ref:`many authenticators ` and third party bundles also implement more complex cases like JWT and oAuth 2.0. However, sometimes you need to implement a custom authentication mechanism that doesn't exist yet or you need to customize one. In such -cases, you must create and use your own authenticator. +cases, you can use the ``make:security:custom`` command to create your own +authenticator. Authenticators should implement the :class:`Symfony\\Component\\Security\\Http\\Authenticator\\AuthenticatorInterface`. From 01ca38dac8964f2e06eb18428854e9f3561a5a17 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Wed, 22 May 2024 21:44:11 +0200 Subject: [PATCH 002/642] [Stopwatch] Add ROOT constant to make it easier to reference --- performance.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/performance.rst b/performance.rst index ca34e95ee37..ffeff6608c1 100644 --- a/performance.rst +++ b/performance.rst @@ -382,10 +382,16 @@ All events that don't belong to any named section are added to the special secti called ``__root__``. This way you can get all stopwatch events, even if you don't know their names, as follows:: - foreach($this->stopwatch->getSectionEvents('__root__') as $event) { + use Symfony\Component\Stopwatch\Stopwatch; + + foreach($this->stopwatch->getSectionEvents(Stopwatch::ROOT) as $event) { echo (string) $event; } +.. versionadded:: 7.2 + + The ``Stopwatch::ROOT`` constant as a shortcut for ``__root__`` was introduced in Symfony 7.2. + Learn more ---------- From 6331dc5d6b6b52425220e5dec44f75be657ea2d7 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Wed, 22 May 2024 21:49:28 +0200 Subject: [PATCH 003/642] [Stopwatch] Add getLastPeriod method to StopwatchEvent --- performance.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/performance.rst b/performance.rst index ca34e95ee37..634ad8572ea 100644 --- a/performance.rst +++ b/performance.rst @@ -362,6 +362,13 @@ method does, which stops an event and then restarts it immediately:: // Lap information is stored as "periods" within the event: // $event->getPeriods(); + // Gets the last event period: + // $event->getLastPeriod(); + +.. versionadded:: 7.2 + + The ``getLastPeriod`` method was introduced in Symfony 7.2. + Profiling Sections .................. From 5bcaf58a7608ad467dffc3be35721d9e3286b4c1 Mon Sep 17 00:00:00 2001 From: Tim <5579551+Timherlaud@users.noreply.github.com> Date: Wed, 22 May 2024 16:31:48 +0200 Subject: [PATCH 004/642] 19909 [FrameworkBundle] Add support for setting headers --- templates.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/templates.rst b/templates.rst index b6ae333cee1..d1c714d4225 100644 --- a/templates.rst +++ b/templates.rst @@ -705,6 +705,11 @@ provided by Symfony: site_name: 'ACME' theme: 'dark' + # optionally you can define HTTP headers to add to the response + headers: + Content-Type: 'text/html' + foo: 'bar' + .. code-block:: xml @@ -734,6 +739,11 @@ provided by Symfony: ACME dark + + + + text/html + @@ -764,11 +774,20 @@ provided by Symfony: 'context' => [ 'site_name' => 'ACME', 'theme' => 'dark', + ], + + // optionally you can define HTTP headers to add to the response + 'headers' => [ + 'Content-Type' => 'text/html', ] ]) ; }; +.. versionadded:: 7.2 + + The ``headers`` option was introduced in Symfony 7.2. + Checking if a Template Exists ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From a9a6f77c095d34d3bb2c6b7e65fd26fb7f2e371d Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Fri, 24 May 2024 15:50:07 +0200 Subject: [PATCH 005/642] Update examples to symfony 7.2 --- contributing/code/pull_requests.rst | 2 +- contributing/code/tests.rst | 2 +- setup.rst | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contributing/code/pull_requests.rst b/contributing/code/pull_requests.rst index 7c9ab2579a5..28a6dbc8169 100644 --- a/contributing/code/pull_requests.rst +++ b/contributing/code/pull_requests.rst @@ -132,7 +132,7 @@ 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``. -* ``7.1``, if you are adding a new feature. +* ``7.2``, 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 diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst index 08f6bc5df12..060e3eda02b 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=7.1.x-dev composer update + $ COMPOSER_ROOT_VERSION=7.2.x-dev composer update .. _running: diff --git a/setup.rst b/setup.rst index 90a89e78e8a..5cf3d2f90ab 100644 --- a/setup.rst +++ b/setup.rst @@ -48,10 +48,10 @@ application: .. code-block:: terminal # run this if you are building a traditional web application - $ symfony new my_project_directory --version="7.1.*" --webapp + $ symfony new my_project_directory --version="7.2.*" --webapp # run this if you are building a microservice, console application or API - $ symfony new my_project_directory --version="7.1.*" + $ symfony new my_project_directory --version="7.2.*" The only difference between these two commands is the number of packages installed by default. The ``--webapp`` option installs extra packages to give @@ -63,12 +63,12 @@ Symfony application using Composer: .. code-block:: terminal # run this if you are building a traditional web application - $ composer create-project symfony/skeleton:"7.1.*" my_project_directory + $ composer create-project symfony/skeleton:"7.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:"7.1.*" my_project_directory + $ composer create-project symfony/skeleton:"7.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 92d1b5a8728b7c83930e497a7c86e1f0b6abcb1f Mon Sep 17 00:00:00 2001 From: Yannick Date: Tue, 21 May 2024 23:52:53 +0200 Subject: [PATCH 006/642] [Validator] feat : add password strength estimator related documentation --- reference/constraints/PasswordStrength.rst | 9 ++++ .../get_or_override_estimate_strength.rst | 48 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 validation/passwordStrength/get_or_override_estimate_strength.rst diff --git a/reference/constraints/PasswordStrength.rst b/reference/constraints/PasswordStrength.rst index 989ffddd100..a44fd70fd4e 100644 --- a/reference/constraints/PasswordStrength.rst +++ b/reference/constraints/PasswordStrength.rst @@ -128,3 +128,12 @@ The default message supplied when the password does not reach the minimum requir ])] protected $rawPassword; } + +Learn more +---------- + +.. toctree:: + :maxdepth: 1 + :glob: + + /validation/passwordStrength/* diff --git a/validation/passwordStrength/get_or_override_estimate_strength.rst b/validation/passwordStrength/get_or_override_estimate_strength.rst new file mode 100644 index 00000000000..9cd24c1b818 --- /dev/null +++ b/validation/passwordStrength/get_or_override_estimate_strength.rst @@ -0,0 +1,48 @@ +How to Get or Override the Default Password Strength Estimation Algorithm +========================================================================= + +Within the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` a `dedicated function`_ is used to estimate the strength of the given password. This function can be retrieved directly from the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` class and can also be overridden. + +Get the default Password strength +--------------------------------- + +In case of need to retrieve the actual strength of a password (e.g. compute the score and display it when the user has defined a password), the ``estimateStrength`` `dedicated function`_ of the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` is a public static function, therefore this function can be retrieved directly from the `PasswordStrengthValidator` class.:: + + use Symfony\Component\Validator\Constraints\PasswordStrengthValidator; + + $passwordEstimatedStrength = PasswordStrengthValidator::estimateStrength($password); + + +Override the default Password strength estimation algorithm +----------------------------------------------------------- + +If you need to override the default password strength estimation algorithm, you can pass a ``Closure`` to the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` constructor. This can be done using the :doc:`/service_container/service_closures`. + +First, create a custom password strength estimation algorithm within a dedicated callable class.:: + + namespace App\Validator; + + class CustomPasswordStrengthEstimator + { + /** + * @param string $password + * + * @return PasswordStrength::STRENGTH_* + */ + public function __invoke(string $password): int + { + // Your custom password strength estimation algorithm + } + } + +Then, configure the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` service to use the custom password strength estimation algorithm.:: + + # config/services.yaml + services: + custom_password_strength_estimator: + class: App\Validator\CustomPasswordStrengthEstimator + + Symfony\Component\Validator\Constraints\PasswordStrengthValidator: + arguments: [!service_closure '@custom_password_strength_estimator'] + +.. _`dedicated function`: https://github.com/symfony/symfony/blob/85db734e06e8cb32365810958326d48084bf48ba/src/Symfony/Component/Validator/Constraints/PasswordStrengthValidator.php#L53-L90 From f6b69918adf973068635a9f39e1e4be5c1f4dc40 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 28 May 2024 10:43:26 +0200 Subject: [PATCH 007/642] Move the docs to the constraint doc --- reference/constraints/PasswordStrength.rst | 87 +++++++++++++++++-- .../get_or_override_estimate_strength.rst | 48 ---------- 2 files changed, 81 insertions(+), 54 deletions(-) delete mode 100644 validation/passwordStrength/get_or_override_estimate_strength.rst diff --git a/reference/constraints/PasswordStrength.rst b/reference/constraints/PasswordStrength.rst index a44fd70fd4e..671b5f495ef 100644 --- a/reference/constraints/PasswordStrength.rst +++ b/reference/constraints/PasswordStrength.rst @@ -129,11 +129,86 @@ The default message supplied when the password does not reach the minimum requir protected $rawPassword; } -Learn more ----------- +Customizing the Password Strength Estimation +-------------------------------------------- -.. toctree:: - :maxdepth: 1 - :glob: +.. versionadded:: 7.2 - /validation/passwordStrength/* + The feature to customize the password strength estimation was introduced in Symfony 7.2. + +By default, this constraint calculates the strength of a password based on its +length and the number of unique characters used. You can get the calculated +password strength (e.g. to display it in the user interface) using the following +static function:: + + use Symfony\Component\Validator\Constraints\PasswordStrengthValidator; + + $passwordEstimatedStrength = PasswordStrengthValidator::estimateStrength($password); + +If you need to override the default password strength estimation algorithm, you +can pass a ``Closure`` to the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` +constructor (e.g. using the :doc:`service closures `). + +First, create a custom password strength estimation algorithm within a dedicated +callable class:: + + namespace App\Validator; + + class CustomPasswordStrengthEstimator + { + /** + * @return PasswordStrength::STRENGTH_* + */ + public function __invoke(string $password): int + { + // Your custom password strength estimation algorithm + } + } + +Then, configure the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` +service to use your own estimator: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + custom_password_strength_estimator: + class: App\Validator\CustomPasswordStrengthEstimator + + Symfony\Component\Validator\Constraints\PasswordStrengthValidator: + arguments: [!service_closure '@custom_password_strength_estimator'] + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + use Symfony\Component\Validator\Constraints\PasswordStrengthValidator; + + return function (ContainerConfigurator $container): void { + $services = $container->services(); + + $services->set('custom_password_strength_estimator', CustomPasswordStrengthEstimator::class); + + $services->set(PasswordStrengthValidator::class) + ->args([service_closure('custom_password_strength_estimator')]); + }; diff --git a/validation/passwordStrength/get_or_override_estimate_strength.rst b/validation/passwordStrength/get_or_override_estimate_strength.rst deleted file mode 100644 index 9cd24c1b818..00000000000 --- a/validation/passwordStrength/get_or_override_estimate_strength.rst +++ /dev/null @@ -1,48 +0,0 @@ -How to Get or Override the Default Password Strength Estimation Algorithm -========================================================================= - -Within the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` a `dedicated function`_ is used to estimate the strength of the given password. This function can be retrieved directly from the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` class and can also be overridden. - -Get the default Password strength ---------------------------------- - -In case of need to retrieve the actual strength of a password (e.g. compute the score and display it when the user has defined a password), the ``estimateStrength`` `dedicated function`_ of the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` is a public static function, therefore this function can be retrieved directly from the `PasswordStrengthValidator` class.:: - - use Symfony\Component\Validator\Constraints\PasswordStrengthValidator; - - $passwordEstimatedStrength = PasswordStrengthValidator::estimateStrength($password); - - -Override the default Password strength estimation algorithm ------------------------------------------------------------ - -If you need to override the default password strength estimation algorithm, you can pass a ``Closure`` to the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` constructor. This can be done using the :doc:`/service_container/service_closures`. - -First, create a custom password strength estimation algorithm within a dedicated callable class.:: - - namespace App\Validator; - - class CustomPasswordStrengthEstimator - { - /** - * @param string $password - * - * @return PasswordStrength::STRENGTH_* - */ - public function __invoke(string $password): int - { - // Your custom password strength estimation algorithm - } - } - -Then, configure the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator` service to use the custom password strength estimation algorithm.:: - - # config/services.yaml - services: - custom_password_strength_estimator: - class: App\Validator\CustomPasswordStrengthEstimator - - Symfony\Component\Validator\Constraints\PasswordStrengthValidator: - arguments: [!service_closure '@custom_password_strength_estimator'] - -.. _`dedicated function`: https://github.com/symfony/symfony/blob/85db734e06e8cb32365810958326d48084bf48ba/src/Symfony/Component/Validator/Constraints/PasswordStrengthValidator.php#L53-L90 From cd3690d3738ff210cd2144f97186c2acce20e38f Mon Sep 17 00:00:00 2001 From: A goazil Date: Tue, 30 Apr 2024 09:52:48 +0200 Subject: [PATCH 008/642] [Notifier] add Primotexto bridge chore: add versionadded directive --- notifier.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/notifier.rst b/notifier.rst index 04ca5a2d4a4..01dfaf17cc6 100644 --- a/notifier.rst +++ b/notifier.rst @@ -138,6 +138,9 @@ Service `Plivo`_ **Install**: ``composer require symfony/plivo-notifier`` \ **DSN**: ``plivo://AUTH_ID:AUTH_TOKEN@default?from=FROM`` \ **Webhook support**: No +`Primotexto`_ **Install**: ``composer require symfony/primotexto-notifier`` \ + **DSN**: ``primotexto://API_KEY@default?from=FROM`` \ + **Webhook support**: No `Redlink`_ **Install**: ``composer require symfony/redlink-notifier`` \ **DSN**: ``redlink://API_KEY:APP_KEY@default?from=SENDER_NAME&version=API_VERSION`` \ **Webhook support**: No @@ -203,6 +206,10 @@ Service **Webhook support**: No ================== ==================================================================================================================================== +.. versionadded:: 7.2 + + The ``Primotexto`` integration was introduced in Symfony 7.2. + .. tip:: Some third party transports, when using the API, support status callbacks @@ -1119,6 +1126,7 @@ is dispatched. Listeners receive a .. _`OvhCloud`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/OvhCloud/README.md .. _`PagerDuty`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/PagerDuty/README.md .. _`Plivo`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Plivo/README.md +.. _`Primotexto`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Primotexto/README.md .. _`Pushover`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Pushover/README.md .. _`Pushy`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Pushy/README.md .. _`Redlink`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Redlink/README.md From dc23e2e0399768c26c495ed8e2cf481843801d57 Mon Sep 17 00:00:00 2001 From: Maximilian Beckers Date: Tue, 4 Jun 2024 12:01:10 +0200 Subject: [PATCH 009/642] [Validator] BicValidator add strict mode to validate bics in strict mode --- reference/constraints/Bic.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/reference/constraints/Bic.rst b/reference/constraints/Bic.rst index 69ce35248f3..a6a745e3a76 100644 --- a/reference/constraints/Bic.rst +++ b/reference/constraints/Bic.rst @@ -121,4 +121,24 @@ Parameter Description .. include:: /reference/constraints/_payload-option.rst.inc +``mode`` +~~~~~~~~ + +**type**: ``string`` **default**: ``strict`` + +The mode in which the BIC is validated can be defined with this option. Valid values are: + +* ``strict`` uses the input BIC and validates it without modification. +* ``case-insensitive`` converts the input value to uppercase before validating the BIC. + +.. tip:: + + The possible values of this option are also defined as PHP constants of + :class:`Symfony\\Component\\Validator\\Constraints\\BIC` + (e.g. ``BIC::VALIDATION_MODE_CASE_INSENSITIVE``). + +.. versionadded:: 7.2 + + The ``mode`` option was introduced in Symfony 7.2. + .. _`Business Identifier Code (BIC)`: https://en.wikipedia.org/wiki/Business_Identifier_Code From 1484f9dc53b784ac79b1b5cb3f7fd000f07ac92c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 5 Jun 2024 11:40:33 +0200 Subject: [PATCH 010/642] Minor tweaks --- reference/constraints/Bic.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/constraints/Bic.rst b/reference/constraints/Bic.rst index a6a745e3a76..3f05e5eac25 100644 --- a/reference/constraints/Bic.rst +++ b/reference/constraints/Bic.rst @@ -126,10 +126,10 @@ Parameter Description **type**: ``string`` **default**: ``strict`` -The mode in which the BIC is validated can be defined with this option. Valid values are: +This option defines how the BIC is validated: -* ``strict`` uses the input BIC and validates it without modification. -* ``case-insensitive`` converts the input value to uppercase before validating the BIC. +* ``strict`` validates the given value without any modification; +* ``case-insensitive`` converts the given value to uppercase before validating it. .. tip:: From 0624d24867ba378c160c0c56fa3127490ca2b065 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 13 Jun 2024 16:48:16 +0200 Subject: [PATCH 011/642] [ExpressionLanguage] Document the null-coalesce operator changes --- reference/formats/expression_language.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/reference/formats/expression_language.rst b/reference/formats/expression_language.rst index c7ce82fb751..818870452a5 100644 --- a/reference/formats/expression_language.rst +++ b/reference/formats/expression_language.rst @@ -124,11 +124,10 @@ returns the right-hand side. Expressions can chain multiple coalescing operators * ``foo[3] ?? 'no'`` * ``foo.baz ?? foo['baz'] ?? 'no'`` -.. note:: +.. versionadded:: 7.2 - The main difference with the `null-coalescing operator in PHP`_ is that - ExpressionLanguage will throw an exception when trying to access a - non-existent variable. + Starting from Symfony 7.2, no exception is thrown when trying to access a + non-existent variable. This is the same behavior as the `null-coalescing operator in PHP`_. .. _component-expression-functions: From 00854911226a5f2ec7299e0e0345fcaacba110ac Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 13 Jun 2024 17:34:08 +0200 Subject: [PATCH 012/642] [Translation] Document `lint:translations` command --- translation.rst | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/translation.rst b/translation.rst index 891f00845cb..0fbd155cfd7 100644 --- a/translation.rst +++ b/translation.rst @@ -1343,7 +1343,7 @@ Symfony processes all the application translation files as part of the process that compiles the application code before executing it. If there's an error in any translation file, you'll see an error message explaining the problem. -If you prefer, you can also validate the contents of any YAML and XLIFF +If you prefer, you can also validate the syntax of any YAML and XLIFF translation file using the ``lint:yaml`` and ``lint:xliff`` commands: .. code-block:: terminal @@ -1384,6 +1384,22 @@ adapted to the format required by GitHub, but you can force that format too: $ php vendor/bin/yaml-lint translations/ +The ``lint:yaml`` and ``lint:xliff`` commands validate the YAML and XML syntax +of the translation files, but not their contents. Use the following command +to check that the translation contents are also correct: + + .. code-block:: terminal + + # checks the contents of all the translation catalogues in all locales + $ php bin/console lint:translations + + # checks the contents of the translation catalogues for Italian (it) and Japanese (ja) locales + $ php bin/console lint:translations --locales=it --locales=ja + +.. versionadded:: 7.2 + + The ``lint:translations`` command was introduced in Symfony 7.2. + Pseudo-localization translator ------------------------------ From 29bdb1612d2a3d7387cad50228cbe226623ea4ec Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 17 Jun 2024 10:36:30 +0200 Subject: [PATCH 013/642] Update the version constraints --- setup.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.rst b/setup.rst index 5cf3d2f90ab..d8196e71b7b 100644 --- a/setup.rst +++ b/setup.rst @@ -48,10 +48,10 @@ application: .. code-block:: terminal # run this if you are building a traditional web application - $ symfony new my_project_directory --version="7.2.*" --webapp + $ symfony new my_project_directory --version="7.2.x-dev" --webapp # run this if you are building a microservice, console application or API - $ symfony new my_project_directory --version="7.2.*" + $ symfony new my_project_directory --version="7.2.x-dev" The only difference between these two commands is the number of packages installed by default. The ``--webapp`` option installs extra packages to give @@ -63,12 +63,12 @@ Symfony application using Composer: .. code-block:: terminal # run this if you are building a traditional web application - $ composer create-project symfony/skeleton:"7.2.*" my_project_directory + $ composer create-project symfony/skeleton:"7.2.x-dev" 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:"7.2.*" my_project_directory + $ composer create-project symfony/skeleton:"7.2.x-dev" 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 15dcd7e221ae7caca0cec61f56b2adab1cdd9b55 Mon Sep 17 00:00:00 2001 From: Patrick Landolt Date: Sun, 9 Jun 2024 18:19:11 +0200 Subject: [PATCH 014/642] [Mailer] Add mailomat mailer --- mailer.rst | 10 ++++++++++ webhook.rst | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/mailer.rst b/mailer.rst index d7bccb11d86..502644a741a 100644 --- a/mailer.rst +++ b/mailer.rst @@ -106,6 +106,7 @@ Service Install with Webhook su `Infobip`_ ``composer require symfony/infobip-mailer`` `Mailgun`_ ``composer require symfony/mailgun-mailer`` yes `Mailjet`_ ``composer require symfony/mailjet-mailer`` yes +`Mailomat`_ ``composer require symfony/mailomat-mailer`` yes `MailPace`_ ``composer require symfony/mail-pace-mailer`` `MailerSend`_ ``composer require symfony/mailer-send-mailer`` `Mandrill`_ ``composer require symfony/mailchimp-mailer`` @@ -119,6 +120,10 @@ Service Install with Webhook su The Azure and Resend integrations were introduced in Symfony 7.1. +.. versionadded:: 7.2 + + The Mailomat integration was introduced in Symfony 7.2. + .. note:: As a convenience, Symfony also provides support for Gmail (``composer @@ -201,6 +206,10 @@ party provider: | | - HTTP n/a | | | - API ``mailjet+api://ACCESS_KEY:SECRET_KEY@default`` | +------------------------+---------------------------------------------------------+ +| `Mailomat`_ | - SMTP ``mailomat+smtp://USERNAME:PASSWORD@default`` | +| | - HTTP n/a | +| | - API ``mailomat+api://KEY@default`` | ++------------------------+---------------------------------------------------------+ | `MailPace`_ | - SMTP ``mailpace+api://API_TOKEN@default`` | | | - HTTP n/a | | | - API ``mailpace+api://API_TOKEN@default`` | @@ -1979,6 +1988,7 @@ the :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\MailerAssertionsTrait`:: .. _`Mailgun`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Mailgun/README.md .. _`Mailjet`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Mailjet/README.md .. _`Markdown syntax`: https://commonmark.org/ +.. _`Mailomat`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Mailomat/README.md .. _`MailPace`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/MailPace/README.md .. _`OpenSSL PHP extension`: https://www.php.net/manual/en/book.openssl.php .. _`PEM encoded`: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail diff --git a/webhook.rst b/webhook.rst index 38f3a4b1004..52633ae83e5 100644 --- a/webhook.rst +++ b/webhook.rst @@ -27,6 +27,7 @@ Brevo ``mailer.webhook.request_parser.brevo`` MailerSend ``mailer.webhook.request_parser.mailersend`` Mailgun ``mailer.webhook.request_parser.mailgun`` Mailjet ``mailer.webhook.request_parser.mailjet`` +Mailomat ``mailer.webhook.request_parser.mailomat`` Postmark ``mailer.webhook.request_parser.postmark`` Resend ``mailer.webhook.request_parser.resend`` Sendgrid ``mailer.webhook.request_parser.sendgrid`` @@ -36,6 +37,10 @@ Sendgrid ``mailer.webhook.request_parser.sendgrid`` The support for ``Resend`` and ``MailerSend`` were introduced in Symfony 7.1. +.. versionadded:: 7.2 + + The ``Mailomat`` integration was introduced in Symfony 7.2. + .. note:: Install the third-party mailer provider you want to use as described in the From 7cdfb52930f9097fef14ed56a3c8d6624461cb52 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 19 Jun 2024 09:20:04 +0200 Subject: [PATCH 015/642] [FrameworkBundle] Simplified the MicroKernelTrait setup --- configuration/micro_kernel_trait.rst | 107 +++++++++++++++------------ 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index b67335514a1..23aa677cf20 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -16,9 +16,7 @@ via Composer: .. code-block:: terminal - $ composer require symfony/config symfony/http-kernel \ - symfony/http-foundation symfony/routing \ - symfony/dependency-injection symfony/framework-bundle + $ composer symfony/framework-bundle symfony/runtime Next, create an ``index.php`` file that defines the kernel class and runs it: @@ -34,19 +32,12 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\Attribute\Route; - require __DIR__.'/vendor/autoload.php'; + require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; class Kernel extends BaseKernel { use MicroKernelTrait; - public function registerBundles(): array - { - return [ - new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - ]; - } - protected function configureContainer(ContainerConfigurator $container): void { // PHP equivalent of config/packages/framework.yaml @@ -64,11 +55,9 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: } } - $kernel = new Kernel('dev', true); - $request = Request::createFromGlobals(); - $response = $kernel->handle($request); - $response->send(); - $kernel->terminate($request, $response); + return static function (array $context) { + return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); + } .. code-block:: php @@ -80,19 +69,12 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: use Symfony\Component\HttpKernel\Kernel as BaseKernel; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; - require __DIR__.'/vendor/autoload.php'; + require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; class Kernel extends BaseKernel { use MicroKernelTrait; - public function registerBundles(): array - { - return [ - new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - ]; - } - protected function configureContainer(ContainerConfigurator $container): void { // PHP equivalent of config/packages/framework.yaml @@ -114,17 +96,9 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: } } - $kernel = new Kernel('dev', true); - $request = Request::createFromGlobals(); - $response = $kernel->handle($request); - $response->send(); - $kernel->terminate($request, $response); - -.. note:: - - In addition to the ``index.php`` file, you'll need to create a directory called - ``config/`` in your project (even if it's empty because you define the configuration - options inside the ``configureContainer()`` method). + return static function (array $context) { + return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); + } That's it! To test it, start the :doc:`Symfony Local Web Server `: @@ -135,6 +109,23 @@ That's it! To test it, start the :doc:`Symfony Local Web Server Then see the JSON response in your browser: http://localhost:8000/random/10 +.. tip:: + + If your kernel only defines a single controller, you can use an invokable method:: + + class Kernel extends BaseKernel + { + use MicroKernelTrait; + + // ... + + #[Route('/random/{limit}', name: 'random_number')] + public function __invoke(int $limit): JsonResponse + { + // ... + } + } + The Methods of a "Micro" Kernel ------------------------------- @@ -142,7 +133,26 @@ When you use the ``MicroKernelTrait``, your kernel needs to have exactly three m that define your bundles, your services and your routes: **registerBundles()** - This is the same ``registerBundles()`` that you see in a normal kernel. + This is the same ``registerBundles()`` that you see in a normal kernel. By + default, the micro kernel only registers the ``FrameworkBundle``. If you need + to register more bundles, override this method:: + + use Symfony\Bundle\FrameworkBundle\FrameworkBundle; + use Symfony\Bundle\TwigBundle\TwigBundle; + // ... + + class Kernel extends BaseKernel + { + use MicroKernelTrait; + + // ... + + public function registerBundles(): array + { + yield new FrameworkBundle(); + yield new TwigBundle(); + } + } **configureContainer(ContainerConfigurator $container)** This method builds and configures the container. In practice, you will use @@ -151,9 +161,13 @@ that define your bundles, your services and your routes: services directly in PHP or load external configuration files (shown below). **configureRoutes(RoutingConfigurator $routes)** - Your job in this method is to add routes to the application. The - ``RoutingConfigurator`` has methods that make adding routes in PHP more - fun. You can also load external routing files (shown below). + In this method, you can use the ``RoutingConfigurator`` object to define routes + in your application and associate them to the controllers defined in this very + same file. + + However, it's more convenient to define the controller routes using PHP attributes, + as shown above. That's why this method is commonly used only to load external + routing files (e.g. from bundles) as shown below. Adding Interfaces to "Micro" Kernel ----------------------------------- @@ -231,7 +245,10 @@ Now it looks like this:: namespace App; use App\DependencyInjection\AppExtension; + use Symfony\Bundle\FrameworkBundle\FrameworkBundle; use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; + use Symfony\Bundle\TwigBundle\TwigBundle; + use Symfony\Bundle\WebProfilerBundle\WebProfilerBundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\HttpKernel\Kernel as BaseKernel; @@ -241,18 +258,14 @@ Now it looks like this:: { use MicroKernelTrait; - public function registerBundles(): array + public function registerBundles(): iterable { - $bundles = [ - new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), - new \Symfony\Bundle\TwigBundle\TwigBundle(), - ]; + yield FrameworkBundle(); + yield TwigBundle(); if ('dev' === $this->getEnvironment()) { - $bundles[] = new \Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); + yield WebProfilerBundle(); } - - return $bundles; } protected function build(ContainerBuilder $containerBuilder): void From d68d6abc1120c766b42498468a6667695ee16805 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 25 Jun 2024 09:43:08 +0200 Subject: [PATCH 016/642] [Validator] Add the format option to the Ulid constraint to allow accepting different ULID formats --- reference/constraints/Ulid.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/reference/constraints/Ulid.rst b/reference/constraints/Ulid.rst index ed7dfe7ed96..4ba5ec3a3f5 100644 --- a/reference/constraints/Ulid.rst +++ b/reference/constraints/Ulid.rst @@ -73,6 +73,20 @@ Basic Usage Options ------- +``format`` +~~~~~~~~~~ + +**type**: ``string`` **default**: ``Ulid::FORMAT_BASE_32`` + +The format of the ULID to validate. The following formats are available: + +* ``Ulid::FORMAT_BASE_32``: The ULID is encoded in base32 (default) +* ``Ulid::FORMAT_BASE_58``: The ULID is encoded in base58 + +.. versionadded:: 7.2 + + The ``format`` option was introduced in Symfony 7.2. + .. include:: /reference/constraints/_groups-option.rst.inc ``message`` From ea63cd7d932c1b77ee589938dda2d3e03c0a9aca Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 25 Jun 2024 10:49:40 +0200 Subject: [PATCH 017/642] [Messenger] Document the --format option of messenger:stats command --- messenger.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/messenger.rst b/messenger.rst index 3dab2caac52..785cc022136 100644 --- a/messenger.rst +++ b/messenger.rst @@ -688,6 +688,14 @@ of some or all transports: # shows stats only for some transports $ php bin/console messenger:stats my_transport_name other_transport_name + # you can also output the stats in JSON format + $ php bin/console messenger:stats --format=json + $ php bin/console messenger:stats my_transport_name other_transport_name --format=json + +.. versionadded:: 7.2 + + The ``format`` option was introduced in Symfony 7.2. + .. note:: In order for this command to work, the configured transport's receiver must implement From 688db2816b0892982b0e2b2ab02fa9cf6a7f0573 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 14 Jun 2024 16:56:00 +0200 Subject: [PATCH 018/642] [Validator] Add the Yaml constraint --- components/yaml.rst | 2 + reference/constraints/Yaml.rst | 152 +++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 reference/constraints/Yaml.rst diff --git a/components/yaml.rst b/components/yaml.rst index 5f724e0572c..2698aae8233 100644 --- a/components/yaml.rst +++ b/components/yaml.rst @@ -214,6 +214,8 @@ During the parsing of the YAML contents, all the ``_`` characters are removed from the numeric literal contents, so there is not a limit in the number of underscores you can include or the way you group contents. +.. _yaml-flags: + Advanced Usage: Flags --------------------- diff --git a/reference/constraints/Yaml.rst b/reference/constraints/Yaml.rst new file mode 100644 index 00000000000..49b65f251e8 --- /dev/null +++ b/reference/constraints/Yaml.rst @@ -0,0 +1,152 @@ +Yaml +==== + +Validates that a value has valid `YAML`_ syntax. + +.. versionadded:: 7.2 + + The ``Yaml`` constraint was introduced in Symfony 7.2. + +========== =================================================================== +Applies to :ref:`property or method ` +Class :class:`Symfony\\Component\\Validator\\Constraints\\Yaml` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\YamlValidator` +========== =================================================================== + +Basic Usage +----------- + +The ``Yaml`` constraint can be applied to a property or a "getter" method: + +.. configuration-block:: + + .. code-block:: php-attributes + + // src/Entity/Report.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Report + { + #[Assert\Yaml( + message: "Your configuration doesn't have valid YAML syntax." + )] + private string $customConfiguration; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Entity\Report: + properties: + customConfiguration: + - Yaml: + message: Your configuration doesn't have valid YAML syntax. + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // src/Entity/Report.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class Report + { + public static function loadValidatorMetadata(ClassMetadata $metadata): void + { + $metadata->addPropertyConstraint('customConfiguration', new Assert\Yaml([ + 'message' => 'Your configuration doesn\'t have valid YAML syntax.', + ])); + } + } + +Options +------- + +``flags`` +~~~~~~~~~ + +**type**: ``integer`` **default**: ``0`` + +This option enables optional features of the YAML parser when validating contents. +Its value is a combination of one or more of the :ref:`flags defined by the Yaml component `: + +.. configuration-block:: + + .. code-block:: php-attributes + + // src/Entity/Report.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Yaml\Yaml; + + class Report + { + #[Assert\Yaml( + message: "Your configuration doesn't have valid YAML syntax.", + flags: Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS | Yaml::PARSE_DATETIME, + )] + private string $customConfiguration; + } + + .. code-block:: php + + // src/Entity/Report.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + use Symfony\Component\Yaml\Yaml; + + class Report + { + public static function loadValidatorMetadata(ClassMetadata $metadata): void + { + $metadata->addPropertyConstraint('customConfiguration', new Assert\Yaml([ + 'message' => 'Your configuration doesn\'t have valid YAML syntax.', + 'flags' => Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS | Yaml::PARSE_DATETIME, + ])); + } + } + +``message`` +~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This value is not valid YAML.`` + +This message shown if the underlying data is not a valid YAML value. + +You can use the following parameters in this message: + +=============== ============================================================== +Parameter Description +=============== ============================================================== +``{{ error }}`` The full error message from the YAML parser +``{{ line }}`` The line where the YAML syntax error happened +=============== ============================================================== + +.. include:: /reference/constraints/_groups-option.rst.inc + +.. include:: /reference/constraints/_payload-option.rst.inc + +.. _`YAML`: https://en.wikipedia.org/wiki/YAML From c3fdc75c27551e2a09bb5298b1d47e7e759ac60c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 26 Jun 2024 09:01:07 +0200 Subject: [PATCH 019/642] [ExpressionLanguage] Add support for comments --- reference/formats/expression_language.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/reference/formats/expression_language.rst b/reference/formats/expression_language.rst index 818870452a5..e88ac7f6c24 100644 --- a/reference/formats/expression_language.rst +++ b/reference/formats/expression_language.rst @@ -20,6 +20,11 @@ The component supports: * **booleans** - ``true`` and ``false`` * **null** - ``null`` * **exponential** - also known as scientific (e.g. ``1.99E+3`` or ``1e-2``) +* **comments** - using ``/*`` and ``*/`` (e.g. ``/* this is a comment */``) + +.. versionadded:: 7.2 + + The support for comments inside expressions was introduced in Symfony 7.2. .. caution:: From 39be3e699885a57f91a2b30a4f34855465f1ec03 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 26 Jun 2024 09:55:47 +0200 Subject: [PATCH 020/642] [DependencyInjection] Add `#[WhenNot]` attribute --- reference/attributes.rst | 1 + service_container.rst | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/reference/attributes.rst b/reference/attributes.rst index 4428dc4c587..d744cb9a9b4 100644 --- a/reference/attributes.rst +++ b/reference/attributes.rst @@ -43,6 +43,7 @@ Dependency Injection * :ref:`TaggedLocator ` * :ref:`Target ` * :ref:`When ` +* :ref:`WhenNot ` EventDispatcher ~~~~~~~~~~~~~~~ diff --git a/service_container.rst b/service_container.rst index ebc01b1fc8a..bc8e5f123b1 100644 --- a/service_container.rst +++ b/service_container.rst @@ -260,6 +260,32 @@ as a service in some environments:: // ... } +If you want to exclude a service from being registered in a specific +environment, you can use the ``#[WhenNot]`` attribute:: + + use Symfony\Component\DependencyInjection\Attribute\WhenNot; + + // SomeClass is registered in all environments except "dev" + + #[WhenNot(env: 'dev')] + class SomeClass + { + // ... + } + + // you can apply more than one WhenNot attribute to the same class + + #[WhenNot(env: 'dev')] + #[WhenNot(env: 'test')] + class AnotherClass + { + // ... + } + +.. versionadded:: 7.2 + + The ``#[WhenNot]`` attribute was introduced in Symfony 7.2. + .. _services-constructor-injection: Injecting Services/Config into a Service From c4e8ace5ce8e095c4c6a67a1db20d979a003195b Mon Sep 17 00:00:00 2001 From: sakul95 <60596924+sakul95@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:10:25 +0200 Subject: [PATCH 021/642] [Notifier] Add Sipgate bridge --- notifier.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/notifier.rst b/notifier.rst index 1a19c135a7c..3ecb8878fb4 100644 --- a/notifier.rst +++ b/notifier.rst @@ -159,6 +159,9 @@ Service `Sinch`_ **Install**: ``composer require symfony/sinch-notifier`` \ **DSN**: ``sinch://ACCOUNT_ID:AUTH_TOKEN@default?from=FROM`` \ **Webhook support**: No +`Sipgate`_ **Install**: ``composer require symfony/sipgate-notifier`` \ + **DSN**: ``sipgate://TOKEN_ID:TOKEN@default?senderId=SENDER_ID`` \ + **Webhook support**: No `SmsSluzba`_ **Install**: ``composer require symfony/sms-sluzba-notifier`` \ **DSN**: ``sms-sluzba://USERNAME:PASSWORD@default`` \ **Webhook support**: No @@ -214,6 +217,10 @@ Service The ``Smsbox``, ``SmsSluzba``, ``SMSense``, ``LOX24`` and ``Unifonic`` integrations were introduced in Symfony 7.1. +.. versionadded:: 7.2 + + The ``Sipgate`` integration was introduced in Symfony 7.2. + .. deprecated:: 7.1 The `Sms77`_ integration is deprecated since @@ -1131,6 +1138,7 @@ is dispatched. Listeners receive a .. _`Seven.io`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sevenio/README.md .. _`SimpleTextin`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SimpleTextin/README.md .. _`Sinch`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sinch/README.md +.. _`Sipgate`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sipgate/README.md .. _`Slack`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Slack/README.md .. _`Sms77`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sms77/README.md .. _`SmsBiuras`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SmsBiuras/README.md From 9f4e5a5fb5418489978f0b30e5803e4eaec80baf Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Sun, 7 Jul 2024 13:56:54 +0200 Subject: [PATCH 022/642] Remove the Gitter bridge --- notifier.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/notifier.rst b/notifier.rst index 5fe86724fe1..cbc23655124 100644 --- a/notifier.rst +++ b/notifier.rst @@ -351,7 +351,6 @@ Service Package D `Discord`_ ``symfony/discord-notifier`` ``discord://TOKEN@default?webhook_id=ID`` `FakeChat`_ ``symfony/fake-chat-notifier`` ``fakechat+email://default?to=TO&from=FROM`` or ``fakechat+logger://default`` `Firebase`_ ``symfony/firebase-notifier`` ``firebase://USERNAME:PASSWORD@default`` -`Gitter`_ ``symfony/gitter-notifier`` ``gitter://TOKEN@default?room_id=ROOM_ID`` `GoogleChat`_ ``symfony/google-chat-notifier`` ``googlechat://ACCESS_KEY:ACCESS_TOKEN@default/SPACE?thread_key=THREAD_KEY`` `LINE Notify`_ ``symfony/line-notify-notifier`` ``linenotify://TOKEN@default`` `LinkedIn`_ ``symfony/linked-in-notifier`` ``linkedin://TOKEN:USER_ID@default`` @@ -1105,7 +1104,6 @@ is dispatched. Listeners receive a .. _`Firebase`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Firebase/README.md .. _`FreeMobile`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/FreeMobile/README.md .. _`GatewayApi`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/GatewayApi/README.md -.. _`Gitter`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Gitter/README.md .. _`GoIP`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/GoIP/README.md .. _`GoogleChat`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/GoogleChat/README.md .. _`Infobip`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Infobip/README.md From 03173f88b1d74cb64a5ae1042bbdd19a3d515531 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 8 Jul 2024 09:39:04 +0200 Subject: [PATCH 023/642] Add a note explaining the removal of Gitter --- notifier.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/notifier.rst b/notifier.rst index cbc23655124..d959b4a73d0 100644 --- a/notifier.rst +++ b/notifier.rst @@ -370,6 +370,11 @@ Service Package D The ``Bluesky`` integration was introduced in Symfony 7.1. +.. deprecated:: 7.2 + + The ``Gitter`` integration was removed in Symfony 7.2 because that service + no longer provides an API. + .. caution:: By default, if you have the :doc:`Messenger component ` installed, From 7928e4d7b21c7e860492c7ee6e9dbcd4a1f2e1ef Mon Sep 17 00:00:00 2001 From: Tim Herlaud Date: Wed, 10 Jul 2024 09:17:34 +0200 Subject: [PATCH 024/642] [String] Add TruncateMode mode to truncate methods --- string.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/string.rst b/string.rst index c58a736da89..c956d33b9af 100644 --- a/string.rst +++ b/string.rst @@ -394,11 +394,16 @@ Methods to Join, Split, Truncate and Reverse u('Lorem Ipsum')->truncate(3); // 'Lor' u('Lorem Ipsum')->truncate(80); // 'Lorem Ipsum' // the second argument is the character(s) added when a string is cut + // the third argument is TruncateMode::Char by default // (the total length includes the length of this character(s)) - u('Lorem Ipsum')->truncate(8, '…'); // 'Lorem I…' - // if the third argument is false, the last word before the cut is kept - // even if that generates a string longer than the desired length - u('Lorem Ipsum')->truncate(8, '…', cut: false); // 'Lorem Ipsum' + u('Lorem Ipsum')->truncate(8, '…'); // 'Lorem I…' + // use options to keep complete words + u('Lorem ipsum dolor sit amet')->truncate(10, '...', TruncateMode::WordBefore); // 'Lorem...' + u('Lorem ipsum dolor sit amet')->truncate(14, '...', TruncateMode::WordAfter); // 'Lorem ipsum...' + +.. versionadded:: 7.2 + + The TruncateMode argument for truncate function was introduced in Symfony 7.2. :: From b607d25adaad279a4c0b974713f3f627c00cff7c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 10 Jul 2024 17:09:38 +0200 Subject: [PATCH 025/642] Reword --- string.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/string.rst b/string.rst index 72dfb5c4ae7..cd104b1f947 100644 --- a/string.rst +++ b/string.rst @@ -394,16 +394,20 @@ Methods to Join, Split, Truncate and Reverse u('Lorem Ipsum')->truncate(3); // 'Lor' u('Lorem Ipsum')->truncate(80); // 'Lorem Ipsum' // the second argument is the character(s) added when a string is cut - // the third argument is TruncateMode::Char by default // (the total length includes the length of this character(s)) + // (note that '…' is a single character that includes three dots; it's not '...') u('Lorem Ipsum')->truncate(8, '…'); // 'Lorem I…' - // use options to keep complete words - u('Lorem ipsum dolor sit amet')->truncate(10, '...', TruncateMode::WordBefore); // 'Lorem...' - u('Lorem ipsum dolor sit amet')->truncate(14, '...', TruncateMode::WordAfter); // 'Lorem ipsum...' + // the third optional argument defines how to cut words when the length is exceeded + // the default value is TruncateMode::Char which cuts the string at the exact given length + u('Lorem ipsum dolor sit amet')->truncate(8, cut: TruncateMode::Char); // 'Lorem ip' + // returns up to the last complete word that fits in the given length without surpassing it + u('Lorem ipsum dolor sit amet')->truncate(8, cut: TruncateMode::WordBefore); // 'Lorem' + // returns up to the last complete word that fits in the given length, surpassing it if needed + u('Lorem ipsum dolor sit amet')->truncate(8, cut: TruncateMode::WordAfter); // 'Lorem ipsum' .. versionadded:: 7.2 - The TruncateMode argument for truncate function was introduced in Symfony 7.2. + The ``TruncateMode`` argument for truncate function was introduced in Symfony 7.2. :: From 3236182336c79d9d3ea5a28a2c21550067b636a0 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Wed, 10 Jul 2024 20:06:51 +0200 Subject: [PATCH 026/642] - --- string.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/string.rst b/string.rst index cd104b1f947..f2856976986 100644 --- a/string.rst +++ b/string.rst @@ -407,7 +407,7 @@ Methods to Join, Split, Truncate and Reverse .. versionadded:: 7.2 - The ``TruncateMode`` argument for truncate function was introduced in Symfony 7.2. + The ``TruncateMode`` parameter for truncate function was introduced in Symfony 7.2. :: From e983455bc391256a77f5397efe650393e9c58c04 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 17 Jul 2024 15:13:29 +0200 Subject: [PATCH 027/642] [Validator] Add the `WordCount` constraint --- reference/constraints/WordCount.rst | 150 ++++++++++++++++++++++++++++ reference/constraints/map.rst.inc | 1 + 2 files changed, 151 insertions(+) create mode 100644 reference/constraints/WordCount.rst diff --git a/reference/constraints/WordCount.rst b/reference/constraints/WordCount.rst new file mode 100644 index 00000000000..74c79216898 --- /dev/null +++ b/reference/constraints/WordCount.rst @@ -0,0 +1,150 @@ +WordCount +========= + +.. versionadded:: 7.2 + + The ``WordCount`` constraint was introduced in Symfony 7.2. + +Validates that a string (or an object implementing the ``Stringable`` PHP interface) +contains a given number of words. Internally, this constraint uses the +:phpclass:`IntlBreakIterator` class to count the words depending on your locale. + +========== ======================================================================= +Applies to :ref:`property or method ` +Class :class:`Symfony\\Component\\Validator\\Constraints\\WordCount` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\WordCountValidator` +========== ======================================================================= + +Basic Usage +----------- + +If you wanted to ensure that the ``content`` property of a ``BlogPostDTO`` +class contains between 100 and 200 words, you could do the following: + +.. configuration-block:: + + .. code-block:: php-attributes + + // src/Entity/BlogPostDTO.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class BlogPostDTO + { + #[Assert\WordCount(min: 100, max: 200)] + protected string $content; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Entity\BlogPostDTO: + properties: + content: + - WordCount: + min: 100 + max: 200 + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: php + + // src/Entity/BlogPostDTO.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class BlogPostDTO + { + // ... + + public static function loadValidatorMetadata(ClassMetadata $metadata): void + { + $metadata->addPropertyConstraint('content', new Assert\WordCount([ + 'min' => 100, + 'max' => 200, + ])); + } + } + +Options +------- + +``min`` +~~~~~~~ + +**type**: ``integer`` **default**: ``null`` + +The minimum number of words that the value must contain. + +``max`` +~~~~~~~ + +**type**: ``integer`` **default**: ``null`` + +The maximum number of words that the value must contain. + +``locale`` +~~~~~~~~~~ + +**type**: ``string`` **default**: ``null`` + +The locale to use for counting the words by using the :phpclass:`IntlBreakIterator` +class. The default value (``null``) means that the constraint uses the current +user locale. + +.. include:: /reference/constraints/_groups-option.rst.inc + +``minMessage`` +~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words.`` + +This is the message that will be shown if the value does not contain at least +the minimum number of words. + +You can use the following parameters in this message: + +================ ================================================== +Parameter Description +================ ================================================== +``{{ min }}`` The minimum number of words +``{{ count }}`` The actual number of words +================ ================================================== + +``maxMessage`` +~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less.`` + +This is the message that will be shown if the value contains more than the +maximum number of words. + +You can use the following parameters in this message: + +================ ================================================== +Parameter Description +================ ================================================== +``{{ max }}`` The maximum number of words +``{{ count }}`` The actual number of words +================ ================================================== + +.. include:: /reference/constraints/_payload-option.rst.inc diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 9f14f418bb1..978951c9de7 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -33,6 +33,7 @@ String Constraints * :doc:`NoSuspiciousCharacters ` * :doc:`Charset ` * :doc:`MacAddress ` +* :doc:`WordCount ` Comparison Constraints ~~~~~~~~~~~~~~~~~~~~~~ From fa572ee1d7fd2cea6717ab3a52a93d30d536c04b Mon Sep 17 00:00:00 2001 From: Petrisor Ciprian Daniel Date: Fri, 19 Jul 2024 23:31:27 +0300 Subject: [PATCH 028/642] [Config] Secrets decrypt exit option --- configuration/secrets.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/configuration/secrets.rst b/configuration/secrets.rst index f717456a22c..b16c36643aa 100644 --- a/configuration/secrets.rst +++ b/configuration/secrets.rst @@ -271,11 +271,20 @@ manually store this file somewhere and deploy it. There are 2 ways to do that: .. code-block:: terminal - $ APP_RUNTIME_ENV=prod php bin/console secrets:decrypt-to-local --force + $ APP_RUNTIME_ENV=prod php bin/console secrets:decrypt-to-local --force --exit This will write all the decrypted secrets into the ``.env.prod.local`` file. After doing this, the decryption key does *not* need to remain on the server(s). + Note the usage of the ``--exit`` option: this forces all secrets to be successfully + decrypted, otherwise a non-zero exit code is returned. + + If you wish to continue regardless of errors occurring during decryption, you may omit this option. + + .. versionadded:: 7.2 + + The ``--exit`` option was introduced in Symfony 7.2. + Rotating Secrets ---------------- From 97103298ebabb6f3fb5c015450e1d8f893149bca Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 22 Jul 2024 09:03:49 +0200 Subject: [PATCH 029/642] Tweaks --- configuration/secrets.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/configuration/secrets.rst b/configuration/secrets.rst index b16c36643aa..734873b255c 100644 --- a/configuration/secrets.rst +++ b/configuration/secrets.rst @@ -276,14 +276,16 @@ manually store this file somewhere and deploy it. There are 2 ways to do that: This will write all the decrypted secrets into the ``.env.prod.local`` file. After doing this, the decryption key does *not* need to remain on the server(s). - Note the usage of the ``--exit`` option: this forces all secrets to be successfully - decrypted, otherwise a non-zero exit code is returned. + Note the usage of the ``--exit`` option: this ensures that all secrets are + successfully decrypted. If any error occurs during the decryption process, + the command will return a non-zero exit code, indicating a failure. - If you wish to continue regardless of errors occurring during decryption, you may omit this option. + If you wish to continue regardless of errors occurring during decryption, + you may omit this option. .. versionadded:: 7.2 - The ``--exit`` option was introduced in Symfony 7.2. + The ``--exit`` option was introduced in Symfony 7.2. Rotating Secrets ---------------- From 8e1d67b18758832865b51fd28066018cfc34b9ac Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 22 Jul 2024 15:16:47 +0200 Subject: [PATCH 030/642] drop the --exit option --- configuration/secrets.rst | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/configuration/secrets.rst b/configuration/secrets.rst index 734873b255c..f717456a22c 100644 --- a/configuration/secrets.rst +++ b/configuration/secrets.rst @@ -271,22 +271,11 @@ manually store this file somewhere and deploy it. There are 2 ways to do that: .. code-block:: terminal - $ APP_RUNTIME_ENV=prod php bin/console secrets:decrypt-to-local --force --exit + $ APP_RUNTIME_ENV=prod php bin/console secrets:decrypt-to-local --force This will write all the decrypted secrets into the ``.env.prod.local`` file. After doing this, the decryption key does *not* need to remain on the server(s). - Note the usage of the ``--exit`` option: this ensures that all secrets are - successfully decrypted. If any error occurs during the decryption process, - the command will return a non-zero exit code, indicating a failure. - - If you wish to continue regardless of errors occurring during decryption, - you may omit this option. - - .. versionadded:: 7.2 - - The ``--exit`` option was introduced in Symfony 7.2. - Rotating Secrets ---------------- From f5f16af36ded58edf37af2be3fe6f078d7c6b64b Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 25 Jul 2024 09:54:38 +0200 Subject: [PATCH 031/642] add the $token argument to checkPostAuth() --- security/user_checkers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/user_checkers.rst b/security/user_checkers.rst index d62cc0bea32..1e1dcaf3e55 100644 --- a/security/user_checkers.rst +++ b/security/user_checkers.rst @@ -40,7 +40,7 @@ displayed to the user:: } } - public function checkPostAuth(UserInterface $user): void + public function checkPostAuth(UserInterface $user, TokenInterface $token): void { if (!$user instanceof AppUser) { return; From aec2ec5ef61cca979c9d830a6fd22d6a64446ab4 Mon Sep 17 00:00:00 2001 From: Marcin Nowak Date: Fri, 26 Jul 2024 14:12:16 +0200 Subject: [PATCH 032/642] Added information about clock parameter in ArrayAdapter --- components/cache/adapters/array_cache_adapter.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/components/cache/adapters/array_cache_adapter.rst b/components/cache/adapters/array_cache_adapter.rst index f903771e468..a6a72514361 100644 --- a/components/cache/adapters/array_cache_adapter.rst +++ b/components/cache/adapters/array_cache_adapter.rst @@ -24,5 +24,10 @@ method:: // the maximum number of items that can be stored in the cache. When the limit // is reached, cache follows the LRU model (least recently used items are deleted) - $maxItems = 0 + $maxItems = 0, + + // implementation of Psr\Clock\ClockInterface (e.g. Symfony\Component\Clock\Clock) + // or null. If clock is provided, cache items lifetime will be calculated + // based on time provided by this clock + $clock = null ); From 87acbbcce5954cd05f16824d8db91b42fe954318 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 26 Jul 2024 14:29:19 +0200 Subject: [PATCH 033/642] fix the syntax of PHP code blocks --- configuration/micro_kernel_trait.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index 23aa677cf20..c9739679f69 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -57,7 +57,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: return static function (array $context) { return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); - } + }; .. code-block:: php @@ -98,7 +98,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: return static function (array $context) { return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); - } + }; That's it! To test it, start the :doc:`Symfony Local Web Server `: From f1c8808ee481a65b1e10453e362b54b98c3ae4cc Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 26 Jul 2024 17:03:46 +0200 Subject: [PATCH 034/642] [Cache] Igbinary extension is no longer used by default when available --- components/cache.rst | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/components/cache.rst b/components/cache.rst index f5a76f2119d..939627b1807 100644 --- a/components/cache.rst +++ b/components/cache.rst @@ -208,16 +208,27 @@ Symfony uses *marshallers* (classes which implement the cache items before storing them. The :class:`Symfony\\Component\\Cache\\Marshaller\\DefaultMarshaller` uses PHP's -``serialize()`` or ``igbinary_serialize()`` if the `Igbinary extension`_ is installed. -There are other *marshallers* that can encrypt or compress the data before storing it:: +``serialize()`` function by default, but you can optionally use the ``igbinary_serialize()`` +function from the `Igbinary extension`_: use Symfony\Component\Cache\Adapter\RedisAdapter; use Symfony\Component\Cache\DefaultMarshaller; use Symfony\Component\Cache\DeflateMarshaller; $marshaller = new DeflateMarshaller(new DefaultMarshaller()); + // you can optionally use the Igbinary extension if you have it installed + // $marshaller = new DeflateMarshaller(new DefaultMarshaller(useIgbinarySerialize: true)); + $cache = new RedisAdapter(new \Redis(), 'namespace', 0, $marshaller); +There are other *marshallers* that can encrypt or compress the data before storing it. + +.. versionadded:: 7.2 + + In Symfony versions prior to 7.2, the ``igbinary_serialize()`` function was + used by default when the Igbinary extension was installed. Starting from + Symfony 7.2, you have to enable Igbinary support explicitly. + Advanced Usage -------------- From d2b384beacf7a749ffaf0e98b9f41315a95893a2 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 30 Jul 2024 16:38:51 +0200 Subject: [PATCH 035/642] fix typo --- performance.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/performance.rst b/performance.rst index 70a6602873e..828333f338b 100644 --- a/performance.rst +++ b/performance.rst @@ -367,7 +367,7 @@ method does, which stops an event and then restarts it immediately:: .. versionadded:: 7.2 - The ``getLastPeriod`` method was introduced in Symfony 7.2. + The ``getLastPeriod()`` method was introduced in Symfony 7.2. Profiling Sections .................. From e2d8f0a36494d6370e0c845a18a36a84f8329ea5 Mon Sep 17 00:00:00 2001 From: Jonas Claes Date: Thu, 1 Aug 2024 19:31:56 +0200 Subject: [PATCH 036/642] Update mailer.rst --- mailer.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mailer.rst b/mailer.rst index 502644a741a..11acd065e52 100644 --- a/mailer.rst +++ b/mailer.rst @@ -110,6 +110,7 @@ Service Install with Webhook su `MailPace`_ ``composer require symfony/mail-pace-mailer`` `MailerSend`_ ``composer require symfony/mailer-send-mailer`` `Mandrill`_ ``composer require symfony/mailchimp-mailer`` +`Postal`_ ``composer require symfony/postal-mailer`` `Postmark`_ ``composer require symfony/postmark-mailer`` yes `Resend`_ ``composer require symfony/resend-mailer`` yes `Scaleway`_ ``composer require symfony/scaleway-mailer`` @@ -122,7 +123,7 @@ Service Install with Webhook su .. versionadded:: 7.2 - The Mailomat integration was introduced in Symfony 7.2. + The Mailomat and Postal integrations were introduced in Symfony 7.2. .. note:: @@ -214,6 +215,10 @@ party provider: | | - HTTP n/a | | | - API ``mailpace+api://API_TOKEN@default`` | +------------------------+---------------------------------------------------------+ +| `Postal`_ | - SMTP n/a | +| | - HTTP n/a | +| | - API ``postal+api://API_KEY@BASE_URL`` | ++------------------------+---------------------------------------------------------+ | `Postmark`_ | - SMTP ``postmark+smtp://ID@default`` | | | - HTTP n/a | | | - API ``postmark+api://KEY@default`` | @@ -1992,6 +1997,7 @@ the :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\MailerAssertionsTrait`:: .. _`MailPace`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/MailPace/README.md .. _`OpenSSL PHP extension`: https://www.php.net/manual/en/book.openssl.php .. _`PEM encoded`: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail +.. _`Postal`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Postal/README.md .. _`Postmark`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Postmark/README.md .. _`Resend`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Resend/README.md .. _`RFC 3986`: https://www.ietf.org/rfc/rfc3986.txt From 4b3fad09b8894e9d61876e1fdd8752275fe57691 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 6 Aug 2024 17:44:01 +0200 Subject: [PATCH 037/642] Add the versionadded directive --- components/cache/adapters/array_cache_adapter.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/components/cache/adapters/array_cache_adapter.rst b/components/cache/adapters/array_cache_adapter.rst index a6a72514361..08f8276db3d 100644 --- a/components/cache/adapters/array_cache_adapter.rst +++ b/components/cache/adapters/array_cache_adapter.rst @@ -26,8 +26,12 @@ method:: // is reached, cache follows the LRU model (least recently used items are deleted) $maxItems = 0, - // implementation of Psr\Clock\ClockInterface (e.g. Symfony\Component\Clock\Clock) - // or null. If clock is provided, cache items lifetime will be calculated - // based on time provided by this clock - $clock = null + // optional implementation of the Psr\Clock\ClockInterface that will be used + // to calculate the lifetime of cache items (for example to get predictable + // lifetimes in tests) + $clock = null, ); + +.. versionadded:: 7.2 + + The optional ``$clock`` argument was introduced in Symfony 7.2. From c7bf80275b6a65807642e860fc19af77ffd6c453 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Wed, 7 Aug 2024 11:02:10 +0200 Subject: [PATCH 038/642] [Validator] Mention `Ulid::FORMAT_RFC4122` --- reference/constraints/Ulid.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/reference/constraints/Ulid.rst b/reference/constraints/Ulid.rst index 4ba5ec3a3f5..a5888409960 100644 --- a/reference/constraints/Ulid.rst +++ b/reference/constraints/Ulid.rst @@ -82,6 +82,7 @@ The format of the ULID to validate. The following formats are available: * ``Ulid::FORMAT_BASE_32``: The ULID is encoded in base32 (default) * ``Ulid::FORMAT_BASE_58``: The ULID is encoded in base58 +* ``Ulid::FORMAT_RFC4122``: The ULID is encoded in the RFC 4122 format .. versionadded:: 7.2 From 9381f4be9bf1149ab63d850b84d678e2e1df6869 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 7 Aug 2024 14:20:31 +0200 Subject: [PATCH 039/642] [Validator] Add links to Ulid constraint formats --- reference/constraints/Ulid.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/reference/constraints/Ulid.rst b/reference/constraints/Ulid.rst index a5888409960..4094bab98f5 100644 --- a/reference/constraints/Ulid.rst +++ b/reference/constraints/Ulid.rst @@ -80,9 +80,9 @@ Options The format of the ULID to validate. The following formats are available: -* ``Ulid::FORMAT_BASE_32``: The ULID is encoded in base32 (default) -* ``Ulid::FORMAT_BASE_58``: The ULID is encoded in base58 -* ``Ulid::FORMAT_RFC4122``: The ULID is encoded in the RFC 4122 format +* ``Ulid::FORMAT_BASE_32``: The ULID is encoded in `base32`_ (default) +* ``Ulid::FORMAT_BASE_58``: The ULID is encoded in `base58`_ +* ``Ulid::FORMAT_RFC4122``: The ULID is encoded in the `RFC 4122 format`_ .. versionadded:: 7.2 @@ -111,3 +111,6 @@ Parameter Description .. include:: /reference/constraints/_payload-option.rst.inc .. _`Universally Unique Lexicographically Sortable Identifier (ULID)`: https://github.com/ulid/spec +.. _`base32`: https://en.wikipedia.org/wiki/Base32 +.. _`base58`: https://en.wikipedia.org/wiki/Binary-to-text_encoding#Base58 +.. _`RFC 4122 format`: https://datatracker.ietf.org/doc/html/rfc4122 From 323391d8876b561f1fa12d12c86c59cf75512c89 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 13 Aug 2024 10:21:05 +0200 Subject: [PATCH 040/642] [Validator] Add `Week` constraint --- reference/constraints/Week.rst | 172 ++++++++++++++++++++++++++++++ reference/constraints/map.rst.inc | 1 + 2 files changed, 173 insertions(+) create mode 100644 reference/constraints/Week.rst diff --git a/reference/constraints/Week.rst b/reference/constraints/Week.rst new file mode 100644 index 00000000000..0aea71bee02 --- /dev/null +++ b/reference/constraints/Week.rst @@ -0,0 +1,172 @@ +Week +==== + +.. versionadded:: 7.2 + + The ``Week`` constraint was introduced in Symfony 7.2. + +Validates that a string (or an object implementing the ``Stringable`` PHP interface) +matches a given week number. The week number format is defined by `ISO-8601`_ +and should be composed of the year and the week number, separated by a hyphen +(e.g. ``2022-W01``). + +========== ======================================================================= +Applies to :ref:`property or method ` +Class :class:`Symfony\\Component\\Validator\\Constraints\\Week` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\WeekValidator` +========== ======================================================================= + +Basic Usage +----------- + +If you wanted to ensure that the ``startWeek`` property of an ``OnlineCourse`` +class is between the first and the twentieth week of the year 2022, you could do +the following: + +.. configuration-block:: + + .. code-block:: php-attributes + + // src/Entity/OnlineCourse.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class OnlineCourse + { + #[Assert\Week(min: '2022-W01', max: '2022-W20')] + protected string $startWeek; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Entity\OnlineCourse: + properties: + startWeek: + - Week: + min: '2022-W01' + max: '2022-W20' + + .. code-block:: xml + + + + + + + + + + + + + + + + .. code-block:: php + + // src/Entity/OnlineCourse.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class OnlineCourse + { + // ... + + public static function loadValidatorMetadata(ClassMetadata $metadata): void + { + $metadata->addPropertyConstraint('startWeek', new Assert\Week([ + 'min' => '2022-W01', + 'max' => '2022-W20', + ])); + } + } + +.. note:: + + The constraint also checks that the given week exists in the calendar. For example, + ``2022-W53`` is not a valid week number for 2022, because 2022 only had 52 weeks. + +Options +------- + +``min`` +~~~~~~~ + +**type**: ``string`` **default**: ``null`` + +The minimum week number that the value must match. + +``max`` +~~~~~~~ + +**type**: ``string`` **default**: ``null`` + +The maximum week number that the value must match. + +.. include:: /reference/constraints/_groups-option.rst.inc + +``invalidFormatMessage`` +~~~~~~~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This value does not represent a valid week in the ISO 8601 format.`` + +This is the message that will be shown if the value does not match the ISO 8601 +week format. + +``invalidWeekNumberMessage`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``The week "{{ value }}" is not a valid week.`` + +This is the message that will be shown if the value does not match a valid week +number. + +You can use the following parameters in this message: + +================ ================================================== +Parameter Description +================ ================================================== +``{{ value }}`` The value that was passed to the constraint +================ ================================================== + +``tooLowMessage`` +~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``The value should not be before week "{{ min }}".`` + +This is the message that will be shown if the value is lower than the minimum +week number. + +You can use the following parameters in this message: + +================ ================================================== +Parameter Description +================ ================================================== +``{{ min }}`` The minimum week number +================ ================================================== + +``tooHighMessage`` +~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``The value should not be after week "{{ max }}".`` + +This is the message that will be shown if the value is higher than the maximum +week number. + +You can use the following parameters in this message: + +================ ================================================== +Parameter Description +================ ================================================== +``{{ max }}`` The maximum week number +================ ================================================== + +.. include:: /reference/constraints/_payload-option.rst.inc + +.. _ISO-8601: https://en.wikipedia.org/wiki/ISO_8601 diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 978951c9de7..e0dbd6c4512 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -64,6 +64,7 @@ Date Constraints * :doc:`DateTime ` * :doc:`Time ` * :doc:`Timezone ` +* :doc:`Week ` Choice Constraints ~~~~~~~~~~~~~~~~~~ From 8d5b1d357df87484858a2902baf6cd0d2e8b59dd Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 19 Aug 2024 10:48:33 +0200 Subject: [PATCH 041/642] [Uid] Add support for binary, base-32 and base-58 representations in `Uuid::isValid()` --- components/uid.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/components/uid.rst b/components/uid.rst index 7195d393ed3..feb58968347 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -314,6 +314,30 @@ UUID objects created with the ``Uuid`` class can use the following methods // * int < 0 if $uuid1 is less than $uuid4 $uuid1->compare($uuid4); // e.g. int(4) +If you're working with different UUIDs format and want to validate them, +you can use the ``$format`` parameter of the :method:`Symfony\\Component\\Uid\\Uuid::isValid` +method to specify the UUID format you're expecting:: + + use Symfony\Component\Uid\Uuid; + + $isValid = Uuid::isValid('90067ce4-f083-47d2-a0f4-c47359de0f97', Uuid::FORMAT_RFC_4122); // accept only RFC 4122 UUIDs + $isValid = Uuid::isValid('3aJ7CNpDMfXPZrCsn4Cgey', Uuid::FORMAT_BASE_32 | Uuid::FORMAT_BASE_58); // accept multiple formats + +The following constants are available: + +* ``Uuid::FORMAT_BINARY`` +* ``Uuid::FORMAT_BASE_32`` +* ``Uuid::FORMAT_BASE_58`` +* ``Uuid::FORMAT_RFC_4122`` + +You can also use the ``Uuid::FORMAT_ALL`` constant to accept any UUID format. +By default, only the RFC 4122 format is accepted. + +.. versionadded:: 7.2 + + The ``$format`` parameter of the :method:`Symfony\\Component\\Uid\\Uuid::isValid` + method and the related constants were introduced in Symfony 7.2. + Storing UUIDs in Databases ~~~~~~~~~~~~~~~~~~~~~~~~~~ From 3567f5b79a01bd878143a3016bc224559f5c4ec4 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 19 Aug 2024 10:58:42 +0200 Subject: [PATCH 042/642] [Serializer] Deprecate passing a non-empty CSV escape char --- components/serializer.rst | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/components/serializer.rst b/components/serializer.rst index 09efbf44e6c..61124e3f030 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -1064,30 +1064,35 @@ which defines the configuration options for the CsvEncoder an associative array: These are the options available: -======================= ===================================================== ========================== -Option Description Default -======================= ===================================================== ========================== -``csv_delimiter`` Sets the field delimiter separating values (one ``,`` +======================= ============================================================= ========================== +Option Description Default +======================= ============================================================= ========================== +``csv_delimiter`` Sets the field delimiter separating values (one ``,`` character only) -``csv_enclosure`` Sets the field enclosure (one character only) ``"`` -``csv_end_of_line`` Sets the character(s) used to mark the end of each ``\n`` +``csv_enclosure`` Sets the field enclosure (one character only) ``"`` +``csv_end_of_line`` Sets the character(s) used to mark the end of each ``\n`` line in the CSV file -``csv_escape_char`` Sets the escape character (at most one character) empty string -``csv_key_separator`` Sets the separator for array's keys during its ``.`` +``csv_escape_char`` Deprecated. Sets the escape character (at most one character) empty string +``csv_key_separator`` Sets the separator for array's keys during its ``.`` flattening ``csv_headers`` Sets the order of the header and data columns E.g.: if ``$data = ['c' => 3, 'a' => 1, 'b' => 2]`` and ``$options = ['csv_headers' => ['a', 'b', 'c']]`` then ``serialize($data, 'csv', $options)`` returns - ``a,b,c\n1,2,3`` ``[]``, inferred from input data's keys -``csv_escape_formulas`` Escapes fields containing formulas by prepending them ``false`` + ``a,b,c\n1,2,3`` ``[]``, inferred from input data's keys +``csv_escape_formulas`` Escapes fields containing formulas by prepending them ``false`` with a ``\t`` character -``as_collection`` Always returns results as a collection, even if only ``true`` +``as_collection`` Always returns results as a collection, even if only ``true`` one line is decoded. -``no_headers`` Setting to ``false`` will use first row as headers. ``false`` +``no_headers`` Setting to ``false`` will use first row as headers. ``false`` ``true`` generate numeric headers. -``output_utf8_bom`` Outputs special `UTF-8 BOM`_ along with encoded data ``false`` -======================= ===================================================== ========================== +``output_utf8_bom`` Outputs special `UTF-8 BOM`_ along with encoded data ``false`` +======================= ============================================================= ========================== + +.. deprecated:: 7.2 + + The ``csv_escape_char`` option and the ``CsvEncoder::ESCAPE_CHAR_KEY`` + constant were deprecated in Symfony 7.2. The ``XmlEncoder`` ~~~~~~~~~~~~~~~~~~ @@ -1304,6 +1309,11 @@ you can use "context builders" to define the context using a fluent interface:: You can also :doc:`create custom context builders ` to deal with your context values. +.. deprecated:: 7.2 + + The ``CsvEncoderContextBuilder::withEscapeChar()`` method was deprecated + in Symfony 7.2. + Skipping ``null`` Values ------------------------ From f39be7640c72c575da3320aa01ff3e7cca466660 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 19 Aug 2024 11:06:42 +0200 Subject: [PATCH 043/642] [FrameworkBundle][HttpFoundation] Deprecate `session.sid_length` and `session.sid_bits_per_character` config options --- reference/configuration/framework.rst | 8 ++++++++ session.rst | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 0921fe9ac2e..72c4b1b94f7 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1874,6 +1874,10 @@ session IDs are harder to guess. If not set, ``php.ini``'s `session.sid_length`_ directive will be relied on. +.. deprecated:: 7.2 + + The ``sid_length`` option was deprecated in Symfony 7.2. + sid_bits_per_character ...................... @@ -1886,6 +1890,10 @@ most environments. If not set, ``php.ini``'s `session.sid_bits_per_character`_ directive will be relied on. +.. deprecated:: 7.2 + + The ``sid_bits_per_character`` option was deprecated in Symfony 7.2. + save_path ......... diff --git a/session.rst b/session.rst index 29d1ba364d1..77aea3bba00 100644 --- a/session.rst +++ b/session.rst @@ -400,6 +400,11 @@ Check out the Symfony config reference to learn more about the other available ``session.auto_start = 1`` This directive should be turned off in ``php.ini``, in the web server directives or in ``.htaccess``. +.. deprecated:: 7.2 + + The ``sid_length`` and ``sid_bits_per_character`` options were deprecated + in Symfony 7.2 and will be ignored in Symfony 8.0. + The session cookie is also available in :ref:`the Response object `. This is useful to get that cookie in the CLI context or when using PHP runners like Roadrunner or Swoole. From 51fdaa869c908061f2fc355442a219c57bfc2f0a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 20 Aug 2024 09:19:34 +0200 Subject: [PATCH 044/642] document the requests constructor argument of the RequestStack class --- components/form.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/form.rst b/components/form.rst index 7584d223032..f463ef5911b 100644 --- a/components/form.rst +++ b/components/form.rst @@ -123,8 +123,7 @@ The following snippet adds CSRF protection to the form factory:: use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage; // creates a RequestStack object using the current request - $requestStack = new RequestStack(); - $requestStack->push($request); + $requestStack = new RequestStack([$request]); $csrfGenerator = new UriSafeTokenGenerator(); $csrfStorage = new SessionTokenStorage($requestStack); @@ -135,6 +134,11 @@ The following snippet adds CSRF protection to the form factory:: ->addExtension(new CsrfExtension($csrfManager)) ->getFormFactory(); +.. versionadded:: 7.2 + + Support for passing requests to the constructor of the ``RequestStack`` + class was introduced in Symfony 7.2. + Internally, this extension will automatically add a hidden field to every form (called ``_token`` by default) whose value is automatically generated by the CSRF generator and validated when binding the form. From bad2a212bf68bdfc6479765d2ecb4123677aee4e Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Tue, 20 Aug 2024 12:01:07 +0200 Subject: [PATCH 045/642] feat(mailer): Add Sweego Mailer doc --- mailer.rst | 8 +++++++- webhook.rst | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mailer.rst b/mailer.rst index 11acd065e52..091e6d6e5b9 100644 --- a/mailer.rst +++ b/mailer.rst @@ -115,6 +115,7 @@ Service Install with Webhook su `Resend`_ ``composer require symfony/resend-mailer`` yes `Scaleway`_ ``composer require symfony/scaleway-mailer`` `SendGrid`_ ``composer require symfony/sendgrid-mailer`` yes +`Sweego`_ ``composer require symfony/sweego-mailer`` yes ===================== =============================================== =============== .. versionadded:: 7.1 @@ -123,7 +124,7 @@ Service Install with Webhook su .. versionadded:: 7.2 - The Mailomat and Postal integrations were introduced in Symfony 7.2. + The Mailomat, Postal and Sweego integrations were introduced in Symfony 7.2. .. note:: @@ -235,6 +236,10 @@ party provider: | | - HTTP n/a | | | - API ``sendgrid+api://KEY@default`` | +------------------------+---------------------------------------------------------+ +| `Sweego`_ | - SMTP ``sweego+smtp://LOGIN:PASSWORD@HOST:PORT`` | +| | - HTTP n/a | +| | - API ``sweego+api://API_KEY@default`` | ++------------------------+---------------------------------------------------------+ .. caution:: @@ -2004,3 +2009,4 @@ the :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\MailerAssertionsTrait`:: .. _`S/MIME`: https://en.wikipedia.org/wiki/S/MIME .. _`Scaleway`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Scaleway/README.md .. _`SendGrid`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Sendgrid/README.md +.. _`Sweego`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Sweego/README.md diff --git a/webhook.rst b/webhook.rst index 52633ae83e5..176abc49cd2 100644 --- a/webhook.rst +++ b/webhook.rst @@ -31,6 +31,7 @@ Mailomat ``mailer.webhook.request_parser.mailomat`` Postmark ``mailer.webhook.request_parser.postmark`` Resend ``mailer.webhook.request_parser.resend`` Sendgrid ``mailer.webhook.request_parser.sendgrid`` +Sweego ``mailer.webhook.request_parser.sweego`` ============== ============================================ .. versionadded:: 7.1 @@ -39,7 +40,7 @@ Sendgrid ``mailer.webhook.request_parser.sendgrid`` .. versionadded:: 7.2 - The ``Mailomat`` integration was introduced in Symfony 7.2. + The ``Mailomat`` and ``Sweego`` integrations were introduced in Symfony 7.2. .. note:: From 84da11cd0affbf26ba61eaaa02015f1b0c85854e Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 20 Aug 2024 14:46:44 +0200 Subject: [PATCH 046/642] [HttpFoundation] Add new parameters to `IpUtils::anonymize()` --- components/http_foundation.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/components/http_foundation.rst b/components/http_foundation.rst index 6c9210b894f..2cdd5a02702 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -362,6 +362,23 @@ analysis purposes. Use the ``anonymize()`` method from the $anonymousIpv6 = IpUtils::anonymize($ipv6); // $anonymousIpv6 = '2a01:198:603:10::' +If you need even more anonymization, you can use the second and third parameters +of the ``anonymize()`` method to specify the number of bytes that should be +anonymized depending on the IP address format:: + + $ipv4 = '123.234.235.236'; + $anonymousIpv4 = IpUtils::anonymize($ipv4, v4Bytes: 3); + // $anonymousIpv4 = '123.0.0.0' + + $ipv6 = '2a01:198:603:10:396e:4789:8e99:890f'; + $anonymousIpv6 = IpUtils::anonymize($ipv6, v6Bytes: 10); + // $anonymousIpv6 = '2a01:198:603::' + +.. versionadded:: 7.2 + + The ``v4Bytes`` and ``v6Bytes`` parameters of the ``anonymize()`` method + were introduced in Symfony 7.2. + Check If an IP Belongs to a CIDR Subnet ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From d00645a6062158478477d9df5324d90bae3b6502 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 22 Aug 2024 09:45:19 +0200 Subject: [PATCH 047/642] [Form] Add support for the `calendar` option in `DateType` --- reference/forms/types/date.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst index 515c12099a1..f9c6d67a70d 100644 --- a/reference/forms/types/date.rst +++ b/reference/forms/types/date.rst @@ -155,6 +155,19 @@ values for the year, month and day fields:: .. include:: /reference/forms/types/options/view_timezone.rst.inc +``calendar`` +~~~~~~~~~~~~ + +**type**: ``\IntlCalendar`` **default**: ``null`` + +The calendar to use for formatting and parsing the date. The value should be +an instance of the :phpclass:`IntlCalendar` to use. By default, the Gregorian +calendar with the application default locale is used. + +.. versionadded:: 7.2 + + The ``calendar`` option was introduced in Symfony 7.2. + .. include:: /reference/forms/types/options/date_widget.rst.inc .. include:: /reference/forms/types/options/years.rst.inc From 429e193a40e93a3e521479aa1cc404c0b84c9df1 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 23 Aug 2024 09:30:27 +0200 Subject: [PATCH 048/642] [ExpressionLanguage] Add support for `<<`, `>>`, and `~` bitwise operators --- reference/formats/expression_language.rst | 72 +++++++++++++---------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/reference/formats/expression_language.rst b/reference/formats/expression_language.rst index e88ac7f6c24..ec592461a4f 100644 --- a/reference/formats/expression_language.rst +++ b/reference/formats/expression_language.rst @@ -284,6 +284,14 @@ Bitwise Operators * ``&`` (and) * ``|`` (or) * ``^`` (xor) +* ``~`` (not) +* ``<<`` (left shift) +* ``>>`` (right shift) + +.. versionadded:: 7.2 + + Support for the ``~``, ``<<`` and ``>>`` bitwise operators was introduced + in Symfony 7.2. Comparison Operators ~~~~~~~~~~~~~~~~~~~~ @@ -449,38 +457,38 @@ parentheses in your expressions (e.g. ``(1 + 2) * 4`` or ``1 + (2 * 4)``. The following table summarizes the operators and their associativity from the **highest to the lowest precedence**: -+----------------------------------------------------------+---------------+ -| Operators | Associativity | -+==========================================================+===============+ -| ``-`` , ``+`` (unary operators that add the number sign) | none | -+----------------------------------------------------------+---------------+ -| ``**`` | right | -+----------------------------------------------------------+---------------+ -| ``*``, ``/``, ``%`` | left | -+----------------------------------------------------------+---------------+ -| ``not``, ``!`` | none | -+----------------------------------------------------------+---------------+ -| ``~`` | left | -+----------------------------------------------------------+---------------+ -| ``+``, ``-`` | left | -+----------------------------------------------------------+---------------+ -| ``..`` | left | -+----------------------------------------------------------+---------------+ -| ``==``, ``===``, ``!=``, ``!==``, | left | -| ``<``, ``>``, ``>=``, ``<=``, | | -| ``not in``, ``in``, ``contains``, | | -| ``starts with``, ``ends with``, ``matches`` | | -+----------------------------------------------------------+---------------+ -| ``&`` | left | -+----------------------------------------------------------+---------------+ -| ``^`` | left | -+----------------------------------------------------------+---------------+ -| ``|`` | left | -+----------------------------------------------------------+---------------+ -| ``and``, ``&&`` | left | -+----------------------------------------------------------+---------------+ -| ``or``, ``||`` | left | -+----------------------------------------------------------+---------------+ ++-----------------------------------------------------------------+---------------+ +| Operators | Associativity | ++=================================================================+===============+ +| ``-`` , ``+``, ``~`` (unary operators that add the number sign) | none | ++-----------------------------------------------------------------+---------------+ +| ``**`` | right | ++-----------------------------------------------------------------+---------------+ +| ``*``, ``/``, ``%`` | left | ++-----------------------------------------------------------------+---------------+ +| ``not``, ``!`` | none | ++-----------------------------------------------------------------+---------------+ +| ``~`` | left | ++-----------------------------------------------------------------+---------------+ +| ``+``, ``-`` | left | ++-----------------------------------------------------------------+---------------+ +| ``..``, ``<<``, ``>>`` | left | ++-----------------------------------------------------------------+---------------+ +| ``==``, ``===``, ``!=``, ``!==``, | left | +| ``<``, ``>``, ``>=``, ``<=``, | | +| ``not in``, ``in``, ``contains``, | | +| ``starts with``, ``ends with``, ``matches`` | | ++-----------------------------------------------------------------+---------------+ +| ``&`` | left | ++-----------------------------------------------------------------+---------------+ +| ``^`` | left | ++-----------------------------------------------------------------+---------------+ +| ``|`` | left | ++-----------------------------------------------------------------+---------------+ +| ``and``, ``&&`` | left | ++-----------------------------------------------------------------+---------------+ +| ``or``, ``||`` | left | ++-----------------------------------------------------------------+---------------+ Built-in Objects and Variables ------------------------------ From d883d5659dab11da2ad39be9a380551f74682e38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Fri, 23 Aug 2024 00:39:01 +0200 Subject: [PATCH 049/642] [Templating] [Template] Render a block with the `#[Template]` attribute --- templates.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/templates.rst b/templates.rst index 795ac3a7ac3..1af3de56c5b 100644 --- a/templates.rst +++ b/templates.rst @@ -632,6 +632,31 @@ This might come handy when dealing with blocks in :ref:`templates inheritance ` or when using `Turbo Streams`_. +Similarly, you can use the ``#[Template]`` attribute on the controller to specify a block +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; + + class ProductController extends AbstractController + { + #[Template('product.html.twig', block: 'price_block')] + public function price(): array + { + return [ + // ... + ]; + } + } + +.. versionadded:: 7.2 + + The ``#[Template]`` attribute's ``block`` argument was introduced in Symfony 7.2. + Rendering a Template in Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From d158b5544cb173ea520724e56df2a1ba1c8c84c0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 26 Aug 2024 09:19:25 +0200 Subject: [PATCH 050/642] Minor tweak --- templates.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates.rst b/templates.rst index 1af3de56c5b..2379a3568dc 100644 --- a/templates.rst +++ b/templates.rst @@ -632,8 +632,8 @@ This might come handy when dealing with blocks in :ref:`templates inheritance ` or when using `Turbo Streams`_. -Similarly, you can use the ``#[Template]`` attribute on the controller to specify a block -to render:: +Similarly, you can use the ``#[Template]`` attribute on the controller to specify +a block to render:: // src/Controller/ProductController.php namespace App\Controller; From bec9e5faa881623aa75a03a78212315f44799a43 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 22 Aug 2024 14:16:06 +0200 Subject: [PATCH 051/642] [Validator] Add `CompoundConstraintTestCase` to ease testing Compound Constraints --- validation/custom_constraint.rst | 74 ++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/validation/custom_constraint.rst b/validation/custom_constraint.rst index 9f0ca4ca07b..4504ceb4012 100644 --- a/validation/custom_constraint.rst +++ b/validation/custom_constraint.rst @@ -502,6 +502,9 @@ A class constraint validator must be applied to the class itself: Testing Custom Constraints -------------------------- +Atomic Constraints +~~~~~~~~~~~~~~~~~~ + Use the :class:`Symfony\\Component\\Validator\\Test\\ConstraintValidatorTestCase` class to simplify writing unit tests for your custom constraints:: @@ -545,3 +548,74 @@ class to simplify writing unit tests for your custom constraints:: // ... } } + +Compound Constraints +~~~~~~~~~~~~~~~~~~~~ + +Let's say you create a compound constraint that checks if a string meets +your minimum requirements for your password policy:: + + // src/Validator/PasswordRequirements.php + namespace App\Validator; + + use Symfony\Component\Validator\Constraints as Assert; + + #[\Attribute] + class PasswordRequirements extends Assert\Compound + { + protected function getConstraints(array $options): array + { + return [ + new Assert\NotBlank(allowNull: false), + new Assert\Length(min: 8, max: 255), + new Assert\NotCompromisedPassword(), + new Assert\Type('string'), + new Assert\Regex('/[A-Z]+/'), + ]; + } + } + +You can use the :class:`Symfony\\Component\\Validator\\Test\\CompoundConstraintTestCase` +class to check precisely which of the constraints failed to pass:: + + // tests/Validator/PasswordRequirementsTest.php + namespace App\Tests\Validator; + + use App\Validator\PasswordRequirements; + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Test\CompoundConstraintTestCase; + + /** + * @extends CompoundConstraintTestCase + */ + class PasswordRequirementsTest extends CompoundConstraintTestCase + { + public function createCompound(): Assert\Compound + { + return new PasswordRequirements(); + } + + public function testInvalidPassword(): void + { + $this->validateValue('azerty123'); + + // check all constraints pass except for the + // password leak and the uppercase letter checks + $this->assertViolationsRaisedByCompound([ + new Assert\NotCompromisedPassword(), + new Assert\Regex('/[A-Z]+/'), + ]); + } + + public function testValid(): void + { + $this->validateValue('VERYSTR0NGP4$$WORD#%!'); + + $this->assertNoViolation(); + } + } + +.. versionadded:: 7.2 + + The :class:`Symfony\\Component\\Validator\\Test\\CompoundConstraintTestCase` + class was introduced in Symfony 7.2. From 3f0fdbad1f95f056c576e2d34f1576c4fe27c720 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 26 Aug 2024 10:21:56 +0200 Subject: [PATCH 052/642] Tweaks --- reference/constraints/Week.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/reference/constraints/Week.rst b/reference/constraints/Week.rst index 0aea71bee02..26d82777636 100644 --- a/reference/constraints/Week.rst +++ b/reference/constraints/Week.rst @@ -5,10 +5,9 @@ Week The ``Week`` constraint was introduced in Symfony 7.2. -Validates that a string (or an object implementing the ``Stringable`` PHP interface) -matches a given week number. The week number format is defined by `ISO-8601`_ -and should be composed of the year and the week number, separated by a hyphen -(e.g. ``2022-W01``). +Validates that a given string (or an object implementing the ``Stringable`` PHP +interface) represents a valid week number according to the `ISO-8601`_ standard +(e.g. ``2025-W01``). ========== ======================================================================= Applies to :ref:`property or method ` @@ -87,10 +86,11 @@ the following: } } -.. note:: - - The constraint also checks that the given week exists in the calendar. For example, - ``2022-W53`` is not a valid week number for 2022, because 2022 only had 52 weeks. +This constraint not only checks that the value matches the week number pattern, +but it also verifies that the specified week actually exists in the calendar. +According to the ISO-8601 standard, years can have either 52 or 53 weeks. For example, +``2022-W53`` is not a valid because 2022 only had 52 weeks; but ``2020-W53`` is +valid because 2020 had 53 weeks. Options ------- @@ -169,4 +169,4 @@ Parameter Description .. include:: /reference/constraints/_payload-option.rst.inc -.. _ISO-8601: https://en.wikipedia.org/wiki/ISO_8601 +.. _`ISO-8601`: https://en.wikipedia.org/wiki/ISO_8601 From 34d159469bd81267e3ae9cfe8d2b65993fd148f1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 26 Aug 2024 10:57:07 +0200 Subject: [PATCH 053/642] Minor tweak --- validation/custom_constraint.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation/custom_constraint.rst b/validation/custom_constraint.rst index 4504ceb4012..435b976d1d3 100644 --- a/validation/custom_constraint.rst +++ b/validation/custom_constraint.rst @@ -552,8 +552,8 @@ class to simplify writing unit tests for your custom constraints:: Compound Constraints ~~~~~~~~~~~~~~~~~~~~ -Let's say you create a compound constraint that checks if a string meets -your minimum requirements for your password policy:: +Consider the following compound constraint that checks if a string meets +the minimum requirements for your password policy:: // src/Validator/PasswordRequirements.php namespace App\Validator; From 41f17860c70df6743740b8c5a0aff21606cee7ac Mon Sep 17 00:00:00 2001 From: eltharin Date: Thu, 15 Aug 2024 15:21:16 +0200 Subject: [PATCH 054/642] [Scheduler] Add capability to skip missed periodic tasks, only the last schedule will be called --- scheduler.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/scheduler.rst b/scheduler.rst index c890b1a1aec..be4f2ca138d 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -889,6 +889,27 @@ This allows the system to retain the state of the schedule, ensuring that when a } } +With ``stateful`` option, All missed messages will be handled, If you nedd handle your message only once you can use the ``processOnlyLastMissedRun`` option.:: + + // src/Scheduler/SaleTaskProvider.php + namespace App\Scheduler; + + #[AsSchedule('uptoyou')] + class SaleTaskProvider implements ScheduleProviderInterface + { + public function getSchedule(): Schedule + { + $this->removeOldReports = RecurringMessage::cron('3 8 * * 1', new CleanUpOldSalesReport()); + + return $this->schedule ??= (new Schedule()) + ->with( + // ... + ) + ->stateful($this->cache) + ->processOnlyLastMissedRun(true) + } + } + To scale your schedules more effectively, you can use multiple workers. In such cases, a good practice is to add a :doc:`lock ` to prevent the same task more than once:: From 0f91311c8572b58c69097b901c7d76cf03ac993a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 26 Aug 2024 12:50:13 +0200 Subject: [PATCH 055/642] Minor tweaks --- scheduler.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scheduler.rst b/scheduler.rst index be4f2ca138d..081f4efd498 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -869,7 +869,8 @@ While this behavior may not necessarily pose a problem, there is a possibility t That's why the scheduler allows to remember the last execution date of a message via the ``stateful`` option (and the :doc:`Cache component `). -This allows the system to retain the state of the schedule, ensuring that when a worker is restarted, it resumes from the point it left off.:: +This allows the system to retain the state of the schedule, ensuring that when a +worker is restarted, it resumes from the point it left off:: // src/Scheduler/SaleTaskProvider.php namespace App\Scheduler; @@ -889,7 +890,8 @@ This allows the system to retain the state of the schedule, ensuring that when a } } -With ``stateful`` option, All missed messages will be handled, If you nedd handle your message only once you can use the ``processOnlyLastMissedRun`` option.:: +With the ``stateful`` option, all missed messages will be handled. If you need to +handle a message only once, you can use the ``processOnlyLastMissedRun`` option:: // src/Scheduler/SaleTaskProvider.php namespace App\Scheduler; @@ -910,6 +912,10 @@ With ``stateful`` option, All missed messages will be handled, If you nedd handl } } +.. versionadded:: 7.2 + + The ``processOnlyLastMissedRun`` option was introduced in Symfony 7.2. + To scale your schedules more effectively, you can use multiple workers. In such cases, a good practice is to add a :doc:`lock ` to prevent the same task more than once:: From 23838fd198c9a98731567284306d56b6c78f7e32 Mon Sep 17 00:00:00 2001 From: Thibaut THOUEMENT Date: Fri, 23 Aug 2024 22:10:10 +0200 Subject: [PATCH 056/642] [Validator] Add errorPath to Unique constraint --- reference/constraints/Unique.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/reference/constraints/Unique.rst b/reference/constraints/Unique.rst index 8954f455086..7c4f78cfcc3 100644 --- a/reference/constraints/Unique.rst +++ b/reference/constraints/Unique.rst @@ -170,6 +170,15 @@ collection:: .. include:: /reference/constraints/_groups-option.rst.inc +``errorPath`` +~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``null`` + +This option allows you to define a custom path for your message. +``#[Assert\Unique(fields: ['latitude', 'longitude'], errorPath: 'point_of_interest')]`` +Instead of ``0: "Error message"``, it will be : ``0.point_of_interest: "Error message"`` + ``message`` ~~~~~~~~~~~ From 03713d55356555cf5933daa8bc1cbe821ccf1591 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 26 Aug 2024 16:58:22 +0200 Subject: [PATCH 057/642] Reword --- reference/constraints/Unique.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/reference/constraints/Unique.rst b/reference/constraints/Unique.rst index 7c4f78cfcc3..68754738271 100644 --- a/reference/constraints/Unique.rst +++ b/reference/constraints/Unique.rst @@ -175,9 +175,16 @@ collection:: **type**: ``string`` **default**: ``null`` -This option allows you to define a custom path for your message. -``#[Assert\Unique(fields: ['latitude', 'longitude'], errorPath: 'point_of_interest')]`` -Instead of ``0: "Error message"``, it will be : ``0.point_of_interest: "Error message"`` +.. versionadded:: 7.2 + + The ``errorPath`` option was introduced in Symfony 7.2. + +If a validation error occurs, the error message is, by default, bound to the +first element in the collection. Use this option to bind the error message to a +specific field within the first item of the collection. + +The value of this option must use any :doc:`valid PropertyAccess syntax ` +(e.g. ``'point_of_interest'``, ``'user.email'``). ``message`` ~~~~~~~~~~~ From 45ba59fd12b5db3fa49a6cb6ac38683d9119442b Mon Sep 17 00:00:00 2001 From: Ahmed Ghanem <124502255+ahmedghanem00@users.noreply.github.com> Date: Mon, 29 Jul 2024 02:56:36 +0300 Subject: [PATCH 058/642] [Notifier] Create `DesktopChannel` and `JoliNotif` bridge --- notifier.rst | 84 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/notifier.rst b/notifier.rst index 6d506910ead..4df4590235e 100644 --- a/notifier.rst +++ b/notifier.rst @@ -16,8 +16,8 @@ Get the Notifier installed using: .. _channels-chatters-texters-email-and-browser: -Channels: Chatters, Texters, Email, Browser and Push ----------------------------------------------------- +Channels: Chatters, Texters, Email, Browser, Push and Desktop +------------------------------------------------------------- The notifier component can send notifications to different channels. Each channel can integrate with different providers (e.g. Slack or Twilio SMS) @@ -32,6 +32,7 @@ The notifier component supports the following channels: * :ref:`Email channel ` integrates the :doc:`Symfony Mailer `; * Browser channel uses :ref:`flash messages `. * :ref:`Push channel ` sends notifications to phones and browsers via push notifications. +* :ref:`Desktop channel ` displays desktop notifications on the same host machine. .. tip:: @@ -623,6 +624,79 @@ configure the ``texter_transports``: ; }; +.. _notifier-desktop-channel: + +Desktop Channel +~~~~~~~~~~~~~~~ + +The desktop channel is used to display desktop notifications on the same host machine using +:class:`Symfony\\Component\\Notifier\\Texter` classes. Currently, Symfony +is integrated with the following providers: + +=============== ==================================== ============================================================================== +Provider Package DSN +=============== ==================================== ============================================================================== +`JoliNotif`_ ``symfony/joli-notif-notifier`` ``jolinotif://default`` +=============== ==================================== ============================================================================== + +.. versionadded: 7.2 + + The JoliNotif bridge was introduced in Symfony 7.2. + +To enable a texter, add the correct DSN in your ``.env`` file and +configure the ``texter_transports``: + +.. code-block:: bash + + # .env + JOLINOTIF=jolinotif://default + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/notifier.yaml + framework: + notifier: + texter_transports: + jolinotif: '%env(JOLINOTIF)%' + + .. code-block:: xml + + + + + + + + + %env(JOLINOTIF)% + + + + + + .. code-block:: php + + // config/packages/notifier.php + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework): void { + $framework->notifier() + ->texterTransport('jolinotif', env('JOLINOTIF')) + ; + }; + +.. versionadded:: 7.2 + + The ``Desktop`` channel was introduced in Symfony 7.2. + Configure to use Failover or Round-Robin Transports ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -926,9 +1000,10 @@ and its ``asChatMessage()`` method:: The :class:`Symfony\\Component\\Notifier\\Notification\\SmsNotificationInterface`, -:class:`Symfony\\Component\\Notifier\\Notification\\EmailNotificationInterface` -and +:class:`Symfony\\Component\\Notifier\\Notification\\EmailNotificationInterface`, :class:`Symfony\\Component\\Notifier\\Notification\\PushNotificationInterface` +and +:class:`Symfony\\Component\\Notifier\\Notification\\DesktopNotificationInterface` also exists to modify messages sent to those channels. Customize Browser Notifications (Flash Messages) @@ -1114,6 +1189,7 @@ is dispatched. Listeners receive a .. _`Infobip`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Infobip/README.md .. _`Iqsms`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Iqsms/README.md .. _`iSendPro`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Isendpro/README.md +.. _`JoliNotif`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/JoliNotif/README.md .. _`KazInfoTeh`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/README.md .. _`LINE Notify`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LineNotify/README.md .. _`LightSms`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LightSms/README.md From cc4de4e630189e65ca2e74f98b469d18d20aca28 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 28 Aug 2024 13:13:14 +0200 Subject: [PATCH 059/642] Keep a reference to not break existing links --- notifier.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/notifier.rst b/notifier.rst index 4df4590235e..5f38a078f4c 100644 --- a/notifier.rst +++ b/notifier.rst @@ -15,6 +15,7 @@ Get the Notifier installed using: $ composer require symfony/notifier .. _channels-chatters-texters-email-and-browser: +.. _channels-chatters-texters-email-browser-and-push: Channels: Chatters, Texters, Email, Browser, Push and Desktop ------------------------------------------------------------- From 0c1a63900ab6ac2aa0f3848541f11ef47b932232 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 2 Sep 2024 13:55:32 +0200 Subject: [PATCH 060/642] fix typo --- reference/constraints/Week.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/constraints/Week.rst b/reference/constraints/Week.rst index 26d82777636..f107d8b4192 100644 --- a/reference/constraints/Week.rst +++ b/reference/constraints/Week.rst @@ -89,7 +89,7 @@ the following: This constraint not only checks that the value matches the week number pattern, but it also verifies that the specified week actually exists in the calendar. According to the ISO-8601 standard, years can have either 52 or 53 weeks. For example, -``2022-W53`` is not a valid because 2022 only had 52 weeks; but ``2020-W53`` is +``2022-W53`` is not valid because 2022 only had 52 weeks; but ``2020-W53`` is valid because 2020 had 53 weeks. Options From 31a33bf2d9e5929f834d25cfad93ac21852bf9ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Geffroy?= <81738559+raphael-geffroy@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:53:21 +0200 Subject: [PATCH 061/642] [FrameworkBundle] Remove default value for `gc_probability` config option --- reference/configuration/framework.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 1e9e43faa0f..c3f341f578b 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1845,13 +1845,19 @@ If not set, ``php.ini``'s `session.gc_divisor`_ directive will be relied on. gc_probability .............. -**type**: ``integer`` **default**: ``1`` +**type**: ``integer`` This defines the probability that the garbage collector (GC) process is started on every session initialization. The probability is calculated by using ``gc_probability`` / ``gc_divisor``, e.g. 1/100 means there is a 1% chance that the GC process will start on each request. +If not set, ``php.ini``'s `session.gc_probability`_ directive will be relied on. + +.. versionadded:: 7.2 + + Relying on ``php.ini``'s directive as default for ``gc_probability`` was introduced in symfony 7.2. + gc_maxlifetime .............. @@ -3893,6 +3899,7 @@ The attributes can also be added to interfaces directly:: .. _`session.cookie_samesite`: https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-samesite .. _`session.cookie_secure`: https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-secure .. _`session.gc_divisor`: https://www.php.net/manual/en/session.configuration.php#ini.session.gc-divisor +.. _`session.gc_probability`: https://www.php.net/manual/en/session.configuration.php#ini.session.gc-probability .. _`session.gc_maxlifetime`: https://www.php.net/manual/en/session.configuration.php#ini.session.gc-maxlifetime .. _`session.sid_length`: https://www.php.net/manual/en/session.configuration.php#ini.session.sid-length .. _`session.sid_bits_per_character`: https://www.php.net/manual/en/session.configuration.php#ini.session.sid-bits-per-character From 5c72f37bca564690a8af6f9797f3eed7e91d0cd8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 9 Sep 2024 08:42:30 +0200 Subject: [PATCH 062/642] Minor tweak --- reference/configuration/framework.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index c3f341f578b..bdcbc4a55af 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1852,11 +1852,13 @@ started on every session initialization. The probability is calculated by using ``gc_probability`` / ``gc_divisor``, e.g. 1/100 means there is a 1% chance that the GC process will start on each request. -If not set, ``php.ini``'s `session.gc_probability`_ directive will be relied on. +If not set, Symfony will use the value of the `session.gc_probability`_ directive +in the ``php.ini`` configuration file. .. versionadded:: 7.2 - Relying on ``php.ini``'s directive as default for ``gc_probability`` was introduced in symfony 7.2. + Relying on ``php.ini``'s directive as default for ``gc_probability`` was + introduced in Symfony 7.2. gc_maxlifetime .............. From e425caf94ceb2d7336a5aff5e7832529d6ce8ebd Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 12 Sep 2024 22:39:59 -0400 Subject: [PATCH 063/642] [Mailer] add Mailtrap docs --- mailer.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mailer.rst b/mailer.rst index 21032732c4b..a8d0eda3072 100644 --- a/mailer.rst +++ b/mailer.rst @@ -109,6 +109,7 @@ Service Install with Webhook su `Mailomat`_ ``composer require symfony/mailomat-mailer`` yes `MailPace`_ ``composer require symfony/mail-pace-mailer`` `MailerSend`_ ``composer require symfony/mailer-send-mailer`` +`Mailtrap`_ ``composer require symfony/mailtrap-mailer`` `Mandrill`_ ``composer require symfony/mailchimp-mailer`` `Postal`_ ``composer require symfony/postal-mailer`` `Postmark`_ ``composer require symfony/postmark-mailer`` yes @@ -124,7 +125,7 @@ Service Install with Webhook su .. versionadded:: 7.2 - The Mailomat, Postal and Sweego integrations were introduced in Symfony 7.2. + The Mailomat, Mailtrap, Postal and Sweego integrations were introduced in Symfony 7.2. .. note:: @@ -216,6 +217,10 @@ party provider: | | - HTTP n/a | | | - API ``mailpace+api://API_TOKEN@default`` | +------------------------+---------------------------------------------------------+ +| `Mailtrap`_ | - SMTP ``mailtrap+smtp://PASSWORD@default`` | +| | - HTTP n/a | +| | - API ``mailtrap+api://API_TOKEN@default`` | ++------------------------+---------------------------------------------------------+ | `Postal`_ | - SMTP n/a | | | - HTTP n/a | | | - API ``postal+api://API_KEY@BASE_URL`` | @@ -1581,6 +1586,7 @@ The following transports currently support tags and metadata: * Brevo * Mailgun +* Mailtrap * Mandrill * Postmark * Sendgrid @@ -2000,6 +2006,7 @@ the :class:`Symfony\\Bundle\\FrameworkBundle\\Test\\MailerAssertionsTrait`:: .. _`PEM encoded`: https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail .. _`Postal`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Postal/README.md .. _`Postmark`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Postmark/README.md +.. _`Mailtrap`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Mailtrap/README.md .. _`Resend`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Mailer/Bridge/Resend/README.md .. _`RFC 3986`: https://www.ietf.org/rfc/rfc3986.txt .. _`S/MIME`: https://en.wikipedia.org/wiki/S/MIME From 4dc4e9d75724eb3874149e7a5455f81dce17cdef Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 17 Sep 2024 10:53:05 +0200 Subject: [PATCH 064/642] Minor tweak --- reference/configuration/framework.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 80c1944eea1..19f72bb6cb3 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1884,7 +1884,8 @@ If not set, ``php.ini``'s `session.sid_length`_ directive will be relied on. .. deprecated:: 7.2 - The ``sid_length`` option was deprecated in Symfony 7.2. + The ``sid_length`` option was deprecated in Symfony 7.2. No alternative is + provided as PHP 8.4 has deprecated the related option. sid_bits_per_character ...................... @@ -1900,7 +1901,8 @@ If not set, ``php.ini``'s `session.sid_bits_per_character`_ directive will be re .. deprecated:: 7.2 - The ``sid_bits_per_character`` option was deprecated in Symfony 7.2. + The ``sid_bits_per_character`` option was deprecated in Symfony 7.2. No alternative + is provided as PHP 8.4 has deprecated the related option. save_path ......... From 36a7c6dd137a8e819360be1b7e107732b0cdd04b Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 17 Sep 2024 14:31:19 +0200 Subject: [PATCH 065/642] [Uid] Add the `Uuid::FORMAT_RFC_9562` constant --- components/uid.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/components/uid.rst b/components/uid.rst index feb58968347..73974ef8732 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -329,6 +329,7 @@ The following constants are available: * ``Uuid::FORMAT_BASE_32`` * ``Uuid::FORMAT_BASE_58`` * ``Uuid::FORMAT_RFC_4122`` +* ``Uuid::FORMAT_RFC_9562`` (equivalent to ``Uuid::FORMAT_RFC_4122``) You can also use the ``Uuid::FORMAT_ALL`` constant to accept any UUID format. By default, only the RFC 4122 format is accepted. From 7a8d0c44f97540e3ef80df087828174f5c8ebec9 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 19 Sep 2024 09:12:56 +0200 Subject: [PATCH 066/642] [Translation] Mention the support of segment attributes in XLIFF --- reference/formats/xliff.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/reference/formats/xliff.rst b/reference/formats/xliff.rst index acb9af36014..b5dc99b4186 100644 --- a/reference/formats/xliff.rst +++ b/reference/formats/xliff.rst @@ -29,7 +29,7 @@ loaded/dumped inside a Symfony application: true user login - + original-content translated-content @@ -37,4 +37,8 @@ loaded/dumped inside a Symfony application: +.. versionadded:: 7.2 + + The support of attributes in the ```` element was introduced in Symfony 7.2. + .. _XLIFF: https://docs.oasis-open.org/xliff/xliff-core/v2.1/xliff-core-v2.1.html From d971cea1c706ff93df2c16bedfd7c0223a5514a8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 19 Sep 2024 13:30:17 +0200 Subject: [PATCH 067/642] =?UTF-8?q?[HttpFoundation]=C2=A0Document=20the=20?= =?UTF-8?q?PRIVATE=5FSUBNETS=20string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deployment/proxies.rst | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/deployment/proxies.rst b/deployment/proxies.rst index 40c2550ee2c..fc6f855451d 100644 --- a/deployment/proxies.rst +++ b/deployment/proxies.rst @@ -143,9 +143,17 @@ In this case, you'll need to - *very carefully* - trust *all* proxies. framework: # ... # trust *all* requests (the 'REMOTE_ADDR' string is replaced at - # run time by $_SERVER['REMOTE_ADDR']) + # runtime by $_SERVER['REMOTE_ADDR']) trusted_proxies: '127.0.0.1,REMOTE_ADDR' + # you can also use the 'PRIVATE_SUBNETS' string, which is replaced at + # runtime by the IpUtils::PRIVATE_SUBNETS constant + # trusted_proxies: '127.0.0.1,PRIVATE_SUBNETS' + +.. versionadded:: 7.2 + + The support for the ``'PRIVATE_SUBNETS'`` string was introduced in Symfony 7.2. + That's it! It's critical that you prevent traffic from all non-trusted sources. If you allow outside traffic, they could "spoof" their true IP address and other information. From 7573d4a1211c81ae08d2ca26df5311322c45bb0b Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Thu, 19 Sep 2024 10:11:30 +0200 Subject: [PATCH 068/642] [Security] Allow passport attributes in `Security::login()` --- security.rst | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/security.rst b/security.rst index a8aa652a222..51ee9b5c3fb 100644 --- a/security.rst +++ b/security.rst @@ -1767,9 +1767,12 @@ You can log in a user programmatically using the ``login()`` method of the // you can also log in on a different firewall... $security->login($user, 'form_login', 'other_firewall'); - // ...and add badges + // ... add badges... $security->login($user, 'form_login', 'other_firewall', [(new RememberMeBadge())->enable()]); + // ... and also add passport attributes + $security->login($user, 'form_login', 'other_firewall', [(new RememberMeBadge())->enable()], ['referer' => 'https://oauth.example.com']); + // use the redirection logic applied to regular login $redirectResponse = $security->login($user); return $redirectResponse; @@ -1779,6 +1782,12 @@ You can log in a user programmatically using the ``login()`` method of the } } +.. versionadded:: 7.2 + + The support for passport attributes in the + :method:`Symfony\\Bundle\\SecurityBundle\\Security::login` method was + introduced in Symfony 7.2. + .. _security-logging-out: Logging Out From b0a91ce7a24f40b52d6732d32fc1dad28043a964 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 19 Sep 2024 09:00:51 +0200 Subject: [PATCH 069/642] [String] Document the Spanish inflector --- string.rst | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/string.rst b/string.rst index f2856976986..cb822e1dbd1 100644 --- a/string.rst +++ b/string.rst @@ -820,11 +820,28 @@ class to convert English words from/to singular/plural with confidence:: The value returned by both methods is always an array because sometimes it's not possible to determine a unique singular/plural form for the given word. +Symfony also provides inflectors for other languages:: + + use Symfony\Component\String\Inflector\FrenchInflector; + + $inflector = new FrenchInflector(); + $result = $inflector->singularize('souris'); // ['souris'] + $result = $inflector->pluralize('hôpital'); // ['hôpitaux'] + + use Symfony\Component\String\Inflector\SpanishInflector; + + $inflector = new SpanishInflector(); + $result = $inflector->singularize('aviones'); // ['avión'] + $result = $inflector->pluralize('miércoles'); // ['miércoles'] + +.. versionadded:: 7.2 + + The ``SpanishInflector`` class was introduced in Symfony 7.2. + .. note:: - Symfony also provides a :class:`Symfony\\Component\\String\\Inflector\\FrenchInflector` - and an :class:`Symfony\\Component\\String\\Inflector\\InflectorInterface` if - you need to implement your own inflector. + Symfony provides a :class:`Symfony\\Component\\String\\Inflector\\InflectorInterface` + in case you need to implement your own inflector. .. _`ASCII`: https://en.wikipedia.org/wiki/ASCII .. _`Unicode`: https://en.wikipedia.org/wiki/Unicode From 3983e81d511ced672866601c191f4ec780492634 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 20 Sep 2024 16:59:19 +0200 Subject: [PATCH 070/642] fix typo --- string.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/string.rst b/string.rst index da82791c740..37f64bc4f3b 100644 --- a/string.rst +++ b/string.rst @@ -674,7 +674,7 @@ Symfony also provides inflectors for other languages:: .. note:: - Symfony provides a :class:`Symfony\\Component\\String\\Inflector\\InflectorInterface` + Symfony provides an :class:`Symfony\\Component\\String\\Inflector\\InflectorInterface` in case you need to implement your own inflector. .. _`ASCII`: https://en.wikipedia.org/wiki/ASCII From fdda76fe73d83158eba51bbff5798ddd38ccf1ef Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 19 Sep 2024 12:41:59 +0200 Subject: [PATCH 071/642] [FrameworkBundle] Document the `resolve-env-vars` option of `lint:container` command --- service_container.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/service_container.rst b/service_container.rst index 9e1cdff2098..f329587eb54 100644 --- a/service_container.rst +++ b/service_container.rst @@ -1069,6 +1069,14 @@ application to production (e.g. in your continuous integration server): $ php bin/console lint:container + # optionally, you can force the resolution of environment variables; + # the command will fail if any of those environment variables are missing + $ php bin/console lint:container --resolve-env-vars + +.. versionadded:: 7.2 + + The ``--resolve-env-vars`` option was introduced in Symfony 7.2. + Performing those checks whenever the container is compiled can hurt performance. That's why they are implemented in :doc:`compiler passes ` called ``CheckTypeDeclarationsPass`` and ``CheckAliasValidityPass``, which are From a78ff470d7019a3cbf550ed3a7412340e18992e7 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Sun, 22 Sep 2024 11:34:16 -0400 Subject: [PATCH 072/642] documenting non-empty parameters --- configuration.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/configuration.rst b/configuration.rst index 36dceae1b71..52dc9b98c95 100644 --- a/configuration.rst +++ b/configuration.rst @@ -382,6 +382,25 @@ a new ``locale`` parameter is added to the ``config/services.yaml`` file). They are useful when working with :ref:`Compiler Passes ` to declare some temporary parameters that won't be available later in the application. +Configuration parameters are usually validation-free, but you can ensure that +essential parameters for your application's functionality are not empty:: + + // ContainerBuilder + $container->parameterCannotBeEmpty('app.private_key', 'Did you forget to configure a non-empty value for "app.private_key" parameter?'); + +If a non-empty parameter is ``null``, an empty string ``''``, or an empty array ``[]``, +Symfony will throw an exception with the custom error message when attempting to +retrieve the value of this parameter. + +.. versionadded:: 7.2 + + Validating non-empty parameters was introduced in Symfony 7.2. + +.. note:: + + Please note that this validation will *only* occur if a non-empty parameter value + is retrieved; otherwise, no exception will be thrown. + .. seealso:: Later in this article you can read how to From 2492dd1a3dc9df692c3061009ba9d21bd9ef8b16 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 23 Sep 2024 08:56:16 +0200 Subject: [PATCH 073/642] [Console] Document the silent verbosity level --- components/console/usage.rst | 8 ++++++++ console/input.rst | 7 ++++++- console/verbosity.rst | 23 ++++++++++++++++++++--- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/components/console/usage.rst b/components/console/usage.rst index d7725e8926e..591994948b8 100644 --- a/components/console/usage.rst +++ b/components/console/usage.rst @@ -65,9 +65,17 @@ You can suppress output with: .. code-block:: terminal + # suppresses all output, including errors + $ php application.php list --silent + + # suppresses all output except errors $ php application.php list --quiet $ php application.php list -q +.. versionadded:: 7.2 + + The ``--silent`` option was introduced in Symfony 7.2. + You can get more verbose messages (if this is supported for a command) with: diff --git a/console/input.rst b/console/input.rst index 6e7fc85a055..c038ace56fc 100644 --- a/console/input.rst +++ b/console/input.rst @@ -446,12 +446,17 @@ The Console component adds some predefined options to all commands: * ``--verbose``: sets the verbosity level (e.g. ``1`` the default, ``2`` and ``3``, or you can use respective shortcuts ``-v``, ``-vv`` and ``-vvv``) -* ``--quiet``: disables output and interaction +* ``--silent``: disables all output and interaction, including errors +* ``--quiet``: disables output and interaction, but errors are still displayed * ``--no-interaction``: disables interaction * ``--version``: outputs the version number of the console application * ``--help``: displays the command help * ``--ansi|--no-ansi``: whether to force of disable coloring the output +.. versionadded:: 7.2 + + The ``--silent`` option was introduced in Symfony 7.2. + When using the ``FrameworkBundle``, two more options are predefined: * ``--env``: sets the Kernel configuration environment (defaults to ``APP_ENV``) diff --git a/console/verbosity.rst b/console/verbosity.rst index f7a1a1e5e59..9910dca0c3d 100644 --- a/console/verbosity.rst +++ b/console/verbosity.rst @@ -7,7 +7,10 @@ messages, but you can control their verbosity with the ``-q`` and ``-v`` options .. code-block:: terminal - # do not output any message (not even the command result messages) + # suppress all output, including errors + $ php bin/console some-command --silent + + # suppress all output (even the command result messages) but display errors $ php bin/console some-command -q $ php bin/console some-command --quiet @@ -23,6 +26,10 @@ messages, but you can control their verbosity with the ``-q`` and ``-v`` options # display all messages (useful to debug errors) $ php bin/console some-command -vvv +.. versionadded:: 7.2 + + The ``--silent`` option was introduced in Symfony 7.2. + The verbosity level can also be controlled globally for all commands with the ``SHELL_VERBOSITY`` environment variable (the ``-q`` and ``-v`` options still have more precedence over the value of ``SHELL_VERBOSITY``): @@ -30,6 +37,7 @@ have more precedence over the value of ``SHELL_VERBOSITY``): ===================== ========================= =========================================== Console option ``SHELL_VERBOSITY`` value Equivalent PHP constant ===================== ========================= =========================================== +``--silent`` ``-2`` ``OutputInterface::VERBOSITY_SILENT`` ``-q`` or ``--quiet`` ``-1`` ``OutputInterface::VERBOSITY_QUIET`` (none) ``0`` ``OutputInterface::VERBOSITY_NORMAL`` ``-v`` ``1`` ``OutputInterface::VERBOSITY_VERBOSE`` @@ -58,7 +66,7 @@ level. For example:: 'Password: '.$input->getArgument('password'), ]); - // available methods: ->isQuiet(), ->isVerbose(), ->isVeryVerbose(), ->isDebug() + // available methods: ->isSilent(), ->isQuiet(), ->isVerbose(), ->isVeryVerbose(), ->isDebug() if ($output->isVerbose()) { $output->writeln('User class: '.get_class($user)); } @@ -73,10 +81,19 @@ level. For example:: } } -When the quiet level is used, all output is suppressed as the default +.. versionadded:: 7.2 + + The ``isSilent()`` method was introduced in Symfony 7.2. + +When the silent or quiet level are used, all output is suppressed as the default :method:`Symfony\\Component\\Console\\Output\\Output::write` method returns without actually printing. +.. tip:: + + When using the ``silent`` verbosity, errors won't be displayed in the console + but they will still be logged through the :doc:`Symfony logger ` integration. + .. tip:: The MonologBridge provides a :class:`Symfony\\Bridge\\Monolog\\Handler\\ConsoleHandler` From ecb343bf576206de34d57a190447b1128684d8cf Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Fri, 27 Sep 2024 13:05:03 +0200 Subject: [PATCH 074/642] [Webhook] add Mailtrap webhook docs --- webhook.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webhook.rst b/webhook.rst index 176abc49cd2..e2c7083ac43 100644 --- a/webhook.rst +++ b/webhook.rst @@ -28,6 +28,7 @@ MailerSend ``mailer.webhook.request_parser.mailersend`` Mailgun ``mailer.webhook.request_parser.mailgun`` Mailjet ``mailer.webhook.request_parser.mailjet`` Mailomat ``mailer.webhook.request_parser.mailomat`` +Mailtrap ``mailer.webhook.request_parser.mailtrap`` Postmark ``mailer.webhook.request_parser.postmark`` Resend ``mailer.webhook.request_parser.resend`` Sendgrid ``mailer.webhook.request_parser.sendgrid`` @@ -40,7 +41,8 @@ Sweego ``mailer.webhook.request_parser.sweego`` .. versionadded:: 7.2 - The ``Mailomat`` and ``Sweego`` integrations were introduced in Symfony 7.2. + The ``Mailomat``, ``Mailtrap``, and ``Sweego`` integrations were introduced in + Symfony 7.2. .. note:: From 0bb5a68545d695d7bf9f9ec31843920756ffdf44 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 30 Sep 2024 09:51:52 +0200 Subject: [PATCH 075/642] [String] Add the `AbstractString::kebab()` method --- string.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/string.rst b/string.rst index f2856976986..d9fdeded470 100644 --- a/string.rst +++ b/string.rst @@ -232,6 +232,8 @@ Methods to Change Case u('Foo: Bar-baz.')->camel(); // 'fooBarBaz' // changes all graphemes/code points to snake_case u('Foo: Bar-baz.')->snake(); // 'foo_bar_baz' + // changes all graphemes/code points to kebab-case + u('Foo: Bar-baz.')->kebab(); // 'foo-bar-baz' // other cases can be achieved by chaining methods. E.g. PascalCase: u('Foo: Bar-baz.')->camel()->title(); // 'FooBarBaz' @@ -240,6 +242,10 @@ Methods to Change Case The ``localeLower()``, ``localeUpper()`` and ``localeTitle()`` methods were introduced in Symfony 7.1. +.. versionadded:: 7.2 + + The ``kebab()`` method was introduced in Symfony 7.2. + The methods of all string classes are case-sensitive by default. You can perform case-insensitive operations with the ``ignoreCase()`` method:: From d810f6caabcc1960f5e3d7ffde08a44fa2733fb1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 30 Sep 2024 10:09:10 +0200 Subject: [PATCH 076/642] Minor tweaks --- configuration.rst | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/configuration.rst b/configuration.rst index 52dc9b98c95..5ebe952740e 100644 --- a/configuration.rst +++ b/configuration.rst @@ -385,22 +385,17 @@ a new ``locale`` parameter is added to the ``config/services.yaml`` file). Configuration parameters are usually validation-free, but you can ensure that essential parameters for your application's functionality are not empty:: - // ContainerBuilder - $container->parameterCannotBeEmpty('app.private_key', 'Did you forget to configure a non-empty value for "app.private_key" parameter?'); + /** @var ContainerBuilder $container */ + $container->parameterCannotBeEmpty('app.private_key', 'Did you forget to set a value for the "app.private_key" parameter?'); If a non-empty parameter is ``null``, an empty string ``''``, or an empty array ``[]``, -Symfony will throw an exception with the custom error message when attempting to -retrieve the value of this parameter. +Symfony will throw an exception. This validation is **not** made at compile time +but when attempting to retrieve the value of the parameter. .. versionadded:: 7.2 Validating non-empty parameters was introduced in Symfony 7.2. -.. note:: - - Please note that this validation will *only* occur if a non-empty parameter value - is retrieved; otherwise, no exception will be thrown. - .. seealso:: Later in this article you can read how to From a3815364478bdf7d2569655a1092880f06bfc94d Mon Sep 17 00:00:00 2001 From: Pierre Rineau Date: Fri, 28 Jun 2024 12:34:40 +0200 Subject: [PATCH 077/642] [Messenger] document the #[AsMessage] attribute --- messenger.rst | 49 ++++++++++++++++++++++++++++++++++++++++ reference/attributes.rst | 1 + 2 files changed, 50 insertions(+) diff --git a/messenger.rst b/messenger.rst index cecb84d9dec..8ba335b4e2b 100644 --- a/messenger.rst +++ b/messenger.rst @@ -345,6 +345,55 @@ to multiple transports: name as its only argument. For more information about stamps, see `Envelopes & Stamps`_. +.. _messenger-message-attribute: + +Configuring Routing Using Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can optionally use the `#[AsMessage]` attribute to configure message transport:: + + // src/Message/SmsNotification.php + namespace App\Message; + + use Symfony\Component\Messenger\Attribute\AsMessage; + + #[AsMessage(transport: 'async')] + class SmsNotification + { + public function __construct( + private string $content, + ) { + } + + public function getContent(): string + { + return $this->content; + } + } + +.. note:: + + If you configure routing with both configuration and attributes, the + configuration will take precedence over the attributes and override + them. This allows to override routing on a per-environment basis + for example: + + .. code-block:: yaml + + # config/packages/messenger.yaml + when@dev: + framework: + messenger: + routing: + # override class attribute + 'App\Message\SmsNotification': sync + +.. tip:: + + The `$transport` parameter can be either a `string` or an `array`: configuring multiple + transports is possible. You may also repeat the attribute if you prefer instead of using + an array. + Doctrine Entities in Messages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/reference/attributes.rst b/reference/attributes.rst index 559893ca2e4..19a27b71793 100644 --- a/reference/attributes.rst +++ b/reference/attributes.rst @@ -79,6 +79,7 @@ HttpKernel Messenger ~~~~~~~~~ +* :ref:`AsMessage ` * :ref:`AsMessageHandler ` RemoteEvent From a8a0e2beed33617c7143fbbeaabee884f3b9f357 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 30 Sep 2024 10:25:40 +0200 Subject: [PATCH 078/642] [AssetMapper] Document the filtering options of debug:asset-map --- frontend/asset_mapper.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index f6300e7adb9..6987718381f 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -137,6 +137,28 @@ This will show you all the mapped paths and the assets inside of each: The "Logical Path" is the path to use when referencing the asset, like from a template. +The ``debug:asset-map`` command provides several options to filter results: + +.. code-block:: terminal + + # provide an asset name or dir to only show results that match it + $ php bin/console debug:asset-map bootstrap.js + $ php bin/console debug:asset-map style/ + + # provide an extension to only show that file type + $ php bin/console debug:asset-map --ext=css + + # you can also only show assets in vendor/ dir or exclude any results from it + $ php bin/console debug:asset-map --vendor + $ php bin/console debug:asset-map --no-vendor + + # you can also combine all filters (e.g. find bold web fonts in your own asset dirs) + $ php bin/console debug:asset-map bold --no-vendor --ext=woff2 + +.. versionadded:: 7.2 + + The options to filter ``debug:asset-map`` results were introduced in Symfony 7.2. + .. _importmaps-javascript: Importmaps & Writing JavaScript From 476c1c1e6795e3384f6d392036147727af627528 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 1 Oct 2024 04:12:12 -0400 Subject: [PATCH 079/642] [Mailer] mark Mailtrap as having Webhook support --- mailer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mailer.rst b/mailer.rst index a8d0eda3072..2954fa7217a 100644 --- a/mailer.rst +++ b/mailer.rst @@ -109,7 +109,7 @@ Service Install with Webhook su `Mailomat`_ ``composer require symfony/mailomat-mailer`` yes `MailPace`_ ``composer require symfony/mail-pace-mailer`` `MailerSend`_ ``composer require symfony/mailer-send-mailer`` -`Mailtrap`_ ``composer require symfony/mailtrap-mailer`` +`Mailtrap`_ ``composer require symfony/mailtrap-mailer`` yes `Mandrill`_ ``composer require symfony/mailchimp-mailer`` `Postal`_ ``composer require symfony/postal-mailer`` `Postmark`_ ``composer require symfony/postmark-mailer`` yes From 9ac77eefb559c3e223722db352b485c374df9f98 Mon Sep 17 00:00:00 2001 From: W0rma Date: Tue, 1 Oct 2024 13:12:01 +0200 Subject: [PATCH 080/642] [Validator] Add example for passing groups and payload to the Compound constraint --- reference/constraints/Compound.rst | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/reference/constraints/Compound.rst b/reference/constraints/Compound.rst index fc8081f5917..0d0dc933ae0 100644 --- a/reference/constraints/Compound.rst +++ b/reference/constraints/Compound.rst @@ -102,6 +102,50 @@ You can now use it anywhere you need it: } } +Validation groups and payload can be passed via constructor: + +.. configuration-block:: + + .. code-block:: php-attributes + + // src/Entity/User.php + namespace App\Entity\User; + + use App\Validator\Constraints as Assert; + + class User + { + #[Assert\PasswordRequirements( + groups: ['registration'], + payload: ['severity' => 'error'], + )] + public string $plainPassword; + } + + .. code-block:: php + + // src/Entity/User.php + namespace App\Entity\User; + + use App\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class User + { + public static function loadValidatorMetadata(ClassMetadata $metadata): void + { + $metadata->addPropertyConstraint('plainPassword', new Assert\PasswordRequirements( + groups: ['registration'], + payload: ['severity' => 'error'], + )); + } + } + +.. versionadded:: 7.2 + + Support for passing validation groups and the payload to the constructor + of the ``Compound`` class was introduced in Symfony 7.2. + Options ------- From 743f4c6096f7d6fdd9fc968e402c486064a3b9a6 Mon Sep 17 00:00:00 2001 From: Pierre Ambroise Date: Tue, 1 Oct 2024 14:45:15 +0200 Subject: [PATCH 081/642] Document logical xor in expression language --- reference/formats/expression_language.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/reference/formats/expression_language.rst b/reference/formats/expression_language.rst index f902087d380..368c95bc2a8 100644 --- a/reference/formats/expression_language.rst +++ b/reference/formats/expression_language.rst @@ -345,6 +345,11 @@ Logical Operators * ``not`` or ``!`` * ``and`` or ``&&`` * ``or`` or ``||`` +* ``xor`` + +.. versionadded:: 7.2 + + Support for the ``xor`` logical operator was introduced in Symfony 7.2. For example:: @@ -487,6 +492,8 @@ The following table summarizes the operators and their associativity from the +-----------------------------------------------------------------+---------------+ | ``and``, ``&&`` | left | +-----------------------------------------------------------------+---------------+ +| ``xor`` | left | ++-----------------------------------------------------------------+---------------+ | ``or``, ``||`` | left | +-----------------------------------------------------------------+---------------+ From e8084a2413e3675713321e3749fe9bbbc37eea79 Mon Sep 17 00:00:00 2001 From: johan Vlaar Date: Tue, 1 Oct 2024 16:10:51 +0200 Subject: [PATCH 082/642] [Mailer][Webhook] Mandrill Webhook support --- mailer.rst | 2 +- webhook.rst | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/mailer.rst b/mailer.rst index 2954fa7217a..3dbf8a79575 100644 --- a/mailer.rst +++ b/mailer.rst @@ -110,7 +110,7 @@ Service Install with Webhook su `MailPace`_ ``composer require symfony/mail-pace-mailer`` `MailerSend`_ ``composer require symfony/mailer-send-mailer`` `Mailtrap`_ ``composer require symfony/mailtrap-mailer`` yes -`Mandrill`_ ``composer require symfony/mailchimp-mailer`` +`Mandrill`_ ``composer require symfony/mailchimp-mailer`` yes `Postal`_ ``composer require symfony/postal-mailer`` `Postmark`_ ``composer require symfony/postmark-mailer`` yes `Resend`_ ``composer require symfony/resend-mailer`` yes diff --git a/webhook.rst b/webhook.rst index e2c7083ac43..6b79da037e4 100644 --- a/webhook.rst +++ b/webhook.rst @@ -24,6 +24,7 @@ Currently, the following third-party mailer providers support webhooks: Mailer Service Parser service name ============== ============================================ Brevo ``mailer.webhook.request_parser.brevo`` +Mandrill ``mailer.webhook.request_parser.mailchimp`` MailerSend ``mailer.webhook.request_parser.mailersend`` Mailgun ``mailer.webhook.request_parser.mailgun`` Mailjet ``mailer.webhook.request_parser.mailjet`` @@ -41,7 +42,7 @@ Sweego ``mailer.webhook.request_parser.sweego`` .. versionadded:: 7.2 - The ``Mailomat``, ``Mailtrap``, and ``Sweego`` integrations were introduced in + The ``Mandrill``, ``Mailomat``, ``Mailtrap``, and ``Sweego`` integrations were introduced in Symfony 7.2. .. note:: From 6e2045a3ed2689f9443eb062ea15a54b7261e781 Mon Sep 17 00:00:00 2001 From: W0rma Date: Wed, 2 Oct 2024 07:21:30 +0200 Subject: [PATCH 083/642] [Messenger] Describe the options of the messenger:failed:retry command --- messenger.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/messenger.rst b/messenger.rst index cecb84d9dec..9f75ce33bed 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1173,6 +1173,8 @@ to retry them: $ php bin/console messenger:failed:show 20 -vv # view and retry messages one-by-one + # for each message the command asks whether the message should be retried, + # skipped or deleted $ php bin/console messenger:failed:retry -vv # retry specific messages @@ -1191,6 +1193,11 @@ If the message fails again, it will be re-sent back to the failure transport due to the normal :ref:`retry rules `. Once the max retry has been hit, the message will be discarded permanently. +.. versionadded:: 7.2 + + The possibility to skip a message in the `messenger:failed:retry` + command was introduced in Symfony 7.2 + Multiple Failed Transports ~~~~~~~~~~~~~~~~~~~~~~~~~~ From 795148ee5958773c4f7be614f56358663a677380 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 2 Oct 2024 17:46:12 +0200 Subject: [PATCH 084/642] Minor tweaks --- messenger.rst | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/messenger.rst b/messenger.rst index 9f75ce33bed..add197e8df6 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1172,9 +1172,7 @@ to retry them: # see details about a specific failure $ php bin/console messenger:failed:show 20 -vv - # view and retry messages one-by-one - # for each message the command asks whether the message should be retried, - # skipped or deleted + # for each message, this command asks whether to retry, skip, or delete $ php bin/console messenger:failed:retry -vv # retry specific messages @@ -1195,8 +1193,8 @@ retry has been hit, the message will be discarded permanently. .. versionadded:: 7.2 - The possibility to skip a message in the `messenger:failed:retry` - command was introduced in Symfony 7.2 + The option to skip a message in the ``messenger:failed:retry`` command was + introduced in Symfony 7.2 Multiple Failed Transports ~~~~~~~~~~~~~~~~~~~~~~~~~~ From 50ab3eed6e865d381b3ef914529a63f0a9bb5395 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 7 Oct 2024 08:37:24 +0200 Subject: [PATCH 085/642] [Mailer] Support unicode email addresses --- mailer.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mailer.rst b/mailer.rst index 2954fa7217a..e21951df557 100644 --- a/mailer.rst +++ b/mailer.rst @@ -542,6 +542,10 @@ both strings or address objects:: // email address as a simple string ->from('fabien@example.com') + // non-ASCII characters are supported both in the local part and the domain; + // if the SMTP server doesn't support this feature, you'll see an exception + ->from('jânë.dœ@ëxãmplę.com') + // email address as an object ->from(new Address('fabien@example.com')) @@ -556,6 +560,11 @@ both strings or address objects:: // ... ; +.. versionadded:: 7.2 + + Support for non-ASCII email addresses (e.g. ``jânë.dœ@ëxãmplę.com``) + was introduced in Symfony 7.2. + .. tip:: Instead of calling ``->from()`` *every* time you create a new email, you can From 861a0968eec52f837115b5667a18df6485bd2ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jib=C3=A9=20Barth?= Date: Sun, 6 Oct 2024 19:28:44 +0200 Subject: [PATCH 086/642] [Console] Document FinishedIndicator for Progress indicator --- .../console/helpers/progressindicator.rst | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/components/console/helpers/progressindicator.rst b/components/console/helpers/progressindicator.rst index d64ec6367b7..dd18c6bdbc9 100644 --- a/components/console/helpers/progressindicator.rst +++ b/components/console/helpers/progressindicator.rst @@ -95,6 +95,26 @@ The progress indicator will now look like this: ⠹ Processing... ⢸ Processing... +Custom Finished Indicator +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Once the progress indicator is finished, it keeps by default the latest indicator value. You can replace it with a your own:: + + $progressIndicator->finish('Finished', '✅'); + +Then the progress indicator will render like this: + +.. code-block:: text + + ⠏ Processing... + ⠛ Processing... + ✅ Finished + +.. versionadded:: 7.2 + + The ``finishedIndicator`` parameter for method ``finish()`` was introduced in Symfony 7.2. + + Customize Placeholders ~~~~~~~~~~~~~~~~~~~~~~ From 1b26f7681a2f4aff35adf89014767972a50a8585 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 7 Oct 2024 10:21:40 +0200 Subject: [PATCH 087/642] Minor tweaks --- components/console/helpers/progressindicator.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/components/console/helpers/progressindicator.rst b/components/console/helpers/progressindicator.rst index dd18c6bdbc9..fedc8439e64 100644 --- a/components/console/helpers/progressindicator.rst +++ b/components/console/helpers/progressindicator.rst @@ -95,10 +95,8 @@ The progress indicator will now look like this: ⠹ Processing... ⢸ Processing... -Custom Finished Indicator -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Once the progress indicator is finished, it keeps by default the latest indicator value. You can replace it with a your own:: +Once the progress indicator is finished, it keeps the latest indicator value by +default. You can replace it with your own:: $progressIndicator->finish('Finished', '✅'); @@ -114,7 +112,6 @@ Then the progress indicator will render like this: The ``finishedIndicator`` parameter for method ``finish()`` was introduced in Symfony 7.2. - Customize Placeholders ~~~~~~~~~~~~~~~~~~~~~~ From 2c93089f1781396bcd54b6f7f8eec403fa1c87a8 Mon Sep 17 00:00:00 2001 From: Maxime Doutreluingne Date: Sat, 28 Sep 2024 18:47:17 +0200 Subject: [PATCH 088/642] [Serializer] Deprecate ``AdvancedNameConverterInterface`` --- components/serializer.rst | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/components/serializer.rst b/components/serializer.rst index de8f67c853d..0764612e28a 100644 --- a/components/serializer.rst +++ b/components/serializer.rst @@ -549,12 +549,12 @@ A custom name converter can handle such cases:: class OrgPrefixNameConverter implements NameConverterInterface { - public function normalize(string $propertyName): string + public function normalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string { return 'org_'.$propertyName; } - public function denormalize(string $propertyName): string + public function denormalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string { // removes 'org_' prefix return str_starts_with($propertyName, 'org_') ? substr($propertyName, 4) : $propertyName; @@ -584,12 +584,6 @@ and :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer`:: $companyCopy = $serializer->deserialize($json, Company::class, 'json'); // Same data as $company -.. note:: - - You can also implement - :class:`Symfony\\Component\\Serializer\\NameConverter\\AdvancedNameConverterInterface` - to access the current class name, format and context. - .. _using-camelized-method-names-for-underscored-attributes: CamelCase to snake_case From e14e05f4eaea753f625656949d9d9fc4814510ff Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 3 Oct 2024 10:13:16 +0200 Subject: [PATCH 089/642] Document the new `SYMFONY_*` env vars --- deployment/proxies.rst | 11 ++++++++++- reference/configuration/framework.rst | 14 ++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/deployment/proxies.rst b/deployment/proxies.rst index fc6f855451d..cd5649d979a 100644 --- a/deployment/proxies.rst +++ b/deployment/proxies.rst @@ -22,7 +22,11 @@ Solution: ``setTrustedProxies()`` --------------------------------- To fix this, you need to tell Symfony which reverse proxy IP addresses to trust -and what headers your reverse proxy uses to send information: +and what headers your reverse proxy uses to send information. + +You can do that by setting the ``SYMFONY_TRUSTED_PROXIES`` and ``SYMFONY_TRUSTED_HEADERS`` +environment variables on your machine. Alternatively, you can configure them +using the following configuration options: .. configuration-block:: @@ -93,6 +97,11 @@ and what headers your reverse proxy uses to send information: ``private_ranges`` as a shortcut for private IP address ranges for the ``trusted_proxies`` option was introduced in Symfony 7.1. +.. versionadded:: 7.2 + + Support for the ``SYMFONY_TRUSTED_PROXIES`` and ``SYMFONY_TRUSTED_HEADERS`` + environment variables was introduced in Symfony 7.2. + .. caution:: Enabling the ``Request::HEADER_X_FORWARDED_HOST`` option exposes the diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 19f72bb6cb3..53edf220642 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -198,7 +198,12 @@ named ``kernel.http_method_override``. trust_x_sendfile_type_header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**type**: ``boolean`` **default**: ``false`` +**type**: ``boolean`` **default**: ``%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%`` + +.. versionadded:: 7.2 + + In Symfony 7.2, the default value of this option was changed from ``false`` to the + value stored in the ``SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER`` environment variable. ``X-Sendfile`` is a special HTTP header that tells web servers to replace the response contents by the file that is defined in that header. This improves @@ -450,7 +455,12 @@ in debug mode. trusted_hosts ~~~~~~~~~~~~~ -**type**: ``array`` | ``string`` **default**: ``[]`` +**type**: ``array`` | ``string`` **default**: ``['%env(default::SYMFONY_TRUSTED_HOSTS)%']`` + +.. versionadded:: 7.2 + + In Symfony 7.2, the default value of this option was changed from ``[]`` to the + value stored in the ``SYMFONY_TRUSTED_HOSTS`` environment variable. A lot of different attacks have been discovered relying on inconsistencies in handling the ``Host`` header by various software (web servers, reverse From 03922de2588b2ec965cd31f40c2ecc4d9b3e0c97 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 3 Oct 2024 16:04:31 +0200 Subject: [PATCH 090/642] [Ldap] Add support for `sasl_bind` and `whoami` LDAP operations --- components/ldap.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/components/ldap.rst b/components/ldap.rst index 89094fad0b7..d5f6bc3fdfe 100644 --- a/components/ldap.rst +++ b/components/ldap.rst @@ -74,6 +74,19 @@ distinguished name (DN) and the password of a user:: When the LDAP server allows unauthenticated binds, a blank password will always be valid. +You can also use the :method:`Symfony\\Component\\Ldap\\Ldap::saslBind` method +for binding to an LDAP server using `SASL`_:: + + // this method defines other optional arguments like $mech, $realm, $authcId, etc. + $ldap->saslBind($dn, $password); + +After binding to the LDAP server, you can use the :method:`Symfony\\Component\\Ldap\\Ldap::whoami` +method to get the distinguished name (DN) of the authenticated and authorized user. + +.. versionadded:: 7.2 + + The ``saslBind()`` and ``whoami()`` methods were introduced in Symfony 7.2. + Once bound (or if you enabled anonymous authentication on your LDAP server), you may query the LDAP server using the :method:`Symfony\\Component\\Ldap\\Ldap::query` method:: @@ -183,3 +196,5 @@ Possible operation types are ``LDAP_MODIFY_BATCH_ADD``, ``LDAP_MODIFY_BATCH_REMO ``LDAP_MODIFY_BATCH_REMOVE_ALL``, ``LDAP_MODIFY_BATCH_REPLACE``. Parameter ``$values`` must be ``NULL`` when using ``LDAP_MODIFY_BATCH_REMOVE_ALL`` operation type. + +.. _`SASL`: https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer From 3df38498bba9aaede83aafad5366d6449630d437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=A6=85KoNekoD?= Date: Thu, 10 Oct 2024 11:01:05 +0300 Subject: [PATCH 091/642] feat(when constraint): add context variable --- reference/constraints/When.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/reference/constraints/When.rst b/reference/constraints/When.rst index e1e8ac895ce..12b43fc7d63 100644 --- a/reference/constraints/When.rst +++ b/reference/constraints/When.rst @@ -163,7 +163,7 @@ validation of constraints won't be triggered. To learn more about the expression language syntax, see :doc:`/reference/formats/expression_language`. -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 1 or 3 variables in your expression: ``this`` @@ -171,6 +171,8 @@ in your expression: ``value`` The value of the property being validated (only available when the constraint is applied to a property). +``context`` + This is the context that is used in validation The ``value`` variable can be used when you want to execute more complex validation based on its value: From 5e8fb6ebd991577c9645d1df13bdabca8cf3b922 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 10 Oct 2024 14:21:34 +0200 Subject: [PATCH 092/642] [Lock] Add NullStore --- components/lock.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/lock.rst b/components/lock.rst index 5a76223112b..69e51a1407b 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -406,6 +406,14 @@ Store Scope Blocking Ex A special ``InMemoryStore`` is available for saving locks in memory during a process, and can be useful for testing. +.. tip:: + + A special ``NullStore`` is available and persist nothing, it can be useful for testing. + +.. versionadded:: 7.2 + + The :class:`Symfony\\Component\\Lock\\Store\\NullStore` was introduced in Symfony 7.2. + .. _lock-store-flock: FlockStore From 52d4f7bc5681bbf563eff8c4db8c439e0151d279 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 10 Oct 2024 16:06:12 +0200 Subject: [PATCH 093/642] Minor reword --- components/lock.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/components/lock.rst b/components/lock.rst index 69e51a1407b..bf75fb49f7a 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -403,12 +403,9 @@ Store Scope Blocking Ex .. tip:: - A special ``InMemoryStore`` is available for saving locks in memory during - a process, and can be useful for testing. - -.. tip:: - - A special ``NullStore`` is available and persist nothing, it can be useful for testing. + Symfony includes two other special stores that are mostly useful for testing: + ``InMemoryStore``, which saves locks in memory during a process, and ``NullStore``, + which doesn't persist anything. .. versionadded:: 7.2 From 16bccc3a3bab72597e7ad7bf23a291a12f4b958e Mon Sep 17 00:00:00 2001 From: Benjamin Georgeault Date: Mon, 14 Oct 2024 11:27:37 +0200 Subject: [PATCH 094/642] Add missing ref in constraint map to the new Yaml constraint. --- 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 e0dbd6c4512..97c049dae90 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -34,6 +34,7 @@ String Constraints * :doc:`Charset ` * :doc:`MacAddress ` * :doc:`WordCount ` +* :doc:`Yaml ` Comparison Constraints ~~~~~~~~~~~~~~~~~~~~~~ From d10abd7ad04171dd74b8032826bdb1db910894c7 Mon Sep 17 00:00:00 2001 From: pan93412 Date: Fri, 11 Oct 2024 14:19:45 +0800 Subject: [PATCH 095/642] [Notifier] Add LINE Bot notifier --- notifier.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/notifier.rst b/notifier.rst index 8b684ef2d1d..e5671eea534 100644 --- a/notifier.rst +++ b/notifier.rst @@ -341,6 +341,7 @@ Service Package D `Firebase`_ ``symfony/firebase-notifier`` ``firebase://USERNAME:PASSWORD@default`` `Gitter`_ ``symfony/gitter-notifier`` ``gitter://TOKEN@default?room_id=ROOM_ID`` `GoogleChat`_ ``symfony/google-chat-notifier`` ``googlechat://ACCESS_KEY:ACCESS_TOKEN@default/SPACE?thread_key=THREAD_KEY`` +`LINE Bot`_ ``symfony/line-bot-notifier`` ``linebot://TOKEN@default?receiver=RECEIVER`` `LINE Notify`_ ``symfony/line-notify-notifier`` ``linenotify://TOKEN@default`` `LinkedIn`_ ``symfony/linked-in-notifier`` ``linkedin://TOKEN:USER_ID@default`` `Mastodon`_ ``symfony/mastodon-notifier`` ``mastodon://ACCESS_TOKEN@HOST`` @@ -355,6 +356,10 @@ Service Package D `Zulip`_ ``symfony/zulip-notifier`` ``zulip://EMAIL:TOKEN@HOST?channel=CHANNEL`` ====================================== ==================================== ============================================================================= +.. versionadded:: 7.2 + + The ``LINE Bot`` integration was introduced in Symfony 7.2. + .. versionadded:: 7.1 The ``Bluesky`` integration was introduced in Symfony 7.1. @@ -1100,6 +1105,7 @@ is dispatched. Listeners receive a .. _`Iqsms`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Iqsms/README.md .. _`iSendPro`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Isendpro/README.md .. _`KazInfoTeh`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/README.md +.. _`LINE Bot`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LineBot/README.md .. _`LINE Notify`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LineNotify/README.md .. _`LightSms`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LightSms/README.md .. _`LinkedIn`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LinkedIn/README.md From b4b0c4c727ae35e16807717307ba37ad48b31777 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 14 Oct 2024 11:43:52 +0200 Subject: [PATCH 096/642] Minor tweak --- notifier.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/notifier.rst b/notifier.rst index 7d4ce6ef6be..71af2ccb440 100644 --- a/notifier.rst +++ b/notifier.rst @@ -370,14 +370,14 @@ Service Package D `Zulip`_ ``symfony/zulip-notifier`` ``zulip://EMAIL:TOKEN@HOST?channel=CHANNEL`` ====================================== ==================================== ============================================================================= -.. versionadded:: 7.2 - - The ``LINE Bot`` integration was introduced in Symfony 7.2. - .. versionadded:: 7.1 The ``Bluesky`` integration was introduced in Symfony 7.1. +.. versionadded:: 7.2 + + The ``LINE Bot`` integration was introduced in Symfony 7.2. + .. deprecated:: 7.2 The ``Gitter`` integration was removed in Symfony 7.2 because that service From e55e7cda09565450489232ab9144b752f8fe7e73 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Sun, 13 Oct 2024 15:23:24 -0400 Subject: [PATCH 097/642] documenting choice_lazy option --- reference/forms/types/choice.rst | 2 ++ .../forms/types/options/choice_lazy.rst.inc | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 reference/forms/types/options/choice_lazy.rst.inc diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index 55f2d016001..2d31aac890c 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -178,6 +178,8 @@ correct types will be assigned to the model. .. include:: /reference/forms/types/options/choice_loader.rst.inc +.. include:: /reference/forms/types/options/choice_lazy.rst.inc + .. include:: /reference/forms/types/options/choice_name.rst.inc .. include:: /reference/forms/types/options/choice_translation_domain_enabled.rst.inc diff --git a/reference/forms/types/options/choice_lazy.rst.inc b/reference/forms/types/options/choice_lazy.rst.inc new file mode 100644 index 00000000000..db8591a613d --- /dev/null +++ b/reference/forms/types/options/choice_lazy.rst.inc @@ -0,0 +1,31 @@ +``choice_lazy`` +~~~~~~~~~~~~~~~ + +**type**: ``boolean`` **default**: ``false`` + +The ``choice_lazy`` option is especially useful when dealing with a large set of +choices, where loading all of them at once could lead to performance issues or +significant delays:: + + use App\Entity\User; + use Symfony\Bridge\Doctrine\Form\Type\EntityType; + + $builder->add('user', EntityType::class, [ + 'class' => User::class, + 'choice_lazy' => true, + ]); + +When set to ``true``, and used in combination with the ``choice_loader`` option, +the form will only load and render the choices that are preset as default values +or submitted. This allows you to defer loading the full list of choices and can +improve the performance of your form. + +.. caution:: + + Please note that when using ``choice_lazy``, you are responsible for providing + the user interface to select choices, typically through a JavaScript plugin that + can handle the dynamic loading of choices. + +.. versionadded:: 7.2 + + The ``choice_lazy`` option was introduced in Symfony 7.2. From e9bd37b9073074b82a31789851c97b6219e79d1e Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 14 Oct 2024 14:58:35 +0200 Subject: [PATCH 098/642] merge versionadded directives --- notifier.rst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/notifier.rst b/notifier.rst index 71af2ccb440..b2329b2b28a 100644 --- a/notifier.rst +++ b/notifier.rst @@ -207,10 +207,6 @@ Service **Webhook support**: No ================== ==================================================================================================================================== -.. versionadded:: 7.2 - - The ``Primotexto`` integration was introduced in Symfony 7.2. - .. tip:: Use :doc:`Symfony configuration secrets ` to securely @@ -229,7 +225,7 @@ Service .. versionadded:: 7.2 - The ``Sipgate`` integration was introduced in Symfony 7.2. + The ``Primotexto`` and ``Sipgate`` integrations were introduced in Symfony 7.2. .. deprecated:: 7.1 From 12dfa63405ec8b7413ec69fa6df8306ce445d648 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 14 Oct 2024 15:46:52 +0200 Subject: [PATCH 099/642] Minor tweaks --- .../forms/types/options/choice_lazy.rst.inc | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/reference/forms/types/options/choice_lazy.rst.inc b/reference/forms/types/options/choice_lazy.rst.inc index db8591a613d..bdcbf178406 100644 --- a/reference/forms/types/options/choice_lazy.rst.inc +++ b/reference/forms/types/options/choice_lazy.rst.inc @@ -3,9 +3,13 @@ **type**: ``boolean`` **default**: ``false`` -The ``choice_lazy`` option is especially useful when dealing with a large set of -choices, where loading all of them at once could lead to performance issues or -significant delays:: +.. versionadded:: 7.2 + + The ``choice_lazy`` option was introduced in Symfony 7.2. + +The ``choice_lazy`` option is particularly useful when dealing with a large set +of choices, where loading them all at once could cause performance issues or +delays:: use App\Entity\User; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -15,17 +19,13 @@ significant delays:: 'choice_lazy' => true, ]); -When set to ``true``, and used in combination with the ``choice_loader`` option, -the form will only load and render the choices that are preset as default values -or submitted. This allows you to defer loading the full list of choices and can -improve the performance of your form. +When set to ``true`` and used alongside the ``choice_loader`` option, the form +will only load and render the choices that are preset as default values or +submitted. This defers the loading of the full list of choices, helping to +improve your form's performance. .. caution:: - Please note that when using ``choice_lazy``, you are responsible for providing - the user interface to select choices, typically through a JavaScript plugin that - can handle the dynamic loading of choices. - -.. versionadded:: 7.2 - - The ``choice_lazy`` option was introduced in Symfony 7.2. + Keep in mind that when using ``choice_lazy``, you are responsible for + providing the user interface for selecting choices, typically through a + JavaScript plugin capable of dynamically loading choices. From 93ca37815417a6c11b7d8807beec1d9bf111b557 Mon Sep 17 00:00:00 2001 From: Laurens Laman Date: Tue, 15 Oct 2024 14:56:21 +0200 Subject: [PATCH 100/642] Update progressindicator.rst Corrected false statement about the finishedIndicator. Make documentation more clear --- .../console/helpers/progressindicator.rst | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/components/console/helpers/progressindicator.rst b/components/console/helpers/progressindicator.rst index fedc8439e64..1d4384958e9 100644 --- a/components/console/helpers/progressindicator.rst +++ b/components/console/helpers/progressindicator.rst @@ -44,18 +44,21 @@ level of verbosity of the ``OutputInterface`` instance: | Processing... / Processing... - Processing... + ✔ Finished # OutputInterface::VERBOSITY_VERBOSE (-v) \ Processing... (1 sec) | Processing... (1 sec) / Processing... (1 sec) - Processing... (1 sec) + ✔ Finished (1 sec) # OutputInterface::VERBOSITY_VERY_VERBOSE (-vv) and OutputInterface::VERBOSITY_DEBUG (-vvv) \ Processing... (1 sec, 6.0 MiB) | Processing... (1 sec, 6.0 MiB) / Processing... (1 sec, 6.0 MiB) - Processing... (1 sec, 6.0 MiB) + ✔ Finished (1 sec, 6.0 MiB) .. tip:: @@ -94,22 +97,23 @@ The progress indicator will now look like this: ⠛ Processing... ⠹ Processing... ⢸ Processing... + ✔ Finished -Once the progress indicator is finished, it keeps the latest indicator value by -default. You can replace it with your own:: +Once the progress indicator is finished, it uses the finishedIndicator value (which defaults to ✔). You can replace it with your own:: - $progressIndicator->finish('Finished', '✅'); - -Then the progress indicator will render like this: - -.. code-block:: text + $progressIndicator = new ProgressIndicator($output, 'verbose', 100, null, '🎉'); + + try { + /* do something */ + $progressIndicator->finish('Finished'); + } catch (\Exception) { + $progressIndicator->finish('Failed', '🚨'); + } - ⠏ Processing... - ⠛ Processing... - ✅ Finished .. versionadded:: 7.2 + The ``finishedIndicator`` parameter for the constructor was introduced in Symfony 7.2. The ``finishedIndicator`` parameter for method ``finish()`` was introduced in Symfony 7.2. Customize Placeholders From d926bbb4e7cb2cd75e0f2f444a3b41dc3a1938d5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 16 Oct 2024 10:53:34 +0200 Subject: [PATCH 101/642] Reword --- messenger.rst | 102 +++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/messenger.rst b/messenger.rst index 03c429e214e..9cb6b4a6ff4 100644 --- a/messenger.rst +++ b/messenger.rst @@ -203,8 +203,23 @@ Routing Messages to a Transport Now that you have a transport configured, instead of handling a message immediately, you can configure them to be sent to a transport: +.. _messenger-message-attribute: + .. configuration-block:: + .. code-block:: php-attributes + + // src/Message/SmsNotification.php + namespace App\Message; + + use Symfony\Component\Messenger\Attribute\AsMessage; + + #[AsMessage('async')] + class SmsNotification + { + // ... + } + .. code-block:: yaml # config/packages/messenger.yaml @@ -251,15 +266,26 @@ you can configure them to be sent to a transport: ; }; +.. versionadded:: 7.2 + + The ``#[AsMessage]`` attribute was introduced in Symfony 7.2. + Thanks to this, the ``App\Message\SmsNotification`` will be sent to the ``async`` transport and its handler(s) will *not* be called immediately. Any messages not matched under ``routing`` will still be handled immediately, i.e. synchronously. .. note:: - You may use a partial PHP namespace like ``'App\Message\*'`` to match all - the messages within the matching namespace. The only requirement is that the - ``'*'`` wildcard has to be placed at the end of the namespace. + If you configure routing with both YAML/XML/PHP configuration files and + PHP attributes, the configuration always takes precedence over the class + attribute. This behavior allows you to override routing on a per-environment basis. + +.. note:: + + When configuring the routing in separate YAML/XML/PHP files, you can use a partial + PHP namespace like ``'App\Message\*'`` to match all the messages within the + matching namespace. The only requirement is that the ``'*'`` wildcard has to + be placed at the end of the namespace. You may use ``'*'`` as the message class. This will act as a default routing rule for any message not matched under ``routing``. This is useful to ensure @@ -275,6 +301,27 @@ to multiple transports: .. configuration-block:: + .. code-block:: php-attributes + + // src/Message/SmsNotification.php + namespace App\Message; + + use Symfony\Component\Messenger\Attribute\AsMessage; + + #[AsMessage(['async', 'audit'])] + class SmsNotification + { + // ... + } + + // if you prefer, you can also apply multiple attributes to the message class + #[AsMessage('async')] + #[AsMessage('audit')] + class SmsNotification + { + // ... + } + .. code-block:: yaml # config/packages/messenger.yaml @@ -345,55 +392,6 @@ to multiple transports: name as its only argument. For more information about stamps, see `Envelopes & Stamps`_. -.. _messenger-message-attribute: - -Configuring Routing Using Attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can optionally use the `#[AsMessage]` attribute to configure message transport:: - - // src/Message/SmsNotification.php - namespace App\Message; - - use Symfony\Component\Messenger\Attribute\AsMessage; - - #[AsMessage(transport: 'async')] - class SmsNotification - { - public function __construct( - private string $content, - ) { - } - - public function getContent(): string - { - return $this->content; - } - } - -.. note:: - - If you configure routing with both configuration and attributes, the - configuration will take precedence over the attributes and override - them. This allows to override routing on a per-environment basis - for example: - - .. code-block:: yaml - - # config/packages/messenger.yaml - when@dev: - framework: - messenger: - routing: - # override class attribute - 'App\Message\SmsNotification': sync - -.. tip:: - - The `$transport` parameter can be either a `string` or an `array`: configuring multiple - transports is possible. You may also repeat the attribute if you prefer instead of using - an array. - Doctrine Entities in Messages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 9d85b6904c11777c31263b276152ad980cbd08fc Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 16 Oct 2024 16:43:56 +0200 Subject: [PATCH 102/642] Tweaks --- components/console/helpers/progressindicator.rst | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/components/console/helpers/progressindicator.rst b/components/console/helpers/progressindicator.rst index 1d4384958e9..0defe7c83fd 100644 --- a/components/console/helpers/progressindicator.rst +++ b/components/console/helpers/progressindicator.rst @@ -99,9 +99,10 @@ The progress indicator will now look like this: ⢸ Processing... ✔ Finished -Once the progress indicator is finished, it uses the finishedIndicator value (which defaults to ✔). You can replace it with your own:: +Once the progress finishes, it displays a special finished indicator (which defaults +to ✔). You can replace it with your own:: - $progressIndicator = new ProgressIndicator($output, 'verbose', 100, null, '🎉'); + $progressIndicator = new ProgressIndicator($output, finishedIndicatorValue: '🎉'); try { /* do something */ @@ -110,6 +111,15 @@ Once the progress indicator is finished, it uses the finishedIndicator value (wh $progressIndicator->finish('Failed', '🚨'); } +The progress indicator will now look like this: + +.. code-block:: text + + \ Processing... + | Processing... + / Processing... + - Processing... + 🎉 Finished .. versionadded:: 7.2 From 6473d6c91f016b26b28ec8dc083fc300b0525133 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 16 Oct 2024 16:50:50 +0200 Subject: [PATCH 103/642] Finished the docs --- security/user_checkers.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/security/user_checkers.rst b/security/user_checkers.rst index 1e1dcaf3e55..ec8f49da522 100644 --- a/security/user_checkers.rst +++ b/security/user_checkers.rst @@ -21,6 +21,8 @@ displayed to the user:: namespace App\Security; use App\Entity\User as AppUser; + use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Exception\AccountExpiredException; use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; use Symfony\Component\Security\Core\User\UserCheckerInterface; @@ -50,9 +52,17 @@ displayed to the user:: if ($user->isExpired()) { throw new AccountExpiredException('...'); } + + if (!\in_array('foo', $token->getRoleNames())) { + throw new AccessDeniedException('...'); + } } } +.. versionadded:: 7.2 + + The ``token`` argument for the ``checkPostAuth()`` method was introduced in Symfony 7.2. + Enabling the Custom User Checker -------------------------------- From f932585d967262ffcd0f2b63ac47faa043e9d3bf Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Sat, 19 Oct 2024 16:52:24 +0200 Subject: [PATCH 104/642] Add Sweego Notifier doc --- notifier.rst | 6 +++++- webhook.rst | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/notifier.rst b/notifier.rst index b2329b2b28a..144044ebe3e 100644 --- a/notifier.rst +++ b/notifier.rst @@ -187,6 +187,9 @@ Service `SpotHit`_ **Install**: ``composer require symfony/spot-hit-notifier`` \ **DSN**: ``spothit://TOKEN@default?from=FROM`` \ **Webhook support**: No +`Sweego`_ **Install**: ``composer require symfony/sweego-notifier`` \ + **DSN**: ``sweego://API_KEY@default?region=REGION&campaign_type=CAMPAIGN_TYPE`` \ + **Webhook support**: Yes `Telnyx`_ **Install**: ``composer require symfony/telnyx-notifier`` \ **DSN**: ``telnyx://API_KEY@default?from=FROM&messaging_profile_id=MESSAGING_PROFILE_ID`` \ **Webhook support**: No @@ -225,7 +228,7 @@ Service .. versionadded:: 7.2 - The ``Primotexto`` and ``Sipgate`` integrations were introduced in Symfony 7.2. + The ``Primotexto``, ``Sipgate`` and ``Sweego`` integrations were introduced in Symfony 7.2. .. deprecated:: 7.1 @@ -1239,6 +1242,7 @@ is dispatched. Listeners receive a .. _`SMSense`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SMSense/README.md .. _`SmsSluzba`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SmsSluzba/README.md .. _`SpotHit`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SpotHit/README.md +.. _`Sweego`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sweego/README.md .. _`Telegram`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Telegram/README.md .. _`Telnyx`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Telnyx/README.md .. _`TurboSms`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/TurboSms/README.md diff --git a/webhook.rst b/webhook.rst index 6b79da037e4..aba95cffb04 100644 --- a/webhook.rst +++ b/webhook.rst @@ -166,6 +166,7 @@ Currently, the following third-party SMS transports support webhooks: SMS service Parser service name ============ ========================================== Twilio ``notifier.webhook.request_parser.twilio`` +Sweego ``notifier.webhook.request_parser.sweego`` Vonage ``notifier.webhook.request_parser.vonage`` ============ ========================================== From ef9baef15df2f02bec0221f78cbab3151695f542 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Mon, 21 Oct 2024 08:56:56 +0200 Subject: [PATCH 105/642] fix param name for lint:translations --- translation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translation.rst b/translation.rst index 4663d9d7c15..a1d012f3b38 100644 --- a/translation.rst +++ b/translation.rst @@ -1405,7 +1405,7 @@ to check that the translation contents are also correct: $ php bin/console lint:translations # checks the contents of the translation catalogues for Italian (it) and Japanese (ja) locales - $ php bin/console lint:translations --locales=it --locales=ja + $ php bin/console lint:translations --locale=it --locale=ja .. versionadded:: 7.2 From b8e5d0c59bf7a287687317026f56b7f5cd8d1f03 Mon Sep 17 00:00:00 2001 From: Jawira Portugal Date: Sun, 27 Oct 2024 19:23:45 +0100 Subject: [PATCH 106/642] Add --prefix and --no-fill to translation page --- translation.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/translation.rst b/translation.rst index a1d012f3b38..2bbce00977a 100644 --- a/translation.rst +++ b/translation.rst @@ -467,8 +467,33 @@ The ``translation:extract`` command looks for missing translations in: $ composer require nikic/php-parser +By default, the ``translation:extract`` command extracts new messages in +*message/translation* pairs, with the translation populated with the same +text as the message, prefixed by ``__``. You can customize this prefix using +the ``--prefix`` option. + +If you want to leave new translations empty, use the ``--no-fill`` option. +When enabled, only messages are created, and the translation strings are left +empty. This is particularly useful when using external translation tools, as +it ensures untranslated strings are easily identifiable without any pre-filled +content. Note that when ``--no-fill`` is used, the ``--prefix`` option has +no effect. + +.. code-block:: terminal + + # changes default prefix + $ php bin/console translation:extract --force --prefix="NEW_" fr + + # leaves new translations empty + $ php bin/console translation:extract --force --no-fill fr + +.. versionadded:: 7.2 + + The ``--no-fill`` option was introduced in Symfony 7.2. + .. _translation-resource-locations: + Translation Resource/File Names and Locations --------------------------------------------- From 65a14d07553951cf305069d4d56485898d8121b6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 28 Oct 2024 17:59:56 +0100 Subject: [PATCH 107/642] Minor tweaks --- translation.rst | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/translation.rst b/translation.rst index 2bbce00977a..a41b305dc8b 100644 --- a/translation.rst +++ b/translation.rst @@ -467,24 +467,23 @@ The ``translation:extract`` command looks for missing translations in: $ composer require nikic/php-parser -By default, the ``translation:extract`` command extracts new messages in -*message/translation* pairs, with the translation populated with the same -text as the message, prefixed by ``__``. You can customize this prefix using -the ``--prefix`` option. - -If you want to leave new translations empty, use the ``--no-fill`` option. -When enabled, only messages are created, and the translation strings are left -empty. This is particularly useful when using external translation tools, as -it ensures untranslated strings are easily identifiable without any pre-filled -content. Note that when ``--no-fill`` is used, the ``--prefix`` option has -no effect. +By default, when the ``translation:extract`` command creates new entries in the +trnaslation file, it uses the same content as both the source and the pending +translation. The only difference is that the pending translation is prefixed by +``__``. You can customize this prefix using the ``--prefix`` option: .. code-block:: terminal - # changes default prefix $ php bin/console translation:extract --force --prefix="NEW_" fr - # leaves new translations empty +Alternatively, you can use the ``--no-fill`` option to leave the pending translation +completely empty when creating new entries in the translation catalog. This is +particularly useful when using external translation tools, as it makes it easier +to spot untranslated strings: + +.. code-block:: terminal + + # when using the --no-fill option, the --prefix option is ignored $ php bin/console translation:extract --force --no-fill fr .. versionadded:: 7.2 @@ -493,7 +492,6 @@ no effect. .. _translation-resource-locations: - Translation Resource/File Names and Locations --------------------------------------------- From 562893be94672b5987b74521d8abebac4d618d30 Mon Sep 17 00:00:00 2001 From: Jawira Portugal Date: Mon, 28 Oct 2024 22:10:26 +0100 Subject: [PATCH 108/642] fix typo --- translation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translation.rst b/translation.rst index a41b305dc8b..0e5f09d342e 100644 --- a/translation.rst +++ b/translation.rst @@ -468,7 +468,7 @@ The ``translation:extract`` command looks for missing translations in: $ composer require nikic/php-parser By default, when the ``translation:extract`` command creates new entries in the -trnaslation file, it uses the same content as both the source and the pending +translation file, it uses the same content as both the source and the pending translation. The only difference is that the pending translation is prefixed by ``__``. You can customize this prefix using the ``--prefix`` option: From e033e433ab3d65e88fc5470662ecc485fc3117a6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 29 Oct 2024 10:07:55 +0100 Subject: [PATCH 109/642] [Notifier] Add some examples to the new Desktop channel notifications --- notifier.rst | 71 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/notifier.rst b/notifier.rst index 144044ebe3e..b28bb541475 100644 --- a/notifier.rst +++ b/notifier.rst @@ -33,8 +33,14 @@ The notifier component supports the following channels: services like Slack and Telegram; * :ref:`Email channel ` integrates the :doc:`Symfony Mailer `; * Browser channel uses :ref:`flash messages `. -* :ref:`Push channel ` sends notifications to phones and browsers via push notifications. -* :ref:`Desktop channel ` displays desktop notifications on the same host machine. +* :ref:`Push channel ` sends notifications to phones and + browsers via push notifications. +* :ref:`Desktop channel ` displays desktop notifications + on the same host machine. + +.. versionadded:: 7.2 + + The ``Desktop`` channel was introduced in Symfony 7.2. .. _notifier-sms-channel: @@ -635,9 +641,9 @@ configure the ``texter_transports``: Desktop Channel ~~~~~~~~~~~~~~~ -The desktop channel is used to display desktop notifications on the same host machine using -:class:`Symfony\\Component\\Notifier\\Texter` classes. Currently, Symfony -is integrated with the following providers: +The desktop channel is used to display local desktop notifications on the same +host machine using :class:`Symfony\\Component\\Notifier\\Texter` classes. Currently, +Symfony is integrated with the following providers: =============== ==================================== ============================================================================== Provider Package DSN @@ -645,18 +651,23 @@ Provider Package DSN `JoliNotif`_ ``symfony/joli-notif-notifier`` ``jolinotif://default`` =============== ==================================== ============================================================================== -.. versionadded: 7.2 +.. versionadded:: 7.2 The JoliNotif bridge was introduced in Symfony 7.2. -To enable a texter, add the correct DSN in your ``.env`` file and -configure the ``texter_transports``: +If you are using :ref:`Symfony Flex `, installing that package will +also create the necessary environment variable and configuration. Otherwise, you'll +need to add the following manually: + +1) Add the correct DSN in your ``.env`` file: .. code-block:: bash # .env JOLINOTIF=jolinotif://default +2) Update the Notifier configuration to add a new texter transport: + .. configuration-block:: .. code-block:: yaml @@ -699,9 +710,49 @@ configure the ``texter_transports``: ; }; -.. versionadded:: 7.2 +Now you can send notifications to your desktop as follows:: - The ``Desktop`` channel was introduced in Symfony 7.2. + // src/Notifier/SomeService.php + use Symfony\Component\Notifier\Message\DesktopMessage; + use Symfony\Component\Notifier\TexterInterface; + // ... + + class SomeService + { + public function __construct( + private TexterInterface $texter, + ) { + } + + public function notifyNewSubscriber(User $user): void + { + $message = new DesktopMessage( + 'New subscription! 🎉', + sprintf('%s is a new subscriber', $user->getFullName()) + ); + + $texter->send($message); + } + } + +These notifications can be customized further, and depending on your operating system, +they may support features like custom sounds, icons, and more:: + + use Symfony\Component\Notifier\Bridge\JoliNotif\JoliNotifOptions; + // ... + + $options = (new JoliNotifOptions()) + ->setIconPath('/path/to/icons/error.png') + ->setExtraOption('sound', 'sosumi') + ->setExtraOption('url', 'https://example.com'); + + $message = new DesktopMessage('Production is down', <<send($message); Configure to use Failover or Round-Robin Transports ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From d20ac34ac17254dc48a74ced515948a825ceae35 Mon Sep 17 00:00:00 2001 From: Raffaele Carelle Date: Mon, 4 Nov 2024 09:48:23 +0100 Subject: [PATCH 110/642] add stringNode documentation --- components/config/definition.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/config/definition.rst b/components/config/definition.rst index 929246fa915..05f64497a66 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -83,6 +83,12 @@ reflect the real structure of the configuration values:: ->scalarNode('default_connection') ->defaultValue('mysql') ->end() + ->stringNode('username') + ->defaultValue('root') + ->end() + ->stringNode('password') + ->defaultValue('root') + ->end() ->end() ; @@ -100,6 +106,7 @@ node definition. Node types are available for: * scalar (generic type that includes booleans, strings, integers, floats and ``null``) * boolean +* string * integer * float * enum (similar to scalar, but it only allows a finite set of values) From 970d37b45d9b609376ce7c77f8a8384d954f509f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 7 Nov 2024 16:47:56 +0100 Subject: [PATCH 111/642] Add the versionadded directives --- components/config/definition.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/components/config/definition.rst b/components/config/definition.rst index 05f64497a66..4e99fb4f6a7 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -92,6 +92,10 @@ reflect the real structure of the configuration values:: ->end() ; +.. versionadded:: 7.2 + + The ``stringNode()`` method was introduced in Symfony 7.2. + The root node itself is an array node, and has children, like the boolean node ``auto_connect`` and the scalar node ``default_connection``. In general: after defining a node, a call to ``end()`` takes you one step up in the @@ -116,6 +120,10 @@ node definition. Node types are available for: and are created with ``node($name, $type)`` or their associated shortcut ``xxxxNode($name)`` method. +.. versionadded:: + + Support for ``string`` types was introduced in Symfony 7.2. + Numeric Node Constraints ~~~~~~~~~~~~~~~~~~~~~~~~ From 7eb2264b2f1dff013aed0a9ffd32c6974f3f1270 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Fri, 8 Nov 2024 14:45:59 +0100 Subject: [PATCH 112/642] fix versionadded directive --- components/config/definition.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/config/definition.rst b/components/config/definition.rst index 4e99fb4f6a7..0db9923a340 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -120,9 +120,9 @@ node definition. Node types are available for: and are created with ``node($name, $type)`` or their associated shortcut ``xxxxNode($name)`` method. -.. versionadded:: +.. versionadded:: 7.2 - Support for ``string`` types was introduced in Symfony 7.2. + Support for the ``string`` type was introduced in Symfony 7.2. Numeric Node Constraints ~~~~~~~~~~~~~~~~~~~~~~~~ From f40f01b78d41fbf7ee52a6f5aba26d866f08129d Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Mon, 11 Nov 2024 17:22:23 +0100 Subject: [PATCH 113/642] [Validator] Improve type for the `mode` property of the `Bic` constraint --- reference/constraints/Bic.rst | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/reference/constraints/Bic.rst b/reference/constraints/Bic.rst index 3f05e5eac25..313de3bdbbf 100644 --- a/reference/constraints/Bic.rst +++ b/reference/constraints/Bic.rst @@ -124,18 +124,13 @@ Parameter Description ``mode`` ~~~~~~~~ -**type**: ``string`` **default**: ``strict`` +**type**: ``string`` **default**: ``BIC::VALIDATION_MODE_STRICT`` -This option defines how the BIC is validated: +This option defines how the BIC is validated. Available constants are defined +in the :class:`Symfony\\Component\\Validator\\Constraints\\BIC` class. -* ``strict`` validates the given value without any modification; -* ``case-insensitive`` converts the given value to uppercase before validating it. - -.. tip:: - - The possible values of this option are also defined as PHP constants of - :class:`Symfony\\Component\\Validator\\Constraints\\BIC` - (e.g. ``BIC::VALIDATION_MODE_CASE_INSENSITIVE``). +* ``BIC::VALIDATION_MODE_STRICT`` validates the given value without any modification; +* ``BIC::VALIDATION_MODE_CASE_INSENSITIVE`` converts the given value to uppercase before validating it. .. versionadded:: 7.2 From bc8c2d222b0f89f92f630ac4de7e5674f9e11a35 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 13 Nov 2024 17:10:23 +0100 Subject: [PATCH 114/642] Minor tweaks --- reference/constraints/Bic.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/reference/constraints/Bic.rst b/reference/constraints/Bic.rst index 313de3bdbbf..6cde4a11bac 100644 --- a/reference/constraints/Bic.rst +++ b/reference/constraints/Bic.rst @@ -124,13 +124,13 @@ Parameter Description ``mode`` ~~~~~~~~ -**type**: ``string`` **default**: ``BIC::VALIDATION_MODE_STRICT`` +**type**: ``string`` **default**: ``Bic::VALIDATION_MODE_STRICT`` -This option defines how the BIC is validated. Available constants are defined -in the :class:`Symfony\\Component\\Validator\\Constraints\\BIC` class. +This option defines how the BIC is validated. The possible values are available +as constants in the :class:`Symfony\\Component\\Validator\\Constraints\\Bic` class: -* ``BIC::VALIDATION_MODE_STRICT`` validates the given value without any modification; -* ``BIC::VALIDATION_MODE_CASE_INSENSITIVE`` converts the given value to uppercase before validating it. +* ``Bic::VALIDATION_MODE_STRICT`` validates the given value without any modification; +* ``Bic::VALIDATION_MODE_CASE_INSENSITIVE`` converts the given value to uppercase before validating it. .. versionadded:: 7.2 From 40058d823673436e935308a05acd87db028fbb7c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 14 Nov 2024 08:39:36 +0100 Subject: [PATCH 115/642] the TypeInfo component is no longer experimental --- components/type_info.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/components/type_info.rst b/components/type_info.rst index 30ae11aa222..f46c8f8ab3a 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -11,11 +11,6 @@ This component provides: * A way to get types from PHP elements such as properties, method arguments, return types, and raw strings. -.. caution:: - - This component is :doc:`experimental ` and - could be changed at any time without prior notice. - Installation ------------ From 8b9ca6f5e9c1c4964cebc08b35de6344f5902870 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 16 Nov 2024 10:22:58 +0100 Subject: [PATCH 116/642] remove documentation about no longer existing getBaseType() method --- components/type_info.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/components/type_info.rst b/components/type_info.rst index f46c8f8ab3a..bbc7d0ea8dc 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -58,12 +58,6 @@ based on reflection or a simple string:: // Type instances have several helper methods - // returns the main type (e.g. in this example it returns an "array" Type instance); - // for nullable types (e.g. string|null) it returns the non-null type (e.g. string) - // and for compound types (e.g. int|string) it throws an exception because both types - // can be considered the main one, so there's no way to pick one - $baseType = $type->getBaseType(); - // for collections, it returns the type of the item used as the key; // in this example, the collection is a list, so it returns an "int" Type instance $keyType = $type->getCollectionKeyType(); From 7caf0dc9291186e3139738188b0ba827274d6745 Mon Sep 17 00:00:00 2001 From: lacatoire Date: Mon, 18 Nov 2024 11:08:55 +0100 Subject: [PATCH 117/642] Clarify documentation about differences between AutowireIterator and AutowireLocator --- .../service_subscribers_locators.rst | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index da5cb415800..ada84158fcc 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -381,19 +381,48 @@ attribute:: :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator` attribute was introduced in Symfony 6.4. -.. note:: +The AutowireIterator Attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Variant of the ``AutowireLocator`` that specifically provides an iterable of services +based on a tag. This allows you to collect all services with a particular tag into +an iterable, which can be useful when you need to iterate over a set of services +rather than retrieving them individually. - To receive an iterable instead of a service locator, you can switch the - :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator` - attribute to - :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireIterator` - attribute. +For example, if you want to collect all the handlers for different command types, +you can use the ``AutowireIterator`` attribute to automatically inject all services +tagged with a specific tag:: + + // src/CommandBus.php + namespace App; + + use App\CommandHandler\BarHandler; + use App\CommandHandler\FooHandler; + use Psr\Container\ContainerInterface; + use Symfony\Component\DependencyInjection\Attribute\AutowireIterator; + + class CommandBus + { + public function __construct( + #[AutowireIterator('command_handler')] + private iterable $handlers, // Collects all services tagged with 'command_handler' + ) { + } - .. versionadded:: 6.4 + public function handle(Command $command): mixed + { + foreach ($this->handlers as $handler) { + if ($handler->supports($command)) { + return $handler->handle($command); + } + } + } + } + +.. versionadded:: 6.4 - The - :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireIterator` - attribute was introduced in Symfony 6.4. + The + :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireIterator` + attribute was introduced in Symfony 6.4. .. _service-subscribers-locators_defining-service-locator: From 337b5974e08ee43012ccd3ec6174f35409ccea41 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Sat, 23 Nov 2024 18:03:07 +0100 Subject: [PATCH 118/642] More syntax fixes --- serializer/encoders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serializer/encoders.rst b/serializer/encoders.rst index 65eece78031..003eb4523fe 100644 --- a/serializer/encoders.rst +++ b/serializer/encoders.rst @@ -65,7 +65,7 @@ are available to customize the behavior of the encoder: ``csv_end_of_line`` (default: ``\n``) Sets the character(s) used to mark the end of each line in the CSV file. ``csv_escape_char`` (default: empty string) - .. deprecated:: + .. deprecated:: 7.2 The ``csv_escape_char`` option was deprecated in Symfony 7.2. From dc999f33194b22851c44ba6147833b1e8003bc8f Mon Sep 17 00:00:00 2001 From: Baptiste Leduc Date: Fri, 15 Nov 2024 10:20:18 +0100 Subject: [PATCH 119/642] Add more details to TypeInfo documentation --- components/type_info.rst | 100 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/components/type_info.rst b/components/type_info.rst index bbc7d0ea8dc..18556e02972 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -40,12 +40,24 @@ to the :class:`Symfony\\Component\\TypeInfo\\Type` static methods as following:: // Many others are available and can be // found in Symfony\Component\TypeInfo\TypeFactoryTrait +Resolvers +~~~~~~~~~ + The second way of using the component is to use ``TypeInfo`` to resolve a type -based on reflection or a simple string:: +based on reflection or a simple string, this is aimed towards libraries that wants to +describe a class or anything that has a type easily:: use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; + class Dummy + { + public function __construct( + public int $id, + ) { + } + } + // Instantiate a new resolver $typeResolver = TypeResolver::create(); @@ -70,6 +82,88 @@ Each of these calls will return you a ``Type`` instance that corresponds to the static method used. You can also resolve types from a string (as shown in the ``bool`` parameter of the previous example) -.. note:: +PHPDoc parsing +~~~~~~~~~~~~~~ - To support raw string resolving, you need to install ``phpstan/phpdoc-parser`` package. +But most times you won't have clean typed properties or you want a more precise type +thank to advanced PHPDoc, to do that you would want a string resolver based on that PHPDoc. +First you will require ``phpstan/phpdoc-parser`` package from composer to support string +revolving. Then you would do as following:: + + use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; + + class Dummy + { + public function __construct( + public int $id, + /** @var string[] $tags */ + public array $tags, + ) { + } + } + + $typeResolver = TypeResolver::create(); + $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns an "int" Type + $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns a collection with "int" as key and "string" as values Type + +Advanced usages +~~~~~~~~~~~~~~~ + +There is many methods to manipulate and check types depending on your needs within the TypeInfo components. + +If you need a check a simple Type:: + + // You need to check if a Type + $type = Type::int(); // with a simple int type + // You can check if a given type comply with a given identifier + $type->isIdentifiedBy(TypeIdentifier::INT); // true + $type->isIdentifiedBy(TypeIdentifier::STRING); // false + + $type = Type::union(Type::string(), Type::int()); // with an union of int and string types + // You can now see that the second check will pass to true since we have an union with a string type + $type->isIdentifiedBy(TypeIdentifier::INT); // true + $type->isIdentifiedBy(TypeIdentifier::STRING); // true + + class DummyParent {} + class Dummy extends DummyParent implements DummyInterface {} + $type = Type::object(Dummy::class); // with an object Type + // You can check is the Type is an object, or even if it's a given class + $type->isIdentifiedBy(TypeIdentifier::OBJECT); // true + $type->isIdentifiedBy(Dummy::class); // true + // Or inherits/implements something + $type->isIdentifiedBy(DummyParent::class); // true + $type->isIdentifiedBy(DummyInterface::class); // true + +Sometimes you want to check for more than one thing at a time so a callable may be better to check everything:: + + class Foo + { + private int $integer; + private string $string; + private ?float $float; + } + + $reflClass = new \ReflectionClass(Foo::class); + + $resolver = TypeResolver::create(); + $integerType = $resolver->resolve($reflClass->getProperty('integer')); + $stringType = $resolver->resolve($reflClass->getProperty('string')); + $floatType = $resolver->resolve($reflClass->getProperty('float')); + + // your callable to check whatever you need + // here we want to validate a given type is a non nullable number + $isNonNullableNumber = function (Type $type): bool { + if ($type->isNullable()) { + return false; + } + + if ($type->isIdentifiedBy(TypeIdentifier::INT) || $type->isIdentifiedBy(TypeIdentifier::FLOAT)) { + return true; + } + + return false; + }; + + $integerType->isSatisfiedBy($isNonNullableNumber); // true + $stringType->isSatisfiedBy($isNonNullableNumber); // false + $floatType->isSatisfiedBy($isNonNullableNumber); // false From 03b029b9f3d144573b16f94a56e51aa7666e1342 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Wed, 27 Nov 2024 06:36:46 +0100 Subject: [PATCH 120/642] minor cs fix for ci --- serializer/encoders.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/serializer/encoders.rst b/serializer/encoders.rst index 003eb4523fe..e36d8731e48 100644 --- a/serializer/encoders.rst +++ b/serializer/encoders.rst @@ -65,6 +65,7 @@ are available to customize the behavior of the encoder: ``csv_end_of_line`` (default: ``\n``) Sets the character(s) used to mark the end of each line in the CSV file. ``csv_escape_char`` (default: empty string) + .. deprecated:: 7.2 The ``csv_escape_char`` option was deprecated in Symfony 7.2. From e427a6cb7466dba166e17f446b0f5a210757c8b9 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Wed, 27 Nov 2024 06:18:03 +0100 Subject: [PATCH 121/642] [Security] Secret with remember me feature --- security/remember_me.rst | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/security/remember_me.rst b/security/remember_me.rst index 8fac6d78849..2fd0f7e8d1e 100644 --- a/security/remember_me.rst +++ b/security/remember_me.rst @@ -19,7 +19,7 @@ the session lasts using a cookie with the ``remember_me`` firewall option: main: # ... remember_me: - secret: '%kernel.secret%' # required + secret: '%kernel.secret%' lifetime: 604800 # 1 week in seconds # by default, the feature is enabled by checking a # checkbox in the login form (see below), uncomment the @@ -44,7 +44,7 @@ the session lasts using a cookie with the ``remember_me`` firewall option: - firewall('main') // ... ->rememberMe() - ->secret('%kernel.secret%') // required + ->secret('%kernel.secret%') ->lifetime(604800) // 1 week in seconds // by default, the feature is enabled by checking a @@ -77,9 +77,11 @@ the session lasts using a cookie with the ``remember_me`` firewall option: ; }; -The ``secret`` option is the only required option and it is used to sign -the remember me cookie. It's common to use the ``kernel.secret`` parameter, -which is defined using the ``APP_SECRET`` environment variable. +.. versionadded:: 7.2 + + The ``secret`` option is no longer required starting from Symfony 7.2. By + default, ``%kernel.secret%`` is used, which is defined using the + ``APP_SECRET`` environment variable. After enabling the ``remember_me`` system in the configuration, there are a couple more things to do before remember me works correctly: @@ -171,7 +173,6 @@ allow users to opt-out. In these cases, you can use the main: # ... remember_me: - secret: '%kernel.secret%' # ... always_remember_me: true @@ -194,7 +195,6 @@ allow users to opt-out. In these cases, you can use the @@ -211,7 +211,6 @@ allow users to opt-out. In these cases, you can use the $security->firewall('main') // ... ->rememberMe() - ->secret('%kernel.secret%') // ... ->alwaysRememberMe(true) ; @@ -335,7 +334,6 @@ are fetched from the user object using the main: # ... remember_me: - secret: '%kernel.secret%' # ... signature_properties: ['password', 'updatedAt'] @@ -357,7 +355,7 @@ are fetched from the user object using the - + password updatedAt @@ -375,7 +373,6 @@ are fetched from the user object using the $security->firewall('main') // ... ->rememberMe() - ->secret('%kernel.secret%') // ... ->signatureProperties(['password', 'updatedAt']) ; @@ -419,7 +416,6 @@ You can enable the doctrine token provider using the ``doctrine`` setting: main: # ... remember_me: - secret: '%kernel.secret%' # ... token_provider: doctrine: true @@ -442,7 +438,7 @@ You can enable the doctrine token provider using the ``doctrine`` setting: - + @@ -459,7 +455,6 @@ You can enable the doctrine token provider using the ``doctrine`` setting: $security->firewall('main') // ... ->rememberMe() - ->secret('%kernel.secret%') // ... ->tokenProvider([ 'doctrine' => true, From c71b9f93795d90f0ae377f17026173f1e1c949a5 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Mon, 2 Dec 2024 08:38:44 +0100 Subject: [PATCH 122/642] remove a gc_probability default mention --- session.rst | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/session.rst b/session.rst index 393cd24a898..aa4c3ba381f 100644 --- a/session.rst +++ b/session.rst @@ -494,18 +494,7 @@ deleted. This allows one 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`` variable 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: - -.. code-block:: yaml - - # config/packages/framework.yaml - framework: - session: - # ... - gc_probability: null +collection. You can configure these settings by passing ``gc_probability``, ``gc_divisor`` and ``gc_maxlifetime`` in an array to the constructor of @@ -513,6 +502,10 @@ and ``gc_maxlifetime`` in an array to the constructor of or to the :method:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\NativeSessionStorage::setOptions` method. +.. versionadded:: 7.2 + + Starting from Symfony 7.2, ``php.ini``'s directive is used as default for ``gc_probability``. + .. _session-database: Store Sessions in a Database From 2083dc2eb14cc9b7d7213249d4d0437d36b997ce Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Mon, 2 Dec 2024 10:40:51 +0100 Subject: [PATCH 123/642] Allow integer for the calendar option of DateType --- reference/forms/types/date.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst index e88e91d80dd..6b8d21a19bd 100644 --- a/reference/forms/types/date.rst +++ b/reference/forms/types/date.rst @@ -156,11 +156,12 @@ values for the year, month and day fields:: ``calendar`` ~~~~~~~~~~~~ -**type**: ``\IntlCalendar`` **default**: ``null`` +**type**: ``integer`` or ``\IntlCalendar`` **default**: ``null`` The calendar to use for formatting and parsing the date. The value should be -an instance of the :phpclass:`IntlCalendar` to use. By default, the Gregorian -calendar with the application default locale is used. +an ``integer`` from :phpclass:`IntlDateFormatter` calendar constants or an instance +of the :phpclass:`IntlCalendar` to use. By default, the Gregorian calendar +with the application default locale is used. .. versionadded:: 7.2 From a0f7fab6e75ac4b76e21f5f412132f1bebed375a Mon Sep 17 00:00:00 2001 From: Morf <1416936+m0rff@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:10:23 +0100 Subject: [PATCH 124/642] fix: Update micro_kernel_trait.rst --- configuration/micro_kernel_trait.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index c9739679f69..62e8c2d4128 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -16,7 +16,7 @@ via Composer: .. code-block:: terminal - $ composer symfony/framework-bundle symfony/runtime + $ composer require symfony/framework-bundle symfony/runtime Next, create an ``index.php`` file that defines the kernel class and runs it: From 646e1995c71abf8b84e75c51cc355b1cc5e258b4 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Mon, 2 Dec 2024 10:13:27 +0100 Subject: [PATCH 125/642] [Messenger] Document `getRetryDelay()` --- messenger.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/messenger.rst b/messenger.rst index 74f7d792436..aa46ef9b1d3 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1134,6 +1134,13 @@ and must be retried. If you throw :class:`Symfony\\Component\\Messenger\\Exception\\RecoverableMessageHandlingException`, the message will always be retried infinitely and ``max_retries`` setting will be ignored. +If you want to override retry delay form the retry strategy. You can achieve this +using by providing it in the exception, using the ``getRetryDelay()`` method from :class:`Symfony\\Component\\Messenger\\Exception\\RecoverableExceptionInterface`. + +.. versionadded:: 7.2 + + The method ``getRetryDelay()`` was introduced in Symfony 7.2. + .. _messenger-failure-transport: Saving & Retrying Failed Messages From f99ae716aeb01cc279f4def8386f4d4fa7d7c190 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 2 Dec 2024 16:21:58 +0100 Subject: [PATCH 126/642] Reword --- messenger.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/messenger.rst b/messenger.rst index 35db17a75b3..053ccfd172e 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1134,12 +1134,14 @@ and must be retried. If you throw :class:`Symfony\\Component\\Messenger\\Exception\\RecoverableMessageHandlingException`, the message will always be retried infinitely and ``max_retries`` setting will be ignored. -If you want to override retry delay form the retry strategy. You can achieve this -using by providing it in the exception, using the ``getRetryDelay()`` method from :class:`Symfony\\Component\\Messenger\\Exception\\RecoverableExceptionInterface`. +You can define a custom retry delay (e.g., to use the value from the ``Retry-After`` +header in an HTTP response) by setting the ``retryDelay`` argument in the +constructor of the ``RecoverableMessageHandlingException``. .. versionadded:: 7.2 - The method ``getRetryDelay()`` was introduced in Symfony 7.2. + The ``retryDelay`` argument and the ``getRetryDelay()`` method were introduced + in Symfony 7.2. .. _messenger-failure-transport: From 977be3230885146769f8304e9aa0a578b4cc372e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 2 Dec 2024 16:34:09 +0100 Subject: [PATCH 127/642] Reword --- session.rst | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/session.rst b/session.rst index aa4c3ba381f..e3f2246b6f6 100644 --- a/session.rst +++ b/session.rst @@ -492,19 +492,30 @@ the ``php.ini`` directive ``session.gc_maxlifetime``. The meaning in this contex that any stored session that was saved more than ``gc_maxlifetime`` ago should be deleted. This allows one 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`` variable to ``0`` to stop PHP doing garbage -collection. +However, some operating systems (e.g. Debian) manage session handling differently +and set the ``session.gc_probability`` variable to ``0`` to prevent PHP from performing +garbage collection. By default, Symfony uses the value of the ``gc_probability`` +directive set in the ``php.ini`` file. If you can't modify this PHP setting, you +can configure it directly in Symfony: -You can configure these settings by passing ``gc_probability``, ``gc_divisor`` -and ``gc_maxlifetime`` in an array to the constructor of +.. code-block:: yaml + + # config/packages/framework.yaml + framework: + session: + # ... + gc_probability: 1 + +Alternatively, 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. .. versionadded:: 7.2 - Starting from Symfony 7.2, ``php.ini``'s directive is used as default for ``gc_probability``. + Using the ``php.ini`` directive as the default value for ``gc_probability`` + was introduced in Symfony 7.2. .. _session-database: From 00a781fdd643e516a6f196e84e765716e5a99e44 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Mon, 2 Dec 2024 17:25:05 +0100 Subject: [PATCH 128/642] Update mailer.rst: Changing order of tips Page: https://symfony.com/doc/current/mailer.html#email-addresses Reason: Bring the 2 IDN-related tips together. --- mailer.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mailer.rst b/mailer.rst index 8ff13c2561a..9cfba6c1a71 100644 --- a/mailer.rst +++ b/mailer.rst @@ -560,17 +560,17 @@ both strings or address objects:: // ... ; -.. versionadded:: 7.2 - - Support for non-ASCII email addresses (e.g. ``jânë.dœ@ëxãmplę.com``) - was introduced in Symfony 7.2. - .. tip:: Instead of calling ``->from()`` *every* time you create a new email, you can :ref:`configure emails globally ` to set the same ``From`` email to all messages. +.. versionadded:: 7.2 + + Support for non-ASCII email addresses (e.g. ``jânë.dœ@ëxãmplę.com``) + was introduced in Symfony 7.2. + .. note:: The local part of the address (what goes before the ``@``) can include UTF-8 From ec2743dd19e580971b8707eff6a66edcdf9f10a2 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Tue, 3 Dec 2024 12:05:33 +0100 Subject: [PATCH 129/642] [HttpFoundation] Do not use named parameters in example --- components/http_foundation.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/http_foundation.rst b/components/http_foundation.rst index 4dcf3b1e4da..c91ec5ced8f 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -367,11 +367,13 @@ of the ``anonymize()`` method to specify the number of bytes that should be anonymized depending on the IP address format:: $ipv4 = '123.234.235.236'; - $anonymousIpv4 = IpUtils::anonymize($ipv4, v4Bytes: 3); + $anonymousIpv4 = IpUtils::anonymize($ipv4, 3); // $anonymousIpv4 = '123.0.0.0' $ipv6 = '2a01:198:603:10:396e:4789:8e99:890f'; - $anonymousIpv6 = IpUtils::anonymize($ipv6, v6Bytes: 10); + // (you must define the second argument (bytes to anonymize in IPv4 addresses) + // even when you are only anonymizing IPv6 addresses) + $anonymousIpv6 = IpUtils::anonymize($ipv6, 3, 10); // $anonymousIpv6 = '2a01:198:603::' .. versionadded:: 7.2 From f58f1ebfa747a4aa900710abed46b0563f55fb76 Mon Sep 17 00:00:00 2001 From: Tim Goudriaan Date: Sat, 7 Dec 2024 11:53:08 +0100 Subject: [PATCH 130/642] [Scheduler] Periodical triggers timing --- scheduler.rst | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/scheduler.rst b/scheduler.rst index 160ba26e0cb..9407b85e57f 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -286,14 +286,37 @@ defined by PHP datetime functions:: RecurringMessage::every('3 weeks', new Message()); RecurringMessage::every('first Monday of next month', new Message()); - $from = new \DateTimeImmutable('13:47', new \DateTimeZone('Europe/Paris')); - $until = '2023-06-12'; - RecurringMessage::every('first Monday of next month', new Message(), $from, $until); - .. tip:: You can also define periodic tasks using :ref:`the AsPeriodicTask attribute `. +Be aware that the message isn't passed to the messenger when you start the +scheduler. The message will only be executed after the first frequency period +has passed. + +It's also possible to pass a from and until time for your schedule. For +example, if you want to execute a command every day at 13:00:: + + $from = new \DateTimeImmutable('13:00', new \DateTimeZone('Europe/Paris')); + RecurringMessage::every('1 day', new Message(), from: $from); + +Or if you want to execute a message every day until a specific date:: + + $until = '2023-06-12'; + RecurringMessage::every('1 day', new Message(), until: $until); + +And you can even combine the from and until parameters for more granular +control:: + + $from = new \DateTimeImmutable('2023-01-01 13:47', new \DateTimeZone('Europe/Paris')); + $until = '2023-06-12'; + RecurringMessage::every('first Monday of next month', new Message(), from: $from, until: $until); + +If you don't pass a from parameter to your schedule, the first frequency period +is counted from the moment the scheduler is started. So if you start your +scheduler at 8:33 and the message is scheduled to perform every hour, it +will be executed at 9:33, 10:33, 11:33 and so on. + Custom Triggers ~~~~~~~~~~~~~~~ From c14a4e733e4ba53e7fc5746356465d8924b22f5c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Sat, 7 Dec 2024 10:48:52 +0100 Subject: [PATCH 131/642] [AssetMapper] Document the feature that precompresses assets --- frontend/asset_mapper.rst | 61 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index f519c7c6109..708de32979f 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -656,7 +656,9 @@ which will automatically do most of these things for you: - **Compress your assets**: Your web server should compress (e.g. using gzip) your assets (JavaScript, CSS, images) before sending them to the browser. This is automatically enabled in Caddy and can be activated in Nginx and Apache. - In Cloudflare, assets are compressed by default. + In Cloudflare, assets are compressed by default. AssetMapper also supports + :ref:`precompressing your web assets ` to further + improve performance. - **Set long-lived cache expiry**: Your web server should set a long-lived ``Cache-Control`` HTTP header on your assets. Because the AssetMapper component includes a version @@ -704,6 +706,57 @@ even though it hasn't yet seen the ``import`` statement for them. Additionally, if the :doc:`WebLink Component ` is available in your application, Symfony will add a ``Link`` header in the response to preload the CSS files. +.. _performance-precompressing: + +Pre-Compressing Assets +---------------------- + +Although most servers (Caddy, Nginx, Apache, FrankenPHP) and services like Cloudflare +provide asset compression features, AssetMapper also allows you to compress all +your assets before serving them. + +This improves performance because you can compress assets using the highest (and +slowest) compression ratios beforehand and provide those compressed assets to the +server, which then returns them to the client without wasting CPU resources on +compression. + +AssetMapper supports `Brotli`_, `Zstandard`_ and `gzip`_ compression formats. +Before using any of them, the server that pre-compresses assets must have +installed the following PHP extensions or CLI commands: + +* Brotli: ``brotli`` CLI command; `brotli PHP extension`_; +* Zstandard: ``zstd`` CLI command; `zstd PHP extension`_; +* gzip: ``gzip`` CLI command; `zlib PHP extension`_. + +Then, update your AssetMapper configuration to define which compression to use +and which file extensions should be compressed: + +.. code-block:: yaml + + # config/packages/asset_mapper.yaml + framework: + asset_mapper: + # ... + + precompress: + format: 'zstandard' + # if you don't define the following option, AssetMapper will compress all + # the extensions considered safe (css, js, json, svg, xml, ttf, otf, wasm, etc.) + extensions: ['css', 'js', 'json', 'svg', 'xml'] + +Now, when running the ``asset-map:compile`` command, all matching files will be +compressed in the configured format and at the highest compression level. The +compressed files are created with the same name as the original but with the +``.br``, ``.zst``, or ``.gz`` extension appended. Web servers that support asset +precompression will use the compressed assets automatically, so there's nothing +else to configure in your server. + +.. tip:: + + AssetMapper provides an ``assets:compress`` CLI command and a service called + ``asset_mapper.compressor`` that you can use anywhere in your application to + compress any kind of files (e.g. files uploaded by users to your application). + Frequently Asked Questions -------------------------- @@ -1195,3 +1248,9 @@ command as part of your CI to be warned anytime a new vulnerability is found. .. _strict-dynamic: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#strict-dynamic .. _kocal/biome-js-bundle: https://github.com/Kocal/BiomeJsBundle .. _`SensioLabs Minify Bundle`: https://github.com/sensiolabs/minify-bundle +.. _`Brotli`: https://en.wikipedia.org/wiki/Brotli +.. _`Zstandard`: https://en.wikipedia.org/wiki/Zstd +.. _`gzip`: https://en.wikipedia.org/wiki/Gzip +.. _`brotli PHP extension`: https://pecl.php.net/package/brotli +.. _`zstd PHP extension`: https://pecl.php.net/package/zstd +.. _`zlib PHP extension`: https://www.php.net/manual/en/book.zlib.php From 0a3fb8d716fd0adf21d56762821ce7b19f71b90b Mon Sep 17 00:00:00 2001 From: Timo Bakx Date: Sat, 7 Dec 2024 12:19:15 +0100 Subject: [PATCH 132/642] Replaced `caution` directive by `warning` Fixes #20371 --- reference/forms/types/options/choice_lazy.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/forms/types/options/choice_lazy.rst.inc b/reference/forms/types/options/choice_lazy.rst.inc index bdcbf178406..08fbe953e41 100644 --- a/reference/forms/types/options/choice_lazy.rst.inc +++ b/reference/forms/types/options/choice_lazy.rst.inc @@ -24,7 +24,7 @@ will only load and render the choices that are preset as default values or submitted. This defers the loading of the full list of choices, helping to improve your form's performance. -.. caution:: +.. warning:: Keep in mind that when using ``choice_lazy``, you are responsible for providing the user interface for selecting choices, typically through a From 1d690fc9714cf2ebb6506c3656e6aa6e7909c33a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sat, 7 Dec 2024 17:04:18 +0100 Subject: [PATCH 133/642] [AssetMapper] mention Zopfli pre-compression and add config for web servers --- frontend/asset_mapper.rst | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index b55803e157f..e83da5aa42b 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -726,7 +726,7 @@ installed the following PHP extensions or CLI commands: * Brotli: ``brotli`` CLI command; `brotli PHP extension`_; * Zstandard: ``zstd`` CLI command; `zstd PHP extension`_; -* gzip: ``gzip`` CLI command; `zlib PHP extension`_. +* gzip: ``zopfli`` (better) or ``gzip`` CLI command; `zlib PHP extension`_. Then, update your AssetMapper configuration to define which compression to use and which file extensions should be compressed: @@ -751,6 +751,27 @@ compressed files are created with the same name as the original but with the precompression will use the compressed assets automatically, so there's nothing else to configure in your server. +Finally, you need to configure your web server to serve the precompressed assets +instead of the original ones: + +.. configuration-block:: + + .. code-block:: caddy + + file_server { + precompressed br zstd gzip + } + + .. code-block:: nginx + + gzip_static on; + + # Requires https://github.com/google/ngx_brotli + brotli_static on; + + # Requires https://github.com/tokers/zstd-nginx-module + zstd_static on; + .. tip:: AssetMapper provides an ``assets:compress`` CLI command and a service called From 9eb33c848bcf7ba26e42f7b8a080358933218cdc Mon Sep 17 00:00:00 2001 From: Antoine M Date: Tue, 10 Dec 2024 12:08:13 +0100 Subject: [PATCH 134/642] Update security.rst --- reference/configuration/security.rst | 52 ++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index b89aab67608..ac9e0382fa5 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -1075,6 +1075,58 @@ the session must not be used when authenticating users: // ... }; +.. _reference-security-lazy: + +lazy +~~~~~~~~~ + +Firewalls can configure a ``lazy`` boolean option in order to load the user and start the session only +if the application actually accesses the User object, +(e.g. via a is_granted() call in a template or isGranted() in a controller or service): + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + # ... + + firewalls: + main: + # ... + lazy: true + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // config/packages/security.php + use Symfony\Config\SecurityConfig; + + return static function (SecurityConfig $security): void { + $mainFirewall = $security->firewall('main'); + $mainFirewall->lazy(true); + // ... + }; + User Checkers ~~~~~~~~~~~~~ From 405f95ef8844382504372d0dc85178505d7cdfed Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 10 Dec 2024 15:51:38 +0100 Subject: [PATCH 135/642] Minor reword --- components/type_info.rst | 64 ++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/components/type_info.rst b/components/type_info.rst index 18556e02972..734ac4140ba 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -43,9 +43,9 @@ to the :class:`Symfony\\Component\\TypeInfo\\Type` static methods as following:: Resolvers ~~~~~~~~~ -The second way of using the component is to use ``TypeInfo`` to resolve a type -based on reflection or a simple string, this is aimed towards libraries that wants to -describe a class or anything that has a type easily:: +The second way to use the component is by using ``TypeInfo`` to resolve a type +based on reflection or a simple string. This approach is designed for libraries +that need a simple way to describe a class or anything with a type:: use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; @@ -82,13 +82,15 @@ Each of these calls will return you a ``Type`` instance that corresponds to the static method used. You can also resolve types from a string (as shown in the ``bool`` parameter of the previous example) -PHPDoc parsing +PHPDoc Parsing ~~~~~~~~~~~~~~ -But most times you won't have clean typed properties or you want a more precise type -thank to advanced PHPDoc, to do that you would want a string resolver based on that PHPDoc. -First you will require ``phpstan/phpdoc-parser`` package from composer to support string -revolving. Then you would do as following:: +In many cases, you may not have cleanly typed properties or may need more precise +type definitions provided by advanced PHPDoc. To achieve this, you can use a string +resolver based on the PHPDoc annotations. + +First, run the command ``composer require phpstan/phpdoc-parser`` to install the +PHP package required for string resolving. Then, follow these steps:: use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; @@ -106,35 +108,40 @@ revolving. Then you would do as following:: $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns an "int" Type $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns a collection with "int" as key and "string" as values Type -Advanced usages +Advanced Usages ~~~~~~~~~~~~~~~ -There is many methods to manipulate and check types depending on your needs within the TypeInfo components. +The TypeInfo component provides various methods to manipulate and check types, +depending on your needs. -If you need a check a simple Type:: +Checking a **simple type**:: - // You need to check if a Type - $type = Type::int(); // with a simple int type - // You can check if a given type comply with a given identifier - $type->isIdentifiedBy(TypeIdentifier::INT); // true + // define a simple integer type + $type = Type::int(); + // check if the type matches a specific identifier + $type->isIdentifiedBy(TypeIdentifier::INT); // true $type->isIdentifiedBy(TypeIdentifier::STRING); // false - $type = Type::union(Type::string(), Type::int()); // with an union of int and string types - // You can now see that the second check will pass to true since we have an union with a string type - $type->isIdentifiedBy(TypeIdentifier::INT); // true + // define a union type (equivalent to PHP's int|string) + $type = Type::union(Type::string(), Type::int()); + // now the second check is true because the union type contains the string type + $type->isIdentifiedBy(TypeIdentifier::INT); // true $type->isIdentifiedBy(TypeIdentifier::STRING); // true class DummyParent {} class Dummy extends DummyParent implements DummyInterface {} - $type = Type::object(Dummy::class); // with an object Type - // You can check is the Type is an object, or even if it's a given class + + // define an object type + $type = Type::object(Dummy::class); + + // check if the type is an object or matches a specific class $type->isIdentifiedBy(TypeIdentifier::OBJECT); // true - $type->isIdentifiedBy(Dummy::class); // true - // Or inherits/implements something - $type->isIdentifiedBy(DummyParent::class); // true - $type->isIdentifiedBy(DummyInterface::class); // true + $type->isIdentifiedBy(Dummy::class); // true + // check if it inherits/implements something + $type->isIdentifiedBy(DummyParent::class); // true + $type->isIdentifiedBy(DummyInterface::class); // true -Sometimes you want to check for more than one thing at a time so a callable may be better to check everything:: +Using callables for **complex checks**: class Foo { @@ -150,8 +157,7 @@ Sometimes you want to check for more than one thing at a time so a callable may $stringType = $resolver->resolve($reflClass->getProperty('string')); $floatType = $resolver->resolve($reflClass->getProperty('float')); - // your callable to check whatever you need - // here we want to validate a given type is a non nullable number + // define a callable to validate non-nullable number types $isNonNullableNumber = function (Type $type): bool { if ($type->isNullable()) { return false; @@ -165,5 +171,5 @@ Sometimes you want to check for more than one thing at a time so a callable may }; $integerType->isSatisfiedBy($isNonNullableNumber); // true - $stringType->isSatisfiedBy($isNonNullableNumber); // false - $floatType->isSatisfiedBy($isNonNullableNumber); // false + $stringType->isSatisfiedBy($isNonNullableNumber); // false + $floatType->isSatisfiedBy($isNonNullableNumber); // false From f87bbdf690f629b3096a1f8d0848f471d5e966ed Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Wed, 11 Dec 2024 10:38:18 +0100 Subject: [PATCH 136/642] [HttpFoundation] Add StreamedResponse string iterable documentation --- components/http_foundation.rst | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/components/http_foundation.rst b/components/http_foundation.rst index c91ec5ced8f..8db6cd27f68 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -681,8 +681,19 @@ Streaming a Response ~~~~~~~~~~~~~~~~~~~~ The :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse` class allows -you to stream the Response back to the client. The response content is -represented by a PHP callable instead of a string:: +you to stream the Response back to the client. The response content can be +represented by a string iterable:: + + use Symfony\Component\HttpFoundation\StreamedResponse; + + $chunks = ['Hello', ' World']; + + $response = new StreamedResponse(); + $response->setChunks($chunks); + $response->send(); + +For most complex use cases, the response content can be instead represented by +a PHP callable:: use Symfony\Component\HttpFoundation\StreamedResponse; @@ -710,6 +721,10 @@ represented by a PHP callable instead of a string:: // disables FastCGI buffering in nginx only for this response $response->headers->set('X-Accel-Buffering', 'no'); +.. versionadded:: 7.3 + + Support for using string iterables was introduced in Symfony 7.3. + Streaming a JSON Response ~~~~~~~~~~~~~~~~~~~~~~~~~ From b66e743657a527d4c14e3e4a1330893c6956cc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20L=C3=A9v=C3=AAque?= Date: Wed, 11 Dec 2024 17:11:32 +0100 Subject: [PATCH 137/642] [Console] Add support of millisecondes for formatTime --- components/console/helpers/formatterhelper.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/components/console/helpers/formatterhelper.rst b/components/console/helpers/formatterhelper.rst index 3cb87c4c307..bff0c82c90f 100644 --- a/components/console/helpers/formatterhelper.rst +++ b/components/console/helpers/formatterhelper.rst @@ -129,10 +129,12 @@ Sometimes you want to format seconds to time. This is possible with the The first argument is the seconds to format and the second argument is the precision (default ``1``) of the result:: - Helper::formatTime(42); // 42 secs - Helper::formatTime(125); // 2 mins - Helper::formatTime(125, 2); // 2 mins, 5 secs - Helper::formatTime(172799, 4); // 1 day, 23 hrs, 59 mins, 59 secs + Helper::formatTime(0.001); // 1 ms + Helper::formatTime(42); // 42 s + Helper::formatTime(125); // 2 min + Helper::formatTime(125, 2); // 2 min, 5 s + Helper::formatTime(172799, 4); // 1 d, 23 h, 59 min, 59 s + Helper::formatTime(172799.056, 5); // 1 d, 23 h, 59 min, 59 s, 56 ms Formatting Memory ----------------- From bf5dd100d61c54ed6e4ed729198d2d04a64940a0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 12 Dec 2024 10:56:45 +0100 Subject: [PATCH 138/642] Add the versionadded directive --- components/console/helpers/formatterhelper.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/components/console/helpers/formatterhelper.rst b/components/console/helpers/formatterhelper.rst index bff0c82c90f..d2b19915a3a 100644 --- a/components/console/helpers/formatterhelper.rst +++ b/components/console/helpers/formatterhelper.rst @@ -136,6 +136,10 @@ precision (default ``1``) of the result:: Helper::formatTime(172799, 4); // 1 d, 23 h, 59 min, 59 s Helper::formatTime(172799.056, 5); // 1 d, 23 h, 59 min, 59 s, 56 ms +.. versionadded:: 7.3 + + Support for formatting up to milliseconds was introduced in Symfony 7.3. + Formatting Memory ----------------- From eb5ffa691cc0239d89c72d9ca74897b2e85b23fc Mon Sep 17 00:00:00 2001 From: Baptiste Leduc Date: Thu, 12 Dec 2024 11:48:47 +0100 Subject: [PATCH 139/642] Fix PHP block in TypeInfo documentation --- components/type_info.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/type_info.rst b/components/type_info.rst index 734ac4140ba..e7062c5f249 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -141,7 +141,7 @@ Checking a **simple type**:: $type->isIdentifiedBy(DummyParent::class); // true $type->isIdentifiedBy(DummyInterface::class); // true -Using callables for **complex checks**: +Using callables for **complex checks**:: class Foo { From 3e4bcf17aae617ddf5295e53b95bd505b1a1d509 Mon Sep 17 00:00:00 2001 From: Baptiste Leduc Date: Thu, 12 Dec 2024 11:48:47 +0100 Subject: [PATCH 140/642] Fix PHP block in TypeInfo documentation --- components/type_info.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/type_info.rst b/components/type_info.rst index 734ac4140ba..e7062c5f249 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -141,7 +141,7 @@ Checking a **simple type**:: $type->isIdentifiedBy(DummyParent::class); // true $type->isIdentifiedBy(DummyInterface::class); // true -Using callables for **complex checks**: +Using callables for **complex checks**:: class Foo { From 2fdd0dbfd61d9bd93d679c8febedcddca40d5e3a Mon Sep 17 00:00:00 2001 From: Felix Eymonot Date: Tue, 17 Dec 2024 12:19:23 +0100 Subject: [PATCH 141/642] docs(controller): add docs for key option in MapQueryString --- controller.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/controller.rst b/controller.rst index c11615d93aa..5da85057bbe 100644 --- a/controller.rst +++ b/controller.rst @@ -443,6 +443,27 @@ HTTP status to return if the validation fails:: The default status code returned if the validation fails is 404. +If you want to map your object to a nested array in your query with a specific key, +you can use the ``key`` option in your :class:`Symfony\\Component\\HttpKernel\\Attribute\\MapQueryString` +attribute:: + + use App\Model\SearchDto; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\Attribute\MapQueryString; + + // ... + + public function dashboard( + #[MapQueryString(key: 'search')] SearchDto $searchDto + ): Response + { + // ... + } + +.. versionadded:: 7.3 + + The ``key`` option of ``#[MapQueryString]`` was introduced in Symfony 7.3. + If you need a valid DTO even when the request query string is empty, set a default value for your controller arguments:: From 8aeceab63ce821c69bb182377ad1c3c0b54bf17e Mon Sep 17 00:00:00 2001 From: Christophe Coevoet Date: Tue, 17 Dec 2024 13:06:15 +0100 Subject: [PATCH 142/642] Fix the configuration for custom password strength estimator --- reference/constraints/PasswordStrength.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/constraints/PasswordStrength.rst b/reference/constraints/PasswordStrength.rst index 671b5f495ef..60125a763a1 100644 --- a/reference/constraints/PasswordStrength.rst +++ b/reference/constraints/PasswordStrength.rst @@ -178,7 +178,7 @@ service to use your own estimator: class: App\Validator\CustomPasswordStrengthEstimator Symfony\Component\Validator\Constraints\PasswordStrengthValidator: - arguments: [!service_closure '@custom_password_strength_estimator'] + arguments: [!closure '@custom_password_strength_estimator'] .. code-block:: xml @@ -192,7 +192,7 @@ service to use your own estimator: - + @@ -210,5 +210,5 @@ service to use your own estimator: $services->set('custom_password_strength_estimator', CustomPasswordStrengthEstimator::class); $services->set(PasswordStrengthValidator::class) - ->args([service_closure('custom_password_strength_estimator')]); + ->args([closure('custom_password_strength_estimator')]); }; From c196b447547b28048a9f1d5849f6964e982933ad Mon Sep 17 00:00:00 2001 From: Nate Wiebe Date: Thu, 9 Mar 2023 09:10:30 -0500 Subject: [PATCH 143/642] Add tip for using new isGrantedForUser() function --- security.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/security.rst b/security.rst index c8dfc8d6233..5da16121b30 100644 --- a/security.rst +++ b/security.rst @@ -2585,6 +2585,18 @@ want to include extra details only for users that have a ``ROLE_SALES_ADMIN`` ro // ... } +.. tip:: + + Using ``isGranted()`` checks authorization against the currently logged in user. If you need to check + against a user that is not the one logged in or if checking authorization when the user session is not + available in a CLI context (example: message queue, cronjob) ``isGrantedForUser()`` can be used to set the + target user explicitly. + + .. versionadded:: 7.3 + + The :method:`Symfony\\Bundle\\SecurityBundle\\Security::isGrantedForUser` + method was introduced in Symfony 7.3. + If you're using the :ref:`default services.yaml configuration `, Symfony will automatically pass the ``security.helper`` to your service thanks to autowiring and the ``Security`` type-hint. From f10c7e9f81e4db9cba84d03027ca7eccf0a9e31f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20PLANUS?= <48881747+leo29plns@users.noreply.github.com> Date: Thu, 19 Dec 2024 08:06:37 +0100 Subject: [PATCH 144/642] Update 7.2.x-dev to 7.2.x --- setup.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.rst b/setup.rst index 1d7081e93b7..853b53f2400 100644 --- a/setup.rst +++ b/setup.rst @@ -48,10 +48,10 @@ application: .. code-block:: terminal # run this if you are building a traditional web application - $ symfony new my_project_directory --version="7.2.x-dev" --webapp + $ symfony new my_project_directory --version="7.2.x" --webapp # run this if you are building a microservice, console application or API - $ symfony new my_project_directory --version="7.2.x-dev" + $ symfony new my_project_directory --version="7.2.x" The only difference between these two commands is the number of packages installed by default. The ``--webapp`` option installs extra packages to give @@ -63,12 +63,12 @@ Symfony application using Composer: .. code-block:: terminal # run this if you are building a traditional web application - $ composer create-project symfony/skeleton:"7.2.x-dev" my_project_directory + $ composer create-project symfony/skeleton:"7.2.x" 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:"7.2.x-dev" my_project_directory + $ composer create-project symfony/skeleton:"7.2.x" 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 ccdeed257e1e882374a185b18437933694b94bb7 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 19 Dec 2024 17:47:14 +0100 Subject: [PATCH 145/642] Minor tweak --- controller.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/controller.rst b/controller.rst index 5da85057bbe..026e76ed0a6 100644 --- a/controller.rst +++ b/controller.rst @@ -443,9 +443,8 @@ HTTP status to return if the validation fails:: The default status code returned if the validation fails is 404. -If you want to map your object to a nested array in your query with a specific key, -you can use the ``key`` option in your :class:`Symfony\\Component\\HttpKernel\\Attribute\\MapQueryString` -attribute:: +If you want to map your object to a nested array in your query using a specific key, +set the ``key`` option in the ``#[MapQueryString]`` attribute:: use App\Model\SearchDto; use Symfony\Component\HttpFoundation\Response; From d99915066523753285953b349f1a9782cc353fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Sun, 22 Dec 2024 14:40:09 +0100 Subject: [PATCH 146/642] [AssetMapper] Update hash examples Since 7.2, hash are shorter. This PR updates the various hashes in code examples, to illustrate more precisely what a user will see on its local install / code. --- frontend/asset_mapper.rst | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index decc7827e0d..95b656f043d 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -12,7 +12,7 @@ The component has two main features: * :ref:`Mapping & Versioning Assets `: All files inside of ``assets/`` are made available publicly and **versioned**. You can reference the file ``assets/images/product.jpg`` in a Twig template with ``{{ asset('images/product.jpg') }}``. - The final URL will include a version hash, like ``/assets/images/product-3c16d9220694c0e56d8648f25e6035e9.jpg``. + The final URL will include a version hash, like ``/assets/images/product-3c16d92m.jpg``. * :ref:`Importmaps `: A native browser feature that makes it easier to use the JavaScript ``import`` statement (e.g. ``import { Modal } from 'bootstrap'``) @@ -70,7 +70,7 @@ The path - ``images/duck.png`` - is relative to your mapped directory (``assets/ This is known as the **logical path** to your asset. If you look at the HTML in your page, the URL will be something -like: ``/assets/images/duck-3c16d9220694c0e56d8648f25e6035e9.png``. If you change +like: ``/assets/images/duck-3c16d92m.png``. If you change the file, the version part of the URL will also change automatically. .. _asset-mapper-compile-assets: @@ -78,7 +78,7 @@ the file, the version part of the URL will also change automatically. Serving Assets in dev vs prod ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In the ``dev`` environment, the URL ``/assets/images/duck-3c16d9220694c0e56d8648f25e6035e9.png`` +In the ``dev`` environment, the URL ``/assets/images/duck-3c16d92m.png`` is handled and returned by your Symfony app. For the ``prod`` environment, before deploy, you should run: @@ -283,9 +283,9 @@ outputs an `importmap`_: @@ -342,8 +342,8 @@ The ``importmap()`` function also outputs a set of "preloads": .. code-block:: html - - + + This is a performance optimization and you can learn more about below in :ref:`Performance: Add Preloading `. @@ -494,9 +494,9 @@ for ``duck.png``: .. code-block:: css - /* public/assets/styles/app-3c16d9220694c0e56d8648f25e6035e9.css */ + /* public/assets/styles/app-3c16d92m.css */ .quack { - background-image: url('../images/duck-3c16d9220694c0e56d8648f25e6035e9.png'); + background-image: url('../images/duck-3c16d92m.png'); } .. _asset-mapper-tailwind: @@ -573,7 +573,7 @@ Sometimes a JavaScript file you're importing (e.g. ``import './duck.js'``), or a CSS/image file you're referencing won't be found, and you'll see a 404 error in your browser's console. You'll also notice that the 404 URL is missing the version hash in the filename (e.g. a 404 to ``/assets/duck.js`` instead of -a path like ``/assets/duck.1b7a64b3b3d31219c262cf72521a5267.js``). +a path like ``/assets/duck-1b7a64b3.js``). This is usually because the path is wrong. If you're referencing the file directly in a Twig template: @@ -848,7 +848,7 @@ be versioned! It will output something like: .. code-block:: html+twig - + Overriding 3rd-Party Assets ~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 47e65dfb47d70815d3bbe8e7c809c421b2ad4ec9 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 26 Dec 2024 11:17:56 +0100 Subject: [PATCH 147/642] Update the Symfony version to install in 7.3 branch --- setup.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.rst b/setup.rst index 853b53f2400..b269da2c476 100644 --- a/setup.rst +++ b/setup.rst @@ -48,10 +48,10 @@ application: .. code-block:: terminal # run this if you are building a traditional web application - $ symfony new my_project_directory --version="7.2.x" --webapp + $ symfony new my_project_directory --version="7.3.x-dev" --webapp # run this if you are building a microservice, console application or API - $ symfony new my_project_directory --version="7.2.x" + $ symfony new my_project_directory --version="7.3.x-dev" The only difference between these two commands is the number of packages installed by default. The ``--webapp`` option installs extra packages to give @@ -63,12 +63,12 @@ Symfony application using Composer: .. code-block:: terminal # run this if you are building a traditional web application - $ composer create-project symfony/skeleton:"7.2.x" my_project_directory + $ composer create-project symfony/skeleton:"7.3.x-dev" 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:"7.2.x" my_project_directory + $ composer create-project symfony/skeleton:"7.3.x-dev" 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 04790be521b670ddffbee654c55da3bc165dfd55 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Mon, 30 Dec 2024 08:38:13 +0100 Subject: [PATCH 148/642] [Validator] Validate SVG ratio in Image validator --- reference/constraints/Image.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/reference/constraints/Image.rst b/reference/constraints/Image.rst index 48dc1d5e0ed..23fbe5a157a 100644 --- a/reference/constraints/Image.rst +++ b/reference/constraints/Image.rst @@ -210,6 +210,10 @@ add several other options. If this option is false, the image cannot be landscape oriented. +.. versionadded:: 7.3 + + The ``allowLandscape`` option support for SVG files was introduced in Symfony 7.3. + ``allowLandscapeMessage`` ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -234,6 +238,10 @@ Parameter Description If this option is false, the image cannot be portrait oriented. +.. versionadded:: 7.3 + + The ``allowPortrait`` option support for SVG files was introduced in Symfony 7.3. + ``allowPortraitMessage`` ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -260,6 +268,10 @@ If this option is false, the image cannot be a square. If you want to force a square image, then leave this option as its default ``true`` value and set `allowLandscape`_ and `allowPortrait`_ both to ``false``. +.. versionadded:: 7.3 + + The ``allowSquare`` option support for SVG files was introduced in Symfony 7.3. + ``allowSquareMessage`` ~~~~~~~~~~~~~~~~~~~~~~ @@ -358,6 +370,10 @@ Parameter Description If set, the aspect ratio (``width / height``) of the image file must be less than or equal to this value. +.. versionadded:: 7.3 + + The ``maxRatio`` option support for SVG files was introduced in Symfony 7.3. + ``maxRatioMessage`` ~~~~~~~~~~~~~~~~~~~ @@ -477,6 +493,10 @@ Parameter Description If set, the aspect ratio (``width / height``) of the image file must be greater than or equal to this value. +.. versionadded:: 7.3 + + The ``minRatio`` option support for SVG files was introduced in Symfony 7.3. + ``minRatioMessage`` ~~~~~~~~~~~~~~~~~~~ From 3596f8c2b09f3380773a42a38b30f152fada315c Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 2 Jan 2025 12:59:47 +0100 Subject: [PATCH 149/642] Add ifFalse() --- components/config/definition.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/config/definition.rst b/components/config/definition.rst index 0e626931568..24c142ec5a5 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -815,6 +815,7 @@ A validation rule always has an "if" part. You can specify this part in the following ways: - ``ifTrue()`` +- ``ifFalse()`` - ``ifString()`` - ``ifNull()`` - ``ifEmpty()`` @@ -833,6 +834,10 @@ A validation rule also requires a "then" part: Usually, "then" is a closure. Its return value will be used as a new value for the node, instead of the node's original value. +.. versionadded:: 7.3 + + The ``ifFalse()`` method was introduced in Symfony 7.3. + Configuring the Node Path Separator ----------------------------------- From e1c23c76557c1bad82005f61effb685fba775c6c Mon Sep 17 00:00:00 2001 From: Nate Wiebe Date: Thu, 2 Jan 2025 10:20:16 -0500 Subject: [PATCH 150/642] Add docs for is_granted_for_user() function --- reference/twig_reference.rst | 21 +++++++++++++++++++++ security.rst | 11 +++++++++++ 2 files changed, 32 insertions(+) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index d8b802dd73e..4485494bd9c 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -174,6 +174,27 @@ Returns ``true`` if the current user has the given role. Optionally, an object can be passed to be used by the voter. More information can be found in :ref:`security-template`. +is_granted_for_user +~~~~~~~~~~~~~~~~~~~ + +.. code-block:: twig + + {{ is_granted_for_user(user, attribute, subject = null, field = null) }} + +``user`` + **type**: ``object`` +``attribute`` + **type**: ``string`` +``subject`` *(optional)* + **type**: ``object`` +``field`` *(optional)* + **type**: ``string`` + +Returns ``true`` if the user is authorized for the specified attribute. + +Optionally, an object can be passed to be used by the voter. More information +can be found in :ref:`security-template`. + logout_path ~~~~~~~~~~~ diff --git a/security.rst b/security.rst index 8026c63484b..7fe452e4fae 100644 --- a/security.rst +++ b/security.rst @@ -2549,6 +2549,17 @@ the built-in ``is_granted()`` helper function in any Twig template: .. _security-isgranted: +Similarly, if you want to check if a specific user has a certain role, you can use +the built-in ``is_granted_for_user()`` helper function: + +.. code-block:: html+twig + + {% if is_granted_for_user(user, 'ROLE_ADMIN') %} + Delete + {% endif %} + +.. _security-isgrantedforuser: + Securing other Services ....................... From ddf1ad3975a30bd1b3fce6528c4795646674508e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jan 2025 13:17:39 +0100 Subject: [PATCH 151/642] Add the missing versionadded directive --- reference/twig_reference.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 4485494bd9c..e01fa3f80e3 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -177,6 +177,10 @@ can be found in :ref:`security-template`. is_granted_for_user ~~~~~~~~~~~~~~~~~~~ +.. versionadded:: 7.3 + + The ``is_granted_for_user()`` function was introduced in Symfony 7.3. + .. code-block:: twig {{ is_granted_for_user(user, attribute, subject = null, field = null) }} From effffa3bb0bcdbb7016d7bd415cd23c4f01fd948 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jan 2025 14:55:02 +0100 Subject: [PATCH 152/642] Minor tweak --- frontend/asset_mapper.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index 81e91c1fc9d..d68c77c0105 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -747,11 +747,9 @@ and which file extensions should be compressed: Now, when running the ``asset-map:compile`` command, all matching files will be compressed in the configured format and at the highest compression level. The compressed files are created with the same name as the original but with the -``.br``, ``.zst``, or ``.gz`` extension appended. Web servers that support asset -precompression will use the compressed assets automatically, so there's nothing -else to configure in your server. +``.br``, ``.zst``, or ``.gz`` extension appended. -Finally, you need to configure your web server to serve the precompressed assets +Then, you need to configure your web server to serve the precompressed assets instead of the original ones: .. configuration-block:: From 9b65586c1ee8ec15873d290b6eaa01e8bd2dc062 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jan 2025 16:43:16 +0100 Subject: [PATCH 153/642] Fix build --- validation/custom_constraint.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/custom_constraint.rst b/validation/custom_constraint.rst index 990e1de9371..59da9e60568 100644 --- a/validation/custom_constraint.rst +++ b/validation/custom_constraint.rst @@ -66,7 +66,7 @@ Constraint with Private Properties ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Constraints are cached for performance reasons. To achieve this, the base -``Constraint`` class uses PHP's :phpfunction:`get_object_vars()` function, which +``Constraint`` class uses PHP's :phpfunction:`get_object_vars` function, which excludes private properties of child classes. If your constraint defines private properties, you must explicitly include them From 1272701ec6e1d15e9bf21d6ab55445d6f68838ba Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jan 2025 16:19:25 +0100 Subject: [PATCH 154/642] [DependencyInjection] Make #[AsTaggedItem] repeatable --- service_container/tags.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/service_container/tags.rst b/service_container/tags.rst index 270d6702f5a..bf50a191ba3 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -1281,4 +1281,19 @@ be used directly on the class of the service you want to configure:: // ... } +You can apply the ``#[AsTaggedItem]`` attribute multiple times to register the +same service under different indexes: + + #[AsTaggedItem(index: 'handler_one', priority: 5)] + #[AsTaggedItem(index: 'handler_two', priority: 20)] + class SomeService + { + // ... + } + +.. versionadded:: 7.3 + + The feature to apply the ``#[AsTaggedItem]`` attribute multiple times was + introduced in Symfony 7.3. + .. _`PHP constructor promotion`: https://www.php.net/manual/en/language.oop5.decon.php#language.oop5.decon.constructor.promotion From 9d4972de1863f3a028c6ce5d5e8d4ad2ac866201 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jan 2025 16:52:36 +0100 Subject: [PATCH 155/642] Minor reword --- security.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/security.rst b/security.rst index baa1a32a3a7..92367f29a88 100644 --- a/security.rst +++ b/security.rst @@ -2596,12 +2596,13 @@ want to include extra details only for users that have a ``ROLE_SALES_ADMIN`` ro // ... } + .. tip:: - Using ``isGranted()`` checks authorization against the currently logged in user. If you need to check - against a user that is not the one logged in or if checking authorization when the user session is not - available in a CLI context (example: message queue, cronjob) ``isGrantedForUser()`` can be used to set the - target user explicitly. + The ``isGranted()`` method checks authorization for the currently logged-in user. + If you need to check authorization for a different user or when the user session + is unavailable (e.g., in a CLI context such as a message queue or cron job), you + can use the ``isGrantedForUser()`` method to explicitly set the target user. .. versionadded:: 7.3 From 1afb8ebf3663a634108eac91b3d74ee20631d0af Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 3 Jan 2025 15:57:26 +0100 Subject: [PATCH 156/642] [HttpFoundation] Generate url-safe hashes for signed urls --- routing.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/routing.rst b/routing.rst index ef3287dd1be..4ab0dce2a82 100644 --- a/routing.rst +++ b/routing.rst @@ -2802,6 +2802,11 @@ argument of :method:`Symfony\\Component\\HttpFoundation\\UriSigner::sign`:: The feature to add an expiration date for a signed URI was introduced in Symfony 7.1. +.. versionadded:: 7.3 + + Starting with Symfony 7.3, signed URI hashes no longer include the ``/`` or + ``+`` characters, as these may cause issues with certain clients. + Troubleshooting --------------- From ab691630aaeeabe52ae7717be558faa560968169 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Mon, 6 Jan 2025 09:11:25 +0100 Subject: [PATCH 157/642] [Yaml] Add support for dumping `null` as an empty value by using the `Yaml::DUMP_NULL_AS_EMPTY` flag --- components/yaml.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/components/yaml.rst b/components/yaml.rst index 30f715a7a24..58436adffc2 100644 --- a/components/yaml.rst +++ b/components/yaml.rst @@ -428,6 +428,16 @@ you can dump them as ``~`` with the ``DUMP_NULL_AS_TILDE`` flag:: $dumped = Yaml::dump(['foo' => null], 2, 4, Yaml::DUMP_NULL_AS_TILDE); // foo: ~ +Another valid representation of the ``null`` value is an empty string. You can +use the ``DUMP_NULL_AS_EMPTY`` flag to dump null values as empty strings:: + + $dumped = Yaml::dump(['foo' => null], 2, 4, Yaml::DUMP_NULL_AS_EMPTY); + // foo: + +.. versionadded:: 7.3 + + The ``DUMP_NULL_AS_EMPTY`` flag was introduced in Symfony 7.3. + Dumping Numeric Keys as Strings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 7d7e77cafbabba22b2443f4edc9fc13e3aba1bdf Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 6 Jan 2025 20:48:52 +0100 Subject: [PATCH 158/642] [PropertyInfo] Add PropertyDescriptionExtractorInterface to PhpStanExtractor --- components/property_info.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/components/property_info.rst b/components/property_info.rst index 2e1ee42dd3f..0ff1768441a 100644 --- a/components/property_info.rst +++ b/components/property_info.rst @@ -469,7 +469,18 @@ information from annotations of properties and methods, such as ``@var``, use App\Domain\Foo; $phpStanExtractor = new PhpStanExtractor(); + + // Type information. $phpStanExtractor->getTypesFromConstructor(Foo::class, 'bar'); + // Description information. + $phpStanExtractor->getShortDescription($class, 'bar'); + $phpStanExtractor->getLongDescription($class, 'bar'); + +.. versionadded:: 7.3 + + The :method:`Symfony\\Component\\PropertyInfo\\Extractor\\PhpStanExtractor::getShortDescription` + and :method:`Symfony\\Component\\PropertyInfo\\Extractor\\PhpStanExtractor::getLongDescription` + methods were introduced in Symfony 7.3. SerializerExtractor ~~~~~~~~~~~~~~~~~~~ From 6d16cd8b4e41fa0b1686c44edb93b1f77023cd6f Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Mon, 6 Jan 2025 21:15:30 +0100 Subject: [PATCH 159/642] [Mailer] Add retry_period option for email transport --- mailer.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/mailer.rst b/mailer.rst index 4f0619147ad..8cc0d8641e3 100644 --- a/mailer.rst +++ b/mailer.rst @@ -331,6 +331,17 @@ The failover-transport starts using the first transport and if it fails, it will retry the same delivery with the next transports until one of them succeeds (or until all of them fail). +By default, the delivery will be retried 60 seconds after previous sending failed. +You can change the retry period by setting the ``retry_period`` option in the DSN: + +.. code-block:: env + + MAILER_DSN="failover(postmark+api://ID@default sendgrid+smtp://KEY@default)?retry_period=15" + +.. versionadded:: 7.3 + + The ``retry_period`` option was introduced in Symfony 7.3. + Load Balancing ~~~~~~~~~~~~~~ @@ -351,6 +362,17 @@ As with the failover transport, round-robin retries deliveries until a transport succeeds (or all fail). In contrast to the failover transport, it *spreads* the load across all its transports. +By default, the delivery will be retried 60 seconds after previous sending failed. +You can change the retry period by setting the ``retry_period`` option in the DSN: + +.. code-block:: env + + MAILER_DSN="roundrobin(postmark+api://ID@default sendgrid+smtp://KEY@default)?retry_period=15" + +.. versionadded:: 7.3 + + The ``retry_period`` option was introduced in Symfony 7.3. + TLS Peer Verification ~~~~~~~~~~~~~~~~~~~~~ From 7d94c8e8fc64032721af5415d8a3c0fe018ecf71 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 7 Jan 2025 08:42:04 +0100 Subject: [PATCH 160/642] Minor reword --- mailer.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mailer.rst b/mailer.rst index 8cc0d8641e3..4fd02eb84d7 100644 --- a/mailer.rst +++ b/mailer.rst @@ -331,8 +331,8 @@ The failover-transport starts using the first transport and if it fails, it will retry the same delivery with the next transports until one of them succeeds (or until all of them fail). -By default, the delivery will be retried 60 seconds after previous sending failed. -You can change the retry period by setting the ``retry_period`` option in the DSN: +By default, delivery is retried 60 seconds after a failed attempt. You can adjust +the retry period by setting the ``retry_period`` option in the DSN: .. code-block:: env @@ -362,8 +362,8 @@ As with the failover transport, round-robin retries deliveries until a transport succeeds (or all fail). In contrast to the failover transport, it *spreads* the load across all its transports. -By default, the delivery will be retried 60 seconds after previous sending failed. -You can change the retry period by setting the ``retry_period`` option in the DSN: +By default, delivery is retried 60 seconds after a failed attempt. You can adjust +the retry period by setting the ``retry_period`` option in the DSN: .. code-block:: env From d1400d406951f664bd159e38eab33c5a93cb73e9 Mon Sep 17 00:00:00 2001 From: Iker Ibarguren Date: Tue, 7 Jan 2025 09:51:40 +0100 Subject: [PATCH 161/642] Update notifier.rst Brevo third-party service now supports webhooks. --- notifier.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifier.rst b/notifier.rst index 36fbd5ada89..5b3f12a67fa 100644 --- a/notifier.rst +++ b/notifier.rst @@ -76,7 +76,7 @@ Service **Webhook support**: No `Brevo`_ **Install**: ``composer require symfony/brevo-notifier`` \ **DSN**: ``brevo://API_KEY@default?sender=SENDER`` \ - **Webhook support**: No + **Webhook support**: Yes `Clickatell`_ **Install**: ``composer require symfony/clickatell-notifier`` \ **DSN**: ``clickatell://ACCESS_TOKEN@default?from=FROM`` \ **Webhook support**: No From e4f8ff86daa93eab17172a0fd001caccd5c93e55 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 7 Jan 2025 12:04:07 +0100 Subject: [PATCH 162/642] Add a versionadded directive --- notifier.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/notifier.rst b/notifier.rst index 5b3f12a67fa..090f4db2007 100644 --- a/notifier.rst +++ b/notifier.rst @@ -236,6 +236,10 @@ Service The ``Primotexto``, ``Sipgate`` and ``Sweego`` integrations were introduced in Symfony 7.2. +.. versionadded:: 7.3 + + Webhook support for the ``Brevo`` integration was introduced in Symfony 7.3. + .. deprecated:: 7.1 The `Sms77`_ integration is deprecated since From 17a46adc143905ee469e48118039c7e8f66760b5 Mon Sep 17 00:00:00 2001 From: Maxime Doutreluingne Date: Sun, 5 Jan 2025 17:34:40 +0100 Subject: [PATCH 163/642] [Validator] [DateTime] Add ``format`` to error messages --- reference/constraints/DateTime.rst | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/reference/constraints/DateTime.rst b/reference/constraints/DateTime.rst index f6bcce7e5f5..ffcfbf55dda 100644 --- a/reference/constraints/DateTime.rst +++ b/reference/constraints/DateTime.rst @@ -99,11 +99,16 @@ This message is shown if the underlying data is not a valid datetime. You can use the following parameters in this message: -=============== ============================================================== -Parameter Description -=============== ============================================================== -``{{ value }}`` The current (invalid) value -``{{ label }}`` Corresponding form field label -=============== ============================================================== +================ ============================================================== +Parameter Description +================ ============================================================== +``{{ value }}`` The current (invalid) value +``{{ label }}`` Corresponding form field label +``{{ format }}`` The date format defined in ``format`` +================ ============================================================== + +.. versionadded:: 7.3 + + The ``{{ format }}`` parameter was introduced in Symfony 7.3. .. include:: /reference/constraints/_payload-option.rst.inc From 45113124f7b2affdcceae4460f218b4efc740839 Mon Sep 17 00:00:00 2001 From: tcoch Date: Mon, 6 Jan 2025 09:22:34 +0100 Subject: [PATCH 164/642] [String] Add `AbstractString::pascal()` method --- string.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/string.rst b/string.rst index 43d3a236ab6..e51e7d1b502 100644 --- a/string.rst +++ b/string.rst @@ -234,8 +234,10 @@ Methods to Change Case u('Foo: Bar-baz.')->snake(); // 'foo_bar_baz' // changes all graphemes/code points to kebab-case u('Foo: Bar-baz.')->kebab(); // 'foo-bar-baz' - // other cases can be achieved by chaining methods. E.g. PascalCase: - u('Foo: Bar-baz.')->camel()->title(); // 'FooBarBaz' + // changes all graphemes/code points to PascalCase + u('Foo: Bar-baz.')->pascal(); // 'FooBarBaz' + // other cases can be achieved by chaining methods, e.g. : + u('Foo: Bar-baz.')->camel()->upper(); // 'FOOBARBAZ' .. versionadded:: 7.1 @@ -246,6 +248,10 @@ Methods to Change Case The ``kebab()`` method was introduced in Symfony 7.2. +.. versionadded:: 7.3 + + The ``pascal()`` method was introduced in Symfony 7.3. + The methods of all string classes are case-sensitive by default. You can perform case-insensitive operations with the ``ignoreCase()`` method:: From 951f461e329cb0c8f275ca9100b2a5789844afec Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Tue, 7 Jan 2025 16:08:07 +0100 Subject: [PATCH 165/642] [TypeInfo] Add `accepts` method --- components/type_info.rst | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/components/type_info.rst b/components/type_info.rst index e7062c5f249..70280d9a348 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -114,7 +114,7 @@ Advanced Usages The TypeInfo component provides various methods to manipulate and check types, depending on your needs. -Checking a **simple type**:: +**Identify** a type:: // define a simple integer type $type = Type::int(); @@ -141,6 +141,23 @@ Checking a **simple type**:: $type->isIdentifiedBy(DummyParent::class); // true $type->isIdentifiedBy(DummyInterface::class); // true +Checking if a type **accepts a value**:: + + $type = Type::int(); + // check if the type accepts a given value + $type->accepts(123); // true + $type->accepts('z'); // false + + $type = Type::union(Type::string(), Type::int()); + // now the second check is true because the union type accepts either a int and a string value + $type->accepts(123); // true + $type->accepts('z'); // true + +.. versionadded:: 7.3 + + The :method:`Symfony\\Component\\TypeInfo\\Type::accepts` + method was introduced in Symfony 7.3. + Using callables for **complex checks**:: class Foo From b4f8f6bf5f596664eb36c6311107b0b0056fd166 Mon Sep 17 00:00:00 2001 From: tcoch Date: Mon, 6 Jan 2025 17:22:10 +0100 Subject: [PATCH 166/642] [Validator] Add Slug constraint --- reference/constraints/Slug.rst | 119 ++++++++++++++++++++++++++++++ reference/constraints/map.rst.inc | 1 + 2 files changed, 120 insertions(+) create mode 100644 reference/constraints/Slug.rst diff --git a/reference/constraints/Slug.rst b/reference/constraints/Slug.rst new file mode 100644 index 00000000000..5b7661b4aba --- /dev/null +++ b/reference/constraints/Slug.rst @@ -0,0 +1,119 @@ +Slug +==== + +.. versionadded:: 7.3 + + The ``Slug`` constraint was introduced in Symfony 7.3. + +Validates that a value is a slug. +A slug is a string that, by default, matches the regex ``/^[a-z0-9]+(?:-[a-z0-9]+)*$/``. + +.. include:: /reference/constraints/_empty-values-are-valid.rst.inc + +========== =================================================================== +Applies to :ref:`property or method ` +Class :class:`Symfony\\Component\\Validator\\Constraints\\Slug` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\SlugValidator` +========== =================================================================== + +Basic Usage +----------- + +The ``Slug`` constraint can be applied to a property or a "getter" method: + +.. configuration-block:: + + .. code-block:: php-attributes + + // src/Entity/Author.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Author + { + #[Assert\Slug] + protected string $slug; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Entity\Author: + properties: + slug: + - Slug: ~ + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // src/Entity/Author.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class Author + { + // ... + + public static function loadValidatorMetadata(ClassMetadata $metadata): void + { + $metadata->addPropertyConstraint('slug', new Assert\Slug()); + } + } + +Examples of valid values : + +* foobar +* foo-bar +* foo123 +* foo-123bar + +Upper case characters would result in an violation of this constraint. + +Options +------- + +``regex`` +~~~~~~~~~ + +**type**: ``string`` default: ``/^[a-z0-9]+(?:-[a-z0-9]+)*$/`` + +This option allows you to modify the regular expression pattern that the input will be matched against +via the :phpfunction:`preg_match` PHP function. + +If you need to use it, you might also want to take a look at the :doc:`Regex constraint `. + +``message`` +~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This value is not a valid slug`` + +This is the message that will be shown if this validator fails. + +You can use the following parameters in this message: + +================= ============================================================== +Parameter Description +================= ============================================================== +``{{ value }}`` The current (invalid) value +================= ============================================================== + +.. include:: /reference/constraints/_groups-option.rst.inc + +.. include:: /reference/constraints/_payload-option.rst.inc diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index f23f5b71aa3..58801a8c653 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -33,6 +33,7 @@ String Constraints * :doc:`NotCompromisedPassword ` * :doc:`PasswordStrength ` * :doc:`Regex ` +* :doc:`Slug ` * :doc:`Ulid ` * :doc:`Url ` * :doc:`UserPassword ` From d688f6312c64050123f0c27b0871f80ab63b677c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 7 Jan 2025 17:56:09 +0100 Subject: [PATCH 167/642] Minor tweaks --- reference/constraints/Slug.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/reference/constraints/Slug.rst b/reference/constraints/Slug.rst index 5b7661b4aba..2eb82cd9c10 100644 --- a/reference/constraints/Slug.rst +++ b/reference/constraints/Slug.rst @@ -5,8 +5,8 @@ Slug The ``Slug`` constraint was introduced in Symfony 7.3. -Validates that a value is a slug. -A slug is a string that, by default, matches the regex ``/^[a-z0-9]+(?:-[a-z0-9]+)*$/``. +Validates that a value is a slug. By default, a slug is a string that matches +the following regular expression: ``/^[a-z0-9]+(?:-[a-z0-9]+)*$/``. .. include:: /reference/constraints/_empty-values-are-valid.rst.inc @@ -19,7 +19,7 @@ Validator :class:`Symfony\\Component\\Validator\\Constraints\\SlugValidator` Basic Usage ----------- -The ``Slug`` constraint can be applied to a property or a "getter" method: +The ``Slug`` constraint can be applied to a property or a getter method: .. configuration-block:: @@ -77,14 +77,14 @@ The ``Slug`` constraint can be applied to a property or a "getter" method: } } -Examples of valid values : +Examples of valid values: * foobar * foo-bar * foo123 * foo-123bar -Upper case characters would result in an violation of this constraint. +Uppercase characters would result in an violation of this constraint. Options ------- @@ -94,8 +94,8 @@ Options **type**: ``string`` default: ``/^[a-z0-9]+(?:-[a-z0-9]+)*$/`` -This option allows you to modify the regular expression pattern that the input will be matched against -via the :phpfunction:`preg_match` PHP function. +This option allows you to modify the regular expression pattern that the input +will be matched against via the :phpfunction:`preg_match` PHP function. If you need to use it, you might also want to take a look at the :doc:`Regex constraint `. From 693b758160d580d9e5bbdb6e0e8b4310baf2d5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Wed, 8 Jan 2025 06:29:55 +0100 Subject: [PATCH 168/642] [Security] Remove mention of is_granted_ `$field` argument The third argument of is_granted / is_granted_for_user is undocumented (except on this page) (neither on Symfony docs, code, nor on... Symfony ACL Bundle docs). It must be kept and maintained for BC reasons. But it can be missleading/frustrating to show this argument to users, when they cannot use it with a standard/recommended Symfony installation. And if they just test it, the error could lead them to believe the AclBundle needs to be installed to use the `is_granted` function(s). As suggested in https://github.com/symfony/symfony/pull/59282#issuecomment-2569716817 I believe "not showing it" is the "best" solution here. --- reference/twig_reference.rst | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index e01fa3f80e3..acbab2f3817 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -160,14 +160,12 @@ is_granted .. code-block:: twig - {{ is_granted(role, object = null, field = null) }} + {{ is_granted(role, object = null) }} ``role`` **type**: ``string`` ``object`` *(optional)* **type**: ``object`` -``field`` *(optional)* - **type**: ``string`` Returns ``true`` if the current user has the given role. @@ -183,7 +181,7 @@ is_granted_for_user .. code-block:: twig - {{ is_granted_for_user(user, attribute, subject = null, field = null) }} + {{ is_granted_for_user(user, attribute, subject = null) }} ``user`` **type**: ``object`` @@ -191,8 +189,6 @@ is_granted_for_user **type**: ``string`` ``subject`` *(optional)* **type**: ``object`` -``field`` *(optional)* - **type**: ``string`` Returns ``true`` if the user is authorized for the specified attribute. From 23800a97fa5114849320dac166b80ff97b3d2d46 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 8 Jan 2025 10:18:55 +0100 Subject: [PATCH 169/642] Minor tweak --- components/type_info.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/type_info.rst b/components/type_info.rst index 70280d9a348..47fe9dfd9ba 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -149,7 +149,7 @@ Checking if a type **accepts a value**:: $type->accepts('z'); // false $type = Type::union(Type::string(), Type::int()); - // now the second check is true because the union type accepts either a int and a string value + // now the second check is true because the union type accepts either an int or a string value $type->accepts(123); // true $type->accepts('z'); // true From cf53d1914e874def9e39324e6c5ad32e0b441f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20L=C3=B8vgaard?= Date: Wed, 8 Jan 2025 12:06:09 +0100 Subject: [PATCH 170/642] Fix typo --- components/type_info.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/type_info.rst b/components/type_info.rst index e7062c5f249..2a92a3db63e 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -106,7 +106,7 @@ PHP package required for string resolving. Then, follow these steps:: $typeResolver = TypeResolver::create(); $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns an "int" Type - $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns a collection with "int" as key and "string" as values Type + $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'tags')); // returns a collection with "int" as key and "string" as values Type Advanced Usages ~~~~~~~~~~~~~~~ From ed80f8b78a6714ed0c092e7cf90eb98afecf75be Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 8 Jan 2025 22:05:23 +0100 Subject: [PATCH 171/642] revert webhook support for the Brevo Notifier bridge --- notifier.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notifier.rst b/notifier.rst index 090f4db2007..f74ff92009e 100644 --- a/notifier.rst +++ b/notifier.rst @@ -76,7 +76,7 @@ Service **Webhook support**: No `Brevo`_ **Install**: ``composer require symfony/brevo-notifier`` \ **DSN**: ``brevo://API_KEY@default?sender=SENDER`` \ - **Webhook support**: Yes + **Webhook support**: No `Clickatell`_ **Install**: ``composer require symfony/clickatell-notifier`` \ **DSN**: ``clickatell://ACCESS_TOKEN@default?from=FROM`` \ **Webhook support**: No From fb028aa7e77e8a2324b3b65e4e9823045d26ef73 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 9 Jan 2025 12:24:39 +0100 Subject: [PATCH 172/642] Remove an uneeded versionadded directive --- notifier.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/notifier.rst b/notifier.rst index f74ff92009e..36fbd5ada89 100644 --- a/notifier.rst +++ b/notifier.rst @@ -236,10 +236,6 @@ Service The ``Primotexto``, ``Sipgate`` and ``Sweego`` integrations were introduced in Symfony 7.2. -.. versionadded:: 7.3 - - Webhook support for the ``Brevo`` integration was introduced in Symfony 7.3. - .. deprecated:: 7.1 The `Sms77`_ integration is deprecated since From f8667f57029ad20b615c6c7f42d13bc02df86fa5 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 6 Jan 2025 21:16:06 +0100 Subject: [PATCH 173/642] [OptionsResolver] Support union of types --- components/options_resolver.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index ff25f9e0fc4..266911bf13d 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -305,13 +305,20 @@ correctly. To validate the types of the options, call // specify multiple allowed types $resolver->setAllowedTypes('port', ['null', 'int']); + // which is similar to + $resolver->setAllowedTypes('port', 'int|null'); // check all items in an array recursively for a type $resolver->setAllowedTypes('dates', 'DateTime[]'); $resolver->setAllowedTypes('ports', 'int[]'); + $resolver->setAllowedTypes('endpoints', '(int|string)[]'); } } +.. versionadded:: 7.3 + + Union of type, using the ``|`` syntax, was introduced in Symfony 7.3. + You can pass any type for which an ``is_()`` function is defined in PHP. You may also pass fully qualified class or interface names (which is checked using ``instanceof``). Additionally, you can validate all items in an array From ca50eeb570eb13b2c35b7fa02f199bfc509755d8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 9 Jan 2025 15:29:40 +0100 Subject: [PATCH 174/642] Minor tweaks --- components/options_resolver.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 266911bf13d..95741a7e372 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -305,19 +305,20 @@ correctly. To validate the types of the options, call // specify multiple allowed types $resolver->setAllowedTypes('port', ['null', 'int']); - // which is similar to + // if you prefer, you can also use the following equivalent syntax $resolver->setAllowedTypes('port', 'int|null'); // check all items in an array recursively for a type $resolver->setAllowedTypes('dates', 'DateTime[]'); $resolver->setAllowedTypes('ports', 'int[]'); + // the following syntax means "an array of integers or an array of strings" $resolver->setAllowedTypes('endpoints', '(int|string)[]'); } } .. versionadded:: 7.3 - Union of type, using the ``|`` syntax, was introduced in Symfony 7.3. + Defining type unions with the ``|`` syntax was introduced in Symfony 7.3. You can pass any type for which an ``is_()`` function is defined in PHP. You may also pass fully qualified class or interface names (which is checked From c532aed720a486a85ba26209cf7ad4185db5c0ec Mon Sep 17 00:00:00 2001 From: chx Date: Mon, 6 Jan 2025 14:36:23 +0100 Subject: [PATCH 175/642] [DependencyInjection] Documented the new @> shorthand --- service_container/service_closures.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/service_container/service_closures.rst b/service_container/service_closures.rst index cedbaaa2bf9..73dc17961fd 100644 --- a/service_container/service_closures.rst +++ b/service_container/service_closures.rst @@ -52,6 +52,14 @@ argument of type ``service_closure``: # In case the dependency is optional # arguments: [!service_closure '@?mailer'] +.. versionadded:: 7.3 + + # A shorthand is available + # arguments: ['@>mailer'] + + # It also works when the dependency is optional + # arguments: ['@>?mailer'] + .. code-block:: xml From 250c35b812814ed105d1790213b9938a2e244666 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 10 Jan 2025 16:08:17 +0100 Subject: [PATCH 176/642] Reword --- service_container/service_closures.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/service_container/service_closures.rst b/service_container/service_closures.rst index 73dc17961fd..88b0ab64002 100644 --- a/service_container/service_closures.rst +++ b/service_container/service_closures.rst @@ -52,12 +52,11 @@ argument of type ``service_closure``: # In case the dependency is optional # arguments: [!service_closure '@?mailer'] -.. versionadded:: 7.3 - - # A shorthand is available - # arguments: ['@>mailer'] + # you can also use the special '@>' syntax as a shortcut of '!service_closure' + App\Service\AnotherService: + arguments: ['@>mailer'] - # It also works when the dependency is optional + # the shortcut also works for optional dependencies # arguments: ['@>?mailer'] .. code-block:: xml @@ -98,6 +97,10 @@ argument of type ``service_closure``: // ->args([service_closure('mailer')->ignoreOnInvalid()]); }; +.. versionadded:: 7.3 + + The ``@>`` shortcut syntax for YAML was introduced in Symfony 7.3. + .. seealso:: Service closures can be injected :ref:`by using autowiring ` From c880a8818fa495a6b34188bddd76b946e54e6d3a Mon Sep 17 00:00:00 2001 From: Javier Spagnoletti Date: Sat, 11 Jan 2025 03:14:50 -0300 Subject: [PATCH 177/642] [Doctrine] Use PDO constants in XML configuration example --- reference/configuration/doctrine.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index cd1852710e7..25621f60493 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -507,9 +507,9 @@ set up the connection using environment variables for the certificate paths: server-version="8.0.31" driver="pdo_mysql"> - %env(MYSQL_SSL_KEY)% - %env(MYSQL_SSL_CERT)% - %env(MYSQL_SSL_CA)% + %env(MYSQL_SSL_KEY)% + %env(MYSQL_SSL_CERT)% + %env(MYSQL_SSL_CA)% From 207933321946e82e537eece0022f2045e22c0ff8 Mon Sep 17 00:00:00 2001 From: Maxime Doutreluingne Date: Sun, 12 Jan 2025 11:09:02 +0100 Subject: [PATCH 178/642] [FrameworkBundle] Remove ``--show-arguments`` option for ``debug:container`` --- controller/value_resolver.rst | 2 +- service_container/debug.rst | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/controller/value_resolver.rst b/controller/value_resolver.rst index dbbea7bcc87..1844ff0c9be 100644 --- a/controller/value_resolver.rst +++ b/controller/value_resolver.rst @@ -396,7 +396,7 @@ command to see which argument resolvers are present and in which order they run: .. code-block:: terminal - $ php bin/console debug:container debug.argument_resolver.inner --show-arguments + $ php bin/console debug:container debug.argument_resolver.inner You can also configure the name passed to the ``ValueResolver`` attribute to target your resolver. Otherwise it will default to the service's id. diff --git a/service_container/debug.rst b/service_container/debug.rst index c09413e7213..0a7898108fb 100644 --- a/service_container/debug.rst +++ b/service_container/debug.rst @@ -51,6 +51,3 @@ its id: .. code-block:: terminal $ php bin/console debug:container App\Service\Mailer - - # to show the service arguments: - $ php bin/console debug:container App\Service\Mailer --show-arguments From e12afdd378e43587f1d9cfb06e8f5708146b2e76 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 13 Jan 2025 09:34:01 +0100 Subject: [PATCH 179/642] Add a versionadded directive --- service_container/debug.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/service_container/debug.rst b/service_container/debug.rst index 0a7898108fb..aab6fa9529f 100644 --- a/service_container/debug.rst +++ b/service_container/debug.rst @@ -51,3 +51,9 @@ its id: .. code-block:: terminal $ php bin/console debug:container App\Service\Mailer + +.. versionadded:: 7.3 + + Starting in Symfony 7.3, this command displays the service arguments by default. + In earlier Symfony versions, you needed to use the ``--show-arguments`` option, + which is now deprecated. From d46522ad4ae49ae8317ad5c762daac0227ed143a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 14 Jan 2025 09:53:02 +0100 Subject: [PATCH 180/642] Add more details about the context variable --- reference/constraints/When.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/reference/constraints/When.rst b/reference/constraints/When.rst index 12b43fc7d63..2a05e58ee9c 100644 --- a/reference/constraints/When.rst +++ b/reference/constraints/When.rst @@ -163,7 +163,7 @@ validation of constraints won't be triggered. To learn more about the expression language syntax, see :doc:`/reference/formats/expression_language`. -Depending on how you use the constraint, you have access to 1 or 3 variables +Depending on how you use the constraint, you have access to different variables in your expression: ``this`` @@ -172,7 +172,13 @@ in your expression: The value of the property being validated (only available when the constraint is applied to a property). ``context`` - This is the context that is used in validation + The :class:`Symfony\\Component\\Validator\\Context\\ExecutionContextInterface` + object that provides information such as the currently validated class, the + name of the currently validated property, the list of violations, etc. + +.. versionadded:: 7.2 + + The ``context`` variable in expressions was introduced in Symfony 7.2. The ``value`` variable can be used when you want to execute more complex validation based on its value: From 1c51901e95a0d05284e311df419bc9828dc983c7 Mon Sep 17 00:00:00 2001 From: Farhad Hedayatifard Date: Tue, 29 Oct 2024 21:10:14 +0330 Subject: [PATCH 181/642] [Mailer] feat(mailer): Add AhaSend Mailer doc --- .doctor-rst.yaml | 4 +- .github/workflows/ci.yaml | 2 +- _build/redirection_map | 5 +- .../serializer/serializer_workflow.svg | 0 .../serializer/serializer_workflow.dia | Bin bundles.rst | 6 +- bundles/best_practices.rst | 2 +- bundles/extension.rst | 2 +- bundles/override.rst | 2 +- cache.rst | 2 +- components/cache/adapters/apcu_adapter.rst | 4 +- .../adapters/couchbasebucket_adapter.rst | 2 +- .../adapters/couchbasecollection_adapter.rst | 2 +- .../cache/adapters/filesystem_adapter.rst | 2 +- .../cache/adapters/memcached_adapter.rst | 4 +- .../cache/adapters/php_files_adapter.rst | 2 +- components/cache/adapters/redis_adapter.rst | 84 +- components/clock.rst | 6 +- components/config/definition.rst | 24 +- .../console/changing_default_command.rst | 2 +- components/console/events.rst | 2 +- .../console/helpers/formatterhelper.rst | 14 +- components/console/helpers/progressbar.rst | 2 +- components/console/helpers/questionhelper.rst | 6 +- components/event_dispatcher.rst | 6 +- components/expression_language.rst | 8 +- components/finder.rst | 2 +- components/form.rst | 2 +- components/http_foundation.rst | 25 +- components/http_kernel.rst | 2 +- components/ldap.rst | 2 +- components/lock.rst | 38 +- components/options_resolver.rst | 4 +- components/phpunit_bridge.rst | 4 +- components/process.rst | 4 +- components/property_access.rst | 8 +- components/property_info.rst | 17 +- components/runtime.rst | 5 +- components/serializer.rst | 1938 -------------- components/type_info.rst | 136 +- components/uid.rst | 4 +- components/validator/resources.rst | 2 +- components/yaml.rst | 10 + configuration.rst | 8 +- configuration/env_var_processors.rst | 2 +- configuration/micro_kernel_trait.rst | 2 +- configuration/multiple_kernels.rst | 2 +- configuration/override_dir_structure.rst | 2 +- console.rst | 10 +- console/calling_commands.rst | 5 +- console/command_in_controller.rst | 2 +- console/commands_as_services.rst | 4 +- console/input.rst | 2 +- contributing/code/bc.rst | 8 +- contributing/code/bugs.rst | 2 +- contributing/code/maintenance.rst | 3 + contributing/documentation/format.rst | 2 +- contributing/documentation/standards.rst | 2 +- controller.rst | 32 +- controller/error_pages.rst | 2 +- deployment.rst | 2 +- deployment/proxies.rst | 2 +- doctrine.rst | 8 +- doctrine/custom_dql_functions.rst | 2 +- doctrine/multiple_entity_managers.rst | 6 +- event_dispatcher.rst | 11 + form/bootstrap5.rst | 6 +- form/create_custom_field_type.rst | 2 +- form/data_mappers.rst | 4 +- form/data_transformers.rst | 6 +- form/direct_submit.rst | 2 +- form/events.rst | 4 +- form/form_collections.rst | 6 +- form/form_customization.rst | 4 +- form/form_themes.rst | 2 +- form/inherit_data_option.rst | 2 +- form/type_guesser.rst | 2 +- form/unit_testing.rst | 6 +- form/without_class.rst | 2 +- forms.rst | 2 +- frontend.rst | 10 + frontend/asset_mapper.rst | 129 +- frontend/encore/installation.rst | 2 +- frontend/encore/simple-example.rst | 4 +- frontend/encore/virtual-machine.rst | 4 +- http_cache/cache_invalidation.rst | 2 +- http_cache/esi.rst | 2 +- http_cache/varnish.rst | 33 +- http_client.rst | 18 +- lock.rst | 2 +- logging/channels_handlers.rst | 2 +- logging/monolog_exclude_http_codes.rst | 2 +- mailer.rst | 62 +- mercure.rst | 93 +- messenger.rst | 29 +- notifier.rst | 85 +- profiler.rst | 4 +- reference/attributes.rst | 12 +- reference/configuration/framework.rst | 11 +- reference/configuration/web_profiler.rst | 2 +- reference/constraints/Bic.rst | 15 +- reference/constraints/Callback.rst | 8 +- reference/constraints/DateTime.rst | 17 +- reference/constraints/EqualTo.rst | 2 +- reference/constraints/File.rst | 9 +- reference/constraints/IdenticalTo.rst | 2 +- reference/constraints/Image.rst | 20 + reference/constraints/MacAddress.rst | 1 + reference/constraints/NotEqualTo.rst | 2 +- reference/constraints/NotIdenticalTo.rst | 2 +- reference/constraints/PasswordStrength.rst | 6 +- reference/constraints/Slug.rst | 119 + reference/constraints/UniqueEntity.rst | 6 +- reference/constraints/_payload-option.rst.inc | 2 - reference/constraints/map.rst.inc | 1 + reference/dic_tags.rst | 4 +- reference/formats/expression_language.rst | 2 +- reference/formats/message_format.rst | 2 +- reference/forms/types/choice.rst | 2 +- reference/forms/types/collection.rst | 6 +- reference/forms/types/country.rst | 2 +- reference/forms/types/currency.rst | 2 +- reference/forms/types/date.rst | 9 +- reference/forms/types/dateinterval.rst | 4 +- reference/forms/types/entity.rst | 2 +- reference/forms/types/language.rst | 2 +- reference/forms/types/locale.rst | 2 +- reference/forms/types/money.rst | 5 +- .../types/options/_date_limitation.rst.inc | 2 +- .../forms/types/options/choice_lazy.rst.inc | 2 +- .../forms/types/options/choice_name.rst.inc | 2 +- reference/forms/types/options/data.rst.inc | 2 +- .../options/empty_data_description.rst.inc | 2 +- .../forms/types/options/inherit_data.rst.inc | 2 +- reference/forms/types/options/value.rst.inc | 2 +- reference/forms/types/password.rst | 2 +- reference/forms/types/textarea.rst | 2 +- reference/forms/types/time.rst | 10 +- reference/forms/types/timezone.rst | 2 +- reference/forms/types/url.rst | 7 + reference/twig_reference.rst | 33 +- routing.rst | 78 +- scheduler.rst | 8 + security.rst | 37 +- security/access_control.rst | 6 +- security/access_token.rst | 4 +- security/custom_authenticator.rst | 27 +- security/impersonating_user.rst | 9 +- security/ldap.rst | 4 +- security/login_link.rst | 2 +- security/passwords.rst | 2 +- security/remember_me.rst | 25 +- security/user_providers.rst | 2 +- security/voters.rst | 31 +- serializer.rst | 2331 ++++++++++++++--- serializer/custom_context_builders.rst | 8 +- serializer/custom_encoders.rst | 61 - serializer/custom_name_converter.rst | 112 + serializer/custom_normalizer.rst | 66 +- serializer/encoders.rst | 373 +++ service_container.rst | 4 +- service_container/compiler_passes.rst | 80 +- service_container/definitions.rst | 4 +- service_container/lazy_services.rst | 2 +- service_container/service_decoration.rst | 76 +- .../service_subscribers_locators.rst | 2 +- service_container/tags.rst | 17 +- session.rst | 26 +- setup.rst | 8 +- setup/_update_all_packages.rst.inc | 2 +- setup/file_permissions.rst | 8 +- setup/symfony_server.rst | 8 +- setup/upgrade_major.rst | 2 +- setup/web_server_configuration.rst | 110 +- string.rst | 10 +- templates.rst | 4 +- testing.rst | 16 +- testing/end_to_end.rst | 2 +- testing/insulating_clients.rst | 2 +- translation.rst | 8 +- validation.rst | 2 +- validation/custom_constraint.rst | 41 + validation/groups.rst | 2 +- validation/sequence_provider.rst | 4 +- web_link.rst | 22 +- webhook.rst | 7 +- workflow.rst | 2 +- 187 files changed, 4032 insertions(+), 3028 deletions(-) rename _images/{components => }/serializer/serializer_workflow.svg (100%) rename _images/sources/{components => }/serializer/serializer_workflow.dia (100%) delete mode 100644 components/serializer.rst create mode 100644 reference/constraints/Slug.rst delete mode 100644 serializer/custom_encoders.rst create mode 100644 serializer/custom_name_converter.rst create mode 100644 serializer/encoders.rst diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml index 5ea86e6abc4..04e720bf653 100644 --- a/.doctor-rst.yaml +++ b/.doctor-rst.yaml @@ -23,6 +23,8 @@ rules: forbidden_directives: directives: - '.. index::' + - directive: '.. caution::' + replacements: ['.. warning::', '.. danger::'] indention: ~ lowercase_as_in_use_statements: ~ max_blank_lines: @@ -46,6 +48,7 @@ rules: no_namespace_after_use_statements: ~ no_php_open_tag_in_code_block_php_directive: ~ no_space_before_self_xml_closing_tag: ~ + non_static_phpunit_assertions: ~ only_backslashes_in_namespace_in_php_code_block: ~ only_backslashes_in_use_statements_in_php_code_block: ~ ordered_use_statements: ~ @@ -99,7 +102,6 @@ whitelist: - '#. The most important config file is ``app/config/services.yml``, which now is' - 'The bin/console Command' - '.. _`LDAP injection`: http://projects.webappsec.org/w/page/13246947/LDAP%20Injection' - - '.. versionadded:: 2.7.2' # Doctrine - '.. versionadded:: 2.8.0' # Doctrine - '.. versionadded:: 1.9.0' # Encore - '.. versionadded:: 1.18' # Flex in setup/upgrade_minor.rst diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index fcbdbe0477b..061b0bb85b0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -73,7 +73,7 @@ jobs: key: ${{ runner.os }}-doctor-rst-${{ steps.extract_base_branch.outputs.branch }} - name: "Run DOCtor-RST" - uses: docker://oskarstark/doctor-rst:1.62.3 + uses: docker://oskarstark/doctor-rst:1.64.0 with: args: --short --error-format=github --cache-file=/github/workspace/.cache/doctor-rst.cache diff --git a/_build/redirection_map b/_build/redirection_map index 115ec75ade2..1701f4a8f70 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -525,8 +525,7 @@ /testing/functional_tests_assertions /testing#testing-application-assertions /components https://symfony.com/components /components/index https://symfony.com/components -/serializer/normalizers /components/serializer#normalizers -/components/serializer#component-serializer-attributes-groups-annotations /components/serializer#component-serializer-attributes-groups-attributes +/serializer/normalizers /serializer#serializer-built-in-normalizers /logging/monolog_regex_based_excludes /logging/monolog_exclude_http_codes /security/named_encoders /security/named_hashers /components/inflector /string#inflector @@ -572,3 +571,5 @@ /doctrine/registration_form /security#security-make-registration-form /form/form_dependencies /form/create_custom_field_type /doctrine/reverse_engineering /doctrine#doctrine-adding-mapping +/components/serializer /serializer +/serializer/custom_encoder /serializer/encoders#serializer-custom-encoder diff --git a/_images/components/serializer/serializer_workflow.svg b/_images/serializer/serializer_workflow.svg similarity index 100% rename from _images/components/serializer/serializer_workflow.svg rename to _images/serializer/serializer_workflow.svg diff --git a/_images/sources/components/serializer/serializer_workflow.dia b/_images/sources/serializer/serializer_workflow.dia similarity index 100% rename from _images/sources/components/serializer/serializer_workflow.dia rename to _images/sources/serializer/serializer_workflow.dia diff --git a/bundles.rst b/bundles.rst index ba3a2209999..878bee3af4a 100644 --- a/bundles.rst +++ b/bundles.rst @@ -3,7 +3,7 @@ The Bundle System ================= -.. caution:: +.. warning:: In Symfony versions prior to 4.0, it was recommended to organize your own application code using bundles. This is :ref:`no longer recommended ` and bundles @@ -58,7 +58,7 @@ Start by creating a new class called ``AcmeBlogBundle``:: { } -.. caution:: +.. warning:: If your bundle must be compatible with previous Symfony versions you have to extend from the :class:`Symfony\\Component\\HttpKernel\\Bundle\\Bundle` instead. @@ -118,7 +118,7 @@ to be adjusted if needed: .. _bundles-legacy-directory-structure: -.. caution:: +.. warning:: The recommended bundle structure was changed in Symfony 5, read the `Symfony 4.4 bundle documentation`_ for information about the old diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst index 5996bcbe43d..376984388db 100644 --- a/bundles/best_practices.rst +++ b/bundles/best_practices.rst @@ -246,7 +246,7 @@ with Symfony Flex to install a specific Symfony version: # recommended to have a better output and faster download time) composer update --prefer-dist --no-progress -.. caution:: +.. warning:: If you want to cache your Composer dependencies, **do not** cache the ``vendor/`` directory as this has side-effects. Instead cache diff --git a/bundles/extension.rst b/bundles/extension.rst index 347f63b7af5..0537eb00c3e 100644 --- a/bundles/extension.rst +++ b/bundles/extension.rst @@ -200,7 +200,7 @@ Patterns are transformed into the actual class namespaces using the classmap generated by Composer. Therefore, before using these patterns, you must generate the full classmap executing the ``dump-autoload`` command of Composer. -.. caution:: +.. warning:: This technique can't be used when the classes to compile use the ``__DIR__`` or ``__FILE__`` constants, because their values will change when loading diff --git a/bundles/override.rst b/bundles/override.rst index 36aea69b231..f25bd785373 100644 --- a/bundles/override.rst +++ b/bundles/override.rst @@ -19,7 +19,7 @@ For example, to override the ``templates/registration/confirmed.html.twig`` template from the AcmeUserBundle, create this template: ``/templates/bundles/AcmeUserBundle/registration/confirmed.html.twig`` -.. caution:: +.. warning:: If you add a template in a new location, you *may* need to clear your cache (``php bin/console cache:clear``), even if you are in debug mode. diff --git a/cache.rst b/cache.rst index 7264585f233..833e4d77007 100644 --- a/cache.rst +++ b/cache.rst @@ -829,7 +829,7 @@ Then, register the ``SodiumMarshaller`` service using this key: //->addArgument(['env(base64:CACHE_DECRYPTION_KEY)', 'env(base64:OLD_CACHE_DECRYPTION_KEY)']) ->addArgument(new Reference('.inner')); -.. caution:: +.. danger:: This will encrypt the values of the cache items, but not the cache keys. Be careful not to leak sensitive data in the keys. diff --git a/components/cache/adapters/apcu_adapter.rst b/components/cache/adapters/apcu_adapter.rst index 99d76ce5d27..f2e92850cd8 100644 --- a/components/cache/adapters/apcu_adapter.rst +++ b/components/cache/adapters/apcu_adapter.rst @@ -5,7 +5,7 @@ This adapter is a high-performance, shared memory cache. It can *significantly* increase an application's performance, as its cache contents are stored in shared memory, a component appreciably faster than many others, such as the filesystem. -.. caution:: +.. warning:: **Requirement:** The `APCu extension`_ must be installed and active to use this adapter. @@ -30,7 +30,7 @@ and cache items version string as constructor arguments:: $version = null ); -.. caution:: +.. warning:: Use of this adapter is discouraged in write/delete heavy workloads, as these operations cause memory fragmentation that results in significantly degraded performance. diff --git a/components/cache/adapters/couchbasebucket_adapter.rst b/components/cache/adapters/couchbasebucket_adapter.rst index aaf400319f4..29c9e26f83c 100644 --- a/components/cache/adapters/couchbasebucket_adapter.rst +++ b/components/cache/adapters/couchbasebucket_adapter.rst @@ -14,7 +14,7 @@ shared memory; you can store contents independent of your PHP environment. The ability to utilize a cluster of servers to provide redundancy and/or fail-over is also available. -.. caution:: +.. warning:: **Requirements:** The `Couchbase PHP extension`_ as well as a `Couchbase server`_ must be installed, active, and running to use this adapter. Version ``2.6`` or diff --git a/components/cache/adapters/couchbasecollection_adapter.rst b/components/cache/adapters/couchbasecollection_adapter.rst index 25640a20b0f..ba78cc46eff 100644 --- a/components/cache/adapters/couchbasecollection_adapter.rst +++ b/components/cache/adapters/couchbasecollection_adapter.rst @@ -8,7 +8,7 @@ shared memory; you can store contents independent of your PHP environment. The ability to utilize a cluster of servers to provide redundancy and/or fail-over is also available. -.. caution:: +.. warning:: **Requirements:** The `Couchbase PHP extension`_ as well as a `Couchbase server`_ must be installed, active, and running to use this adapter. Version ``3.0`` or diff --git a/components/cache/adapters/filesystem_adapter.rst b/components/cache/adapters/filesystem_adapter.rst index 26ef48af27c..db877454859 100644 --- a/components/cache/adapters/filesystem_adapter.rst +++ b/components/cache/adapters/filesystem_adapter.rst @@ -33,7 +33,7 @@ and cache root path as constructor parameters:: $directory = null ); -.. caution:: +.. warning:: The overhead of filesystem IO often makes this adapter one of the *slower* choices. If throughput is paramount, the in-memory adapters diff --git a/components/cache/adapters/memcached_adapter.rst b/components/cache/adapters/memcached_adapter.rst index d68d3e3b9ac..64baf0d4702 100644 --- a/components/cache/adapters/memcached_adapter.rst +++ b/components/cache/adapters/memcached_adapter.rst @@ -8,7 +8,7 @@ shared memory; you can store contents independent of your PHP environment. The ability to utilize a cluster of servers to provide redundancy and/or fail-over is also available. -.. caution:: +.. warning:: **Requirements:** The `Memcached PHP extension`_ as well as a `Memcached server`_ must be installed, active, and running to use this adapter. Version ``2.2`` or @@ -256,7 +256,7 @@ Available Options executed in a "fire-and-forget" manner; no attempt to ensure the operation has been received or acted on will be made once the client has executed it. - .. caution:: + .. warning:: Not all library operations are tested in this mode. Mixed TCP and UDP servers are not allowed. diff --git a/components/cache/adapters/php_files_adapter.rst b/components/cache/adapters/php_files_adapter.rst index efd2cf0e964..6f171f0fede 100644 --- a/components/cache/adapters/php_files_adapter.rst +++ b/components/cache/adapters/php_files_adapter.rst @@ -28,7 +28,7 @@ file similar to the following:: handles file includes, this adapter has the potential to be much faster than other filesystem-based caches. -.. caution:: +.. warning:: While it supports updates and because it is using OPcache as a backend, this adapter is better suited for append-mostly needs. Using it in other scenarios might lead to diff --git a/components/cache/adapters/redis_adapter.rst b/components/cache/adapters/redis_adapter.rst index 719d6056f19..3362f4cc2db 100644 --- a/components/cache/adapters/redis_adapter.rst +++ b/components/cache/adapters/redis_adapter.rst @@ -15,7 +15,7 @@ Unlike the :doc:`APCu adapter `, and si shared memory; you can store contents independent of your PHP environment. The ability to utilize a cluster of servers to provide redundancy and/or fail-over is also available. -.. caution:: +.. warning:: **Requirements:** At least one `Redis server`_ must be installed and running to use this adapter. Additionally, this adapter requires a compatible extension or library that implements @@ -38,7 +38,13 @@ as the second and third parameters:: // the default lifetime (in seconds) for cache items that do not define their // own lifetime, with a value 0 causing items to be stored indefinitely (i.e. // until RedisAdapter::clear() is invoked or the server(s) are purged) - $defaultLifetime = 0 + $defaultLifetime = 0, + + // $marshaller (optional) An instance of MarshallerInterface to control the serialization + // and deserialization of cache items. By default, native PHP serialization is used. + // This can be useful for compressing data, applying custom serialization logic, or + // optimizing the size and performance of cached items + ?MarshallerInterface $marshaller = null ); Configure the Connection @@ -266,6 +272,80 @@ performance when using tag-based invalidation:: Read more about this topic in the official `Redis LRU Cache Documentation`_. +Working with Marshaller +----------------------- + +TagAwareMarshaller for Tag-Based Caching +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Optimizes caching for tag-based retrieval, allowing efficient management of related items:: + + $marshaller = new TagAwareMarshaller(); + + $cache = new RedisAdapter($redis, 'tagged_namespace', 3600, $marshaller); + + $item = $cache->getItem('tagged_key'); + $item->set(['value' => 'some_data', 'tags' => ['tag1', 'tag2']]); + $cache->save($item); + +SodiumMarshaller for Encrypted Caching +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Encrypts cached data using Sodium for enhanced security:: + + $encryptionKeys = [sodium_crypto_box_keypair()]; + $marshaller = new SodiumMarshaller($encryptionKeys); + + $cache = new RedisAdapter($redis, 'secure_namespace', 3600, $marshaller); + + $item = $cache->getItem('secure_key'); + $item->set('confidential_data'); + $cache->save($item); + +DefaultMarshaller with igbinary Serialization +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Uses ``igbinary` for faster and more efficient serialization when available:: + + $marshaller = new DefaultMarshaller(true); + + $cache = new RedisAdapter($redis, 'optimized_namespace', 3600, $marshaller); + + $item = $cache->getItem('optimized_key'); + $item->set(['data' => 'optimized_data']); + $cache->save($item); + +DefaultMarshaller with Exception on Failure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Throws an exception if serialization fails, facilitating error handling:: + + $marshaller = new DefaultMarshaller(false, true); + + $cache = new RedisAdapter($redis, 'error_namespace', 3600, $marshaller); + + try { + $item = $cache->getItem('error_key'); + $item->set('data'); + $cache->save($item); + } catch (\ValueError $e) { + echo 'Serialization failed: '.$e->getMessage(); + } + +SodiumMarshaller with Key Rotation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Supports key rotation, ensuring secure decryption with both old and new keys:: + + $keys = [sodium_crypto_box_keypair(), sodium_crypto_box_keypair()]; + $marshaller = new SodiumMarshaller($keys); + + $cache = new RedisAdapter($redis, 'rotated_namespace', 3600, $marshaller); + + $item = $cache->getItem('rotated_key'); + $item->set('data_to_encrypt'); + $cache->save($item); + .. _`Data Source Name (DSN)`: https://en.wikipedia.org/wiki/Data_source_name .. _`Redis server`: https://redis.io/ .. _`Redis`: https://github.com/phpredis/phpredis diff --git a/components/clock.rst b/components/clock.rst index cdbbdd56e6b..5b20e6000b9 100644 --- a/components/clock.rst +++ b/components/clock.rst @@ -129,18 +129,18 @@ is expired or not, by modifying the clock's time:: $validUntil = new DateTimeImmutable('2022-11-16 15:25:00'); // $validUntil is in the future, so it is not expired - static::assertFalse($expirationChecker->isExpired($validUntil)); + $this->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 (600 seconds) // modify the clock, accepts all formats supported by DateTimeImmutable::modify() - static::assertTrue($expirationChecker->isExpired($validUntil)); + $this->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)); + $this->assertFalse($expirationChecker->isExpired($validUntil)); } } diff --git a/components/config/definition.rst b/components/config/definition.rst index 929246fa915..24c142ec5a5 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -83,9 +83,19 @@ reflect the real structure of the configuration values:: ->scalarNode('default_connection') ->defaultValue('mysql') ->end() + ->stringNode('username') + ->defaultValue('root') + ->end() + ->stringNode('password') + ->defaultValue('root') + ->end() ->end() ; +.. versionadded:: 7.2 + + The ``stringNode()`` method was introduced in Symfony 7.2. + The root node itself is an array node, and has children, like the boolean node ``auto_connect`` and the scalar node ``default_connection``. In general: after defining a node, a call to ``end()`` takes you one step up in the @@ -100,6 +110,7 @@ node definition. Node types are available for: * scalar (generic type that includes booleans, strings, integers, floats and ``null``) * boolean +* string * integer * float * enum (similar to scalar, but it only allows a finite set of values) @@ -109,6 +120,10 @@ node definition. Node types are available for: and are created with ``node($name, $type)`` or their associated shortcut ``xxxxNode($name)`` method. +.. versionadded:: 7.2 + + Support for the ``string`` type was introduced in Symfony 7.2. + Numeric Node Constraints ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -670,7 +685,7 @@ The separator used in keys is typically ``_`` in YAML and ``-`` in XML. For example, ``auto_connect`` in YAML and ``auto-connect`` in XML. The normalization would make both of these ``auto_connect``. -.. caution:: +.. warning:: The target key will not be altered if it's mixed like ``foo-bar_moo`` or if it already exists. @@ -800,6 +815,7 @@ A validation rule always has an "if" part. You can specify this part in the following ways: - ``ifTrue()`` +- ``ifFalse()`` - ``ifString()`` - ``ifNull()`` - ``ifEmpty()`` @@ -818,6 +834,10 @@ A validation rule also requires a "then" part: Usually, "then" is a closure. Its return value will be used as a new value for the node, instead of the node's original value. +.. versionadded:: 7.3 + + The ``ifFalse()`` method was introduced in Symfony 7.3. + Configuring the Node Path Separator ----------------------------------- @@ -889,7 +909,7 @@ Otherwise the result is a clean array of configuration values:: $configs ); -.. caution:: +.. warning:: When processing the configuration tree, the processor assumes that the top level array key (which matches the extension name) is already stripped off. diff --git a/components/console/changing_default_command.rst b/components/console/changing_default_command.rst index b739e3b39ba..c69995ea395 100644 --- a/components/console/changing_default_command.rst +++ b/components/console/changing_default_command.rst @@ -52,7 +52,7 @@ This will print the following to the command line: Hello World -.. caution:: +.. warning:: This feature has a limitation: you cannot pass any argument or option to the default command because they are ignored. diff --git a/components/console/events.rst b/components/console/events.rst index f0edf2205ac..e550025b7dd 100644 --- a/components/console/events.rst +++ b/components/console/events.rst @@ -14,7 +14,7 @@ the wheel, it uses the Symfony EventDispatcher component to do the work:: $application->setDispatcher($dispatcher); $application->run(); -.. caution:: +.. warning:: Console events are only triggered by the main command being executed. Commands called by the main command will not trigger any event, unless diff --git a/components/console/helpers/formatterhelper.rst b/components/console/helpers/formatterhelper.rst index 3cb87c4c307..d2b19915a3a 100644 --- a/components/console/helpers/formatterhelper.rst +++ b/components/console/helpers/formatterhelper.rst @@ -129,10 +129,16 @@ Sometimes you want to format seconds to time. This is possible with the The first argument is the seconds to format and the second argument is the precision (default ``1``) of the result:: - Helper::formatTime(42); // 42 secs - Helper::formatTime(125); // 2 mins - Helper::formatTime(125, 2); // 2 mins, 5 secs - Helper::formatTime(172799, 4); // 1 day, 23 hrs, 59 mins, 59 secs + Helper::formatTime(0.001); // 1 ms + Helper::formatTime(42); // 42 s + Helper::formatTime(125); // 2 min + Helper::formatTime(125, 2); // 2 min, 5 s + Helper::formatTime(172799, 4); // 1 d, 23 h, 59 min, 59 s + Helper::formatTime(172799.056, 5); // 1 d, 23 h, 59 min, 59 s, 56 ms + +.. versionadded:: 7.3 + + Support for formatting up to milliseconds was introduced in Symfony 7.3. Formatting Memory ----------------- diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst index 4d524a2008e..19e2d0daef5 100644 --- a/components/console/helpers/progressbar.rst +++ b/components/console/helpers/progressbar.rst @@ -323,7 +323,7 @@ to display it can be customized:: // the bar width $progressBar->setBarWidth(50); -.. caution:: +.. warning:: For performance reasons, Symfony redraws the screen once every 100ms. If this is too fast or too slow for your application, use the methods diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst index e33c4ed5fa7..2670ec3084a 100644 --- a/components/console/helpers/questionhelper.rst +++ b/components/console/helpers/questionhelper.rst @@ -329,7 +329,7 @@ convenient for passwords:: return Command::SUCCESS; } -.. caution:: +.. warning:: When you ask for a hidden response, Symfony will use either a binary, change ``stty`` mode or use another trick to hide the response. If none is available, @@ -392,7 +392,7 @@ method:: return Command::SUCCESS; } -.. caution:: +.. warning:: The normalizer is called first and the returned value is used as the input of the validator. If the answer is invalid, don't throw exceptions in the @@ -540,7 +540,7 @@ This way you can test any user interaction (even complex ones) by passing the ap simulates a user hitting ``ENTER`` after each input, no need for passing an additional input. -.. caution:: +.. warning:: On Windows systems Symfony uses a special binary to implement hidden questions. This means that those questions don't use the default ``Input`` diff --git a/components/event_dispatcher.rst b/components/event_dispatcher.rst index 83cead3d19c..8cd676dd5fe 100644 --- a/components/event_dispatcher.rst +++ b/components/event_dispatcher.rst @@ -476,11 +476,7 @@ with some other dispatchers: Learn More ---------- -.. toctree:: - :maxdepth: 1 - - /components/event_dispatcher/generic_event - +* :doc:`/components/event_dispatcher/generic_event` * :ref:`The kernel.event_listener tag ` * :ref:`The kernel.event_subscriber tag ` diff --git a/components/expression_language.rst b/components/expression_language.rst index 785beebd9da..b0dd10b0f42 100644 --- a/components/expression_language.rst +++ b/components/expression_language.rst @@ -106,6 +106,10 @@ if the expression is not valid:: $expressionLanguage->lint('1 + 2', []); // doesn't throw anything + $expressionLanguage->lint('1 + a', []); + // throws a SyntaxError exception: + // "Variable "a" is not valid around position 5 for expression `1 + a`." + The behavior of these methods can be configured with some flags defined in the :class:`Symfony\\Component\\ExpressionLanguage\\Parser` class: @@ -121,8 +125,8 @@ This is how you can use these flags:: $expressionLanguage = new ExpressionLanguage(); - // this returns true because the unknown variables and functions are ignored - var_dump($expressionLanguage->lint('unknown_var + unknown_function()', Parser::IGNORE_UNKNOWN_VARIABLES | Parser::IGNORE_UNKNOWN_FUNCTIONS)); + // does not throw a SyntaxError because the unknown variables and functions are ignored + $expressionLanguage->lint('unknown_var + unknown_function()', [], Parser::IGNORE_UNKNOWN_VARIABLES | Parser::IGNORE_UNKNOWN_FUNCTIONS); .. versionadded:: 7.1 diff --git a/components/finder.rst b/components/finder.rst index a3b91470b62..cecc597ac64 100644 --- a/components/finder.rst +++ b/components/finder.rst @@ -41,7 +41,7 @@ The ``$file`` variable is an instance of :class:`Symfony\\Component\\Finder\\SplFileInfo` which extends PHP's own :phpclass:`SplFileInfo` to provide methods to work with relative paths. -.. caution:: +.. warning:: The ``Finder`` object doesn't reset its internal state automatically. This means that you need to create a new instance if you do not want diff --git a/components/form.rst b/components/form.rst index f463ef5911b..44f407e4c8e 100644 --- a/components/form.rst +++ b/components/form.rst @@ -644,7 +644,7 @@ method: // ... -.. caution:: +.. warning:: The form's ``createView()`` method should be called *after* ``handleRequest()`` is called. Otherwise, when using :doc:`form events `, changes done diff --git a/components/http_foundation.rst b/components/http_foundation.rst index 4dcf3b1e4da..8db6cd27f68 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -367,11 +367,13 @@ of the ``anonymize()`` method to specify the number of bytes that should be anonymized depending on the IP address format:: $ipv4 = '123.234.235.236'; - $anonymousIpv4 = IpUtils::anonymize($ipv4, v4Bytes: 3); + $anonymousIpv4 = IpUtils::anonymize($ipv4, 3); // $anonymousIpv4 = '123.0.0.0' $ipv6 = '2a01:198:603:10:396e:4789:8e99:890f'; - $anonymousIpv6 = IpUtils::anonymize($ipv6, v6Bytes: 10); + // (you must define the second argument (bytes to anonymize in IPv4 addresses) + // even when you are only anonymizing IPv6 addresses) + $anonymousIpv6 = IpUtils::anonymize($ipv6, 3, 10); // $anonymousIpv6 = '2a01:198:603::' .. versionadded:: 7.2 @@ -679,8 +681,19 @@ Streaming a Response ~~~~~~~~~~~~~~~~~~~~ The :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse` class allows -you to stream the Response back to the client. The response content is -represented by a PHP callable instead of a string:: +you to stream the Response back to the client. The response content can be +represented by a string iterable:: + + use Symfony\Component\HttpFoundation\StreamedResponse; + + $chunks = ['Hello', ' World']; + + $response = new StreamedResponse(); + $response->setChunks($chunks); + $response->send(); + +For most complex use cases, the response content can be instead represented by +a PHP callable:: use Symfony\Component\HttpFoundation\StreamedResponse; @@ -708,6 +721,10 @@ represented by a PHP callable instead of a string:: // disables FastCGI buffering in nginx only for this response $response->headers->set('X-Accel-Buffering', 'no'); +.. versionadded:: 7.3 + + Support for using string iterables was introduced in Symfony 7.3. + Streaming a JSON Response ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/components/http_kernel.rst b/components/http_kernel.rst index 97de70b66df..02791b370bc 100644 --- a/components/http_kernel.rst +++ b/components/http_kernel.rst @@ -471,7 +471,7 @@ you will trigger the ``kernel.terminate`` event where you can perform certain actions that you may have delayed in order to return the response as quickly as possible to the client (e.g. sending emails). -.. caution:: +.. warning:: Internally, the HttpKernel makes use of the :phpfunction:`fastcgi_finish_request` PHP function. This means that at the moment, only the `PHP FPM`_ server diff --git a/components/ldap.rst b/components/ldap.rst index d5f6bc3fdfe..e52a341986c 100644 --- a/components/ldap.rst +++ b/components/ldap.rst @@ -70,7 +70,7 @@ distinguished name (DN) and the password of a user:: $ldap->bind($dn, $password); -.. caution:: +.. danger:: When the LDAP server allows unauthenticated binds, a blank password will always be valid. diff --git a/components/lock.rst b/components/lock.rst index bf75fb49f7a..b8ba38c8fc7 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -359,7 +359,7 @@ lose the lock it acquired automatically:: throw new \Exception('Process failed'); } -.. caution:: +.. warning:: 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 @@ -427,7 +427,7 @@ when the PHP process ends):: // if none is given, sys_get_temp_dir() is used internally. $store = new FlockStore('/var/stores'); -.. caution:: +.. warning:: 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 @@ -668,7 +668,7 @@ the stores:: $store = new CombinedStore($stores, new UnanimousStrategy()); -.. caution:: +.. warning:: In order to get high availability when using the ``ConsensusStrategy``, the minimum cluster size must be three servers. This allows the cluster to keep @@ -720,7 +720,7 @@ the ``Lock``. 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:: +.. warning:: To guarantee that the same server will always be safe, do not use Memcached behind a LoadBalancer, a cluster or round-robin DNS. Even if the main server @@ -762,12 +762,12 @@ Using the above methods, a robust code would be:: // Perform the task whose duration MUST be less than 5 seconds } -.. caution:: +.. warning:: Choose wisely the lifetime of the ``Lock`` and check whether its remaining time to live is enough to perform the task. -.. caution:: +.. warning:: Storing a ``Lock`` usually takes a few milliseconds, but network conditions may increase that time a lot (up to a few seconds). Take that into account @@ -776,7 +776,7 @@ Using the above methods, a robust code would be:: 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:: +.. warning:: To guarantee that date won't change, the NTP service should be disabled and the date should be updated when the service is stopped. @@ -798,7 +798,7 @@ deployments. Some file systems (such as some types of NFS) do not support locking. -.. caution:: +.. warning:: 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. @@ -827,7 +827,7 @@ and may disappear by mistake at any time. If the Memcached service or the machine hosting it restarts, every lock would be lost without notifying the running processes. -.. caution:: +.. warning:: To avoid that someone else acquires a lock after a restart, it's recommended to delay service start and wait at least as long as the longest lock TTL. @@ -835,7 +835,7 @@ be lost without notifying the running processes. By default Memcached uses a LRU mechanism to remove old entries when the service needs space to add new items. -.. caution:: +.. warning:: The number of items stored in Memcached must be under control. If it's not possible, LRU should be disabled and Lock should be stored in a dedicated @@ -853,7 +853,7 @@ method uses the Memcached's ``flush()`` method which purges and removes everythi MongoDbStore ~~~~~~~~~~~~ -.. caution:: +.. warning:: The locked resource name is indexed in the ``_id`` field of the lock collection. Beware that an indexed field's value in MongoDB can be @@ -879,7 +879,7 @@ about `Expire Data from Collections by Setting TTL`_ in MongoDB. recommended to set constructor option ``gcProbability`` to ``0.0`` to disable this behavior if you have manually dealt with TTL index creation. -.. caution:: +.. warning:: This store relies on all PHP application and database nodes to have synchronized clocks for lock expiry to occur at the correct time. To ensure @@ -896,12 +896,12 @@ PdoStore The PdoStore relies on the `ACID`_ properties of the SQL engine. -.. caution:: +.. warning:: In a cluster configured with multiple primaries, ensure writes are synchronously propagated to every node, or always use the same node. -.. caution:: +.. warning:: Some SQL engines like MySQL allow to disable the unique constraint check. Ensure that this is not the case ``SET unique_checks=1;``. @@ -910,7 +910,7 @@ In order to purge old locks, this store uses a current datetime to define an expiration date reference. This mechanism relies on all server nodes to have synchronized clocks. -.. caution:: +.. warning:: To ensure locks don't expire prematurely; the TTLs should be set with enough extra time to account for any clock drift between nodes. @@ -939,7 +939,7 @@ and may disappear by mistake at any time. If the Redis service or the machine hosting it restarts, every locks would be lost without notifying the running processes. -.. caution:: +.. warning:: To avoid that someone else acquires a lock after a restart, it's recommended to delay service start and wait at least as long as the longest lock TTL. @@ -967,7 +967,7 @@ The ``CombinedStore`` will be, at best, as reliable as the least reliable of all managed stores. As soon as one managed store returns erroneous information, the ``CombinedStore`` won't be reliable. -.. caution:: +.. warning:: All concurrent processes must use the same configuration, with the same amount of managed stored and the same endpoint. @@ -985,13 +985,13 @@ must run on the same machine, virtual machine or container. Be careful when updating a Kubernetes or Swarm service because for a short period of time, there can be two running containers in parallel. -.. caution:: +.. warning:: All concurrent processes must use the same machine. Before starting a concurrent process on a new machine, check that other processes are stopped on the old one. -.. caution:: +.. warning:: When running on systemd with non-system user and option ``RemoveIPC=yes`` (default value), locks are deleted by systemd when that user logs out. diff --git a/components/options_resolver.rst b/components/options_resolver.rst index c8052d0d395..ff25f9e0fc4 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -485,7 +485,7 @@ these options, you can return the desired default value:: } } -.. caution:: +.. warning:: The argument of the callable must be type hinted as ``Options``. Otherwise, the callable itself is considered as the default value of the option. @@ -699,7 +699,7 @@ to the closure to access to them:: } } -.. caution:: +.. warning:: The arguments of the closure must be type hinted as ``OptionsResolver`` and ``Options`` respectively. Otherwise, the closure itself is considered as the diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst index ba37bc0ecda..5ce4c003a11 100644 --- a/components/phpunit_bridge.rst +++ b/components/phpunit_bridge.rst @@ -253,7 +253,7 @@ deprecations but: * forget to mark appropriate tests with the ``@group legacy`` annotations. By using ``SYMFONY_DEPRECATIONS_HELPER=max[self]=0``, deprecations that are -triggered outside the ``vendors`` directory will be accounted for separately, +triggered outside the ``vendor/`` directory will be accounted for separately, while deprecations triggered from a library inside it will not (unless you reach 999999 of these), giving you the best of both worlds. @@ -621,7 +621,7 @@ test:: And that's all! -.. caution:: +.. warning:: Time-based function mocking follows the `PHP namespace resolutions rules`_ so "fully qualified function calls" (e.g ``\time()``) cannot be mocked. diff --git a/components/process.rst b/components/process.rst index 9502665dde1..f6c8837d2c3 100644 --- a/components/process.rst +++ b/components/process.rst @@ -108,7 +108,7 @@ You can configure the options passed to the ``other_options`` argument of // this option allows a subprocess to continue running after the main script exited $process->setOptions(['create_new_console' => true]); -.. caution:: +.. warning:: Most of the options defined by ``proc_open()`` (such as ``create_new_console`` and ``suppress_errors``) are only supported on Windows operating systems. @@ -552,7 +552,7 @@ Use :method:`Symfony\\Component\\Process\\Process::disableOutput` and $process->disableOutput(); $process->run(); -.. caution:: +.. warning:: You cannot enable or disable the output while the process is running. diff --git a/components/property_access.rst b/components/property_access.rst index 600481dce1a..f608640fa9b 100644 --- a/components/property_access.rst +++ b/components/property_access.rst @@ -26,6 +26,8 @@ default configuration:: $propertyAccessor = PropertyAccess::createPropertyAccessor(); +.. _property-access-reading-arrays: + Reading from Arrays ------------------- @@ -112,7 +114,7 @@ To read from properties, use the "dot" notation:: var_dump($propertyAccessor->getValue($person, 'children[0].firstName')); // 'Bar' -.. caution:: +.. warning:: Accessing public properties is the last option used by ``PropertyAccessor``. It tries to access the value using the below methods first before using @@ -260,7 +262,7 @@ The ``getValue()`` method can also use the magic ``__get()`` method:: var_dump($propertyAccessor->getValue($person, 'Wouter')); // [...] -.. caution:: +.. warning:: When implementing the magic ``__get()`` method, you also need to implement ``__isset()``. @@ -301,7 +303,7 @@ enable this feature by using :class:`Symfony\\Component\\PropertyAccess\\Propert var_dump($propertyAccessor->getValue($person, 'wouter')); // [...] -.. caution:: +.. warning:: The ``__call()`` feature is disabled by default, you can enable it by calling :method:`Symfony\\Component\\PropertyAccess\\PropertyAccessorBuilder::enableMagicCall` diff --git a/components/property_info.rst b/components/property_info.rst index 892cd5345a3..0ff1768441a 100644 --- a/components/property_info.rst +++ b/components/property_info.rst @@ -469,7 +469,18 @@ information from annotations of properties and methods, such as ``@var``, use App\Domain\Foo; $phpStanExtractor = new PhpStanExtractor(); + + // Type information. $phpStanExtractor->getTypesFromConstructor(Foo::class, 'bar'); + // Description information. + $phpStanExtractor->getShortDescription($class, 'bar'); + $phpStanExtractor->getLongDescription($class, 'bar'); + +.. versionadded:: 7.3 + + The :method:`Symfony\\Component\\PropertyInfo\\Extractor\\PhpStanExtractor::getShortDescription` + and :method:`Symfony\\Component\\PropertyInfo\\Extractor\\PhpStanExtractor::getLongDescription` + methods were introduced in Symfony 7.3. SerializerExtractor ~~~~~~~~~~~~~~~~~~~ @@ -478,9 +489,9 @@ SerializerExtractor This extractor depends on the `symfony/serializer`_ library. -Using :ref:`groups metadata ` -from the :doc:`Serializer component `, -the :class:`Symfony\\Component\\PropertyInfo\\Extractor\\SerializerExtractor` +Using :ref:`groups metadata ` from the +:doc:`Serializer component `, the +:class:`Symfony\\Component\\PropertyInfo\\Extractor\\SerializerExtractor` provides list information. This extractor is *not* registered automatically with the ``property_info`` service in the Symfony Framework:: diff --git a/components/runtime.rst b/components/runtime.rst index 7d17e7e7456..4eb75de2a75 100644 --- a/components/runtime.rst +++ b/components/runtime.rst @@ -3,7 +3,7 @@ The Runtime Component The Runtime Component decouples the bootstrapping logic from any global state to make sure the application can run with runtimes like `PHP-PM`_, `ReactPHP`_, - `Swoole`_, etc. without any changes. + `Swoole`_, `FrankenPHP`_ etc. without any changes. Installation ------------ @@ -42,7 +42,7 @@ the component. This file runs the following logic: #. At last, the Runtime is used to run the application (i.e. calling ``$kernel->handle(Request::createFromGlobals())->send()``). -.. caution:: +.. warning:: If you use the Composer ``--no-plugins`` option, the ``autoload_runtime.php`` file won't be created. @@ -487,6 +487,7 @@ The end user will now be able to create front controller like:: .. _PHP-PM: https://github.com/php-pm/php-pm .. _Swoole: https://openswoole.com/ +.. _FrankenPHP: https://frankenphp.dev/ .. _ReactPHP: https://reactphp.org/ .. _`PSR-15`: https://www.php-fig.org/psr/psr-15/ .. _`runtime template file`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Runtime/Internal/autoload_runtime.template diff --git a/components/serializer.rst b/components/serializer.rst deleted file mode 100644 index 0764612e28a..00000000000 --- a/components/serializer.rst +++ /dev/null @@ -1,1938 +0,0 @@ -The Serializer Component -======================== - - The Serializer component is meant to be used to turn objects into a - specific format (XML, JSON, YAML, ...) and the other way around. - -In order to do so, the Serializer component follows the following schema. - -.. raw:: html - - - -When (de)serializing objects, the Serializer uses an array as the intermediary -between objects and serialized contents. Encoders will only deal with -turning specific **formats** into **arrays** and vice versa. The same way, -normalizers will deal with turning specific **objects** into **arrays** and -vice versa. The Serializer deals with calling the normalizers and encoders -when serializing objects or deserializing formats. - -Serialization is a complex topic. This component may not cover all your use -cases out of the box, but it can be useful for developing tools to -serialize and deserialize your objects. - -Installation ------------- - -.. code-block:: terminal - - $ composer require symfony/serializer - -.. include:: /components/require_autoload.rst.inc - -To use the ``ObjectNormalizer``, the :doc:`PropertyAccess component ` -must also be installed. - -Usage ------ - -.. seealso:: - - This article explains the philosophy of the Serializer and gets you familiar - with the concepts of normalizers and encoders. The code examples assume - that you use the Serializer as an independent component. If you are using - the Serializer in a Symfony application, read :doc:`/serializer` after you - finish this article. - -To use the Serializer component, set up the -:class:`Symfony\\Component\\Serializer\\Serializer` specifying which encoders -and normalizer are going to be available:: - - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Encoder\XmlEncoder; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $encoders = [new XmlEncoder(), new JsonEncoder()]; - $normalizers = [new ObjectNormalizer()]; - - $serializer = new Serializer($normalizers, $encoders); - -The preferred normalizer is the -:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`, -but other normalizers are available. All the examples shown below use -the ``ObjectNormalizer``. - -Serializing an Object ---------------------- - -For the sake of this example, assume the following class already -exists in your project:: - - namespace App\Model; - - class Person - { - private int $age; - private string $name; - private bool $sportsperson; - private ?\DateTimeInterface $createdAt; - - // Getters - public function getAge(): int - { - return $this->age; - } - - public function getName(): string - { - return $this->name; - } - - public function getCreatedAt(): ?\DateTimeInterface - { - return $this->createdAt; - } - - // Issers - public function isSportsperson(): bool - { - return $this->sportsperson; - } - - // Setters - public function setAge(int $age): void - { - $this->age = $age; - } - - public function setName(string $name): void - { - $this->name = $name; - } - - public function setSportsperson(bool $sportsperson): void - { - $this->sportsperson = $sportsperson; - } - - public function setCreatedAt(?\DateTimeInterface $createdAt = null): void - { - $this->createdAt = $createdAt; - } - } - -Now, if you want to serialize this object into JSON, you only need to -use the Serializer service created before:: - - use App\Model\Person; - - $person = new Person(); - $person->setName('foo'); - $person->setAge(99); - $person->setSportsperson(false); - - $jsonContent = $serializer->serialize($person, 'json'); - - // $jsonContent contains {"name":"foo","age":99,"sportsperson":false,"createdAt":null} - - echo $jsonContent; // or return it in a Response - -The first parameter of the :method:`Symfony\\Component\\Serializer\\Serializer::serialize` -is the object to be serialized and the second is used to choose the proper encoder, -in this case :class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder`. - -Deserializing an Object ------------------------ - -You'll now learn how to do the exact opposite. This time, the information -of the ``Person`` class would be encoded in XML format:: - - use App\Model\Person; - - $data = << - foo - 99 - false - - EOF; - - $person = $serializer->deserialize($data, Person::class, 'xml'); - -In this case, :method:`Symfony\\Component\\Serializer\\Serializer::deserialize` -needs three parameters: - -#. The information to be decoded -#. The name of the class this information will be decoded to -#. The encoder used to convert that information into an array - -By default, additional attributes that are not mapped to the denormalized object -will be ignored by the Serializer component. If you prefer to throw an exception -when this happens, set the ``AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES`` context option to -``false`` and provide an object that implements ``ClassMetadataFactoryInterface`` -when constructing the normalizer:: - - use App\Model\Person; - - $data = << - foo - 99 - Paris - - EOF; - - // $loader is any of the valid loaders explained later in this article - $classMetadataFactory = new ClassMetadataFactory($loader); - $normalizer = new ObjectNormalizer($classMetadataFactory); - $serializer = new Serializer([$normalizer]); - - // this will throw a Symfony\Component\Serializer\Exception\ExtraAttributesException - // because "city" is not an attribute of the Person class - $person = $serializer->deserialize($data, Person::class, 'xml', [ - AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false, - ]); - -Deserializing in an Existing Object -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The serializer can also be used to update an existing object:: - - // ... - $person = new Person(); - $person->setName('bar'); - $person->setAge(99); - $person->setSportsperson(true); - - $data = << - foo - 69 - - EOF; - - $serializer->deserialize($data, Person::class, 'xml', [AbstractNormalizer::OBJECT_TO_POPULATE => $person]); - // $person = App\Model\Person(name: 'foo', age: '69', sportsperson: true) - -This is a common need when working with an ORM. - -The ``AbstractNormalizer::OBJECT_TO_POPULATE`` is only used for the top level object. If that object -is the root of a tree structure, all child elements that exist in the -normalized data will be re-created with new instances. - -When the ``AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE`` option is set to -true, existing children of the root ``OBJECT_TO_POPULATE`` are updated from the -normalized data, instead of the denormalizer re-creating them. Note that -``DEEP_OBJECT_TO_POPULATE`` only works for single child objects, but not for -arrays of objects. Those will still be replaced when present in the normalized -data. - -Context -------- - -Many Serializer features can be configured :ref:`using a context `. - -.. _component-serializer-attributes-groups: - -Attributes Groups ------------------ - -Sometimes, you want to serialize different sets of attributes from your -entities. Groups are a handy way to achieve this need. - -Assume you have the following plain-old-PHP object:: - - namespace Acme; - - class MyObj - { - public string $foo; - - private string $bar; - - public function getBar(): string - { - return $this->bar; - } - - public function setBar($bar): string - { - return $this->bar = $bar; - } - } - -The definition of serialization can be specified using attributes, XML or YAML. -The :class:`Symfony\\Component\\Serializer\\Mapping\\Factory\\ClassMetadataFactory` -that will be used by the normalizer must be aware of the format to use. - -The following code shows how to initialize the :class:`Symfony\\Component\\Serializer\\Mapping\\Factory\\ClassMetadataFactory` -for each format: - -* Attributes in PHP files:: - - use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; - use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader; - - $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - -* YAML files:: - - use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; - use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader; - - $classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader('/path/to/your/definition.yaml')); - -* XML files:: - - use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; - use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader; - - $classMetadataFactory = new ClassMetadataFactory(new XmlFileLoader('/path/to/your/definition.xml')); - -.. _component-serializer-attributes-groups-attributes: - -Then, create your groups definition: - -.. configuration-block:: - - .. code-block:: php-attributes - - namespace Acme; - - use Symfony\Component\Serializer\Annotation\Groups; - - class MyObj - { - #[Groups(['group1', 'group2'])] - public string $foo; - - #[Groups(['group4'])] - public string $anotherProperty; - - #[Groups(['group3'])] - public function getBar() // is* methods are also supported - { - return $this->bar; - } - - // ... - } - - .. code-block:: yaml - - Acme\MyObj: - attributes: - foo: - groups: ['group1', 'group2'] - anotherProperty: - groups: ['group4'] - bar: - groups: ['group3'] - - .. code-block:: xml - - - - - - group1 - group2 - - - - group4 - - - - group3 - - - - -You are now able to serialize only attributes in the groups you want:: - - use Acme\MyObj; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $obj = new MyObj(); - $obj->foo = 'foo'; - $obj->anotherProperty = 'anotherProperty'; - $obj->setBar('bar'); - - $normalizer = new ObjectNormalizer($classMetadataFactory); - $serializer = new Serializer([$normalizer]); - - $data = $serializer->normalize($obj, null, ['groups' => 'group1']); - // $data = ['foo' => 'foo']; - - $obj2 = $serializer->denormalize( - ['foo' => 'foo', 'anotherProperty' => 'anotherProperty', 'bar' => 'bar'], - MyObj::class, - null, - ['groups' => ['group1', 'group3']] - ); - // $obj2 = MyObj(foo: 'foo', bar: 'bar') - - // To get all groups, use the special value `*` in `groups` - $obj3 = $serializer->denormalize( - ['foo' => 'foo', 'anotherProperty' => 'anotherProperty', 'bar' => 'bar'], - MyObj::class, - null, - ['groups' => ['*']] - ); - // $obj2 = MyObj(foo: 'foo', anotherProperty: 'anotherProperty', bar: 'bar') - -.. _ignoring-attributes-when-serializing: - -Selecting Specific Attributes ------------------------------ - -It is also possible to serialize only a set of specific attributes:: - - use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - class User - { - public string $familyName; - public string $givenName; - public Company $company; - } - - class Company - { - public string $name; - public string $address; - } - - $company = new Company(); - $company->name = 'Les-Tilleuls.coop'; - $company->address = 'Lille, France'; - - $user = new User(); - $user->familyName = 'Dunglas'; - $user->givenName = 'Kévin'; - $user->company = $company; - - $serializer = new Serializer([new ObjectNormalizer()]); - - $data = $serializer->normalize($user, null, [AbstractNormalizer::ATTRIBUTES => ['familyName', 'company' => ['name']]]); - // $data = ['familyName' => 'Dunglas', 'company' => ['name' => 'Les-Tilleuls.coop']]; - -Only attributes that are not ignored (see below) are available. -If some serialization groups are set, only attributes allowed by those groups can be used. - -As for groups, attributes can be selected during both the serialization and deserialization processes. - -.. _serializer_ignoring-attributes: - -Ignoring Attributes -------------------- - -All accessible attributes are included by default when serializing objects. -There are two options to ignore some of those attributes. - -Option 1: Using ``#[Ignore]`` Attribute -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. configuration-block:: - - .. code-block:: php-attributes - - namespace App\Model; - - use Symfony\Component\Serializer\Annotation\Ignore; - - class MyClass - { - public string $foo; - - #[Ignore] - public string $bar; - } - - .. code-block:: yaml - - App\Model\MyClass: - attributes: - bar: - ignore: true - - .. code-block:: xml - - - - - - - - -You can now ignore specific attributes during serialization:: - - use App\Model\MyClass; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $obj = new MyClass(); - $obj->foo = 'foo'; - $obj->bar = 'bar'; - - $normalizer = new ObjectNormalizer($classMetadataFactory); - $serializer = new Serializer([$normalizer]); - - $data = $serializer->normalize($obj); - // $data = ['foo' => 'foo']; - -Option 2: Using the Context -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Pass an array with the names of the attributes to ignore using the -``AbstractNormalizer::IGNORED_ATTRIBUTES`` key in the ``context`` of the -serializer method:: - - use Acme\Person; - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $person = new Person(); - $person->setName('foo'); - $person->setAge(99); - - $normalizer = new ObjectNormalizer(); - $encoder = new JsonEncoder(); - - $serializer = new Serializer([$normalizer], [$encoder]); - $serializer->serialize($person, 'json', [AbstractNormalizer::IGNORED_ATTRIBUTES => ['age']]); // Output: {"name":"foo"} - -.. _component-serializer-converting-property-names-when-serializing-and-deserializing: - -Converting Property Names when Serializing and Deserializing ------------------------------------------------------------- - -Sometimes serialized attributes must be named differently than properties -or getter/setter methods of PHP classes. - -The Serializer component provides a handy way to translate or map PHP field -names to serialized names: The Name Converter System. - -Given you have the following object:: - - class Company - { - public string $name; - public string $address; - } - -And in the serialized form, all attributes must be prefixed by ``org_`` like -the following:: - - {"org_name": "Acme Inc.", "org_address": "123 Main Street, Big City"} - -A custom name converter can handle such cases:: - - use Symfony\Component\Serializer\NameConverter\NameConverterInterface; - - class OrgPrefixNameConverter implements NameConverterInterface - { - public function normalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string - { - return 'org_'.$propertyName; - } - - public function denormalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string - { - // removes 'org_' prefix - return str_starts_with($propertyName, 'org_') ? substr($propertyName, 4) : $propertyName; - } - } - -The custom name converter can be used by passing it as second parameter of any -class extending :class:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer`, -including :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` -and :class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer`:: - - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $nameConverter = new OrgPrefixNameConverter(); - $normalizer = new ObjectNormalizer(null, $nameConverter); - - $serializer = new Serializer([$normalizer], [new JsonEncoder()]); - - $company = new Company(); - $company->name = 'Acme Inc.'; - $company->address = '123 Main Street, Big City'; - - $json = $serializer->serialize($company, 'json'); - // {"org_name": "Acme Inc.", "org_address": "123 Main Street, Big City"} - $companyCopy = $serializer->deserialize($json, Company::class, 'json'); - // Same data as $company - -.. _using-camelized-method-names-for-underscored-attributes: - -CamelCase to snake_case -~~~~~~~~~~~~~~~~~~~~~~~ - -In many formats, it's common to use underscores to separate words (also known -as snake_case). However, in Symfony applications is common to use CamelCase to -name properties (even though the `PSR-1 standard`_ doesn't recommend any -specific case for property names). - -Symfony provides a built-in name converter designed to transform between -snake_case and CamelCased styles during serialization and deserialization -processes:: - - use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - - $normalizer = new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter()); - - class Person - { - public function __construct( - private string $firstName, - ) { - } - - public function getFirstName(): string - { - return $this->firstName; - } - } - - $kevin = new Person('Kévin'); - $normalizer->normalize($kevin); - // ['first_name' => 'Kévin']; - - $anne = $normalizer->denormalize(['first_name' => 'Anne'], 'Person'); - // Person object with firstName: 'Anne' - -.. _serializer_name-conversion: - -Configure name conversion using metadata -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When using this component inside a Symfony application and the class metadata -factory is enabled as explained in the :ref:`Attributes Groups section `, -this is already set up and you only need to provide the configuration. Otherwise:: - - // ... - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - - $metadataAwareNameConverter = new MetadataAwareNameConverter($classMetadataFactory); - - $serializer = new Serializer( - [new ObjectNormalizer($classMetadataFactory, $metadataAwareNameConverter)], - ['json' => new JsonEncoder()] - ); - -Now configure your name conversion mapping. Consider an application that -defines a ``Person`` entity with a ``firstName`` property: - -.. configuration-block:: - - .. code-block:: php-attributes - - namespace App\Entity; - - use Symfony\Component\Serializer\Annotation\SerializedName; - - class Person - { - public function __construct( - #[SerializedName('customer_name')] - private string $firstName, - ) { - } - - // ... - } - - .. code-block:: yaml - - App\Entity\Person: - attributes: - firstName: - serialized_name: customer_name - - .. code-block:: xml - - - - - - - - -This custom mapping is used to convert property names when serializing and -deserializing objects:: - - $serialized = $serializer->serialize(new Person('Kévin'), 'json'); - // {"customer_name": "Kévin"} - -.. _serializing-boolean-attributes: - -Handling Boolean Attributes And Values --------------------------------------- - -During Serialization -~~~~~~~~~~~~~~~~~~~~ - -If you are using isser methods (methods prefixed by ``is``, like -``App\Model\Person::isSportsperson()``), the Serializer component will -automatically detect and use it to serialize related attributes. - -The ``ObjectNormalizer`` also takes care of methods starting with ``has``, ``get``, -and ``can``. - -During Deserialization -~~~~~~~~~~~~~~~~~~~~~~ - -PHP considers many different values as true or false. For example, the -strings ``true``, ``1``, and ``yes`` are considered true, while -``false``, ``0``, and ``no`` are considered false. - -When deserializing, the Serializer component can take care of this -automatically. This can be done by using the ``AbstractNormalizer::FILTER_BOOL`` -context option:: - - use Acme\Person; - use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $normalizer = new ObjectNormalizer(); - $serializer = new Serializer([$normalizer]); - - $data = $serializer->denormalize(['sportsperson' => 'yes'], Person::class, context: [AbstractNormalizer::FILTER_BOOL => true]); - -This context makes the deserialization process behave like the -:phpfunction:`filter_var` function with the ``FILTER_VALIDATE_BOOL`` flag. - -.. versionadded:: 7.1 - - The ``AbstractNormalizer::FILTER_BOOL`` context option was introduced in Symfony 7.1. - -Using Callbacks to Serialize Properties with Object Instances -------------------------------------------------------------- - -When serializing, you can set a callback to format a specific object property:: - - use App\Model\Person; - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; - use Symfony\Component\Serializer\Serializer; - - $encoder = new JsonEncoder(); - - // all callback parameters are optional (you can omit the ones you don't use) - $dateCallback = function (object $attributeValue, object $object, string $attributeName, ?string $format = null, array $context = []): string { - return $attributeValue instanceof \DateTime ? $attributeValue->format(\DateTime::ATOM) : ''; - }; - - $defaultContext = [ - AbstractNormalizer::CALLBACKS => [ - 'createdAt' => $dateCallback, - ], - ]; - - $normalizer = new GetSetMethodNormalizer(null, null, null, null, null, $defaultContext); - - $serializer = new Serializer([$normalizer], [$encoder]); - - $person = new Person(); - $person->setName('cordoval'); - $person->setAge(34); - $person->setCreatedAt(new \DateTime('now')); - - $serializer->serialize($person, 'json'); - // Output: {"name":"cordoval", "age": 34, "createdAt": "2014-03-22T09:43:12-0500"} - -.. _component-serializer-normalizers: - -Normalizers ------------ - -Normalizers turn **objects** into **arrays** and vice versa. They implement -:class:`Symfony\\Component\\Serializer\\Normalizer\\NormalizerInterface` for -normalizing (object to array) and -:class:`Symfony\\Component\\Serializer\\Normalizer\\DenormalizerInterface` for -denormalizing (array to object). - -Normalizers are enabled in the serializer passing them as its first argument:: - - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $normalizers = [new ObjectNormalizer()]; - $serializer = new Serializer($normalizers, []); - -Built-in Normalizers -~~~~~~~~~~~~~~~~~~~~ - -The Serializer component provides several built-in normalizers: - -:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` - This normalizer leverages the :doc:`PropertyAccess Component ` - to read and write in the object. It means that it can access to properties - directly and through getters, setters, hassers, issers, canners, adders and removers. - It supports calling the constructor during the denormalization process. - - Objects are normalized to a map of property names and values (names are - generated by removing the ``get``, ``set``, ``has``, ``is``, ``can``, ``add`` or ``remove`` - prefix from the method name and transforming the first letter to lowercase; e.g. - ``getFirstName()`` -> ``firstName``). - - The ``ObjectNormalizer`` is the most powerful normalizer. It is configured by - default in Symfony applications with the Serializer component enabled. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` - This normalizer reads the content of the class by calling the "getters" - (public methods starting with "get"). It will denormalize data by calling - the constructor and the "setters" (public methods starting with "set"). - - Objects are normalized to a map of property names and values (names are - generated by removing the ``get`` prefix from the method name and transforming - the first letter to lowercase; e.g. ``getFirstName()`` -> ``firstName``). - -:class:`Symfony\\Component\\Serializer\\Normalizer\\PropertyNormalizer` - This normalizer directly reads and writes public properties as well as - **private and protected** properties (from both the class and all of its - parent classes) by using `PHP reflection`_. It supports calling the constructor - during the denormalization process. - - Objects are normalized to a map of property names to property values. - - If you prefer to only normalize certain properties (e.g. only public properties) - set the ``PropertyNormalizer::NORMALIZE_VISIBILITY`` context option and - combine the following values: ``PropertyNormalizer::NORMALIZE_PUBLIC``, - ``PropertyNormalizer::NORMALIZE_PROTECTED`` or ``PropertyNormalizer::NORMALIZE_PRIVATE``. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\JsonSerializableNormalizer` - This normalizer works with classes that implement :phpclass:`JsonSerializable`. - - It will call the :phpmethod:`JsonSerializable::jsonSerialize` method and - then further normalize the result. This means that nested - :phpclass:`JsonSerializable` classes will also be normalized. - - This normalizer is particularly helpful when you want to gradually migrate - from an existing codebase using simple :phpfunction:`json_encode` to the Symfony - Serializer by allowing you to mix which normalizers are used for which classes. - - Unlike with :phpfunction:`json_encode` circular references can be handled. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\DateTimeNormalizer` - This normalizer converts :phpclass:`DateTimeInterface` objects (e.g. - :phpclass:`DateTime` and :phpclass:`DateTimeImmutable`) into strings, - integers or floats. By default, it converts them to strings using the `RFC3339`_ format. - To convert the objects to integers or floats, set the serializer context option - ``DateTimeNormalizer::CAST_KEY`` to ``int`` or ``float``. - - .. versionadded:: 7.1 - - The ``DateTimeNormalizer::CAST_KEY`` context option was introduced in Symfony 7.1. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\DateTimeZoneNormalizer` - This normalizer converts :phpclass:`DateTimeZone` objects into strings that - represent the name of the timezone according to the `list of PHP timezones`_. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\DataUriNormalizer` - This normalizer converts :phpclass:`SplFileInfo` objects into a `data URI`_ - string (``data:...``) such that files can be embedded into serialized data. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\DateIntervalNormalizer` - This normalizer converts :phpclass:`DateInterval` objects into strings. - By default, it uses the ``P%yY%mM%dDT%hH%iM%sS`` format. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\BackedEnumNormalizer` - This normalizer converts a \BackedEnum objects into strings or integers. - - By default, an exception is thrown when data is not a valid backed enumeration. If you - want ``null`` instead, you can set the ``BackedEnumNormalizer::ALLOW_INVALID_VALUES`` option. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\FormErrorNormalizer` - This normalizer works with classes that implement - :class:`Symfony\\Component\\Form\\FormInterface`. - - It will get errors from the form and normalize them into a normalized array. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\ConstraintViolationListNormalizer` - This normalizer converts objects that implement - :class:`Symfony\\Component\\Validator\\ConstraintViolationListInterface` - into a list of errors according to the `RFC 7807`_ standard. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\ProblemNormalizer` - Normalizes errors according to the API Problem spec `RFC 7807`_. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\CustomNormalizer` - Normalizes a PHP object using an object that implements :class:`Symfony\\Component\\Serializer\\Normalizer\\NormalizableInterface`. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\UidNormalizer` - This normalizer converts objects that extend - :class:`Symfony\\Component\\Uid\\AbstractUid` into strings. - The default normalization format for objects that implement :class:`Symfony\\Component\\Uid\\Uuid` - is the `RFC 4122`_ format (example: ``d9e7a184-5d5b-11ea-a62a-3499710062d0``). - The default normalization format for objects that implement :class:`Symfony\\Component\\Uid\\Ulid` - is the Base 32 format (example: ``01E439TP9XJZ9RPFH3T1PYBCR8``). - You can change the string format by setting the serializer context option - ``UidNormalizer::NORMALIZATION_FORMAT_KEY`` to ``UidNormalizer::NORMALIZATION_FORMAT_BASE_58``, - ``UidNormalizer::NORMALIZATION_FORMAT_BASE_32`` or ``UidNormalizer::NORMALIZATION_FORMAT_RFC_4122``. - - Also it can denormalize ``uuid`` or ``ulid`` strings to :class:`Symfony\\Component\\Uid\\Uuid` - or :class:`Symfony\\Component\\Uid\\Ulid`. The format does not matter. - -:class:`Symfony\\Component\\Serializer\\Normalizer\\TranslatableNormalizer` - This normalizer converts objects that implement - :class:`Symfony\\Contracts\\Translation\\TranslatableInterface` into - translated strings, using the - :method:`Symfony\\Contracts\\Translation\\TranslatableInterface::trans` - method. You can define the locale to use to translate the object by - setting the ``TranslatableNormalizer::NORMALIZATION_LOCALE_KEY`` serializer - context option. - -.. note:: - - You can also create your own Normalizer to use another structure. Read more at - :doc:`/serializer/custom_normalizer`. - -Certain normalizers are enabled by default when using the Serializer component -in a Symfony application, additional ones can be enabled by tagging them with -:ref:`serializer.normalizer `. - -Here is an example of how to enable the built-in -:class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer`, a -faster alternative to the -:class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`: - -.. configuration-block:: - - .. code-block:: yaml - - # config/services.yaml - services: - # ... - - get_set_method_normalizer: - class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer - tags: [serializer.normalizer] - - .. code-block:: xml - - - - - - - - - - - - - - .. code-block:: php - - // config/services.php - namespace Symfony\Component\DependencyInjection\Loader\Configurator; - - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; - - return static function (ContainerConfigurator $container): void { - $container->services() - // ... - ->set('get_set_method_normalizer', GetSetMethodNormalizer::class) - ->tag('serializer.normalizer') - ; - }; - -.. _component-serializer-encoders: - -Encoders --------- - -Encoders turn **arrays** into **formats** and vice versa. They implement -:class:`Symfony\\Component\\Serializer\\Encoder\\EncoderInterface` -for encoding (array to format) and -:class:`Symfony\\Component\\Serializer\\Encoder\\DecoderInterface` for decoding -(format to array). - -You can add new encoders to a Serializer instance by using its second constructor argument:: - - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Encoder\XmlEncoder; - use Symfony\Component\Serializer\Serializer; - - $encoders = [new XmlEncoder(), new JsonEncoder()]; - $serializer = new Serializer([], $encoders); - -Built-in Encoders -~~~~~~~~~~~~~~~~~ - -The Serializer component provides several built-in encoders: - -:class:`Symfony\\Component\\Serializer\\Encoder\\JsonEncoder` - This class encodes and decodes data in `JSON`_. - -:class:`Symfony\\Component\\Serializer\\Encoder\\XmlEncoder` - This class encodes and decodes data in `XML`_. - -:class:`Symfony\\Component\\Serializer\\Encoder\\YamlEncoder` - This encoder encodes and decodes data in `YAML`_. This encoder requires the - :doc:`Yaml Component `. - -:class:`Symfony\\Component\\Serializer\\Encoder\\CsvEncoder` - This encoder encodes and decodes data in `CSV`_. - -.. note:: - - You can also create your own Encoder to use another structure. Read more at - :doc:`/serializer/custom_encoders`. - -All these encoders are enabled by default when using the Serializer component -in a Symfony application. - -The ``JsonEncoder`` -~~~~~~~~~~~~~~~~~~~ - -The ``JsonEncoder`` encodes to and decodes from JSON strings, based on the PHP -:phpfunction:`json_encode` and :phpfunction:`json_decode` functions. It can be -useful to modify how these functions operate in certain instances by providing -options such as ``JSON_PRESERVE_ZERO_FRACTION``. You can use the serialization -context to pass in these options using the key ``json_encode_options`` or -``json_decode_options`` respectively:: - - $this->serializer->serialize($data, 'json', ['json_encode_options' => \JSON_PRESERVE_ZERO_FRACTION]); - -These are the options available: - -=============================== =========================================================================================================== ================================ -Option Description Default -=============================== ========================================================================================================== ================================ -``json_decode_associative`` If set to true returns the result as an array, returns a nested ``stdClass`` hierarchy otherwise. ``false`` -``json_decode_detailed_errors`` If set to true, exceptions thrown on parsing of JSON are more specific. Requires `seld/jsonlint`_ package. ``false`` -``json_decode_options`` `$flags`_ passed to :phpfunction:`json_decode` function. ``0`` -``json_encode_options`` `$flags`_ passed to :phpfunction:`json_encode` function. ``\JSON_PRESERVE_ZERO_FRACTION`` -``json_decode_recursion_depth`` Sets maximum recursion depth. ``512`` -=============================== ========================================================================================================== ================================ - -The ``CsvEncoder`` -~~~~~~~~~~~~~~~~~~ - -The ``CsvEncoder`` encodes to and decodes from CSV. - -The ``CsvEncoder`` Context Options -.................................. - -The ``encode()`` method defines a third optional parameter called ``context`` -which defines the configuration options for the CsvEncoder an associative array:: - - $csvEncoder->encode($array, 'csv', $context); - -These are the options available: - -======================= ============================================================= ========================== -Option Description Default -======================= ============================================================= ========================== -``csv_delimiter`` Sets the field delimiter separating values (one ``,`` - character only) -``csv_enclosure`` Sets the field enclosure (one character only) ``"`` -``csv_end_of_line`` Sets the character(s) used to mark the end of each ``\n`` - line in the CSV file -``csv_escape_char`` Deprecated. Sets the escape character (at most one character) empty string -``csv_key_separator`` Sets the separator for array's keys during its ``.`` - flattening -``csv_headers`` Sets the order of the header and data columns - E.g.: if ``$data = ['c' => 3, 'a' => 1, 'b' => 2]`` - and ``$options = ['csv_headers' => ['a', 'b', 'c']]`` - then ``serialize($data, 'csv', $options)`` returns - ``a,b,c\n1,2,3`` ``[]``, inferred from input data's keys -``csv_escape_formulas`` Escapes fields containing formulas by prepending them ``false`` - with a ``\t`` character -``as_collection`` Always returns results as a collection, even if only ``true`` - one line is decoded. -``no_headers`` Setting to ``false`` will use first row as headers. ``false`` - ``true`` generate numeric headers. -``output_utf8_bom`` Outputs special `UTF-8 BOM`_ along with encoded data ``false`` -======================= ============================================================= ========================== - -.. deprecated:: 7.2 - - The ``csv_escape_char`` option and the ``CsvEncoder::ESCAPE_CHAR_KEY`` - constant were deprecated in Symfony 7.2. - -The ``XmlEncoder`` -~~~~~~~~~~~~~~~~~~ - -This encoder transforms arrays into XML and vice versa. - -For example, take an object normalized as following:: - - ['foo' => [1, 2], 'bar' => true]; - -The ``XmlEncoder`` will encode this object like that: - -.. code-block:: xml - - - - 1 - 2 - 1 - - -The special ``#`` key can be used to define the data of a node:: - - ['foo' => ['@bar' => 'value', '#' => 'baz']]; - - // is encoded as follows: - // - // - // - // baz - // - // - -Furthermore, keys beginning with ``@`` will be considered attributes, and -the key ``#comment`` can be used for encoding XML comments:: - - $encoder = new XmlEncoder(); - $encoder->encode([ - 'foo' => ['@bar' => 'value'], - 'qux' => ['#comment' => 'A comment'], - ], 'xml'); - // will return: - // - // - // - // - // - -You can pass the context key ``as_collection`` in order to have the results -always as a collection. - -.. note:: - - You may need to add some attributes on the root node:: - - $encoder = new XmlEncoder(); - $encoder->encode([ - '@attribute1' => 'foo', - '@attribute2' => 'bar', - '#' => ['foo' => ['@bar' => 'value', '#' => 'baz']] - ], 'xml'); - - // will return: - // - // - // baz - // - -.. tip:: - - XML comments are ignored by default when decoding contents, but this - behavior can be changed with the optional context key ``XmlEncoder::DECODER_IGNORED_NODE_TYPES``. - - Data with ``#comment`` keys are encoded to XML comments by default. This can be - changed by adding the ``\XML_COMMENT_NODE`` option to the ``XmlEncoder::ENCODER_IGNORED_NODE_TYPES`` - key of the ``$defaultContext`` of the ``XmlEncoder`` constructor or - directly to the ``$context`` argument of the ``encode()`` method:: - - $xmlEncoder->encode($array, 'xml', [XmlEncoder::ENCODER_IGNORED_NODE_TYPES => [\XML_COMMENT_NODE]]); - -The ``XmlEncoder`` Context Options -.................................. - -The ``encode()`` method defines a third optional parameter called ``context`` -which defines the configuration options for the XmlEncoder an associative array:: - - $xmlEncoder->encode($array, 'xml', $context); - -These are the options available: - -============================== ================================================= ========================== -Option Description Default -============================== ================================================= ========================== -``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.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`` - type casting -``xml_root_node_name`` Sets the root node name ``response`` -``as_collection`` Always returns results as a collection, even if ``false`` - only one line is decoded -``decoder_ignored_node_types`` Array of node types (`DOM XML_* constants`_) ``[\XML_PI_NODE, \XML_COMMENT_NODE]`` - to be ignored while decoding -``encoder_ignored_node_types`` Array of node types (`DOM XML_* constants`_) ``[]`` - to be ignored while encoding -``load_options`` XML loading `options with libxml`_ ``\LIBXML_NONET | \LIBXML_NOBLANKS`` -``save_options`` XML saving `options with libxml`_ ``0`` -``remove_empty_tags`` If set to true, removes all empty tags in the ``false`` - generated XML -``cdata_wrapping`` If set to false, will not wrap any value ``true`` - matching the ``cdata_wrapping_pattern`` regex in - `a CDATA section`_ like following: - ```` -``cdata_wrapping_pattern`` A regular expression pattern to determine if a ``/[<>&]/`` - value should be wrapped in a CDATA section -============================== ================================================= ========================== - -.. versionadded:: 7.1 - - The ``cdata_wrapping_pattern`` option was introduced in Symfony 7.1. - -Example with custom ``context``:: - - use Symfony\Component\Serializer\Encoder\XmlEncoder; - - // create encoder with specified options as new default settings - $xmlEncoder = new XmlEncoder(['xml_format_output' => true]); - - $data = [ - 'id' => 'IDHNQIItNyQ', - 'date' => '2019-10-24', - ]; - - // encode with default context - $xmlEncoder->encode($data, 'xml'); - // outputs: - // - // - // IDHNQIItNyQ - // 2019-10-24 - // - - // encode with modified context - $xmlEncoder->encode($data, 'xml', [ - 'xml_root_node_name' => 'track', - 'encoder_ignored_node_types' => [ - \XML_PI_NODE, // removes XML declaration (the leading xml tag) - ], - ]); - // outputs: - // - // IDHNQIItNyQ - // 2019-10-24 - // - -The ``YamlEncoder`` -~~~~~~~~~~~~~~~~~~~ - -This encoder requires the :doc:`Yaml Component ` and -transforms from and to Yaml. - -The ``YamlEncoder`` Context Options -................................... - -The ``encode()`` method, like other encoder, uses ``context`` to set -configuration options for the YamlEncoder an associative array:: - - $yamlEncoder->encode($array, 'yaml', $context); - -These are the options available: - -=============== ======================================================== ========================== -Option Description Default -=============== ======================================================== ========================== -``yaml_inline`` The level where you switch to inline YAML ``0`` -``yaml_indent`` The level of indentation (used internally) ``0`` -``yaml_flags`` A bit field of ``Yaml::DUMP_*`` / ``PARSE_*`` constants ``0`` - to customize the encoding / decoding YAML string -=============== ======================================================== ========================== - -.. _component-serializer-context-builders: - -Context Builders ----------------- - -Instead of passing plain PHP arrays to the :ref:`serialization context `, -you can use "context builders" to define the context using a fluent interface:: - - use Symfony\Component\Serializer\Context\Encoder\CsvEncoderContextBuilder; - use Symfony\Component\Serializer\Context\Normalizer\ObjectNormalizerContextBuilder; - - $initialContext = [ - 'custom_key' => 'custom_value', - ]; - - $contextBuilder = (new ObjectNormalizerContextBuilder()) - ->withContext($initialContext) - ->withGroups(['group1', 'group2']); - - $contextBuilder = (new CsvEncoderContextBuilder()) - ->withContext($contextBuilder) - ->withDelimiter(';'); - - $serializer->serialize($something, 'csv', $contextBuilder->toArray()); - -.. note:: - - The Serializer component provides a context builder - for each :ref:`normalizer ` - and :ref:`encoder `. - - You can also :doc:`create custom context builders ` - to deal with your context values. - -.. deprecated:: 7.2 - - The ``CsvEncoderContextBuilder::withEscapeChar()`` method was deprecated - in Symfony 7.2. - -Skipping ``null`` Values ------------------------- - -By default, the Serializer will preserve properties containing a ``null`` value. -You can change this behavior by setting the ``AbstractObjectNormalizer::SKIP_NULL_VALUES`` context option -to ``true``:: - - $dummy = new class { - public ?string $foo = null; - public string $bar = 'notNull'; - }; - - $normalizer = new ObjectNormalizer(); - $result = $normalizer->normalize($dummy, 'json', [AbstractObjectNormalizer::SKIP_NULL_VALUES => true]); - // ['bar' => 'notNull'] - -Require all Properties ----------------------- - -By default, the Serializer will add ``null`` to nullable properties when the parameters for those are not provided. -You can change this behavior by setting the ``AbstractNormalizer::REQUIRE_ALL_PROPERTIES`` context option -to ``true``:: - - class Dummy - { - public function __construct( - public string $foo, - public ?string $bar, - ) { - } - } - - $data = ['foo' => 'notNull']; - - $normalizer = new ObjectNormalizer(); - $result = $normalizer->denormalize($data, Dummy::class, 'json', [AbstractNormalizer::REQUIRE_ALL_PROPERTIES => true]); - // throws Symfony\Component\Serializer\Exception\MissingConstructorArgumentException - -Skipping Uninitialized Properties ---------------------------------- - -In PHP, typed properties have an ``uninitialized`` state which is different -from the default ``null`` of untyped properties. When you try to access a typed -property before giving it an explicit value, you get an error. - -To avoid the Serializer throwing an error when serializing or normalizing an -object with uninitialized properties, by default the object normalizer catches -these errors and ignores such properties. - -You can disable this behavior by setting the ``AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES`` -context option to ``false``:: - - class Dummy { - public string $foo = 'initialized'; - public string $bar; // uninitialized - } - - $normalizer = new ObjectNormalizer(); - $result = $normalizer->normalize(new Dummy(), 'json', [AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES => false]); - // throws Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException as normalizer cannot read uninitialized properties - -.. note:: - - Calling ``PropertyNormalizer::normalize`` or ``GetSetMethodNormalizer::normalize`` - with ``AbstractObjectNormalizer::SKIP_UNINITIALIZED_VALUES`` context option set - to ``false`` will throw an ``\Error`` instance if the given object has uninitialized - properties as the normalizer cannot read them (directly or via getter/isser methods). - -.. _component-serializer-handling-circular-references: - -Collecting Type Errors While Denormalizing ------------------------------------------- - -When denormalizing a payload to an object with typed properties, you'll get an -exception if the payload contains properties that don't have the same type as -the object. - -In those situations, use the ``COLLECT_DENORMALIZATION_ERRORS`` option to -collect all exceptions at once, and to get the object partially denormalized:: - - try { - $dto = $serializer->deserialize($request->getContent(), MyDto::class, 'json', [ - DenormalizerInterface::COLLECT_DENORMALIZATION_ERRORS => true, - ]); - } catch (PartialDenormalizationException $e) { - $violations = new ConstraintViolationList(); - /** @var NotNormalizableValueException $exception */ - foreach ($e->getErrors() as $exception) { - $message = sprintf('The type must be one of "%s" ("%s" given).', implode(', ', $exception->getExpectedTypes()), $exception->getCurrentType()); - $parameters = []; - if ($exception->canUseMessageForUser()) { - $parameters['hint'] = $exception->getMessage(); - } - $violations->add(new ConstraintViolation($message, '', $parameters, null, $exception->getPath(), null)); - } - - return $this->json($violations, 400); - } - -Handling Circular References ----------------------------- - -Circular references are common when dealing with entity relations:: - - class Organization - { - private string $name; - private array $members; - - public function setName($name): void - { - $this->name = $name; - } - - public function getName(): string - { - return $this->name; - } - - public function setMembers(array $members): void - { - $this->members = $members; - } - - public function getMembers(): array - { - return $this->members; - } - } - - class Member - { - private string $name; - private Organization $organization; - - public function setName(string $name): void - { - $this->name = $name; - } - - public function getName(): string - { - return $this->name; - } - - public function setOrganization(Organization $organization): void - { - $this->organization = $organization; - } - - public function getOrganization(): Organization - { - return $this->organization; - } - } - -To avoid infinite loops, :class:`Symfony\\Component\\Serializer\\Normalizer\\GetSetMethodNormalizer` -or :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` -throw a :class:`Symfony\\Component\\Serializer\\Exception\\CircularReferenceException` -when such a case is encountered:: - - $member = new Member(); - $member->setName('Kévin'); - - $organization = new Organization(); - $organization->setName('Les-Tilleuls.coop'); - $organization->setMembers([$member]); - - $member->setOrganization($organization); - - echo $serializer->serialize($organization, 'json'); // Throws a CircularReferenceException - -The key ``circular_reference_limit`` in the default context sets the number of -times it will serialize the same object before considering it a circular -reference. The default value is ``1``. - -Instead of throwing an exception, circular references can also be handled -by custom callables. This is especially useful when serializing entities -having unique identifiers:: - - $encoder = new JsonEncoder(); - $defaultContext = [ - AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER => function (object $object, ?string $format, array $context): string { - return $object->getName(); - }, - ]; - $normalizer = new ObjectNormalizer(null, null, null, null, null, null, $defaultContext); - - $serializer = new Serializer([$normalizer], [$encoder]); - var_dump($serializer->serialize($org, 'json')); - // {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]} - -.. _serializer_handling-serialization-depth: - -Handling Serialization Depth ----------------------------- - -The Serializer component is able to detect and limit the serialization depth. -It is especially useful when serializing large trees. Assume the following data -structure:: - - namespace Acme; - - class MyObj - { - public string $foo; - - /** - * @var self - */ - public MyObj $child; - } - - $level1 = new MyObj(); - $level1->foo = 'level1'; - - $level2 = new MyObj(); - $level2->foo = 'level2'; - $level1->child = $level2; - - $level3 = new MyObj(); - $level3->foo = 'level3'; - $level2->child = $level3; - -The serializer can be configured to set a maximum depth for a given property. -Here, we set it to 2 for the ``$child`` property: - -.. configuration-block:: - - .. code-block:: php-attributes - - namespace Acme; - - use Symfony\Component\Serializer\Annotation\MaxDepth; - - class MyObj - { - #[MaxDepth(2)] - public MyObj $child; - - // ... - } - - .. code-block:: yaml - - Acme\MyObj: - attributes: - child: - max_depth: 2 - - .. code-block:: xml - - - - - - - - -The metadata loader corresponding to the chosen format must be configured in -order to use this feature. It is done automatically when using the Serializer component -in a Symfony application. When using the standalone component, refer to -:ref:`the groups documentation ` to -learn how to do that. - -The check is only done if the ``AbstractObjectNormalizer::ENABLE_MAX_DEPTH`` key of the serializer context -is set to ``true``. In the following example, the third level is not serialized -because it is deeper than the configured maximum depth of 2:: - - $result = $serializer->normalize($level1, null, [AbstractObjectNormalizer::ENABLE_MAX_DEPTH => true]); - /* - $result = [ - 'foo' => 'level1', - 'child' => [ - 'foo' => 'level2', - 'child' => [ - 'child' => null, - ], - ], - ]; - */ - -Instead of throwing an exception, a custom callable can be executed when the -maximum depth is reached. This is especially useful when serializing entities -having unique identifiers:: - - use Symfony\Component\Serializer\Annotation\MaxDepth; - use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; - use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader; - use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - class Foo - { - public int $id; - - #[MaxDepth(1)] - public MyObj $child; - } - - $level1 = new Foo(); - $level1->id = 1; - - $level2 = new Foo(); - $level2->id = 2; - $level1->child = $level2; - - $level3 = new Foo(); - $level3->id = 3; - $level2->child = $level3; - - $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - - // all callback parameters are optional (you can omit the ones you don't use) - $maxDepthHandler = function (object $innerObject, object $outerObject, string $attributeName, ?string $format = null, array $context = []): string { - return '/foos/'.$innerObject->id; - }; - - $defaultContext = [ - AbstractObjectNormalizer::MAX_DEPTH_HANDLER => $maxDepthHandler, - ]; - $normalizer = new ObjectNormalizer($classMetadataFactory, null, null, null, null, null, $defaultContext); - - $serializer = new Serializer([$normalizer]); - - $result = $serializer->normalize($level1, null, [AbstractObjectNormalizer::ENABLE_MAX_DEPTH => true]); - /* - $result = [ - 'id' => 1, - 'child' => [ - 'id' => 2, - 'child' => '/foos/3', - ], - ]; - */ - -Handling Arrays ---------------- - -The Serializer component is capable of handling arrays of objects as well. -Serializing arrays works just like serializing a single object:: - - use Acme\Person; - - $person1 = new Person(); - $person1->setName('foo'); - $person1->setAge(99); - $person1->setSportsman(false); - - $person2 = new Person(); - $person2->setName('bar'); - $person2->setAge(33); - $person2->setSportsman(true); - - $persons = [$person1, $person2]; - $data = $serializer->serialize($persons, 'json'); - - // $data contains [{"name":"foo","age":99,"sportsman":false},{"name":"bar","age":33,"sportsman":true}] - -If you want to deserialize such a structure, you need to add the -:class:`Symfony\\Component\\Serializer\\Normalizer\\ArrayDenormalizer` -to the set of normalizers. By appending ``[]`` to the type parameter of the -:method:`Symfony\\Component\\Serializer\\Serializer::deserialize` method, -you indicate that you're expecting an array instead of a single object:: - - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; - use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer; - use Symfony\Component\Serializer\Serializer; - - $serializer = new Serializer( - [new GetSetMethodNormalizer(), new ArrayDenormalizer()], - [new JsonEncoder()] - ); - - $data = ...; // The serialized data from the previous example - $persons = $serializer->deserialize($data, 'Acme\Person[]', 'json'); - -Handling Constructor Arguments ------------------------------- - -If the class constructor defines arguments, as usually happens with -`Value Objects`_, the serializer won't be able to create the object if some -arguments are missing. In those cases, use the ``default_constructor_arguments`` -context option:: - - use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - class MyObj - { - public function __construct( - private string $foo, - private string $bar, - ) { - } - } - - $normalizer = new ObjectNormalizer(); - $serializer = new Serializer([$normalizer]); - - $data = $serializer->denormalize( - ['foo' => 'Hello'], - 'MyObj', - null, - [AbstractNormalizer::DEFAULT_CONSTRUCTOR_ARGUMENTS => [ - 'MyObj' => ['foo' => '', 'bar' => ''], - ]] - ); - // $data = new MyObj('Hello', ''); - -Recursive Denormalization and Type Safety ------------------------------------------ - -The Serializer component can use the :doc:`PropertyInfo Component ` to denormalize -complex types (objects). The type of the class' property will be guessed using the provided -extractor and used to recursively denormalize the inner data. - -When using this component in a Symfony application, all normalizers are automatically configured to use the registered extractors. -When using the component standalone, an implementation of :class:`Symfony\\Component\\PropertyInfo\\PropertyTypeExtractorInterface`, -(usually an instance of :class:`Symfony\\Component\\PropertyInfo\\PropertyInfoExtractor`) must be passed as the 4th -parameter of the ``ObjectNormalizer``:: - - namespace Acme; - - use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; - use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - class ObjectOuter - { - private ObjectInner $inner; - private \DateTimeInterface $date; - - public function getInner(): ObjectInner - { - return $this->inner; - } - - public function setInner(ObjectInner $inner): void - { - $this->inner = $inner; - } - - public function getDate(): \DateTimeInterface - { - return $this->date; - } - - public function setDate(\DateTimeInterface $date): void - { - $this->date = $date; - } - } - - class ObjectInner - { - public string $foo; - public string $bar; - } - - $normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor()); - $serializer = new Serializer([new DateTimeNormalizer(), $normalizer]); - - $obj = $serializer->denormalize( - ['inner' => ['foo' => 'foo', 'bar' => 'bar'], 'date' => '1988/01/21'], - 'Acme\ObjectOuter' - ); - - dump($obj->getInner()->foo); // 'foo' - dump($obj->getInner()->bar); // 'bar' - dump($obj->getDate()->format('Y-m-d')); // '1988-01-21' - -When a ``PropertyTypeExtractor`` is available, the normalizer will also check that the data to denormalize -matches the type of the property (even for primitive types). For instance, if a ``string`` is provided, but -the type of the property is ``int``, an :class:`Symfony\\Component\\Serializer\\Exception\\UnexpectedValueException` -will be thrown. The type enforcement of the properties can be disabled by setting -the serializer context option ``ObjectNormalizer::DISABLE_TYPE_ENFORCEMENT`` -to ``true``. - -.. _serializer_interfaces-and-abstract-classes: - -Serializing Interfaces and Abstract Classes -------------------------------------------- - -When dealing with objects that are fairly similar or share properties, you may -use interfaces or abstract classes. The Serializer component allows you to -serialize and deserialize these objects using a *"discriminator class mapping"*. - -The discriminator is the field (in the serialized string) used to differentiate -between the possible objects. In practice, when using the Serializer component, -pass a :class:`Symfony\\Component\\Serializer\\Mapping\\ClassDiscriminatorResolverInterface` -implementation to the :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer`. - -The Serializer component provides an implementation of ``ClassDiscriminatorResolverInterface`` -called :class:`Symfony\\Component\\Serializer\\Mapping\\ClassDiscriminatorFromClassMetadata` -which uses the class metadata factory and a mapping configuration to serialize -and deserialize objects of the correct class. - -When using this component inside a Symfony application and the class metadata factory is enabled -as explained in the :ref:`Attributes Groups section `, -this is already set up and you only need to provide the configuration. Otherwise:: - - // ... - use Symfony\Component\Serializer\Encoder\JsonEncoder; - use Symfony\Component\Serializer\Mapping\ClassDiscriminatorFromClassMetadata; - use Symfony\Component\Serializer\Mapping\ClassDiscriminatorMapping; - use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; - use Symfony\Component\Serializer\Serializer; - - $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - - $discriminator = new ClassDiscriminatorFromClassMetadata($classMetadataFactory); - - $serializer = new Serializer( - [new ObjectNormalizer($classMetadataFactory, null, null, null, $discriminator)], - ['json' => new JsonEncoder()] - ); - -Now configure your discriminator class mapping. Consider an application that -defines an abstract ``CodeRepository`` class extended by ``GitHubCodeRepository`` -and ``BitBucketCodeRepository`` classes: - -.. configuration-block:: - - .. code-block:: php-attributes - - namespace App; - - use App\BitBucketCodeRepository; - use App\GitHubCodeRepository; - use Symfony\Component\Serializer\Annotation\DiscriminatorMap; - - #[DiscriminatorMap(typeProperty: 'type', mapping: [ - 'github' => GitHubCodeRepository::class, - 'bitbucket' => BitBucketCodeRepository::class, - ])] - abstract class CodeRepository - { - // ... - } - - .. code-block:: yaml - - App\CodeRepository: - discriminator_map: - type_property: type - mapping: - github: 'App\GitHubCodeRepository' - bitbucket: 'App\BitBucketCodeRepository' - - .. code-block:: xml - - - - - - - - - - - -.. note:: - - The values of the ``mapping`` array option must be strings. - Otherwise, they will be cast into strings automatically. - -Once configured, the serializer uses the mapping to pick the correct class:: - - $serialized = $serializer->serialize(new GitHubCodeRepository(), 'json'); - // {"type": "github"} - - $repository = $serializer->deserialize($serialized, CodeRepository::class, 'json'); - // instanceof GitHubCodeRepository - -Learn more ----------- - -.. toctree:: - :maxdepth: 1 - :glob: - - /serializer - -.. seealso:: - - Normalizers for the Symfony Serializer Component supporting popular web API formats - (JSON-LD, GraphQL, OpenAPI, HAL, JSON:API) are available as part of the `API Platform`_ project. - -.. seealso:: - - A popular alternative to the Symfony Serializer component is the third-party - library, `JMS serializer`_ (versions before ``v1.12.0`` were released under - the Apache license, so incompatible with GPLv2 projects). - -.. _`PSR-1 standard`: https://www.php-fig.org/psr/psr-1/ -.. _`JMS serializer`: https://github.com/schmittjoh/serializer -.. _RFC3339: https://tools.ietf.org/html/rfc3339#section-5.8 -.. _`options with libxml`: https://www.php.net/manual/en/libxml.constants.php -.. _`DOM XML_* constants`: https://www.php.net/manual/en/dom.constants.php -.. _JSON: https://www.json.org/json-en.html -.. _XML: https://www.w3.org/XML/ -.. _YAML: https://yaml.org/ -.. _CSV: https://tools.ietf.org/html/rfc4180 -.. _`RFC 7807`: https://tools.ietf.org/html/rfc7807 -.. _`UTF-8 BOM`: https://en.wikipedia.org/wiki/Byte_order_mark -.. _`Value Objects`: https://en.wikipedia.org/wiki/Value_object -.. _`API Platform`: https://api-platform.com -.. _`list of PHP timezones`: https://www.php.net/manual/en/timezones.php -.. _`RFC 4122`: https://tools.ietf.org/html/rfc4122 -.. _`PHP reflection`: https://php.net/manual/en/book.reflection.php -.. _`data URI`: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs -.. _seld/jsonlint: https://github.com/Seldaek/jsonlint -.. _$flags: https://www.php.net/manual/en/json.constants.php -.. _`a CDATA section`: https://en.wikipedia.org/wiki/CDATA diff --git a/components/type_info.rst b/components/type_info.rst index 30ae11aa222..47fe9dfd9ba 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -11,11 +11,6 @@ This component provides: * A way to get types from PHP elements such as properties, method arguments, return types, and raw strings. -.. caution:: - - This component is :doc:`experimental ` and - could be changed at any time without prior notice. - Installation ------------ @@ -45,12 +40,24 @@ to the :class:`Symfony\\Component\\TypeInfo\\Type` static methods as following:: // Many others are available and can be // found in Symfony\Component\TypeInfo\TypeFactoryTrait -The second way of using the component is to use ``TypeInfo`` to resolve a type -based on reflection or a simple string:: +Resolvers +~~~~~~~~~ + +The second way to use the component is by using ``TypeInfo`` to resolve a type +based on reflection or a simple string. This approach is designed for libraries +that need a simple way to describe a class or anything with a type:: use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; + class Dummy + { + public function __construct( + public int $id, + ) { + } + } + // Instantiate a new resolver $typeResolver = TypeResolver::create(); @@ -63,12 +70,6 @@ based on reflection or a simple string:: // Type instances have several helper methods - // returns the main type (e.g. in this example it returns an "array" Type instance); - // for nullable types (e.g. string|null) it returns the non-null type (e.g. string) - // and for compound types (e.g. int|string) it throws an exception because both types - // can be considered the main one, so there's no way to pick one - $baseType = $type->getBaseType(); - // for collections, it returns the type of the item used as the key; // in this example, the collection is a list, so it returns an "int" Type instance $keyType = $type->getCollectionKeyType(); @@ -81,6 +82,111 @@ Each of these calls will return you a ``Type`` instance that corresponds to the static method used. You can also resolve types from a string (as shown in the ``bool`` parameter of the previous example) -.. note:: +PHPDoc Parsing +~~~~~~~~~~~~~~ + +In many cases, you may not have cleanly typed properties or may need more precise +type definitions provided by advanced PHPDoc. To achieve this, you can use a string +resolver based on the PHPDoc annotations. + +First, run the command ``composer require phpstan/phpdoc-parser`` to install the +PHP package required for string resolving. Then, follow these steps:: + + use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; + + class Dummy + { + public function __construct( + public int $id, + /** @var string[] $tags */ + public array $tags, + ) { + } + } + + $typeResolver = TypeResolver::create(); + $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns an "int" Type + $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns a collection with "int" as key and "string" as values Type + +Advanced Usages +~~~~~~~~~~~~~~~ + +The TypeInfo component provides various methods to manipulate and check types, +depending on your needs. + +**Identify** a type:: + + // define a simple integer type + $type = Type::int(); + // check if the type matches a specific identifier + $type->isIdentifiedBy(TypeIdentifier::INT); // true + $type->isIdentifiedBy(TypeIdentifier::STRING); // false + + // define a union type (equivalent to PHP's int|string) + $type = Type::union(Type::string(), Type::int()); + // now the second check is true because the union type contains the string type + $type->isIdentifiedBy(TypeIdentifier::INT); // true + $type->isIdentifiedBy(TypeIdentifier::STRING); // true + + class DummyParent {} + class Dummy extends DummyParent implements DummyInterface {} + + // define an object type + $type = Type::object(Dummy::class); + + // check if the type is an object or matches a specific class + $type->isIdentifiedBy(TypeIdentifier::OBJECT); // true + $type->isIdentifiedBy(Dummy::class); // true + // check if it inherits/implements something + $type->isIdentifiedBy(DummyParent::class); // true + $type->isIdentifiedBy(DummyInterface::class); // true + +Checking if a type **accepts a value**:: + + $type = Type::int(); + // check if the type accepts a given value + $type->accepts(123); // true + $type->accepts('z'); // false + + $type = Type::union(Type::string(), Type::int()); + // now the second check is true because the union type accepts either an int or a string value + $type->accepts(123); // true + $type->accepts('z'); // true + +.. versionadded:: 7.3 + + The :method:`Symfony\\Component\\TypeInfo\\Type::accepts` + method was introduced in Symfony 7.3. + +Using callables for **complex checks**:: + + class Foo + { + private int $integer; + private string $string; + private ?float $float; + } + + $reflClass = new \ReflectionClass(Foo::class); + + $resolver = TypeResolver::create(); + $integerType = $resolver->resolve($reflClass->getProperty('integer')); + $stringType = $resolver->resolve($reflClass->getProperty('string')); + $floatType = $resolver->resolve($reflClass->getProperty('float')); + + // define a callable to validate non-nullable number types + $isNonNullableNumber = function (Type $type): bool { + if ($type->isNullable()) { + return false; + } + + if ($type->isIdentifiedBy(TypeIdentifier::INT) || $type->isIdentifiedBy(TypeIdentifier::FLOAT)) { + return true; + } + + return false; + }; - To support raw string resolving, you need to install ``phpstan/phpdoc-parser`` package. + $integerType->isSatisfiedBy($isNonNullableNumber); // true + $stringType->isSatisfiedBy($isNonNullableNumber); // false + $floatType->isSatisfiedBy($isNonNullableNumber); // false diff --git a/components/uid.rst b/components/uid.rst index 73974ef8732..6c92fff0af9 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -386,7 +386,7 @@ entity primary keys:: // ... } -.. caution:: +.. warning:: Using UUIDs as primary keys is usually not recommended for performance reasons: indexes are slower and take more space (because UUIDs in binary format take @@ -574,7 +574,7 @@ entity primary keys:: // ... } -.. caution:: +.. warning:: Using ULIDs as primary keys is usually not recommended for performance reasons. Although ULIDs don't suffer from index fragmentation issues (because the values diff --git a/components/validator/resources.rst b/components/validator/resources.rst index c1474c1710d..5b1448dfba1 100644 --- a/components/validator/resources.rst +++ b/components/validator/resources.rst @@ -171,7 +171,7 @@ You can set this custom implementation using ->setMetadataFactory(new CustomMetadataFactory(...)) ->getValidator(); -.. caution:: +.. warning:: Since you are using a custom metadata factory, you can't configure loaders and caches using the ``add*Mapping()`` methods anymore. You now have to diff --git a/components/yaml.rst b/components/yaml.rst index 30f715a7a24..58436adffc2 100644 --- a/components/yaml.rst +++ b/components/yaml.rst @@ -428,6 +428,16 @@ you can dump them as ``~`` with the ``DUMP_NULL_AS_TILDE`` flag:: $dumped = Yaml::dump(['foo' => null], 2, 4, Yaml::DUMP_NULL_AS_TILDE); // foo: ~ +Another valid representation of the ``null`` value is an empty string. You can +use the ``DUMP_NULL_AS_EMPTY`` flag to dump null values as empty strings:: + + $dumped = Yaml::dump(['foo' => null], 2, 4, Yaml::DUMP_NULL_AS_EMPTY); + // foo: + +.. versionadded:: 7.3 + + The ``DUMP_NULL_AS_EMPTY`` flag was introduced in Symfony 7.3. + Dumping Numeric Keys as Strings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/configuration.rst b/configuration.rst index 51ad9368098..35bc2fb7eec 100644 --- a/configuration.rst +++ b/configuration.rst @@ -267,7 +267,7 @@ reusable configuration value. By convention, parameters are defined under the // ... -.. caution:: +.. warning:: By default and when using XML configuration, the values between ```` tags are not trimmed. This means that the value of the following parameter will be @@ -379,7 +379,7 @@ a new ``locale`` parameter is added to the ``config/services.yaml`` file). By convention, parameters whose names start with a dot ``.`` (for example, ``.mailer.transport``), are available only during the container compilation. - They are useful when working with :ref:`Compiler Passes ` + They are useful when working with :doc:`Compiler Passes ` to declare some temporary parameters that won't be available later in the application. Configuration parameters are usually validation-free, but you can ensure that @@ -809,7 +809,7 @@ Use environment variables in values by prefixing variables with ``$``: DB_USER=root DB_PASS=${DB_USER}pass # include the user as a password prefix -.. caution:: +.. warning:: The order is important when some env var depends on the value of other env vars. In the above example, ``DB_PASS`` must be defined after ``DB_USER``. @@ -830,7 +830,7 @@ Embed commands via ``$()`` (not supported on Windows): START_TIME=$(date) -.. caution:: +.. warning:: Using ``$()`` might not work depending on your shell. diff --git a/configuration/env_var_processors.rst b/configuration/env_var_processors.rst index baf4037d05a..2e82104db66 100644 --- a/configuration/env_var_processors.rst +++ b/configuration/env_var_processors.rst @@ -687,7 +687,7 @@ Symfony provides the following env var processors: ], ]); - .. caution:: + .. warning:: In order to ease extraction of the resource from the URL, the leading ``/`` is trimmed from the ``path`` component. diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index c9739679f69..62e8c2d4128 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -16,7 +16,7 @@ via Composer: .. code-block:: terminal - $ composer symfony/framework-bundle symfony/runtime + $ composer require symfony/framework-bundle symfony/runtime Next, create an ``index.php`` file that defines the kernel class and runs it: diff --git a/configuration/multiple_kernels.rst b/configuration/multiple_kernels.rst index 512ea57f24d..ec8742213b5 100644 --- a/configuration/multiple_kernels.rst +++ b/configuration/multiple_kernels.rst @@ -229,7 +229,7 @@ but it should typically be added to your web server configuration. # .env APP_ID=api -.. caution:: +.. warning:: The value of this variable must match the application directory within ``apps/`` as it is used in the Kernel to load the specific application diff --git a/configuration/override_dir_structure.rst b/configuration/override_dir_structure.rst index d17b67aedba..e5dff35b6d0 100644 --- a/configuration/override_dir_structure.rst +++ b/configuration/override_dir_structure.rst @@ -111,7 +111,7 @@ In this case you have changed the location of the cache directory to You can also change the cache directory by defining an environment variable named ``APP_CACHE_DIR`` whose value is the full path of the cache folder. -.. caution:: +.. warning:: You should keep the cache directory different for each environment, otherwise some unexpected behavior may happen. Each environment generates diff --git a/console.rst b/console.rst index 57f322c983d..6a3ba70300f 100644 --- a/console.rst +++ b/console.rst @@ -366,7 +366,7 @@ Output sections let you manipulate the Console output in advanced ways, such as are updated independently and :ref:`appending rows to tables ` that have already been rendered. -.. caution:: +.. warning:: Terminals only allow overwriting the visible content, so you must take into account the console height when trying to write/overwrite section contents. @@ -531,13 +531,13 @@ call ``setAutoExit(false)`` on it to get the command result in ``CommandTester`` You can also test a whole console application by using :class:`Symfony\\Component\\Console\\Tester\\ApplicationTester`. -.. caution:: +.. warning:: When testing commands using the ``CommandTester`` class, console events are not dispatched. If you need to test those events, use the :class:`Symfony\\Component\\Console\\Tester\\ApplicationTester` instead. -.. caution:: +.. warning:: When testing commands using the :class:`Symfony\\Component\\Console\\Tester\\ApplicationTester` class, don't forget to disable the auto exit flag:: @@ -547,7 +547,7 @@ call ``setAutoExit(false)`` on it to get the command result in ``CommandTester`` $tester = new ApplicationTester($application); -.. caution:: +.. warning:: When testing ``InputOption::VALUE_NONE`` command options, you must pass ``true`` to them:: @@ -619,7 +619,7 @@ profile is accessible through the web page of the profiler. terminal supports links). If you run it in debug verbosity (``-vvv``) you'll also see the time and memory consumed by the command. -.. caution:: +.. warning:: When profiling the ``messenger:consume`` command from the :doc:`Messenger ` component, add the ``--no-reset`` option to the command or you won't get any diff --git a/console/calling_commands.rst b/console/calling_commands.rst index c5bfc6e5a72..349f1357682 100644 --- a/console/calling_commands.rst +++ b/console/calling_commands.rst @@ -36,6 +36,9 @@ method):: '--yell' => true, ]); + // disable interactive behavior for the greet command + $greetInput->setInteractive(false); + $returnCode = $this->getApplication()->doRun($greetInput, $output); // ... @@ -57,7 +60,7 @@ method):: ``$this->getApplication()->find('demo:greet')->run()`` will allow proper events to be dispatched for that inner command as well. -.. caution:: +.. warning:: Note that all the commands will run in the same process and some of Symfony's built-in commands may not work well this way. For instance, the ``cache:clear`` diff --git a/console/command_in_controller.rst b/console/command_in_controller.rst index 64475bff103..74af9e17c15 100644 --- a/console/command_in_controller.rst +++ b/console/command_in_controller.rst @@ -11,7 +11,7 @@ service that can be reused in the controller. However, when the command is part of a third-party library, you don't want to modify or duplicate their code. Instead, you can run the command directly from the controller. -.. caution:: +.. warning:: In comparison with a direct call from the console, calling a command from a controller has a slight performance impact because of the request stack diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst index 75aa13d5be8..1393879a1df 100644 --- a/console/commands_as_services.rst +++ b/console/commands_as_services.rst @@ -51,7 +51,7 @@ argument (thanks to autowiring). In other words, you only need to create this class and everything works automatically! You can call the ``app:sunshine`` command and start logging. -.. caution:: +.. warning:: You *do* have access to services in ``configure()``. However, if your command is not :ref:`lazy `, try to avoid doing any @@ -130,7 +130,7 @@ only when the ``app:sunshine`` command is actually called. You don't need to call ``setName()`` for configuring the command when it is lazy. -.. caution:: +.. warning:: Calling the ``list`` command will instantiate all commands, including lazy commands. However, if the command is a ``Symfony\Component\Console\Command\LazyCommand``, then diff --git a/console/input.rst b/console/input.rst index c038ace56fc..baf47c6fd15 100644 --- a/console/input.rst +++ b/console/input.rst @@ -197,7 +197,7 @@ values after a whitespace or an ``=`` sign (e.g. ``--iterations 5`` or ``--iterations=5``), but short options can only use whitespaces or no separation at all (e.g. ``-i 5`` or ``-i5``). -.. caution:: +.. warning:: While it is possible to separate an option from its value with a whitespace, using this form leads to an ambiguity should the option appear before the diff --git a/contributing/code/bc.rst b/contributing/code/bc.rst index cff99a1554f..497c70fb01d 100644 --- a/contributing/code/bc.rst +++ b/contributing/code/bc.rst @@ -30,7 +30,7 @@ The second section, "Working on Symfony Code", is targeted at Symfony contributors. This section lists detailed rules that every contributor needs to follow to ensure smooth upgrades for our users. -.. caution:: +.. warning:: :doc:`Experimental Features ` and code marked with the ``@internal`` tags are excluded from our Backward @@ -53,7 +53,7 @@ All interfaces shipped with Symfony can be used in type hints. You can also call any of the methods that they declare. We guarantee that we won't break code that sticks to these rules. -.. caution:: +.. warning:: The exception to this rule are interfaces tagged with ``@internal``. Such interfaces should not be used or implemented. @@ -89,7 +89,7 @@ Using our Classes All classes provided by Symfony may be instantiated and accessed through their public methods and properties. -.. caution:: +.. warning:: Classes, properties and methods that bear the tag ``@internal`` as well as the classes located in the various ``*\Tests\`` namespaces are an @@ -146,7 +146,7 @@ Using our Traits All traits provided by Symfony may be used in your classes. -.. caution:: +.. warning:: The exception to this rule are traits tagged with ``@internal``. Such traits should not be used. diff --git a/contributing/code/bugs.rst b/contributing/code/bugs.rst index fba68617ee3..b0a46766026 100644 --- a/contributing/code/bugs.rst +++ b/contributing/code/bugs.rst @@ -4,7 +4,7 @@ Reporting a Bug Whenever you find a bug in Symfony, we kindly ask you to report it. It helps us make a better Symfony. -.. caution:: +.. warning:: If you think you've found a security issue, please use the special :doc:`procedure ` instead. diff --git a/contributing/code/maintenance.rst b/contributing/code/maintenance.rst index 04740ce8c6e..27e4fd73ea0 100644 --- a/contributing/code/maintenance.rst +++ b/contributing/code/maintenance.rst @@ -67,6 +67,9 @@ issue): * **Adding new deprecations**: After a version reaches stability, new deprecations cannot be added anymore. +* **Adding or updating annotations**: Adding or updating annotations (PHPDoc + annotations for instance) is not allowed; fixing them might be accepted. + Anything not explicitly listed above should be done on the next minor or major version instead. For instance, the following changes are never accepted in a patch version: diff --git a/contributing/documentation/format.rst b/contributing/documentation/format.rst index d933f3bcead..e81abe92b79 100644 --- a/contributing/documentation/format.rst +++ b/contributing/documentation/format.rst @@ -16,7 +16,7 @@ source code. If you want to learn more about this format, check out the `reStructuredText Primer`_ tutorial and the `reStructuredText Reference`_. -.. caution:: +.. warning:: If you are familiar with Markdown, be careful as things are sometimes very similar but different: diff --git a/contributing/documentation/standards.rst b/contributing/documentation/standards.rst index 420780d25f5..5e195d008fd 100644 --- a/contributing/documentation/standards.rst +++ b/contributing/documentation/standards.rst @@ -122,7 +122,7 @@ Example } } -.. caution:: +.. warning:: In YAML you should put a space after ``{`` and before ``}`` (e.g. ``{ _controller: ... }``), but this should not be done in Twig (e.g. ``{'hello' : 'value'}``). diff --git a/controller.rst b/controller.rst index 4fd03948ae2..026e76ed0a6 100644 --- a/controller.rst +++ b/controller.rst @@ -443,6 +443,26 @@ HTTP status to return if the validation fails:: The default status code returned if the validation fails is 404. +If you want to map your object to a nested array in your query using a specific key, +set the ``key`` option in the ``#[MapQueryString]`` attribute:: + + use App\Model\SearchDto; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\Attribute\MapQueryString; + + // ... + + public function dashboard( + #[MapQueryString(key: 'search')] SearchDto $searchDto + ): Response + { + // ... + } + +.. versionadded:: 7.3 + + The ``key`` option of ``#[MapQueryString]`` was introduced in Symfony 7.3. + If you need a valid DTO even when the request query string is empty, set a default value for your controller arguments:: @@ -788,6 +808,14 @@ response types. Some of these are mentioned below. To learn more about the ``Request`` and ``Response`` (and different ``Response`` classes), see the :ref:`HttpFoundation component documentation `. +.. note:: + + Technically, a controller can return a value other than a ``Response``. + However, your application is responsible for transforming that value into a + ``Response`` object. This is handled using :doc:`events ` + (specifically the :ref:`kernel.view event `), + an advanced feature you'll learn about later. + Accessing Configuration Values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -890,7 +918,7 @@ method:: { $response = $this->sendEarlyHints([ new Link(rel: 'preconnect', href: 'https://fonts.google.com'), - (new Link(href: '/style.css'))->withAttribute('as', 'stylesheet'), + (new Link(href: '/style.css'))->withAttribute('as', 'style'), (new Link(href: '/script.js'))->withAttribute('as', 'script'), ]); @@ -946,6 +974,6 @@ Learn more about Controllers .. _`Early hints`: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103 .. _`SAPI`: https://www.php.net/manual/en/function.php-sapi-name.php .. _`FrankenPHP`: https://frankenphp.dev -.. _`Validate Filters`: https://www.php.net/manual/en/filter.filters.validate.php +.. _`Validate Filters`: https://www.php.net/manual/en/filter.constants.php .. _`phpstan/phpdoc-parser`: https://packagist.org/packages/phpstan/phpdoc-parser .. _`phpdocumentor/type-resolver`: https://packagist.org/packages/phpdocumentor/type-resolver diff --git a/controller/error_pages.rst b/controller/error_pages.rst index 001e637c03e..fc36b88779a 100644 --- a/controller/error_pages.rst +++ b/controller/error_pages.rst @@ -319,7 +319,7 @@ error pages. .. note:: - If your listener calls ``setThrowable()`` on the + If your listener calls ``setResponse()`` on the :class:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent` event, propagation will be stopped and the response will be sent to the client. diff --git a/deployment.rst b/deployment.rst index 3edbc34dd6b..864ebc7a963 100644 --- a/deployment.rst +++ b/deployment.rst @@ -184,7 +184,7 @@ as you normally do: significantly by building a "class map". The ``--no-dev`` flag ensures that development packages are not installed in the production environment. -.. caution:: +.. warning:: If you get a "class not found" error during this step, you may need to run ``export APP_ENV=prod`` (or ``export SYMFONY_ENV=prod`` if you're not diff --git a/deployment/proxies.rst b/deployment/proxies.rst index cd5649d979a..4dad6f95fb1 100644 --- a/deployment/proxies.rst +++ b/deployment/proxies.rst @@ -102,7 +102,7 @@ using the following configuration options: Support for the ``SYMFONY_TRUSTED_PROXIES`` and ``SYMFONY_TRUSTED_HEADERS`` environment variables was introduced in Symfony 7.2. -.. caution:: +.. danger:: Enabling the ``Request::HEADER_X_FORWARDED_HOST`` option exposes the application to `HTTP Host header attacks`_. Make sure the proxy really diff --git a/doctrine.rst b/doctrine.rst index dc42a5b9e73..171f8a3348a 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -58,7 +58,7 @@ The database connection information is stored as an environment variable called # to use oracle: # DATABASE_URL="oci8://db_user:db_password@127.0.0.1:1521/db_name" -.. caution:: +.. warning:: If the username, password, host or database name contain any character considered special in a URI (such as ``: / ? # [ ] @ ! $ & ' ( ) * + , ; =``), @@ -174,7 +174,7 @@ Whoa! You now have a new ``src/Entity/Product.php`` file:: Confused why the price is an integer? Don't worry: this is just an example. But, storing prices as integers (e.g. 100 = $1 USD) can avoid rounding issues. -.. caution:: +.. warning:: There is a `limit of 767 bytes for the index key prefix`_ when using InnoDB tables in MySQL 5.6 and earlier versions. String columns with 255 @@ -204,7 +204,7 @@ If you want to use XML instead of attributes, add ``type: xml`` and ``dir: '%kernel.project_dir%/config/doctrine'`` to the entity mappings in your ``config/packages/doctrine.yaml`` file. -.. caution:: +.. warning:: Be careful not to use reserved SQL keywords as your table or column names (e.g. ``GROUP`` or ``USER``). See Doctrine's `Reserved SQL keywords documentation`_ @@ -320,7 +320,7 @@ before, execute your migrations: $ php bin/console doctrine:migrations:migrate -.. caution:: +.. warning:: If you are using an SQLite database, you'll see the following error: *PDOException: SQLSTATE[HY000]: General error: 1 Cannot add a NOT NULL diff --git a/doctrine/custom_dql_functions.rst b/doctrine/custom_dql_functions.rst index 1b3aa4aa185..e5b21819f58 100644 --- a/doctrine/custom_dql_functions.rst +++ b/doctrine/custom_dql_functions.rst @@ -132,7 +132,7 @@ In Symfony, you can register your custom DQL functions as follows: ->datetimeFunction('test_datetime', DatetimeFunction::class); }; -.. caution:: +.. warning:: DQL functions are instantiated by Doctrine outside of the Symfony :doc:`service container ` so you can't inject services diff --git a/doctrine/multiple_entity_managers.rst b/doctrine/multiple_entity_managers.rst index 014d9e4dccb..1a56c55ddad 100644 --- a/doctrine/multiple_entity_managers.rst +++ b/doctrine/multiple_entity_managers.rst @@ -15,7 +15,7 @@ entities, each with their own database connection strings or separate cache conf advanced and not usually required. Be sure you actually need multiple entity managers before adding in this layer of complexity. -.. caution:: +.. warning:: Entities cannot define associations across different entity managers. If you need that, there are `several alternatives`_ that require some custom setup. @@ -142,7 +142,7 @@ and ``customer``. The ``default`` entity manager manages entities in the entities in ``src/Entity/Customer``. You've also defined two connections, one for each entity manager, but you are free to define the same connection for both. -.. caution:: +.. warning:: When working with multiple connections and entity managers, you should be explicit about which configuration you want. If you *do* omit the name of @@ -251,7 +251,7 @@ The same applies to repository calls:: } } -.. caution:: +.. warning:: One entity can be managed by more than one entity manager. This however results in unexpected behavior when extending from ``ServiceEntityRepository`` diff --git a/event_dispatcher.rst b/event_dispatcher.rst index 6787cba2d83..27885af267b 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -41,6 +41,9 @@ The most common way to listen to an event is to register an **event listener**:: // Customize your response object to display the exception details $response = new Response(); $response->setContent($message); + // the exception message can contain unfiltered user input; + // set the content-type to text to avoid XSS issues + $response->headers->set('Content-Type', 'text/plain; charset=utf-8'); // HttpExceptionInterface is a special type of exception that // holds status code and header details @@ -798,3 +801,11 @@ could listen to the ``mailer.post_send`` event and change the method's return va That's it! Your subscriber should be called automatically (or read more about :ref:`event subscriber configuration `). + +Learn More +---------- + +- :ref:`The Request-Response Lifecycle ` +- :doc:`/reference/events` +- :ref:`Security-related Events ` +- :doc:`/components/event_dispatcher` diff --git a/form/bootstrap5.rst b/form/bootstrap5.rst index 400747bba12..db098a1ba09 100644 --- a/form/bootstrap5.rst +++ b/form/bootstrap5.rst @@ -171,7 +171,7 @@ class to the label: ], // ... -.. caution:: +.. warning:: Switches only work with **checkbox**. @@ -201,7 +201,7 @@ class to the ``row_attr`` option. } }) }} -.. caution:: +.. warning:: If you fill the ``help`` option of your form, it will also be rendered as part of the group. @@ -239,7 +239,7 @@ of your form type. } }) }} -.. caution:: +.. warning:: You **must** provide a ``label`` and a ``placeholder`` to make floating labels work properly. diff --git a/form/create_custom_field_type.rst b/form/create_custom_field_type.rst index 709f3321544..0d92a967fa0 100644 --- a/form/create_custom_field_type.rst +++ b/form/create_custom_field_type.rst @@ -449,7 +449,7 @@ are some examples of Twig block names for the postal address type: ``postal_address_zipCode_label`` The label block of the ZIP Code field. -.. caution:: +.. warning:: When the name of your form class matches any of the built-in field types, your form might not be rendered correctly. A form type named diff --git a/form/data_mappers.rst b/form/data_mappers.rst index cb5c7936701..38c92ce35ae 100644 --- a/form/data_mappers.rst +++ b/form/data_mappers.rst @@ -126,7 +126,7 @@ in your form type:: } } -.. caution:: +.. warning:: The data passed to the mapper is *not yet validated*. This means that your objects should allow being created in an invalid state in order to produce @@ -215,7 +215,7 @@ If available, these options have priority over the property path accessor and the default data mapper will still use the :doc:`PropertyAccess component ` for the other form fields. -.. caution:: +.. warning:: When a form has the ``inherit_data`` option set to ``true``, it does not use the data mapper and lets its parent map inner values. diff --git a/form/data_transformers.rst b/form/data_transformers.rst index 4e81fc3e930..db051a04bbc 100644 --- a/form/data_transformers.rst +++ b/form/data_transformers.rst @@ -8,7 +8,7 @@ can be rendered as a ``yyyy-MM-dd``-formatted input text box. Internally, a data 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:: +.. warning:: When a form field has the ``inherit_data`` option set to ``true``, data transformers are not applied to that field. @@ -340,7 +340,7 @@ that, after a successful submission, the Form component will pass a real If the issue isn't found, a form error will be created for that field and its error message can be controlled with the ``invalid_message`` field option. -.. caution:: +.. warning:: Be careful when adding your transformers. For example, the following is **wrong**, as the transformer would be applied to the entire form, instead of just this @@ -472,7 +472,7 @@ Which transformer you need depends on your situation. To use the view transformer, call ``addViewTransformer()``. -.. caution:: +.. warning:: Be careful with model transformers and :doc:`Collection ` field types. diff --git a/form/direct_submit.rst b/form/direct_submit.rst index 7b98134af18..7a08fb6978a 100644 --- a/form/direct_submit.rst +++ b/form/direct_submit.rst @@ -65,7 +65,7 @@ the fields defined by the form class. Otherwise, you'll see a form validation er argument to ``submit()``. Passing ``false`` will remove any missing fields within the form object. Otherwise, the missing fields will be set to ``null``. -.. caution:: +.. warning:: When the second parameter ``$clearMissing`` is ``false``, like with the "PATCH" method, the validation will only apply to the submitted fields. If diff --git a/form/events.rst b/form/events.rst index 745df2df453..dad6c242ddd 100644 --- a/form/events.rst +++ b/form/events.rst @@ -192,7 +192,7 @@ Form view data Same as in ``FormEvents::POST_SET_DATA`` See all form events at a glance in the :ref:`Form Events Information Table `. -.. caution:: +.. warning:: At this point, you cannot add or remove fields to the form. @@ -225,7 +225,7 @@ Form view data Normalized data transformed using a view transformer See all form events at a glance in the :ref:`Form Events Information Table `. -.. caution:: +.. warning:: At this point, you cannot add or remove fields to the current form and its children. diff --git a/form/form_collections.rst b/form/form_collections.rst index f0ad76a8a61..2a0ba99657f 100644 --- a/form/form_collections.rst +++ b/form/form_collections.rst @@ -195,7 +195,7 @@ then set on the ``tag`` field of the ``Task`` and can be accessed via ``$task->g So far, this works great, but only to edit *existing* tags. It doesn't allow us yet to add new tags or delete existing ones. -.. caution:: +.. warning:: You can embed nested collections as many levels down as you like. However, if you use Xdebug, you may receive a ``Maximum function nesting level of '100' @@ -427,13 +427,13 @@ That was fine, but forcing the use of the "adder" method makes handling these new ``Tag`` objects easier (especially if you're using Doctrine, which you will learn about next!). -.. caution:: +.. warning:: You have to create **both** ``addTag()`` and ``removeTag()`` methods, otherwise the form will still use ``setTag()`` even if ``by_reference`` is ``false``. You'll learn more about the ``removeTag()`` method later in this article. -.. caution:: +.. warning:: Symfony can only make the plural-to-singular conversion (e.g. from the ``tags`` property to the ``addTag()`` method) for English words. Code diff --git a/form/form_customization.rst b/form/form_customization.rst index 3f3cd0bbc89..1c23601a883 100644 --- a/form/form_customization.rst +++ b/form/form_customization.rst @@ -74,7 +74,7 @@ control over how each form field is rendered, so you can fully customize them: -.. caution:: +.. warning:: If you're rendering each field manually, make sure you don't forget the ``_token`` field that is automatically added for CSRF protection. @@ -305,7 +305,7 @@ Renders any errors for the given field. {# render any "global" errors not associated to any form field #} {{ form_errors(form) }} -.. caution:: +.. warning:: In the Bootstrap 4 form theme, ``form_errors()`` is already included in ``form_label()``. Read more about this in the diff --git a/form/form_themes.rst b/form/form_themes.rst index eb6f6f2ae22..8b82982edaa 100644 --- a/form/form_themes.rst +++ b/form/form_themes.rst @@ -177,7 +177,7 @@ of form themes: {# ... #} -.. caution:: +.. warning:: When using the ``only`` keyword, none of Symfony's built-in form themes (``form_div_layout.html.twig``, etc.) will be applied. In order to render diff --git a/form/inherit_data_option.rst b/form/inherit_data_option.rst index 19b14b27bcd..2caa0afcdbe 100644 --- a/form/inherit_data_option.rst +++ b/form/inherit_data_option.rst @@ -165,6 +165,6 @@ Finally, make this work by adding the location form to your two original forms:: That's it! You have extracted duplicated field definitions to a separate location form that you can reuse wherever you need it. -.. caution:: +.. warning:: Forms with the ``inherit_data`` option set cannot have ``*_SET_DATA`` event listeners. diff --git a/form/type_guesser.rst b/form/type_guesser.rst index 111f1b77986..106eb4e7742 100644 --- a/form/type_guesser.rst +++ b/form/type_guesser.rst @@ -162,7 +162,7 @@ instance with the value of the option. This constructor has 2 arguments: ``null`` is guessed when you believe the value of the option should not be set. -.. caution:: +.. warning:: You should be very careful using the ``guessMaxLength()`` method. When the type is a float, you cannot determine a length (e.g. you want a float to be diff --git a/form/unit_testing.rst b/form/unit_testing.rst index bf57e6d1afc..9603c5bc0d2 100644 --- a/form/unit_testing.rst +++ b/form/unit_testing.rst @@ -1,7 +1,7 @@ How to Unit Test your Forms =========================== -.. caution:: +.. warning:: This article is intended for developers who create :doc:`custom form types `. If you are using @@ -121,7 +121,7 @@ variable exists and will be available in your form themes:: Use `PHPUnit data providers`_ to test multiple form conditions using the same test code. -.. caution:: +.. warning:: When your type relies on the ``EntityType``, you should register the :class:`Symfony\\Bridge\\Doctrine\\Form\\DoctrineOrmExtension`, which will @@ -214,7 +214,7 @@ allows you to return a list of extensions to register:: { $validator = Validation::createValidator(); - // or if you also need to read constraints from annotations + // or if you also need to read constraints from attributes $validator = Validation::createValidatorBuilder() ->enableAttributeMapping() ->getValidator(); diff --git a/form/without_class.rst b/form/without_class.rst index 589f8a4739e..436976bdfcc 100644 --- a/form/without_class.rst +++ b/form/without_class.rst @@ -121,7 +121,7 @@ but here's a short example:: submitted data is validated using the ``Symfony\Component\Validator\Constraints\Valid`` constraint, unless you :doc:`disable validation `. -.. caution:: +.. warning:: When a form is only partially submitted (for example, in an HTTP PATCH request), only the constraints from the submitted form fields will be diff --git a/forms.rst b/forms.rst index a90e4ee1772..008c60a66c6 100644 --- a/forms.rst +++ b/forms.rst @@ -869,7 +869,7 @@ pass ``null`` to it:: } } -.. caution:: +.. warning:: When using a specific :doc:`form validation group `, the field type guesser will still consider *all* validation constraints when diff --git a/frontend.rst b/frontend.rst index f498dc737b5..c28e6fcf222 100644 --- a/frontend.rst +++ b/frontend.rst @@ -61,6 +61,10 @@ be executed by a browser. AssetMapper (Recommended) ~~~~~~~~~~~~~~~~~~~~~~~~~ +.. screencast:: + + Do you prefer video tutorials? Check out the `AssetMapper screencast series`_. + AssetMapper is the recommended system for handling your assets. It runs entirely in PHP with no complex build step or dependencies. It does this by leveraging the ``importmap`` feature of your browser, which is available in all browsers thanks @@ -118,6 +122,10 @@ the `StimulusBundle Documentation`_ Using a Front-end Framework (React, Vue, Svelte, etc) ----------------------------------------------------- +.. screencast:: + + Do you prefer video tutorials? Check out the `API Platform screencast series`_. + If you want to use a front-end framework (Next.js, React, Vue, Svelte, etc), we recommend using their native tools and using Symfony as a pure API. A wonderful tool to do that is `API Platform`_. Their standard distribution comes with a @@ -143,3 +151,5 @@ Other Front-End Articles .. _`Symfony UX`: https://ux.symfony.com .. _`API Platform`: https://api-platform.com/ .. _`SensioLabs Minify Bundle`: https://github.com/sensiolabs/minify-bundle +.. _`AssetMapper screencast series`: https://symfonycasts.com/screencast/asset-mapper +.. _`API Platform screencast series`: https://symfonycasts.com/screencast/api-platform diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index 685474e66d3..d68c77c0105 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -12,7 +12,7 @@ The component has two main features: * :ref:`Mapping & Versioning Assets `: All files inside of ``assets/`` are made available publicly and **versioned**. You can reference the file ``assets/images/product.jpg`` in a Twig template with ``{{ asset('images/product.jpg') }}``. - The final URL will include a version hash, like ``/assets/images/product-3c16d9220694c0e56d8648f25e6035e9.jpg``. + The final URL will include a version hash, like ``/assets/images/product-3c16d92m.jpg``. * :ref:`Importmaps `: A native browser feature that makes it easier to use the JavaScript ``import`` statement (e.g. ``import { Modal } from 'bootstrap'``) @@ -70,7 +70,7 @@ The path - ``images/duck.png`` - is relative to your mapped directory (``assets/ This is known as the **logical path** to your asset. If you look at the HTML in your page, the URL will be something -like: ``/assets/images/duck-3c16d9220694c0e56d8648f25e6035e9.png``. If you change +like: ``/assets/images/duck-3c16d92m.png``. If you change the file, the version part of the URL will also change automatically. .. _asset-mapper-compile-assets: @@ -78,7 +78,7 @@ the file, the version part of the URL will also change automatically. Serving Assets in dev vs prod ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In the ``dev`` environment, the URL ``/assets/images/duck-3c16d9220694c0e56d8648f25e6035e9.png`` +In the ``dev`` environment, the URL ``/assets/images/duck-3c16d92m.png`` is handled and returned by your Symfony app. For the ``prod`` environment, before deploy, you should run: @@ -91,7 +91,7 @@ This will physically copy all the files from your mapped directories to ``public/assets/`` so that they're served directly by your web server. See :ref:`Deployment ` for more details. -.. caution:: +.. warning:: If you run the ``asset-map:compile`` command on your development machine, you won't see any changes made to your assets when reloading the page. @@ -283,9 +283,9 @@ outputs an `importmap`_: @@ -342,8 +342,8 @@ The ``importmap()`` function also outputs a set of "preloads": .. code-block:: html - - + + This is a performance optimization and you can learn more about below in :ref:`Performance: Add Preloading `. @@ -415,6 +415,8 @@ from inside ``app.js``: // things on "window" become global variables window.$ = $; +.. _asset-mapper-handling-css: + Handling CSS ------------ @@ -492,9 +494,9 @@ for ``duck.png``: .. code-block:: css - /* public/assets/styles/app-3c16d9220694c0e56d8648f25e6035e9.css */ + /* public/assets/styles/app-3c16d92m.css */ .quack { - background-image: url('../images/duck-3c16d9220694c0e56d8648f25e6035e9.png'); + background-image: url('../images/duck-3c16d92m.png'); } .. _asset-mapper-tailwind: @@ -571,7 +573,7 @@ Sometimes a JavaScript file you're importing (e.g. ``import './duck.js'``), or a CSS/image file you're referencing won't be found, and you'll see a 404 error in your browser's console. You'll also notice that the 404 URL is missing the version hash in the filename (e.g. a 404 to ``/assets/duck.js`` instead of -a path like ``/assets/duck.1b7a64b3b3d31219c262cf72521a5267.js``). +a path like ``/assets/duck-1b7a64b3.js``). This is usually because the path is wrong. If you're referencing the file directly in a Twig template: @@ -646,7 +648,7 @@ To make your AssetMapper-powered site fly, there are a few things you need to do. If you want to take a shortcut, you can use a service like `Cloudflare`_, which will automatically do most of these things for you: -- **Use HTTP/2**: Your web server should be running HTTP/2 (or HTTP/3) so the +- **Use HTTP/2**: Your web server should be running HTTP/2 or HTTP/3 so the browser can download assets in parallel. HTTP/2 is automatically enabled in Caddy and can be activated in Nginx and Apache. Or, proxy your site through a service like Cloudflare, which will automatically enable HTTP/2 for you. @@ -654,7 +656,9 @@ which will automatically do most of these things for you: - **Compress your assets**: Your web server should compress (e.g. using gzip) your assets (JavaScript, CSS, images) before sending them to the browser. This is automatically enabled in Caddy and can be activated in Nginx and Apache. - In Cloudflare, assets are compressed by default. + In Cloudflare, assets are compressed by default. AssetMapper also supports + :ref:`precompressing your web assets ` to further + improve performance. - **Set long-lived cache expiry**: Your web server should set a long-lived ``Cache-Control`` HTTP header on your assets. Because the AssetMapper component includes a version @@ -702,6 +706,76 @@ even though it hasn't yet seen the ``import`` statement for them. Additionally, if the :doc:`WebLink Component ` is available in your application, Symfony will add a ``Link`` header in the response to preload the CSS files. +.. _performance-precompressing: + +Pre-Compressing Assets +---------------------- + +Although most servers (Caddy, Nginx, Apache, FrankenPHP) and services like Cloudflare +provide asset compression features, AssetMapper also allows you to compress all +your assets before serving them. + +This improves performance because you can compress assets using the highest (and +slowest) compression ratios beforehand and provide those compressed assets to the +server, which then returns them to the client without wasting CPU resources on +compression. + +AssetMapper supports `Brotli`_, `Zstandard`_ and `gzip`_ compression formats. +Before using any of them, the server that pre-compresses assets must have +installed the following PHP extensions or CLI commands: + +* Brotli: ``brotli`` CLI command; `brotli PHP extension`_; +* Zstandard: ``zstd`` CLI command; `zstd PHP extension`_; +* gzip: ``zopfli`` (better) or ``gzip`` CLI command; `zlib PHP extension`_. + +Then, update your AssetMapper configuration to define which compression to use +and which file extensions should be compressed: + +.. code-block:: yaml + + # config/packages/asset_mapper.yaml + framework: + asset_mapper: + # ... + + precompress: + format: 'zstandard' + # if you don't define the following option, AssetMapper will compress all + # the extensions considered safe (css, js, json, svg, xml, ttf, otf, wasm, etc.) + extensions: ['css', 'js', 'json', 'svg', 'xml'] + +Now, when running the ``asset-map:compile`` command, all matching files will be +compressed in the configured format and at the highest compression level. The +compressed files are created with the same name as the original but with the +``.br``, ``.zst``, or ``.gz`` extension appended. + +Then, you need to configure your web server to serve the precompressed assets +instead of the original ones: + +.. configuration-block:: + + .. code-block:: caddy + + file_server { + precompressed br zstd gzip + } + + .. code-block:: nginx + + gzip_static on; + + # Requires https://github.com/google/ngx_brotli + brotli_static on; + + # Requires https://github.com/tokers/zstd-nginx-module + zstd_static on; + +.. tip:: + + AssetMapper provides an ``assets:compress`` CLI command and a service called + ``asset_mapper.compressor`` that you can use anywhere in your application to + compress any kind of files (e.g. files uploaded by users to your application). + Frequently Asked Questions -------------------------- @@ -846,7 +920,7 @@ be versioned! It will output something like: .. code-block:: html+twig - + Overriding 3rd-Party Assets ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1093,6 +1167,24 @@ it in the CSP header, and then pass the same nonce to the Twig function: {# the csp_nonce() function is defined by the NelmioSecurityBundle #} {{ importmap('app', {'nonce': csp_nonce('script')}) }} +Content Security Policy and CSS Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your importmap includes CSS files, AssetMapper uses a trick to load those by +adding ``data:application/javascript`` to the rendered importmap (see +:ref:`Handling CSS `). + +This can cause browsers to report CSP violations and block the CSS files from +being loaded. To prevent this, you can add `strict-dynamic`_ to the ``script-src`` +directive of your Content Security Policy, to tell the browser that the importmap +is allowed to load other resources. + +.. note:: + + When using ``strict-dynamic``, the browser will ignore any other sources in + ``script-src`` such as ``'self'`` or ``'unsafe-inline'``, so any other + `` + +
+ Then retrieve it from your JS file: .. code-block:: javascript @@ -290,6 +295,9 @@ Then retrieve it from your JS file: const eventSource = new EventSource(url); // ... + // with Stimulus + this.eventSource = new EventSource(this.mercureUrlValue); + Mercure also allows subscribing to several topics, and to use URI Templates or the special value ``*`` (matched by all topics) as patterns: @@ -407,7 +415,7 @@ of the ``Update`` constructor to ``true``:: $update = new Update( 'https://example.com/books/1', json_encode(['status' => 'OutOfStock']), - true // private + private: true ); // Publisher's JWT must contain this topic, a URI template it matches or * in mercure.publish or you'll get a 401 @@ -704,6 +712,8 @@ enable it:: :alt: The Mercure panel of the Symfony Profiler, showing information like time, memory, topics and data of each message sent by Mercure. :class: with-browser +The Mercure hub itself provides a debug tool that can be enabled and available on `/.well-known/mercure/ui/` + Async dispatching ----------------- From 4a324e82ced465a1cebf186d694aa69f5703aded Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 18 Mar 2025 13:46:47 +0100 Subject: [PATCH 303/642] Tweaks --- mercure.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mercure.rst b/mercure.rst index 125511f6f2b..c2aa068167d 100644 --- a/mercure.rst +++ b/mercure.rst @@ -415,7 +415,7 @@ of the ``Update`` constructor to ``true``:: $update = new Update( 'https://example.com/books/1', json_encode(['status' => 'OutOfStock']), - private: true + true // private ); // Publisher's JWT must contain this topic, a URI template it matches or * in mercure.publish or you'll get a 401 @@ -712,7 +712,8 @@ enable it:: :alt: The Mercure panel of the Symfony Profiler, showing information like time, memory, topics and data of each message sent by Mercure. :class: with-browser -The Mercure hub itself provides a debug tool that can be enabled and available on `/.well-known/mercure/ui/` +The Mercure hub itself provides a debug tool that can be enabled and it's +available on ``/.well-known/mercure/ui/`` Async dispatching ----------------- From 1e8d50b5aac5e54cfd07214e2b3fdab15c205e01 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 18 Mar 2025 13:49:23 +0100 Subject: [PATCH 304/642] Update the docs builder dependencies --- _build/composer.lock | 519 +++++++++++++++++++------------------------ 1 file changed, 231 insertions(+), 288 deletions(-) diff --git a/_build/composer.lock b/_build/composer.lock index 89a4e7da3c6..a30ed16776b 100644 --- a/_build/composer.lock +++ b/_build/composer.lock @@ -4,77 +4,33 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8a771cef10c68c570bff7875e4bdece3", + "content-hash": "6524e63395216a3a297f1c78c7f39d86", "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.2.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/doctrine/event-manager.git", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520" + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/95aa4cb529f1e96576f3fda9f5705ada4056a520", - "reference": "95aa4cb529f1e96576f3fda9f5705ada4056a520", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/b680156fa328f1dfd874fd48c7026c41570b9c6e", + "reference": "b680156fa328f1dfd874fd48c7026c41570b9c6e", "shasum": "" }, "require": { - "doctrine/deprecations": "^0.5.3 || ^1", - "php": "^7.1 || ^8.0" + "php": "^8.1" }, "conflict": { "doctrine/common": "<2.9" }, "require-dev": { - "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" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^10.5", + "vimeo/psalm": "^5.24" }, "type": "library", "autoload": { @@ -123,7 +79,7 @@ ], "support": { "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/1.2.0" + "source": "https://github.com/doctrine/event-manager/tree/2.0.1" }, "funding": [ { @@ -139,42 +95,42 @@ "type": "tidelift" } ], - "time": "2022-10-12T20:51:15+00:00" + "time": "2024-05-22T20:47:39+00:00" }, { "name": "doctrine/rst-parser", - "version": "0.5.3", + "version": "0.5.6", "source": { "type": "git", "url": "https://github.com/doctrine/rst-parser.git", - "reference": "0b1d413d6bb27699ccec1151da6f617554d02c13" + "reference": "ca7f5f31f9ea58fde5aeffe0f7b8eb569e71a104" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/rst-parser/zipball/0b1d413d6bb27699ccec1151da6f617554d02c13", - "reference": "0b1d413d6bb27699ccec1151da6f617554d02c13", + "url": "https://api.github.com/repos/doctrine/rst-parser/zipball/ca7f5f31f9ea58fde5aeffe0f7b8eb569e71a104", + "reference": "ca7f5f31f9ea58fde5aeffe0f7b8eb569e71a104", "shasum": "" }, "require": { - "doctrine/event-manager": "^1.0", + "doctrine/event-manager": "^1.0 || ^2.0", "php": "^7.2 || ^8.0", - "symfony/filesystem": "^4.1 || ^5.0 || ^6.0", - "symfony/finder": "^4.1 || ^5.0 || ^6.0", + "symfony/filesystem": "^4.1 || ^5.0 || ^6.0 || ^7.0", + "symfony/finder": "^4.1 || ^5.0 || ^6.0 || ^7.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/string": "^5.3 || ^6.0", - "symfony/translation-contracts": "^1.1 || ^2.0", + "symfony/string": "^5.3 || ^6.0 || ^7.0", + "symfony/translation-contracts": "^1.1 || ^2.0 || ^3.0", "twig/twig": "^2.9 || ^3.3" }, "require-dev": { - "doctrine/coding-standard": "^10.0", + "doctrine/coding-standard": "^11.0", "gajus/dindent": "^2.0.2", "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" + "symfony/css-selector": "4.4 || ^5.2 || ^6.0 || ^7.0", + "symfony/dom-crawler": "4.4 || ^5.2 || ^6.0 || ^7.0" }, "type": "library", "autoload": { @@ -210,32 +166,30 @@ ], "support": { "issues": "https://github.com/doctrine/rst-parser/issues", - "source": "https://github.com/doctrine/rst-parser/tree/0.5.3" + "source": "https://github.com/doctrine/rst-parser/tree/0.5.6" }, - "time": "2022-12-29T16:24:52+00:00" + "time": "2024-01-14T11:02:23+00:00" }, { "name": "masterminds/html5", - "version": "2.7.6", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "897eb517a343a2281f11bc5556d6548db7d93947" + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/897eb517a343a2281f11bc5556d6548db7d93947", - "reference": "897eb517a343a2281f11bc5556d6548db7d93947", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", "shasum": "" }, "require": { - "ext-ctype": "*", "ext-dom": "*", - "ext-libxml": "*", "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7" + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9" }, "type": "library", "extra": { @@ -279,9 +233,9 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.7.6" + "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" }, - "time": "2022-08-18T16:18:26+00:00" + "time": "2024-03-31T07:05:07+00:00" }, { "name": "psr/container", @@ -338,16 +292,16 @@ }, { "name": "psr/log", - "version": "3.0.0", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, "require": { @@ -382,9 +336,9 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { "name": "scrivo/highlight.php", @@ -520,24 +474,24 @@ }, { "name": "symfony/console", - "version": "v6.2.8", + "version": "v6.4.17", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b" + "reference": "799445db3f15768ecc382ac5699e6da0520a0a04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3582d68a64a86ec25240aaa521ec8bc2342b369b", - "reference": "3582d68a64a86ec25240aaa521ec8bc2342b369b", + "url": "https://api.github.com/repos/symfony/console/zipball/799445db3f15768ecc382ac5699e6da0520a0a04", + "reference": "799445db3f15768ecc382ac5699e6da0520a0a04", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/deprecation-contracts": "^2.1|^3", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.4|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" }, "conflict": { "symfony/dependency-injection": "<5.4", @@ -551,18 +505,16 @@ }, "require-dev": { "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", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -596,7 +548,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.8" + "source": "https://github.com/symfony/console/tree/v6.4.17" }, "funding": [ { @@ -612,20 +564,20 @@ "type": "tidelift" } ], - "time": "2023-03-29T21:42:15+00:00" + "time": "2024-12-07T12:07:30+00:00" }, { "name": "symfony/css-selector", - "version": "v6.2.7", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "aedf3cb0f5b929ec255d96bbb4909e9932c769e0" + "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/aedf3cb0f5b929ec255d96bbb4909e9932c769e0", - "reference": "aedf3cb0f5b929ec255d96bbb4909e9932c769e0", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/cb23e97813c5837a041b73a6d63a9ddff0778f5e", + "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e", "shasum": "" }, "require": { @@ -661,7 +613,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.2.7" + "source": "https://github.com/symfony/css-selector/tree/v6.4.13" }, "funding": [ { @@ -677,20 +629,20 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.2.1", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e" + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", - "reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", "shasum": "" }, "require": { @@ -698,12 +650,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.3-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -728,7 +680,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" }, "funding": [ { @@ -744,20 +696,20 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:25:55+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/dom-crawler", - "version": "v6.2.8", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "0e0d0f709997ad1224ef22bb0a28287c44b7840f" + "reference": "19073e3e0bb50cbc1cb286077069b3107085206f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/0e0d0f709997ad1224ef22bb0a28287c44b7840f", - "reference": "0e0d0f709997ad1224ef22bb0a28287c44b7840f", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/19073e3e0bb50cbc1cb286077069b3107085206f", + "reference": "19073e3e0bb50cbc1cb286077069b3107085206f", "shasum": "" }, "require": { @@ -767,10 +719,7 @@ "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "^5.4|^6.0" - }, - "suggest": { - "symfony/css-selector": "" + "symfony/css-selector": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -798,7 +747,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.2.8" + "source": "https://github.com/symfony/dom-crawler/tree/v6.4.19" }, "funding": [ { @@ -814,20 +763,20 @@ "type": "tidelift" } ], - "time": "2023-03-09T16:20:02+00:00" + "time": "2025-02-14T17:58:34+00:00" }, { "name": "symfony/filesystem", - "version": "v6.2.7", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3" + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { @@ -835,6 +784,9 @@ "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" + }, "type": "library", "autoload": { "psr-4": { @@ -861,7 +813,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.7" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "funding": [ { @@ -877,27 +829,27 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/finder", - "version": "v6.2.7", + "version": "v6.4.17", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb" + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/20808dc6631aecafbe67c186af5dcb370be3a0eb", - "reference": "20808dc6631aecafbe67c186af5dcb370be3a0eb", + "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "symfony/filesystem": "^6.0" + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -925,7 +877,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.2.7" + "source": "https://github.com/symfony/finder/tree/v6.4.17" }, "funding": [ { @@ -941,28 +893,32 @@ "type": "tidelift" } ], - "time": "2023-02-16T09:57:23+00:00" + "time": "2024-12-29T13:51:37+00:00" }, { "name": "symfony/http-client", - "version": "v6.2.8", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "66391ba3a8862c560e1d9134c96d9bd2a619b477" + "reference": "3294a433fc9d12ae58128174896b5b1822c28dad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/66391ba3a8862c560e1d9134c96d9bd2a619b477", - "reference": "66391ba3a8862c560e1d9134c96d9bd2a619b477", + "url": "https://api.github.com/repos/symfony/http-client/zipball/3294a433fc9d12ae58128174896b5b1822c28dad", + "reference": "3294a433fc9d12ae58128174896b5b1822c28dad", "shasum": "" }, "require": { "php": ">=8.1", "psr/log": "^1|^2|^3", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/http-client-contracts": "^3", - "symfony/service-contracts": "^1.0|^2|^3" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.3" }, "provide": { "php-http/async-client-implementation": "*", @@ -975,14 +931,15 @@ "amphp/http-client": "^4.2.1", "amphp/http-tunnel": "^1.0", "amphp/socket": "^1.1", - "guzzlehttp/promises": "^1.4", + "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.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" + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -1013,7 +970,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.2.8" + "source": "https://github.com/symfony/http-client/tree/v6.4.19" }, "funding": [ { @@ -1029,36 +986,33 @@ "type": "tidelift" } ], - "time": "2023-03-31T09:14:44+00:00" + "time": "2025-02-13T09:55:13+00:00" }, { "name": "symfony/http-client-contracts", - "version": "v3.2.1", + "version": "v3.5.2", "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "df2ecd6cb70e73c1080e6478aea85f5f4da2c48b" + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/df2ecd6cb70e73c1080e6478aea85f5f4da2c48b", - "reference": "df2ecd6cb70e73c1080e6478aea85f5f4da2c48b", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645", + "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645", "shasum": "" }, "require": { "php": ">=8.1" }, - "suggest": { - "symfony/http-client-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.3-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -1094,7 +1048,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/http-client-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2" }, "funding": [ { @@ -1110,24 +1064,24 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2024-12-07T08:49:48+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.27.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", - "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -1137,12 +1091,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1176,7 +1127,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -1192,36 +1143,33 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.27.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", - "reference": "511a08c03c1960e08a883f4cffcacd219b758354", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1257,7 +1205,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -1273,36 +1221,33 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.27.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1341,7 +1286,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -1357,24 +1302,24 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -1384,12 +1329,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.27-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -1424,7 +1366,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -1440,20 +1382,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", - "version": "v6.2.8", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "75ed64103df4f6615e15a7fe38b8111099f47416" + "reference": "7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/75ed64103df4f6615e15a7fe38b8111099f47416", - "reference": "75ed64103df4f6615e15a7fe38b8111099f47416", + "url": "https://api.github.com/repos/symfony/process/zipball/7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3", + "reference": "7a1c12e87b08ec9c97abdd188c9b3f5a40e37fc3", "shasum": "" }, "require": { @@ -1485,7 +1427,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.2.8" + "source": "https://github.com/symfony/process/tree/v6.4.19" }, "funding": [ { @@ -1501,40 +1443,38 @@ "type": "tidelift" } ], - "time": "2023-03-09T16:20:02+00:00" + "time": "2025-02-04T13:35:48+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.2.1", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a" + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/a8c9cedf55f314f3a186041d19537303766df09a", - "reference": "a8c9cedf55f314f3a186041d19537303766df09a", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^2.0" + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.3-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { @@ -1570,7 +1510,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.2.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" }, "funding": [ { @@ -1586,20 +1526,20 @@ "type": "tidelift" } ], - "time": "2023-03-01T10:32:47+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "symfony/string", - "version": "v6.2.8", + "version": "v6.4.15", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" + "reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", - "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", + "url": "https://api.github.com/repos/symfony/string/zipball/73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f", + "reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f", "shasum": "" }, "require": { @@ -1610,14 +1550,14 @@ "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/translation-contracts": "<2.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "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" + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -1656,7 +1596,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.8" + "source": "https://github.com/symfony/string/tree/v6.4.15" }, "funding": [ { @@ -1672,42 +1612,42 @@ "type": "tidelift" } ], - "time": "2023-03-20T16:06:02+00:00" + "time": "2024-11-13T13:31:12+00:00" }, { "name": "symfony/translation-contracts", - "version": "v2.5.2", + "version": "v3.5.1", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe" + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", "shasum": "" }, "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/translation-implementation": "" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1734,7 +1674,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" }, "funding": [ { @@ -1750,38 +1690,41 @@ "type": "tidelift" } ], - "time": "2022-06-27T16:58:25+00:00" + "time": "2024-09-25T14:20:29+00:00" }, { "name": "twig/twig", - "version": "v3.5.1", + "version": "v3.20.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15" + "reference": "3468920399451a384bef53cf7996965f7cd40183" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6e0510cc793912b451fd40ab983a1d28f611c15", - "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3468920399451a384bef53cf7996965f7cd40183", + "reference": "3468920399451a384bef53cf7996965f7cd40183", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { - "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + "phpstan/phpstan": "^2.0", + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.5-dev" - } - }, "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -1814,7 +1757,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.5.1" + "source": "https://github.com/twigphp/Twig/tree/v3.20.0" }, "funding": [ { @@ -1826,21 +1769,21 @@ "type": "tidelift" } ], - "time": "2023-02-08T07:49:20+00:00" + "time": "2025-02-13T08:34:43+00:00" } ], "packages-dev": [], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { "php": ">=8.1" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.1.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } From c679fe5d157635c1f02bedcfc2353199d1f12977 Mon Sep 17 00:00:00 2001 From: EtienneHosman <43244960+EtienneHosman@users.noreply.github.com> Date: Tue, 18 Mar 2025 13:51:03 +0100 Subject: [PATCH 305/642] Update virtual-machine.rst Syntax error: made 'all' a string --- frontend/encore/virtual-machine.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/encore/virtual-machine.rst b/frontend/encore/virtual-machine.rst index d18026d3633..34587173b93 100644 --- a/frontend/encore/virtual-machine.rst +++ b/frontend/encore/virtual-machine.rst @@ -107,7 +107,7 @@ the dev-server. To fix this, set the ``allowedHosts`` option: // ... .configureDevServerOptions(options => { - options.allowedHosts = all; + options.allowedHosts = 'all'; }) .. warning:: From b8af29ce4c629060b2f167a45afe77acac4bdf32 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 18 Mar 2025 16:04:59 +0100 Subject: [PATCH 306/642] Reword --- routing.rst | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/routing.rst b/routing.rst index c5cf5cd7f33..68d6a30fc41 100644 --- a/routing.rst +++ b/routing.rst @@ -434,6 +434,21 @@ evaluates them: blog_show ANY ANY ANY /blog/{slug} ---------------- ------- ------- ----- -------------------------------------------- + # pass this option to also display all the defined route aliases + $ php bin/console debug:router --show-aliases + + # pass this option to only display routes that match the given HTTP method + # (you can use the special value ANY to see routes that match any method) + $ php bin/console debug:router --method=GET + $ php bin/console debug:router --method=ANY + + # pass the option more than once to display the routes that match all the given methods + $ php bin/console debug:router --method=GET --method=PATCH + +.. versionadded:: 7.3 + + The ``--method`` option was introduced in Symfony 7.3. + Pass the name (or part of the name) of some route to this argument to print the route details: @@ -451,15 +466,6 @@ route details: | | utf8: true | +-------------+---------------------------------------------------------+ -.. tip:: - - Use the ``--show-aliases`` option to show all available aliases for a given - route. - -.. tip:: - - Use the ``--method`` option to filter routes by HTTP method. For example, to only show routes that use the ``GET`` method, add ``--method=GET`` - The other command is called ``router:match`` and it shows which route will match the given URL. It's useful to find out why some URL is not executing the controller action that you expect: From 387943cee13b5c0dc01b7b85289cd46a12f2940b Mon Sep 17 00:00:00 2001 From: Maxime Doutreluingne Date: Sun, 23 Feb 2025 18:45:08 +0100 Subject: [PATCH 307/642] [Validator] Allow Unique constraint validation on all elements --- reference/constraints/Unique.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/reference/constraints/Unique.rst b/reference/constraints/Unique.rst index a1be67d6f2f..a37eba30c61 100644 --- a/reference/constraints/Unique.rst +++ b/reference/constraints/Unique.rst @@ -216,4 +216,17 @@ trailing whitespace during validation. .. include:: /reference/constraints/_payload-option.rst.inc +``stopOnFirstError`` +~~~~~~~~~~~~~~~~~~~~ + +**type**: ``boolean`` **default**: ``true`` + +By default, this constraint stops at the first violation. If the option is set to ``false``, +validation will continue on all elements and return all detected +:class:`Symfony\\Component\\Validator\\ConstraintViolation` object. + +.. versionadded:: 7.3 + + The ``stopOnFirstError`` option was introduced in Symfony 7.3. + .. _`PHP callable`: https://www.php.net/callable From 8801059a62bcc91918964f46c28142c947a40d62 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 18 Mar 2025 16:17:32 +0100 Subject: [PATCH 308/642] Minor tweak --- reference/constraints/Unique.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/reference/constraints/Unique.rst b/reference/constraints/Unique.rst index a37eba30c61..9ce84139cd5 100644 --- a/reference/constraints/Unique.rst +++ b/reference/constraints/Unique.rst @@ -221,9 +221,9 @@ trailing whitespace during validation. **type**: ``boolean`` **default**: ``true`` -By default, this constraint stops at the first violation. If the option is set to ``false``, -validation will continue on all elements and return all detected -:class:`Symfony\\Component\\Validator\\ConstraintViolation` object. +By default, this constraint stops at the first violation. If this option is set +to ``false``, validation continues on all elements and returns all detected +:class:`Symfony\\Component\\Validator\\ConstraintViolation` objects. .. versionadded:: 7.3 From 4213bb57c7f139451d8df44c1f6a91d07559ec5f Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Tue, 18 Mar 2025 22:26:31 +0100 Subject: [PATCH 309/642] [Serializer] Add SnakeCaseToCamelCaseNameConverter --- serializer.rst | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/serializer.rst b/serializer.rst index cd181787763..bd8506cbbe3 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1239,6 +1239,67 @@ setting the ``name_converter`` setting to ]; $serializer = new Serializer($normalizers, $encoders); +snake_case to CamelCase +~~~~~~~~~~~~~~~~~~~~~~~ + +In Symfony applications is common to use camelCase to name properties. However +some packages can use snake_case as convention. + +Symfony provides a built-in name converter designed to transform between +CamelCase and snake_cased styles during serialization and deserialization +processes. You can use it instead of the metadata aware name converter by +setting the ``name_converter`` setting to +``serializer.name_converter.snake_case_to_camel_case``: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/serializer.yaml + framework: + serializer: + name_converter: 'serializer.name_converter.snake_case_to_camel_case' + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/packages/serializer.php + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework): void { + $framework->serializer() + ->nameConverter('serializer.name_converter.snake_case_to_camel_case') + ; + }; + + .. code-block:: php-standalone + + use Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + + // ... + $normalizers = [ + new ObjectNormalizer(null, new SnakeCaseToCamelCaseNameConverter()), + ]; + $serializer = new Serializer($normalizers, $encoders); + .. _serializer-built-in-normalizers: Serializer Normalizers From a1647c609f11b860abf7de38aadb261fbbf9122a Mon Sep 17 00:00:00 2001 From: HypeMC Date: Tue, 18 Mar 2025 23:37:06 +0100 Subject: [PATCH 310/642] [Serializer] Add missing space + fix import --- serializer.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serializer.rst b/serializer.rst index 5c82e6e566a..1c30ad89c6c 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1377,7 +1377,7 @@ Built-in Normalizers ~~~~~~~~~~~~~~~~~~~~ Besides the normalizers registered by default (see previous section), the -serializer component also provides some extra normalizers.You can register +serializer component also provides some extra normalizers. You can register these by defining a service and tag it with :ref:`serializer.normalizer `. For instance, to use the ``CustomNormalizer`` you have to define a service like: @@ -1421,7 +1421,7 @@ like: .. code-block:: php // config/services.php - namespace Symfony\Component\DependencyInjection\Loader\Configurator; + namespace Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Component\Serializer\Normalizer\CustomNormalizer; From 020ef3a702bb8b173d164d40d73626dc449d559a Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Tue, 18 Mar 2025 22:56:19 +0100 Subject: [PATCH 311/642] Replace TaggedIterator and TaggedLocator by Au*towireIterator and AutowireLocator --- .../service_subscribers_locators.rst | 20 +++++++++---------- service_container/tags.rst | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index 9c6451931d1..a2aabb48901 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -270,8 +270,8 @@ the following dependency injection attributes in the ``getSubscribedServices()`` method directly: * :class:`Symfony\\Component\\DependencyInjection\\Attribute\\Autowire` -* :class:`Symfony\\Component\\DependencyInjection\\Attribute\\TaggedIterator` -* :class:`Symfony\\Component\\DependencyInjection\\Attribute\\TaggedLocator` +* :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireIterator` +* :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator` * :class:`Symfony\\Component\\DependencyInjection\\Attribute\\Target` * :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireDecorated` @@ -282,8 +282,8 @@ This is done by having ``getSubscribedServices()`` return an array of use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; - use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; - use Symfony\Component\DependencyInjection\Attribute\TaggedLocator; + use Symfony\Component\DependencyInjection\Attribute\AutowireIterator; + use Symfony\Component\DependencyInjection\Attribute\AutowireLocator; use Symfony\Component\DependencyInjection\Attribute\Target; use Symfony\Contracts\Service\Attribute\SubscribedService; @@ -299,11 +299,11 @@ This is done by having ``getSubscribedServices()`` return an array of // Target new SubscribedService('event.logger', LoggerInterface::class, attributes: new Target('eventLogger')), - // TaggedIterator - new SubscribedService('loggers', 'iterable', attributes: new TaggedIterator('logger.tag')), + // AutowireIterator + new SubscribedService('loggers', 'iterable', attributes: new AutowireIterator('logger.tag')), - // TaggedLocator - new SubscribedService('handlers', ContainerInterface::class, attributes: new TaggedLocator('handler.tag')), + // AutowireLocator + new SubscribedService('handlers', ContainerInterface::class, attributes: new AutowireLocator('handler.tag')), ]; } @@ -975,8 +975,8 @@ You can use the ``attributes`` argument of ``SubscribedService`` to add any of the following dependency injection attributes: * :class:`Symfony\\Component\\DependencyInjection\\Attribute\\Autowire` -* :class:`Symfony\\Component\\DependencyInjection\\Attribute\\TaggedIterator` -* :class:`Symfony\\Component\\DependencyInjection\\Attribute\\TaggedLocator` +* :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireIterator` +* :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator` * :class:`Symfony\\Component\\DependencyInjection\\Attribute\\Target` * :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireDecorated` diff --git a/service_container/tags.rst b/service_container/tags.rst index 270d6702f5a..39262fa51be 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -750,7 +750,7 @@ directly via PHP attributes: .. note:: - Some IDEs will show an error when using ``#[TaggedIterator]`` together + Some IDEs will show an error when using ``#[AutowireIterator]`` together with the `PHP constructor promotion`_: *"Attribute cannot be applied to a property because it does not contain the 'Attribute::TARGET_PROPERTY' flag"*. The reason is that those constructor arguments are both parameters and class From 10565283d5234c46b8ed4510d79d2d75310b2f9c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 19 Mar 2025 08:35:26 +0100 Subject: [PATCH 312/642] Tweaks --- serializer.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/serializer.rst b/serializer.rst index bd8506cbbe3..54555d7b340 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1242,12 +1242,12 @@ setting the ``name_converter`` setting to snake_case to CamelCase ~~~~~~~~~~~~~~~~~~~~~~~ -In Symfony applications is common to use camelCase to name properties. However -some packages can use snake_case as convention. +In Symfony applications, it is common to use camelCase for naming properties. +However some packages may follow a snake_case convention. Symfony provides a built-in name converter designed to transform between -CamelCase and snake_cased styles during serialization and deserialization -processes. You can use it instead of the metadata aware name converter by +CamelCase and snake_case styles during serialization and deserialization +processes. You can use it instead of the metadata-aware name converter by setting the ``name_converter`` setting to ``serializer.name_converter.snake_case_to_camel_case``: @@ -1300,6 +1300,10 @@ setting the ``name_converter`` setting to ]; $serializer = new Serializer($normalizers, $encoders); +.. versionadded:: 7.2 + + The snake_case to CamelCase converter was introduced in Symfony 7.2. + .. _serializer-built-in-normalizers: Serializer Normalizers From d0a17c54a538f747bf30968ac246d3adb6405b36 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Wed, 19 Mar 2025 09:34:50 +0100 Subject: [PATCH 313/642] [Serializer] Fix namespace --- serializer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serializer.rst b/serializer.rst index 1c30ad89c6c..7f2569eb104 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1421,7 +1421,7 @@ like: .. code-block:: php // config/services.php - namespace Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; + namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Component\Serializer\Normalizer\CustomNormalizer; From d009744f9140c536a5ae3b4a578f7780fb53771a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 19 Mar 2025 09:58:18 +0100 Subject: [PATCH 314/642] Tweaks --- components/options_resolver.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 5063f5b11d9..6f3a6751f28 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -688,13 +688,13 @@ default value:: .. deprecated:: 7.3 - Defining nested option via :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDefault` - was deprecated since Symfony 7.3, use the :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setOptions` - instead. Allowing to define default values for prototyped options. + Defining nested options via :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDefault` + is deprecated since Symfony 7.3. Use the :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setOptions` + method instead, which also allows defining default values for prototyped options. .. versionadded:: 7.3 - The :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setOptions` was introduced in Symfony 7.3. + The ``setOptions()`` method was introduced in Symfony 7.3. Nested options also support required options, validation (type, value) and normalization of their values. If the default value of a nested option depends From c2b17b0f1bbaa0be008cd400757863862c186ae6 Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Wed, 19 Mar 2025 16:34:54 +0100 Subject: [PATCH 315/642] alias ref in autowiring doc --- service_container/autowiring.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service_container/autowiring.rst b/service_container/autowiring.rst index c711f86ecf1..ec25f2d7dae 100644 --- a/service_container/autowiring.rst +++ b/service_container/autowiring.rst @@ -408,8 +408,8 @@ Suppose you create a second class - ``UppercaseTransformer`` that implements If you register this as a service, you now have *two* services that implement the ``App\Util\TransformerInterface`` type. Autowiring subsystem can not decide which one to use. Remember, autowiring isn't magic; it looks for a service -whose id matches the type-hint. So you need to choose one by creating an alias -from the type to the correct service id (see :ref:`autowiring-interface-alias`). +whose id matches the type-hint. So you need to choose one by :ref:`creating an alias +` from the type to the correct service id. Additionally, you can define several named autowiring aliases if you want to use one implementation in some cases, and another implementation in some other cases. From 1b09d4c56c5d47cc928a49c249c5abc97457b282 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Wed, 19 Mar 2025 01:49:32 +0100 Subject: [PATCH 316/642] [Serializer] Document named serializers --- serializer.rst | 249 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) diff --git a/serializer.rst b/serializer.rst index 605946956ac..5f9144abae0 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1537,6 +1537,255 @@ like: PropertyNormalizer::NORMALIZE_VISIBILITY => PropertyNormalizer::NORMALIZE_PUBLIC | PropertyNormalizer::NORMALIZE_PROTECTED, ]); +Named Serializers +----------------- + +.. versionadded:: 7.2 + + Named serializers were introduced in Symfony 7.2. + +Sometimes, you may need multiple configurations for the serializer, such +as different default contexts, name converters, or sets of normalizers and +encoders, depending on the use case. For example, when your application +communicates with multiple APIs, each with its own set of rules. + +This can be achieved by configuring multiple instances of the serializer +using the ``named_serializers`` option: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/serializer.yaml + framework: + serializer: + named_serializers: + api_client1: + name_converter: 'serializer.name_converter.camel_case_to_snake_case' + default_context: + enable_max_depth: true + api_client2: + default_context: + enable_max_depth: false + + .. code-block:: xml + + + + + + + + + + + true + + + + + + false + + + + + + + + .. code-block:: php + + // config/packages/serializer.php + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework): void { + $framework->serializer() + ->namedSerializer('api_client1') + ->nameConverter('serializer.name_converter.camel_case_to_snake_case') + ->defaultContext([ + 'enable_max_depth' => true, + ]) + ; + $framework->serializer() + ->namedSerializer('api_client2') + ->defaultContext([ + 'enable_max_depth' => false, + ]) + ; + }; + +You can inject these different serializer instances +using :ref:`named aliases `:: + + namespace App\Controller; + + // ... + use Symfony\Component\DependencyInjection\Attribute\Target; + + class PersonController extends AbstractController + { + public function index( + SerializerInterface $serializer, // Default serializer + SerializerInterface $apiClient1Serializer, // api_client1 serializer + #[Target('apiClient2.serializer')] // api_client2 serializer + SerializerInterface $customName, + ) { + // ... + } + } + +Named serializers are configured with the default set of normalizers and encoders. + +You can register additional normalizers and encoders with a specific named +serializer by adding a ``serializer`` attribute to +the :ref:`serializer.normalizer ` +or :ref:`serializer.encoder ` tags: + +.. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + + Symfony\Component\Serializer\Normalizer\CustomNormalizer: + # Prevent this normalizer from automatically being included in the default serializer + autoconfigure: false + tags: + # Include this normalizer in a single serializer + - serializer.normalizer: { serializer: 'api_client1' } + # Include this normalizer in multiple serializers + - serializer.normalizer: { serializer: [ 'api_client1', 'api_client2' ] } + # Include this normalizer in all serializers (including the default one) + - serializer.normalizer: { serializer: '*' } + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + use Symfony\Component\Serializer\Normalizer\CustomNormalizer; + + return function(ContainerConfigurator $container) { + // ... + + $services->set(CustomNormalizer::class) + // Prevent this normalizer from automatically being included in the default serializer + ->autoconfigure(false) + + // Include this normalizer in a single serializer + ->tag('serializer.normalizer', ['serializer' => 'api_client1']) + // Include this normalizer in multiple serializers + ->tag('serializer.normalizer', ['serializer' => ['api_client1', 'api_client2']]) + // Include this normalizer in all serializers (including the default one) + ->tag('serializer.normalizer', ['serializer' => '*']) + ; + }; + +When the ``serializer`` attribute is not set, the service is registered with +the default serializer. + +Each normalizer and encoder used in a named serializer is tagged with +a ``serializer.normalizer.`` or ``serializer.encoder.`` tag, +which can be used to list their priorities using the following command: + +.. code-block:: terminal + + $ php bin/console debug:container --tag serializer.. + +Additionally, you can exclude the default set of normalizers and encoders by +setting the ``include_built_in_normalizers`` and ``include_built_in_encoders`` +options to ``false``: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/serializer.yaml + framework: + serializer: + named_serializers: + api_client1: + include_built_in_normalizers: false + include_built_in_encoders: true + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // config/packages/serializer.php + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework): void { + $framework->serializer() + ->namedSerializer('api_client1') + ->includeBuiltInNormalizers(false) + ->includeBuiltInEncoders(true) + ; + }; + Debugging the Serializer ------------------------ From fd26bacbbb4598f2f4d4f95cdd0546efe456bd58 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 20 Mar 2025 10:41:34 +0100 Subject: [PATCH 317/642] Minor tweaks --- messenger.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/messenger.rst b/messenger.rst index 105bbaad8c9..6d1602f410c 100644 --- a/messenger.rst +++ b/messenger.rst @@ -2314,8 +2314,8 @@ will take care of creating a new process with the parameters you passed:: } } -A static factory :method:`Symfony\\Component\\Process\\Messenger\\RunProcessMessage::fromShellCommandline` is also -available if you want to use features of your shell such as redirections or pipes:: +If you want to use shell features such as redirections or pipes, use the static +factory :method:Symfony\\Component\\Process\\Messenger\\RunProcessMessage::fromShellCommandline:: use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Process\Messenger\RunProcessMessage; @@ -2335,8 +2335,8 @@ available if you want to use features of your shell such as redirections or pipe } } -For more information, see the -dedicated :ref:`Using Features From the OS Shell ` documentation. +For more information, read the documentation about +:ref:`using features from the OS shell `. .. versionadded:: 7.3 From 081a975430c701cff5d876f3e4d962fd794953c6 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 20 Mar 2025 21:15:48 +0100 Subject: [PATCH 318/642] [TypeInfo] Add TypeFactoryTrait::fromValue method --- components/type_info.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/components/type_info.rst b/components/type_info.rst index b97fbf4cc8b..5418c765ce7 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -36,10 +36,15 @@ to the :class:`Symfony\\Component\\TypeInfo\\Type` static methods as following:: Type::generic(Type::object(Collection::class), Type::int()); Type::list(Type::bool()); Type::intersection(Type::object(\Stringable::class), Type::object(\Iterator::class)); + Type::fromValue(1.1) // same as Type::float() // Many others are available and can be // found in Symfony\Component\TypeInfo\TypeFactoryTrait +.. versionadded:: 7.3 + + The ``fromValue()`` method was introduced in Symfony 7.3. + Resolvers ~~~~~~~~~ From d873aec48150e48f16afb2fa70b1a531c6827c85 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 20 Mar 2025 22:38:11 +0100 Subject: [PATCH 319/642] Document supported types #[MapQueryParameter] --- controller.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/controller.rst b/controller.rst index 3d4f84b4888..f09769ebc02 100644 --- a/controller.rst +++ b/controller.rst @@ -367,6 +367,16 @@ attribute, arguments of your controller's action can be automatically fulfilled: // ... } +:class:`Symfony\\Component\\HttpKernel\\Attribute\\MapQueryParameter` support +arguments of type: + +- ``string`` +- ``int`` +- ``float`` +- ``bool`` +- ``array`` +- ``\BackedEnum`` + ``#[MapQueryParameter]`` can take an optional argument called ``filter``. You can use the `Validate Filters`_ constants defined in PHP:: From 005ad0266ce29494e1fa89df0b5fdb3596fb459f Mon Sep 17 00:00:00 2001 From: MrYamous Date: Fri, 21 Mar 2025 07:01:55 +0100 Subject: [PATCH 320/642] refer TypeFactoryTrait to github file --- components/type_info.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/type_info.rst b/components/type_info.rst index 2a92a3db63e..3d1aa569fec 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -37,8 +37,8 @@ to the :class:`Symfony\\Component\\TypeInfo\\Type` static methods as following:: Type::list(Type::bool()); Type::intersection(Type::object(\Stringable::class), Type::object(\Iterator::class)); - // Many others are available and can be - // found in Symfony\Component\TypeInfo\TypeFactoryTrait +Many others methods are available and can be found +in :class:`Symfony\\Component\\TypeInfo\\TypeFactoryTrait`. Resolvers ~~~~~~~~~ From 8967cd3617dfd0fd3be2f7f00b97b50e5f1b0060 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 21 Mar 2025 09:11:31 +0100 Subject: [PATCH 321/642] Reword --- components/type_info.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/components/type_info.rst b/components/type_info.rst index 5418c765ce7..25d654995e0 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -36,10 +36,12 @@ to the :class:`Symfony\\Component\\TypeInfo\\Type` static methods as following:: Type::generic(Type::object(Collection::class), Type::int()); Type::list(Type::bool()); Type::intersection(Type::object(\Stringable::class), Type::object(\Iterator::class)); - Type::fromValue(1.1) // same as Type::float() + // ... and more methods defined in Symfony\Component\TypeInfo\TypeFactoryTrait - // Many others are available and can be - // found in Symfony\Component\TypeInfo\TypeFactoryTrait + // you can also use a generic method that detects the type automatically + Type::fromValue(1.1) // same as Type::float() + Type::fromValue('...') // same as Type::string() + Type::fromValue(false) // same as Type::false() .. versionadded:: 7.3 From 0772b75238fbc4523113d97581400833fa570eca Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 21 Mar 2025 09:20:30 +0100 Subject: [PATCH 322/642] Reword --- controller.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/controller.rst b/controller.rst index f09769ebc02..a198a6c9018 100644 --- a/controller.rst +++ b/controller.rst @@ -367,15 +367,14 @@ attribute, arguments of your controller's action can be automatically fulfilled: // ... } -:class:`Symfony\\Component\\HttpKernel\\Attribute\\MapQueryParameter` support -arguments of type: - -- ``string`` -- ``int`` -- ``float`` -- ``bool`` -- ``array`` -- ``\BackedEnum`` +The ``MapQueryParameter`` attribute supports the following argument types: + +* ``\BackedEnum`` +* ``array`` +* ``bool`` +* ``float`` +* ``int`` +* ``string`` ``#[MapQueryParameter]`` can take an optional argument called ``filter``. You can use the `Validate Filters`_ constants defined in PHP:: From 006b41e3f87536a2f57accf755bb8b06225bb24c Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Fri, 21 Mar 2025 09:23:37 +0100 Subject: [PATCH 323/642] Update type_info.rst --- components/type_info.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/type_info.rst b/components/type_info.rst index 25d654995e0..7f767c1149e 100644 --- a/components/type_info.rst +++ b/components/type_info.rst @@ -39,9 +39,9 @@ to the :class:`Symfony\\Component\\TypeInfo\\Type` static methods as following:: // ... and more methods defined in Symfony\Component\TypeInfo\TypeFactoryTrait // you can also use a generic method that detects the type automatically - Type::fromValue(1.1) // same as Type::float() - Type::fromValue('...') // same as Type::string() - Type::fromValue(false) // same as Type::false() + Type::fromValue(1.1); // same as Type::float() + Type::fromValue('...'); // same as Type::string() + Type::fromValue(false); // same as Type::false() .. versionadded:: 7.3 From 2243ab9246cb22dfe571f262ac30698d897b1585 Mon Sep 17 00:00:00 2001 From: Florian Cellier Date: Fri, 21 Mar 2025 12:37:40 +0100 Subject: [PATCH 324/642] doc: Add doc for option `--dry-run` for the command `importmap:require` of the AssetMapper component --- frontend/asset_mapper.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index d68c77c0105..2354485a2bf 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -215,6 +215,11 @@ to add any `npm package`_: $ php bin/console importmap:require bootstrap +.. tip:: + + The command takes the well-know ``--dry-run`` option to simulate the installation of the packages, useful + to show what will be installed without altering anything. + This adds the ``bootstrap`` package to your ``importmap.php`` file:: // importmap.php From 4372e674fa7f62efb1cc161dee39b6faf6508ac3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 21 Mar 2025 15:09:40 +0100 Subject: [PATCH 325/642] Update the doc builder dependencies --- _build/composer.json | 6 +- _build/composer.lock | 157 ++++++++++++++++++++++--------------------- 2 files changed, 83 insertions(+), 80 deletions(-) diff --git a/_build/composer.json b/_build/composer.json index e09d79de52f..f77976b10f4 100644 --- a/_build/composer.json +++ b/_build/composer.json @@ -3,7 +3,7 @@ "prefer-stable": true, "config": { "platform": { - "php": "8.1.0" + "php": "8.3" }, "preferred-install": { "*": "dist" @@ -14,9 +14,9 @@ } }, "require": { - "php": ">=8.1", + "php": ">=8.3", "symfony/console": "^6.2", "symfony/process": "^6.2", - "symfony-tools/docs-builder": "^0.21" + "symfony-tools/docs-builder": "^0.27" } } diff --git a/_build/composer.lock b/_build/composer.lock index a30ed16776b..b9a4646f8ae 100644 --- a/_build/composer.lock +++ b/_build/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6524e63395216a3a297f1c78c7f39d86", + "content-hash": "e38eca557458275428db96db370d2c74", "packages": [ { "name": "doctrine/event-manager", @@ -420,37 +420,37 @@ }, { "name": "symfony-tools/docs-builder", - "version": "v0.21.0", + "version": "0.27.0", "source": { "type": "git", "url": "https://github.com/symfony-tools/docs-builder.git", - "reference": "7ab92db15e9be7d6af51b86db87c7e41a14ba18b" + "reference": "720b52b2805122a4c08376496bd9661944c2624a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony-tools/docs-builder/zipball/7ab92db15e9be7d6af51b86db87c7e41a14ba18b", - "reference": "7ab92db15e9be7d6af51b86db87c7e41a14ba18b", + "url": "https://api.github.com/repos/symfony-tools/docs-builder/zipball/720b52b2805122a4c08376496bd9661944c2624a", + "reference": "720b52b2805122a4c08376496bd9661944c2624a", "shasum": "" }, "require": { "doctrine/rst-parser": "^0.5", "ext-curl": "*", "ext-json": "*", - "php": ">=7.4", - "scrivo/highlight.php": "^9.12.0", - "symfony/console": "^5.2 || ^6.0", - "symfony/css-selector": "^5.2 || ^6.0", - "symfony/dom-crawler": "^5.2 || ^6.0", - "symfony/filesystem": "^5.2 || ^6.0", - "symfony/finder": "^5.2 || ^6.0", - "symfony/http-client": "^5.2 || ^6.0", + "php": ">=8.3", + "scrivo/highlight.php": "^9.18.1", + "symfony/console": "^5.2 || ^6.0 || ^7.0", + "symfony/css-selector": "^5.2 || ^6.0 || ^7.0", + "symfony/dom-crawler": "^5.2 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.2 || ^6.0 || ^7.0", + "symfony/finder": "^5.2 || ^6.0 || ^7.0", + "symfony/http-client": "^5.2 || ^6.0 || ^7.0", "twig/twig": "^2.14 || ^3.3" }, "require-dev": { "gajus/dindent": "^2.0", "masterminds/html5": "^2.7", - "symfony/phpunit-bridge": "^5.2 || ^6.0", - "symfony/process": "^5.2 || ^6.0" + "symfony/phpunit-bridge": "^5.2 || ^6.0 || ^7.0", + "symfony/process": "^5.2 || ^6.0 || ^7.0" }, "bin": [ "bin/docs-builder" @@ -468,9 +468,9 @@ "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.21.0" + "source": "https://github.com/symfony-tools/docs-builder/tree/0.27.0" }, - "time": "2023-07-11T15:21:07+00:00" + "time": "2025-03-21T09:48:45+00:00" }, { "name": "symfony/console", @@ -568,20 +568,20 @@ }, { "name": "symfony/css-selector", - "version": "v6.4.13", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e" + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/cb23e97813c5837a041b73a6d63a9ddff0778f5e", - "reference": "cb23e97813c5837a041b73a6d63a9ddff0778f5e", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "type": "library", "autoload": { @@ -613,7 +613,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v6.4.13" + "source": "https://github.com/symfony/css-selector/tree/v7.2.0" }, "funding": [ { @@ -629,7 +629,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/deprecation-contracts", @@ -700,26 +700,26 @@ }, { "name": "symfony/dom-crawler", - "version": "v6.4.19", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "19073e3e0bb50cbc1cb286077069b3107085206f" + "reference": "19cc7b08efe9ad1ab1b56e0948e8d02e15ed3ef7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/19073e3e0bb50cbc1cb286077069b3107085206f", - "reference": "19073e3e0bb50cbc1cb286077069b3107085206f", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/19cc7b08efe9ad1ab1b56e0948e8d02e15ed3ef7", + "reference": "19cc7b08efe9ad1ab1b56e0948e8d02e15ed3ef7", "shasum": "" }, "require": { "masterminds/html5": "^2.6", - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "^5.4|^6.0|^7.0" + "symfony/css-selector": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -747,7 +747,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.4.19" + "source": "https://github.com/symfony/dom-crawler/tree/v7.2.4" }, "funding": [ { @@ -763,29 +763,29 @@ "type": "tidelift" } ], - "time": "2025-02-14T17:58:34+00:00" + "time": "2025-02-17T15:53:07+00:00" }, { "name": "symfony/filesystem", - "version": "v6.4.13", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^5.4|^6.4|^7.0" + "symfony/process": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -813,7 +813,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.13" + "source": "https://github.com/symfony/filesystem/tree/v7.2.0" }, "funding": [ { @@ -829,27 +829,27 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2024-10-25T15:15:23+00:00" }, { "name": "symfony/finder", - "version": "v6.4.17", + "version": "v7.2.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" + "reference": "87a71856f2f56e4100373e92529eed3171695cfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", - "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", + "reference": "87a71856f2f56e4100373e92529eed3171695cfb", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.0|^7.0" + "symfony/filesystem": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -877,7 +877,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.17" + "source": "https://github.com/symfony/finder/tree/v7.2.2" }, "funding": [ { @@ -893,32 +893,33 @@ "type": "tidelift" } ], - "time": "2024-12-29T13:51:37+00:00" + "time": "2024-12-30T19:00:17+00:00" }, { "name": "symfony/http-client", - "version": "v6.4.19", + "version": "v7.2.4", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "3294a433fc9d12ae58128174896b5b1822c28dad" + "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/3294a433fc9d12ae58128174896b5b1822c28dad", - "reference": "3294a433fc9d12ae58128174896b5b1822c28dad", + "url": "https://api.github.com/repos/symfony/http-client/zipball/78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", + "reference": "78981a2ffef6437ed92d4d7e2a86a82f256c6dc6", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", "symfony/http-client-contracts": "~3.4.4|^3.5.2", "symfony/service-contracts": "^2.5|^3" }, "conflict": { + "amphp/amp": "<2.5", "php-http/discovery": "<1.15", - "symfony/http-foundation": "<6.3" + "symfony/http-foundation": "<6.4" }, "provide": { "php-http/async-client-implementation": "*", @@ -927,19 +928,20 @@ "symfony/http-client-implementation": "3.0" }, "require-dev": { - "amphp/amp": "^2.5", - "amphp/http-client": "^4.2.1", - "amphp/http-tunnel": "^1.0", + "amphp/http-client": "^4.2.1|^5.0", + "amphp/http-tunnel": "^1.0|^2.0", "amphp/socket": "^1.1", "guzzlehttp/promises": "^1.4|^2.0", "nyholm/psr7": "^1.0", "php-http/httplug": "^1.0|^2.0", "psr/http-client": "^1.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0" + "symfony/amphp-http-client-meta": "^1.0|^2.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/rate-limiter": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -970,7 +972,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.19" + "source": "https://github.com/symfony/http-client/tree/v7.2.4" }, "funding": [ { @@ -986,7 +988,7 @@ "type": "tidelift" } ], - "time": "2025-02-13T09:55:13+00:00" + "time": "2025-02-13T10:27:23+00:00" }, { "name": "symfony/http-client-contracts", @@ -1530,20 +1532,20 @@ }, { "name": "symfony/string", - "version": "v6.4.15", + "version": "v7.2.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f" + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f", - "reference": "73a5e66ea2e1677c98d4449177c5a9cf9d8b4c6f", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", "shasum": "" }, "require": { - "php": ">=8.1", + "php": ">=8.2", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -1553,11 +1555,12 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", + "symfony/emoji": "^7.1", + "symfony/error-handler": "^6.4|^7.0", + "symfony/http-client": "^6.4|^7.0", + "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "symfony/var-exporter": "^6.4|^7.0" }, "type": "library", "autoload": { @@ -1596,7 +1599,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.15" + "source": "https://github.com/symfony/string/tree/v7.2.0" }, "funding": [ { @@ -1612,7 +1615,7 @@ "type": "tidelift" } ], - "time": "2024-11-13T13:31:12+00:00" + "time": "2024-11-13T13:31:26+00:00" }, { "name": "symfony/translation-contracts", @@ -1779,11 +1782,11 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.1" + "php": ">=8.3" }, "platform-dev": {}, "platform-overrides": { - "php": "8.1.0" + "php": "8.3" }, "plugin-api-version": "2.6.0" } From d802769826898db0def76c0cf1814c9b57b3033e Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 21 Mar 2025 17:32:40 +0100 Subject: [PATCH 326/642] [Security]: Removing duplicate sentence Page: https://symfony.com/doc/6.4/security.html#the-firewall Namely: > Here, all real URLs are handled by the ``main`` firewall (no ``pattern`` key means it matches *all* URLs). --- security.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/security.rst b/security.rst index b725672dd5f..5fff8725f93 100644 --- a/security.rst +++ b/security.rst @@ -575,7 +575,8 @@ Only one firewall is active on each request: Symfony uses the ``pattern`` key to find the first match (you can also :doc:`match by host or other things `). Here, all real URLs are handled by the ``main`` firewall (no ``pattern`` key means -it matches *all* URLs). +it matches *all* URLs). A firewall can have many modes of authentication, +in other words, it enables many ways to ask the question "Who are you?". The ``dev`` firewall is really a fake firewall: it makes sure that you don't accidentally block Symfony's dev tools - which live under URLs like @@ -630,10 +631,6 @@ don't accidentally block Symfony's dev tools - which live under URLs like The feature to use an array of regex was introduced in Symfony 6.4. -All *real* URLs are handled by the ``main`` firewall (no ``pattern`` key means -it matches *all* URLs). A firewall can have many modes of authentication, -in other words, it enables many ways to ask the question "Who are you?". - Often, the user is unknown (i.e. not logged in) when they first visit your website. If you visit your homepage right now, you *will* have access and you'll see that you're visiting a page behind the firewall in the toolbar: From 981b4e6fa1b9c8ad78b1dd349e666f53ceb56803 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 21 Mar 2025 17:51:46 +0100 Subject: [PATCH 327/642] Reword --- security.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security.rst b/security.rst index 5fff8725f93..fc0cf9c9377 100644 --- a/security.rst +++ b/security.rst @@ -575,8 +575,7 @@ Only one firewall is active on each request: Symfony uses the ``pattern`` key to find the first match (you can also :doc:`match by host or other things `). Here, all real URLs are handled by the ``main`` firewall (no ``pattern`` key means -it matches *all* URLs). A firewall can have many modes of authentication, -in other words, it enables many ways to ask the question "Who are you?". +it matches *all* URLs). The ``dev`` firewall is really a fake firewall: it makes sure that you don't accidentally block Symfony's dev tools - which live under URLs like @@ -631,9 +630,10 @@ don't accidentally block Symfony's dev tools - which live under URLs like The feature to use an array of regex was introduced in Symfony 6.4. -Often, the user is unknown (i.e. not logged in) when they first visit your -website. If you visit your homepage right now, you *will* have access and -you'll see that you're visiting a page behind the firewall in the toolbar: +A firewall can have many modes of authentication, in other words, it enables many +ways to ask the question "Who are you?". Often, the user is unknown (i.e. not logged in) +when they first visit your website. If you visit your homepage right now, you *will* +have access and you'll see that you're visiting a page behind the firewall in the toolbar: .. image:: /_images/security/anonymous_wdt.png :alt: The Symfony profiler toolbar where the Security information shows "Authenticated: no" and "Firewall name: main" From 929ffc2b883af40fb5c2b681ee822275ad6e4710 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 21 Mar 2025 20:34:37 +0100 Subject: [PATCH 328/642] [Security] Chain Providers: Fixing PHP code sample Page: https://symfony.com/doc/6.4/security/user_providers.html#security-chain-user-provider When passing in the variables, I got: > Cannot use values of type "Symfony\Config\Security\ProviderConfig\EntityConfig" in service configuration files in .../config/packages/security.php (which is being imported from ".../src/Kernel.php"). --- security/user_providers.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/user_providers.rst b/security/user_providers.rst index 09d47c270f2..7e9de36eff1 100644 --- a/security/user_providers.rst +++ b/security/user_providers.rst @@ -347,23 +347,23 @@ providers until the user is found: return static function (SecurityConfig $security): void { // ... - $backendProvider = $security->provider('backend_users') + $security->provider('backend_users') ->ldap() // ... ; - $legacyProvider = $security->provider('legacy_users') + $security->provider('legacy_users') ->entity() // ... ; - $userProvider = $security->provider('users') + $security->provider('users') ->entity() // ... ; - $allProviders = $security->provider('all_users')->chain() - ->providers([$backendProvider, $legacyProvider, $userProvider]) + $security->provider('all_users')->chain() + ->providers(['backend_users', 'legacy_users', 'users']) ; }; From 20929b4a573a5cbf7bd344ffa2d08dece074a848 Mon Sep 17 00:00:00 2001 From: Julien Bonnier Date: Sat, 22 Mar 2025 12:13:55 -0400 Subject: [PATCH 329/642] fix: Change translation domain name for bundle translation and add an example --- bundles/best_practices.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst index 376984388db..37dc386b8e4 100644 --- a/bundles/best_practices.rst +++ b/bundles/best_practices.rst @@ -397,10 +397,14 @@ Translation Files ----------------- If a bundle provides message translations, they must be defined in the XLIFF -format; the domain should be named after the bundle name (``acme_blog``). +format; the domain should be named after the bundle name (``AcmeBlog``). A bundle must not override existing messages from another bundle. +The translation domain must match the translation file names. For example, +if the translation domain is ``AcmeBlog``, the English translation file name +should be ``AcmeBlog.en.xlf``. + Configuration ------------- From 78eaf0d0715259cdc4c212b277861296ba552c0e Mon Sep 17 00:00:00 2001 From: Lucas Mlsna Date: Sat, 22 Mar 2025 23:33:16 -0500 Subject: [PATCH 330/642] Update micro_kernel_trait.rst Yielded classes in registerBundles() function example code require "new" keyword to work. Also removed getLogDir() and getCacheDir() functions in example code (these functions are already defined in an identical way by the parent class). --- configuration/micro_kernel_trait.rst | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index 62e8c2d4128..dbe8d1b27b9 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -260,11 +260,11 @@ Now it looks like this:: public function registerBundles(): iterable { - yield FrameworkBundle(); - yield TwigBundle(); + yield new FrameworkBundle(); + yield new TwigBundle(); if ('dev' === $this->getEnvironment()) { - yield WebProfilerBundle(); + yield new WebProfilerBundle(); } } @@ -305,18 +305,6 @@ Now it looks like this:: // (use 'annotation' as the second argument if you define routes as annotations) $routes->import(__DIR__.'/Controller/', 'attribute'); } - - // optional, to use the standard Symfony cache directory - public function getCacheDir(): string - { - return __DIR__.'/../var/cache/'.$this->getEnvironment(); - } - - // optional, to use the standard Symfony logs directory - public function getLogDir(): string - { - return __DIR__.'/../var/log'; - } } Before continuing, run this command to add support for the new dependencies: From 0bf6bc3bd0ec2c9a806384e06b90a78fde226522 Mon Sep 17 00:00:00 2001 From: Santiago San Martin Date: Sun, 23 Mar 2025 14:39:47 -0300 Subject: [PATCH 331/642] Fix documentation on router method option usage --- routing.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/routing.rst b/routing.rst index 68d6a30fc41..445f4a4d886 100644 --- a/routing.rst +++ b/routing.rst @@ -442,9 +442,6 @@ evaluates them: $ php bin/console debug:router --method=GET $ php bin/console debug:router --method=ANY - # pass the option more than once to display the routes that match all the given methods - $ php bin/console debug:router --method=GET --method=PATCH - .. versionadded:: 7.3 The ``--method`` option was introduced in Symfony 7.3. From 2a2faa4f4689920cbade1fc1867e9f5e587794c7 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 20 Mar 2025 22:45:30 +0100 Subject: [PATCH 332/642] [HttpKernel] Support Uid in #[MapQueryParameter] --- controller.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/controller.rst b/controller.rst index b33eed08360..693784885da 100644 --- a/controller.rst +++ b/controller.rst @@ -371,6 +371,11 @@ The ``MapQueryParameter`` attribute supports the following argument types: * ``float`` * ``int`` * ``string`` +* Objects that extend :class:`Symfony\\Component\\Uid\\AbstractUid` + +.. versionadded:: 7.3 + + The support of ``AbstractUid`` was introduced in Symfony 7.3. ``#[MapQueryParameter]`` can take an optional argument called ``filter``. You can use the `Validate Filters`_ constants defined in PHP:: From 996f3d374d798c1b5d76916018ba8aa9186681b5 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 20 Mar 2025 21:53:05 +0100 Subject: [PATCH 333/642] [Serializer] Add NumberNormalizer --- serializer.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/serializer.rst b/serializer.rst index 7f2f7d7c2f1..18f1b06b92c 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1387,6 +1387,14 @@ normalizers (in order of priority): By default, an exception is thrown when data is not a valid backed enumeration. If you want ``null`` instead, you can set the ``BackedEnumNormalizer::ALLOW_INVALID_VALUES`` option. +:class:`Symfony\\Component\\Serializer\\Normalizer\\NumberNormalizer` + This normalizer converts between :phpclass:`BcMath\Number` or :phpclass:`GMP` objects and + strings or integers. + +.. versionadded:: 7.2 + + The ``NumberNormalizer`` was introduced in Symfony 7.2. + :class:`Symfony\\Component\\Serializer\\Normalizer\\DataUriNormalizer` This normalizer converts between :phpclass:`SplFileInfo` objects and a `data URI`_ string (``data:...``) such that files can be embedded into From e24fb9a8b3176f6b07ed23c72df57a8742610001 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sun, 23 Mar 2025 19:19:44 +0100 Subject: [PATCH 334/642] [Scheduler] Add MessageHandler result to the PostRunEvent --- scheduler.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scheduler.rst b/scheduler.rst index 0c6c14915c7..64a5a9f7612 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -723,10 +723,15 @@ after a message is consumed:: $schedule = $event->getSchedule(); $context = $event->getMessageContext(); $message = $event->getMessage(); + $result = $event->getResult(); - // do something with the schedule, context or message + // do something with the schedule, context, message or result } +.. versionadded:: 7.3 + + The ``getResult()`` method was introduced in Symfony 7.3. + Execute this command to find out which listeners are registered for this event and their priorities: From b67e277663beeb002583b470cd01670d4292621c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 24 Mar 2025 09:19:44 +0100 Subject: [PATCH 335/642] Minor tweak --- configuration/micro_kernel_trait.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index dbe8d1b27b9..f919b1f7a74 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -305,6 +305,9 @@ Now it looks like this:: // (use 'annotation' as the second argument if you define routes as annotations) $routes->import(__DIR__.'/Controller/', 'attribute'); } + + // optionally, you can define the getCacheDir() and getLogDir() methods + // to override the default locations for these directories } Before continuing, run this command to add support for the new dependencies: From b95b1041d2840213468e1714d4a0d09cc39a9273 Mon Sep 17 00:00:00 2001 From: Andrey Bolonin Date: Mon, 24 Mar 2025 11:41:00 +0300 Subject: [PATCH 336/642] Update custom_normalizer.rst --- serializer/custom_normalizer.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serializer/custom_normalizer.rst b/serializer/custom_normalizer.rst index d6ba66f89b6..8072b590ced 100644 --- a/serializer/custom_normalizer.rst +++ b/serializer/custom_normalizer.rst @@ -33,9 +33,9 @@ to customize the normalized data. To do that, leverage the ``ObjectNormalizer``: ) { } - public function normalize($topic, ?string $format = null, array $context = []): array + public function normalize(mixed $object, ?string $format = null, array $context = []): array { - $data = $this->normalizer->normalize($topic, $format, $context); + $data = $this->normalizer->normalize($object, $format, $context); // Here, add, edit, or delete some data: $data['href']['self'] = $this->router->generate('topic_show', [ From d9a48bea00bb340b407402a3f6ada1a265e5eba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Fri, 21 Mar 2025 03:23:02 +0100 Subject: [PATCH 337/642] [Console] Document the `TreeHelper` --- components/console/helpers/map.rst.inc | 1 + components/console/helpers/tree.rst | 237 +++++++++++++++++++++++++ console/style.rst | 26 +++ 3 files changed, 264 insertions(+) create mode 100644 components/console/helpers/tree.rst diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc index 8f9ce0ca0f3..b190d1dce44 100644 --- a/components/console/helpers/map.rst.inc +++ b/components/console/helpers/map.rst.inc @@ -3,5 +3,6 @@ * :doc:`/components/console/helpers/progressbar` * :doc:`/components/console/helpers/questionhelper` * :doc:`/components/console/helpers/table` +* :doc:`/components/console/helpers/tree` * :doc:`/components/console/helpers/debug_formatter` * :doc:`/components/console/helpers/cursor` diff --git a/components/console/helpers/tree.rst b/components/console/helpers/tree.rst new file mode 100644 index 00000000000..2e688770c65 --- /dev/null +++ b/components/console/helpers/tree.rst @@ -0,0 +1,237 @@ +Tree Helper +=========== + +The Tree Helper allows you to build and display tree structures in the console. + +.. versionadded:: 7.3 + + The ``TreeHelper`` class was introduced in Symfony 7.3. + +Rendering a Tree +---------------- + +The :method:`Symfony\\Component\\Console\\Helper\\TreeHelper::createTree` method creates a tree structure from an array and returns a :class:`Symfony\\Component\\Console\\Helper\\Tree` +object that can be rendered in the console. + +Building a Tree from an Array +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can build a tree from an array by passing the array to the :method:`Symfony\\Component\\Console\\Helper\\TreeHelper::createTree` +method:: + + use Symfony\Component\Console\Helper\TreeHelper; + + $tree = TreeHelper::createTree($io, null, [ + 'src' => [ + 'Command', + 'Controller' => [ + 'DefaultController.php', + ], + 'Kernel.php', + ], + 'templates' => [ + 'base.html.twig', + ], + ]); + + $tree->render(); + +The above code will output the following tree: + +.. code-block:: text + + ├── src + │ ├── Command + │ ├── Controller + │ │ └── DefaultController.php + │ └── Kernel.php + └── templates + └── base.html.twig + +Manually Creating a Tree +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can manually create a tree by creating a new instance of the :class:`Symfony\\Component\\Console\\Helper\\Tree` class and adding nodes to it:: + + use Symfony\Component\Console\Helper\TreeHelper; + use Symfony\Component\Console\Helper\TreeNode; + + $node = TreeNode::fromValues([ + 'Command', + 'Controller' => [ + 'DefaultController.php', + ], + 'Kernel.php', + ]); + $node->addChild('templates'); + $node->addChild('tests'); + + $tree = TreeHelper::createTree($io, $node); + $tree->render(); + +Customizing the Tree Style +-------------------------- + +Built-in Tree Styles +~~~~~~~~~~~~~~~~~~~~ + +The tree helper provides a few built-in styles that you can use to customize the output of the tree. + +:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::default` + + .. code-block:: text + + ├── config + │ ├── packages + │ └── routes + │ ├── framework.yaml + │ └── web_profiler.yaml + ├── src + │ ├── Command + │ ├── Controller + │ │ └── DefaultController.php + │ └── Kernel.php + └── templates + └── base.html.twig + +:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::box` + + .. code-block:: text + + ┃╸ config + ┃ ┃╸ packages + ┃ ┗╸ routes + ┃ ┃╸ framework.yaml + ┃ ┗╸ web_profiler.yaml + ┃╸ src + ┃ ┃╸ Command + ┃ ┃╸ Controller + ┃ ┃ ┗╸ DefaultController.php + ┃ ┗╸ Kernel.php + ┗╸ templates + ┗╸ base.html.twig + +:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::doubleBox` + + .. code-block:: text + + ╠═ config + ║ ╠═ packages + ║ ╚═ routes + ║ ╠═ framework.yaml + ║ ╚═ web_profiler.yaml + ╠═ src + ║ ╠═ Command + ║ ╠═ Controller + ║ ║ ╚═ DefaultController.php + ║ ╚═ Kernel.php + ╚═ templates + ╚═ base.html.twig + +:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::compact` + + .. code-block:: text + + ├ config + │ ├ packages + │ └ routes + │ ├ framework.yaml + │ └ web_profiler.yaml + ├ src + │ ├ Command + │ ├ Controller + │ │ └ DefaultController.php + │ └ Kernel.php + └ templates + └ base.html.twig + +:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::light` + + .. code-block:: text + + |-- config + | |-- packages + | `-- routes + | |-- framework.yaml + | `-- web_profiler.yaml + |-- src + | |-- Command + | |-- Controller + | | `-- DefaultController.php + | `-- Kernel.php + `-- templates + `-- base.html.twig + +:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::minimal` + + .. code-block:: text + + . config + . . packages + . . routes + . . framework.yaml + . . web_profiler.yaml + . src + . . Command + . . Controller + . . . DefaultController.php + . . Kernel.php + . templates + . base.html.twig + +:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::rounded` + + .. code-block:: text + + ├─ config + │ ├─ packages + │ ╰─ routes + │ ├─ framework.yaml + │ ╰─ web_profiler.yaml + ├─ src + │ ├─ Command + │ ├─ Controller + │ │ ╰─ DefaultController.php + │ ╰─ Kernel.php + ╰─ templates + ╰─ base.html.twig + +Making a Custom Tree Style +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can create your own tree style by passing the characters to the constructor +of the :class:`Symfony\\Component\\Console\\Helper\\TreeStyle` class:: + + use Symfony\Component\Console\Helper\TreeHelper; + use Symfony\Component\Console\Helper\TreeStyle; + + $customStyle = new TreeStyle('🟣 ', '🟠 ', '🔵 ', '🟢 ', '🔴 ', '🟡 '); + + // Pass the custom style to the createTree method + + $tree = TreeHelper::createTree($io, null, [ + 'src' => [ + 'Command', + 'Controller' => [ + 'DefaultController.php', + ], + 'Kernel.php', + ], + 'templates' => [ + 'base.html.twig', + ], + ], $customStyle); + + $tree->render(); + +The above code will output the following tree: + +.. code-block:: text + + 🔵 🟣 🟡 src + 🔵 🟢 🟣 🟡 Command + 🔵 🟢 🟣 🟡 Controller + 🔵 🟢 🟢 🟠 🟡 DefaultController.php + 🔵 🟢 🟠 🟡 Kernel.php + 🔵 🟠 🟡 templates + 🔵 🔴 🟠 🟡 base.html.twig diff --git a/console/style.rst b/console/style.rst index 0aaaa3f675e..e1e5df38ffe 100644 --- a/console/style.rst +++ b/console/style.rst @@ -169,6 +169,32 @@ Content Methods styled according to the Symfony Style Guide, which allows you to use features such as dynamically appending rows. +:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::tree` + It displays the given nested array as a formatted directory/file tree + structure in the console output:: + + $io->tree([ + 'src' => [ + 'Controller' => [ + 'DefaultController.php', + ], + 'Kernel.php', + ], + 'templates' => [ + 'base.html.twig', + ], + ]); + +.. versionadded:: 7.3 + + The ``SymfonyStyle::tree()`` and the ``SymfonyStyle::createTree()`` methods + were introduced in Symfony 7.3. + +:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::createTree` + Creates an instance of :class:`Symfony\\Component\\Console\\Helper\\TreeHelper` + styled according to the Symfony Style Guide, which allows you to use + features such as dynamically nesting nodes. + :method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::newLine` It displays a blank line in the command output. Although it may seem useful, most of the times you won't need it at all. The reason is that every helper From a0758ac62303e037fb903ba59fe807fd798f561d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 24 Mar 2025 09:52:46 +0100 Subject: [PATCH 338/642] Tweaks --- components/console/helpers/tree.rst | 86 +++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 18 deletions(-) diff --git a/components/console/helpers/tree.rst b/components/console/helpers/tree.rst index 2e688770c65..4203f8bc790 100644 --- a/components/console/helpers/tree.rst +++ b/components/console/helpers/tree.rst @@ -2,6 +2,8 @@ Tree Helper =========== The Tree Helper allows you to build and display tree structures in the console. +It's commonly used to render directory hierarchies, but you can also use it to render +any tree-like content, such us organizational charts, product category trees, taxonomies, etc. .. versionadded:: 7.3 @@ -10,16 +12,62 @@ The Tree Helper allows you to build and display tree structures in the console. Rendering a Tree ---------------- -The :method:`Symfony\\Component\\Console\\Helper\\TreeHelper::createTree` method creates a tree structure from an array and returns a :class:`Symfony\\Component\\Console\\Helper\\Tree` +The :method:`Symfony\\Component\\Console\\Helper\\TreeHelper::createTree` method +creates a tree structure from an array and returns a :class:`Symfony\\Component\\Console\\Helper\\Tree` object that can be rendered in the console. -Building a Tree from an Array -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Rendering a Tree from an Array +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can build a tree from an array by passing the array to the +:method:`Symfony\\Component\\Console\\Helper\\TreeHelper::createTree` method +inside your console command:: -You can build a tree from an array by passing the array to the :method:`Symfony\\Component\\Console\\Helper\\TreeHelper::createTree` -method:: + namespace App\Command; + use Symfony\Component\Console\Attribute\AsCommand; + use Symfony\Component\Console\Command\Command; + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Output\OutputInterface; + use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Helper\TreeHelper; + use Symfony\Component\Console\Helper\TreeNode; + + #[AsCommand(name: 'app:some-command', description: '...')] + class SomeCommand extends Command + { + // ... + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $node = TreeNode::fromValues([ + 'config/', + 'public/', + 'src/', + 'templates/', + 'tests/', + ]); + + $tree = TreeHelper::createTree($io, $node); + $tree->render(); + + // ... + } + } + +This exampe would output the following: + +.. code-block:: terminal + + ├── config/ + ├── public/ + ├── src/ + ├── templates/ + └── tests/ + +The given contents can be defined in a multi-dimensional array: $tree = TreeHelper::createTree($io, null, [ 'src' => [ @@ -38,7 +86,7 @@ method:: The above code will output the following tree: -.. code-block:: text +.. code-block:: terminal ├── src │ ├── Command @@ -48,10 +96,11 @@ The above code will output the following tree: └── templates └── base.html.twig -Manually Creating a Tree -~~~~~~~~~~~~~~~~~~~~~~~~ +Building and Rendering a Tree +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can manually create a tree by creating a new instance of the :class:`Symfony\\Component\\Console\\Helper\\Tree` class and adding nodes to it:: +You can build a tree by creating a new instance of the +:class:`Symfony\\Component\\Console\\Helper\\Tree` class and adding nodes to it:: use Symfony\Component\Console\Helper\TreeHelper; use Symfony\Component\Console\Helper\TreeNode; @@ -75,11 +124,12 @@ Customizing the Tree Style Built-in Tree Styles ~~~~~~~~~~~~~~~~~~~~ -The tree helper provides a few built-in styles that you can use to customize the output of the tree. +The tree helper provides a few built-in styles that you can use to customize the +output of the tree. :method:`Symfony\\Component\\Console\\Helper\\TreeStyle::default` - .. code-block:: text + .. code-block:: terminal ├── config │ ├── packages @@ -96,7 +146,7 @@ The tree helper provides a few built-in styles that you can use to customize the :method:`Symfony\\Component\\Console\\Helper\\TreeStyle::box` - .. code-block:: text + .. code-block:: terminal ┃╸ config ┃ ┃╸ packages @@ -113,7 +163,7 @@ The tree helper provides a few built-in styles that you can use to customize the :method:`Symfony\\Component\\Console\\Helper\\TreeStyle::doubleBox` - .. code-block:: text + .. code-block:: terminal ╠═ config ║ ╠═ packages @@ -130,7 +180,7 @@ The tree helper provides a few built-in styles that you can use to customize the :method:`Symfony\\Component\\Console\\Helper\\TreeStyle::compact` - .. code-block:: text + .. code-block:: terminal ├ config │ ├ packages @@ -147,7 +197,7 @@ The tree helper provides a few built-in styles that you can use to customize the :method:`Symfony\\Component\\Console\\Helper\\TreeStyle::light` - .. code-block:: text + .. code-block:: terminal |-- config | |-- packages @@ -164,7 +214,7 @@ The tree helper provides a few built-in styles that you can use to customize the :method:`Symfony\\Component\\Console\\Helper\\TreeStyle::minimal` - .. code-block:: text + .. code-block:: terminal . config . . packages @@ -181,7 +231,7 @@ The tree helper provides a few built-in styles that you can use to customize the :method:`Symfony\\Component\\Console\\Helper\\TreeStyle::rounded` - .. code-block:: text + .. code-block:: terminal ├─ config │ ├─ packages @@ -226,7 +276,7 @@ of the :class:`Symfony\\Component\\Console\\Helper\\TreeStyle` class:: The above code will output the following tree: -.. code-block:: text +.. code-block:: terminal 🔵 🟣 🟡 src 🔵 🟢 🟣 🟡 Command From 16861e3df96cedc24460d3a134c34e7278ac8624 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 24 Mar 2025 13:14:22 +0100 Subject: [PATCH 339/642] order use statements alphabetically --- components/console/helpers/tree.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/console/helpers/tree.rst b/components/console/helpers/tree.rst index 4203f8bc790..ae0c24ff01f 100644 --- a/components/console/helpers/tree.rst +++ b/components/console/helpers/tree.rst @@ -27,11 +27,11 @@ inside your console command:: use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; + use Symfony\Component\Console\Helper\TreeHelper; + use Symfony\Component\Console\Helper\TreeNode; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; - use Symfony\Component\Console\Helper\TreeHelper; - use Symfony\Component\Console\Helper\TreeNode; #[AsCommand(name: 'app:some-command', description: '...')] class SomeCommand extends Command From bdca4e901550d29fd3ec5c38adb02365459b1460 Mon Sep 17 00:00:00 2001 From: Arnaud De Abreu Date: Mon, 24 Mar 2025 09:25:46 +0100 Subject: [PATCH 340/642] [Messenger] Add `--class-filter` option to the `messenger:failed:remove` command --- messenger.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/messenger.rst b/messenger.rst index 6d1602f410c..8632ebdb4e0 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1312,6 +1312,9 @@ to retry them: # remove all messages in the failure transport $ php bin/console messenger:failed:remove --all + # remove only MyClass messages + $ php bin/console messenger:failed:remove --class-filter='MyClass' + If the message fails again, it will be re-sent back to the failure transport due to the normal :ref:`retry rules `. Once the max retry has been hit, the message will be discarded permanently. @@ -1321,6 +1324,11 @@ retry has been hit, the message will be discarded permanently. The option to skip a message in the ``messenger:failed:retry`` command was introduced in Symfony 7.2 +.. versionadded:: 7.3 + + The option to filter by a message class in the ``messenger:failed:remove`` command was + introduced in Symfony 7.3 + Multiple Failed Transports ~~~~~~~~~~~~~~~~~~~~~~~~~~ From da62e32f9693a21f4b3130110abf6c8e2c6beab1 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 24 Mar 2025 13:56:01 +0100 Subject: [PATCH 341/642] minor --- messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index 8632ebdb4e0..cbd79784555 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1313,7 +1313,7 @@ to retry them: $ php bin/console messenger:failed:remove --all # remove only MyClass messages - $ php bin/console messenger:failed:remove --class-filter='MyClass' + $ php bin/console messenger:failed:remove --class-filter='App\Message\MyMessage' If the message fails again, it will be re-sent back to the failure transport due to the normal :ref:`retry rules `. Once the max From a93e2656a9583fa44aa93befe0db7660c689eab1 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 24 Mar 2025 13:56:46 +0100 Subject: [PATCH 342/642] minor --- messenger.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/messenger.rst b/messenger.rst index d25800ef00d..e86bc73d1c6 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1161,8 +1161,8 @@ to retry them: # see the 10 first messages $ php bin/console messenger:failed:show --max=10 - # see only MyClass messages - $ php bin/console messenger:failed:show --class-filter='MyClass' + # see only App\Message\MyMessage messages + $ php bin/console messenger:failed:show --class-filter='App\Message\MyMessage' # see the number of messages by message class $ php bin/console messenger:failed:show --stats From 137134ad97da5b2fecc4a61027c1701c50d9faff Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Mon, 24 Mar 2025 13:57:13 +0100 Subject: [PATCH 343/642] minor --- messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index 59f2aa0cc19..0f1eba49a9f 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1312,7 +1312,7 @@ to retry them: # remove all messages in the failure transport $ php bin/console messenger:failed:remove --all - # remove only MyClass messages + # remove only App\Message\MyMessage messages $ php bin/console messenger:failed:remove --class-filter='App\Message\MyMessage' If the message fails again, it will be re-sent back to the failure transport From bc3412b33493e9a2d03a189f073a863f6030e909 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 24 Mar 2025 15:23:03 +0100 Subject: [PATCH 344/642] Fix syntax issue --- serializer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serializer.rst b/serializer.rst index 18f1b06b92c..7b42ea9485c 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1388,7 +1388,7 @@ normalizers (in order of priority): want ``null`` instead, you can set the ``BackedEnumNormalizer::ALLOW_INVALID_VALUES`` option. :class:`Symfony\\Component\\Serializer\\Normalizer\\NumberNormalizer` - This normalizer converts between :phpclass:`BcMath\Number` or :phpclass:`GMP` objects and + This normalizer converts between :phpclass:`BcMath\\Number` or :phpclass:`GMP` objects and strings or integers. .. versionadded:: 7.2 From c018ddc9c619c68c09551e21d5d4fa86d028a71f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 24 Mar 2025 16:01:48 +0100 Subject: [PATCH 345/642] Minor tweak --- controller.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller.rst b/controller.rst index 693784885da..05abdaee4ea 100644 --- a/controller.rst +++ b/controller.rst @@ -375,7 +375,7 @@ The ``MapQueryParameter`` attribute supports the following argument types: .. versionadded:: 7.3 - The support of ``AbstractUid`` was introduced in Symfony 7.3. + Support for ``AbstractUid`` objects was introduced in Symfony 7.3. ``#[MapQueryParameter]`` can take an optional argument called ``filter``. You can use the `Validate Filters`_ constants defined in PHP:: From 5f722a34043d63e3f778ce4d9a485784d6879e45 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Mon, 24 Mar 2025 16:07:22 +0100 Subject: [PATCH 346/642] Add Tugdual Saunier as a core team member --- contributing/core_team.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contributing/core_team.rst b/contributing/core_team.rst index d461aa44485..2edb42a842d 100644 --- a/contributing/core_team.rst +++ b/contributing/core_team.rst @@ -86,6 +86,7 @@ Active Core Members * **Berislav Balogović** (`hypemc`_); * **Mathias Arlaud** (`mtarld`_); * **Florent Morselli** (`spomky`_); + * **Tugdual Saunier** (`tucksaun`_); * **Alexandre Daubois** (`alexandre-daubois`_). * **Security Team** (``@symfony/security`` on GitHub): @@ -263,3 +264,4 @@ discretion of the **Project Leader**. .. _`mtarld`: https://github.com/mtarld/ .. _`spomky`: https://github.com/spomky/ .. _`alexandre-daubois`: https://github.com/alexandre-daubois/ +.. _`tucksaun`: https://github.com/tucksaun/ \ No newline at end of file From ce6c3a9992c30ddef1cd27a7c1a31b81625dc939 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 24 Mar 2025 17:09:08 +0100 Subject: [PATCH 347/642] Reword --- frontend/asset_mapper.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index 2354485a2bf..8b27cd8ba16 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -217,8 +217,12 @@ to add any `npm package`_: .. tip:: - The command takes the well-know ``--dry-run`` option to simulate the installation of the packages, useful - to show what will be installed without altering anything. + Add the ``--dry-run`` option to simulate package installation without actually + making any changes (e.g. ``php bin/console importmap:require bootstrap --dry-run``) + + .. versionadded:: 7.3 + + The ``--dry-run`` option was introduced in Symfony 7.3. This adds the ``bootstrap`` package to your ``importmap.php`` file:: From 0dd6f3462e07172e8d8f7825ef0d175b62b74d85 Mon Sep 17 00:00:00 2001 From: Massimiliano Arione Date: Mon, 24 Mar 2025 17:52:45 +0100 Subject: [PATCH 348/642] document DatePointType --- components/clock.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/components/clock.rst b/components/clock.rst index 5b20e6000b9..e1a631a9548 100644 --- a/components/clock.rst +++ b/components/clock.rst @@ -269,6 +269,31 @@ timestamps:: .. _clock_writing-tests: +Storing DatePoints in Databases +------------------------------- + +If you :doc:`use Doctrine `, consider using the ``date_point`` Doctrine +type, which converts to/from ``DatePoint`` objects automatically:: + + // src/Entity/Product.php + namespace App\Entity; + + use Doctrine\ORM\Mapping as ORM; + use Symfony\Component\Clock\DatePoint; + + #[ORM\Entity] + class Product + { + #[ORM\Column] + private DatePoint $created; + + // ... + } + +.. versionadded:: 7.3 + + The `DatePointType` was introduced in Symfony 7.3. + Writing Time-Sensitive Tests ---------------------------- From 823c8432df0cd3ddd685e023ea863ab4051b71f9 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 25 Mar 2025 09:58:07 +0100 Subject: [PATCH 349/642] Minor tweaks --- components/clock.rst | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/components/clock.rst b/components/clock.rst index e1a631a9548..c4ac88e9092 100644 --- a/components/clock.rst +++ b/components/clock.rst @@ -267,13 +267,11 @@ timestamps:: :method:`Symfony\\Component\\Clock\\DatePoint::getMicrosecond` methods were introduced in Symfony 7.1. -.. _clock_writing-tests: - -Storing DatePoints in Databases -------------------------------- +Storing DatePoints in the Database +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If you :doc:`use Doctrine `, consider using the ``date_point`` Doctrine -type, which converts to/from ``DatePoint`` objects automatically:: +If you :doc:`use Doctrine ` to work with databases, consider using the +``date_point`` Doctrine type, which converts to/from ``DatePoint`` objects automatically:: // src/Entity/Product.php namespace App\Entity; @@ -284,15 +282,22 @@ type, which converts to/from ``DatePoint`` objects automatically:: #[ORM\Entity] class Product { + // if you don't define the Doctrine type explicitly, Symfony will autodetect it: #[ORM\Column] - private DatePoint $created; + private DatePoint $createdAt; + + // if you prefer to define the Doctrine type explicitly: + #[ORM\Column(type: 'date_point')] + private DatePoint $updatedAt; // ... } .. versionadded:: 7.3 - The `DatePointType` was introduced in Symfony 7.3. + The ``DatePointType`` was introduced in Symfony 7.3. + +.. _clock_writing-tests: Writing Time-Sensitive Tests ---------------------------- From 5844ee66a68c19bbcff3f7300756ed1df31c4c3a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 25 Mar 2025 16:39:30 +0100 Subject: [PATCH 350/642] [Console] Tweak the tree helper --- components/console/helpers/tree.rst | 250 ++++++++++++++-------------- 1 file changed, 129 insertions(+), 121 deletions(-) diff --git a/components/console/helpers/tree.rst b/components/console/helpers/tree.rst index ae0c24ff01f..1161d00e942 100644 --- a/components/console/helpers/tree.rst +++ b/components/console/helpers/tree.rst @@ -67,7 +67,7 @@ This exampe would output the following: ├── templates/ └── tests/ -The given contents can be defined in a multi-dimensional array: +The given contents can be defined in a multi-dimensional array:: $tree = TreeHelper::createTree($io, null, [ 'src' => [ @@ -125,126 +125,132 @@ Built-in Tree Styles ~~~~~~~~~~~~~~~~~~~~ The tree helper provides a few built-in styles that you can use to customize the -output of the tree. - -:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::default` - - .. code-block:: terminal - - ├── config - │ ├── packages - │ └── routes - │ ├── framework.yaml - │ └── web_profiler.yaml - ├── src - │ ├── Command - │ ├── Controller - │ │ └── DefaultController.php - │ └── Kernel.php - └── templates - └── base.html.twig - -:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::box` - - .. code-block:: terminal - - ┃╸ config - ┃ ┃╸ packages - ┃ ┗╸ routes - ┃ ┃╸ framework.yaml - ┃ ┗╸ web_profiler.yaml - ┃╸ src - ┃ ┃╸ Command - ┃ ┃╸ Controller - ┃ ┃ ┗╸ DefaultController.php - ┃ ┗╸ Kernel.php - ┗╸ templates - ┗╸ base.html.twig - -:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::doubleBox` - - .. code-block:: terminal - - ╠═ config - ║ ╠═ packages - ║ ╚═ routes - ║ ╠═ framework.yaml - ║ ╚═ web_profiler.yaml - ╠═ src - ║ ╠═ Command - ║ ╠═ Controller - ║ ║ ╚═ DefaultController.php - ║ ╚═ Kernel.php - ╚═ templates - ╚═ base.html.twig - -:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::compact` - - .. code-block:: terminal - - ├ config - │ ├ packages - │ └ routes - │ ├ framework.yaml - │ └ web_profiler.yaml - ├ src - │ ├ Command - │ ├ Controller - │ │ └ DefaultController.php - │ └ Kernel.php - └ templates - └ base.html.twig - -:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::light` - - .. code-block:: terminal - - |-- config - | |-- packages - | `-- routes - | |-- framework.yaml - | `-- web_profiler.yaml - |-- src - | |-- Command - | |-- Controller - | | `-- DefaultController.php - | `-- Kernel.php - `-- templates - `-- base.html.twig - -:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::minimal` - - .. code-block:: terminal - - . config - . . packages - . . routes - . . framework.yaml - . . web_profiler.yaml - . src - . . Command - . . Controller - . . . DefaultController.php - . . Kernel.php - . templates - . base.html.twig - -:method:`Symfony\\Component\\Console\\Helper\\TreeStyle::rounded` - - .. code-block:: terminal - - ├─ config - │ ├─ packages - │ ╰─ routes - │ ├─ framework.yaml - │ ╰─ web_profiler.yaml - ├─ src - │ ├─ Command - │ ├─ Controller - │ │ ╰─ DefaultController.php - │ ╰─ Kernel.php - ╰─ templates - ╰─ base.html.twig +output of the tree:: + + use Symfony\Component\Console\Helper\TreeStyle; + // ... + + $tree = TreeHelper::createTree($io, $node, [], TreeStyle::compact()); + $tree->render(); + +``TreeHelper::createTree($io, $node, [], TreeStyle::default())`` (`details`_) + +.. code-block:: terminal + + ├── config + │ ├── packages + │ └── routes + │ ├── framework.yaml + │ └── web_profiler.yaml + ├── src + │ ├── Command + │ ├── Controller + │ │ └── DefaultController.php + │ └── Kernel.php + └── templates + └── base.html.twig + +``TreeHelper::createTree($io, $node, [], TreeStyle::box())`` (`details`_) + +.. code-block:: terminal + + ┃╸ config + ┃ ┃╸ packages + ┃ ┗╸ routes + ┃ ┃╸ framework.yaml + ┃ ┗╸ web_profiler.yaml + ┃╸ src + ┃ ┃╸ Command + ┃ ┃╸ Controller + ┃ ┃ ┗╸ DefaultController.php + ┃ ┗╸ Kernel.php + ┗╸ templates + ┗╸ base.html.twig + +``TreeHelper::createTree($io, $node, [], TreeStyle::doubleBox())`` (`details`_) + +.. code-block:: terminal + + ╠═ config + ║ ╠═ packages + ║ ╚═ routes + ║ ╠═ framework.yaml + ║ ╚═ web_profiler.yaml + ╠═ src + ║ ╠═ Command + ║ ╠═ Controller + ║ ║ ╚═ DefaultController.php + ║ ╚═ Kernel.php + ╚═ templates + ╚═ base.html.twig + +``TreeHelper::createTree($io, $node, [], TreeStyle::compact())`` (`details`_) + +.. code-block:: terminal + + ├ config + │ ├ packages + │ └ routes + │ ├ framework.yaml + │ └ web_profiler.yaml + ├ src + │ ├ Command + │ ├ Controller + │ │ └ DefaultController.php + │ └ Kernel.php + └ templates + └ base.html.twig + +``TreeHelper::createTree($io, $node, [], TreeStyle::light())`` (`details`_) + +.. code-block:: terminal + + |-- config + | |-- packages + | `-- routes + | |-- framework.yaml + | `-- web_profiler.yaml + |-- src + | |-- Command + | |-- Controller + | | `-- DefaultController.php + | `-- Kernel.php + `-- templates + `-- base.html.twig + +``TreeHelper::createTree($io, $node, [], TreeStyle::minimal())`` (`details`_) + +.. code-block:: terminal + + . config + . . packages + . . routes + . . framework.yaml + . . web_profiler.yaml + . src + . . Command + . . Controller + . . . DefaultController.php + . . Kernel.php + . templates + . base.html.twig + +``TreeHelper::createTree($io, $node, [], TreeStyle::rounded())`` (`details`_) + +.. code-block:: terminal + + ├─ config + │ ├─ packages + │ ╰─ routes + │ ├─ framework.yaml + │ ╰─ web_profiler.yaml + ├─ src + │ ├─ Command + │ ├─ Controller + │ │ ╰─ DefaultController.php + │ ╰─ Kernel.php + ╰─ templates + ╰─ base.html.twig Making a Custom Tree Style ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -285,3 +291,5 @@ The above code will output the following tree: 🔵 🟢 🟠 🟡 Kernel.php 🔵 🟠 🟡 templates 🔵 🔴 🟠 🟡 base.html.twig + +.. _`details`: https://github.com/symfony/symfony/blob/7.3/src/Symfony/Component/Console/Helper/TreeStyle.php From 4191969aff9f5737264eb238e9403199af9f3278 Mon Sep 17 00:00:00 2001 From: Pierre Ambroise Date: Mon, 24 Mar 2025 21:17:09 +0100 Subject: [PATCH 351/642] Document log_channel --- reference/configuration/framework.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 528543a5178..7986140e050 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1025,7 +1025,7 @@ exceptions **type**: ``array`` -Defines the :ref:`log level ` and HTTP status code applied to the +Defines the :ref:`log level `, :ref:`log channel ` and HTTP status code applied to the exceptions that match the given exception class: .. configuration-block:: @@ -1038,6 +1038,7 @@ exceptions that match the given exception class: Symfony\Component\HttpKernel\Exception\BadRequestHttpException: log_level: 'debug' status_code: 422 + log_channel: 'custom_channel' .. code-block:: xml @@ -1055,6 +1056,7 @@ exceptions that match the given exception class: class="Symfony\Component\HttpKernel\Exception\BadRequestHttpException" log-level="debug" status-code="422" + log-channel="custom_channel" /> @@ -1070,9 +1072,14 @@ exceptions that match the given exception class: $framework->exception(BadRequestHttpException::class) ->logLevel('debug') ->statusCode(422) + ->logChannel('custom_channel') ; }; +.. versionadded:: 7.3 + + The ``log_channel`` option was introduced in Symfony 7.3. + The order in which you configure exceptions is important because Symfony will use the configuration of the first exception that matches ``instanceof``: From dae33e402c969606c0c6daa58d99b0caadaf749a Mon Sep 17 00:00:00 2001 From: Silas Joisten Date: Wed, 26 Mar 2025 11:10:39 +0200 Subject: [PATCH 352/642] Adds missing use statement --- routing.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/routing.rst b/routing.rst index a41b9bba6a6..7d489912345 100644 --- a/routing.rst +++ b/routing.rst @@ -969,6 +969,7 @@ optional ``priority`` parameter in those routes to control their priority: namespace App\Controller; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; class BlogController extends AbstractController From 6d7c87f8eaea42c26004206fd1dc465df44e29cd Mon Sep 17 00:00:00 2001 From: Oviglo Date: Mon, 24 Mar 2025 10:51:29 +0100 Subject: [PATCH 353/642] [Security] Add methods param doc for isCsrfTokenValid attribute --- security/csrf.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/security/csrf.rst b/security/csrf.rst index be8348597c7..fa15cee3db3 100644 --- a/security/csrf.rst +++ b/security/csrf.rst @@ -288,6 +288,15 @@ object evaluated to the id:: // ... do something, like deleting an object } +You can use the ``methods`` parameter to the attribute to specify the HTTP methods that are allowed for +the token validation, :class:`Symfony\\Component\\Security\\Http\\Attribute\\IsCsrfTokenValid` is ignored for other methods. By default, the attribute allows all methods:: + + #[IsCsrfTokenValid('delete-item', tokenKey: 'token', methods: ['DELETE'])] + public function delete(Post $post): Response + { + // ... delete the object + } + .. versionadded:: 7.1 The :class:`Symfony\\Component\\Security\\Http\\Attribute\\IsCsrfTokenValid` From 84634a2c44d66ea4931e3ab991252a8e03a3000d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 26 Mar 2025 10:31:19 +0100 Subject: [PATCH 354/642] Reword --- security/csrf.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/security/csrf.rst b/security/csrf.rst index fa15cee3db3..cc9b15253bc 100644 --- a/security/csrf.rst +++ b/security/csrf.rst @@ -288,8 +288,10 @@ object evaluated to the id:: // ... do something, like deleting an object } -You can use the ``methods`` parameter to the attribute to specify the HTTP methods that are allowed for -the token validation, :class:`Symfony\\Component\\Security\\Http\\Attribute\\IsCsrfTokenValid` is ignored for other methods. By default, the attribute allows all methods:: +By default, the ``IsCsrfTokenValid`` attribute performs the CSRF token check for +all HTTP methods. You can restrict this validation to specific methods using the +``methods`` parameter. If the request uses a method not listed in the ``methods`` +array, the attribute is ignored for that request, and no CSRF validation occurs:: #[IsCsrfTokenValid('delete-item', tokenKey: 'token', methods: ['DELETE'])] public function delete(Post $post): Response @@ -302,6 +304,10 @@ the token validation, :class:`Symfony\\Component\\Security\\Http\\Attribute\\IsC The :class:`Symfony\\Component\\Security\\Http\\Attribute\\IsCsrfTokenValid` attribute was introduced in Symfony 7.1. +.. versionadded:: 7.3 + + The ``methods`` parameter was introduced in Symfony 7.3. + CSRF Tokens and Compression Side-Channel Attacks ------------------------------------------------ From 2153ebbc5dc3918794514e16004ae497a4b84cf6 Mon Sep 17 00:00:00 2001 From: Silas Joisten Date: Wed, 26 Mar 2025 11:03:49 +0200 Subject: [PATCH 355/642] Add type hint --- routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.rst b/routing.rst index 7d489912345..79a513ba8dd 100644 --- a/routing.rst +++ b/routing.rst @@ -595,7 +595,7 @@ the ``{page}`` parameter using the ``requirements`` option: } #[Route('/blog/{slug}', name: 'blog_show')] - public function show($slug): Response + public function show(string $slug): Response { // ... } From 2f28d22096fab76a30caed3acd096b5b61bfaf1b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 26 Mar 2025 11:05:49 +0100 Subject: [PATCH 356/642] Minor fix --- serializer/custom_normalizer.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serializer/custom_normalizer.rst b/serializer/custom_normalizer.rst index 8072b590ced..4221b3ad808 100644 --- a/serializer/custom_normalizer.rst +++ b/serializer/custom_normalizer.rst @@ -39,7 +39,7 @@ to customize the normalized data. To do that, leverage the ``ObjectNormalizer``: // Here, add, edit, or delete some data: $data['href']['self'] = $this->router->generate('topic_show', [ - 'id' => $topic->getId(), + 'id' => $object->getId(), ], UrlGeneratorInterface::ABSOLUTE_URL); return $data; From d6b4ff9e7bfd146eb037894705d6a9ff95b7d611 Mon Sep 17 00:00:00 2001 From: Wouter de Jong Date: Tue, 4 Mar 2025 22:06:02 +0100 Subject: [PATCH 357/642] Add more information about core team processes --- contributing/core_team.rst | 140 ++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 16 deletions(-) diff --git a/contributing/core_team.rst b/contributing/core_team.rst index 2edb42a842d..88c9edf45ab 100644 --- a/contributing/core_team.rst +++ b/contributing/core_team.rst @@ -27,10 +27,24 @@ Core Team Member Responsibilities Core Team members are unpaid volunteers and as such, they are not expected to dedicate any specific amount of time on Symfony. They are expected to help the -project in any way they can, from reviewing pull requests, writing documentation -to participating in discussions and helping the community in general, but their -involvement is completely voluntary and can be as much or as little as they -want. +project in any way they can. From reviewing pull requests and writing documentation, +to participating in discussions and helping the community in general. However, +their involvement is completely voluntary and can be as much or as little as +they want. + +Core Team Communication +~~~~~~~~~~~~~~~~~~~~~~~ + +As an open source project, public discussions and documentation is favored +over private ones. All communication in the Symfony community conforms to +the :doc:`/contributing/code_of_conduct/code_of_conduct`. Request +assistance from other Core and CARE team members when getting in situations +not following the Code of Conduct. + +Core Team members are invited in a private Slack channel, for quick +interactions and private processes (e.g. security issues). Each member +should feel free to ask for assistance for anything they may encounter. +Expect no judgement from other team members. Core Organization ----------------- @@ -146,6 +160,14 @@ A Symfony Core membership can be revoked for any of the following reasons: * Willful negligence or intent to harm the Symfony project; * Upon decision of the **Project Leader**. +Core Membership Compensation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Core Team members work on Symfony on a purely voluntary basis. In return +for their work for the Symfony project, members can get free access to +Symfony conferences. Personal vouchers for Symfony conferences are handed out +on request by the **Project Leader**. + Code Development Rules ---------------------- @@ -170,7 +192,7 @@ Pull Request Merging Policy A pull request **can be merged** if: -* It is a :ref:`minor change `; +* It is a :ref:`unsubstantial change `; * Enough time was given for peer reviews; * It is a bug fix and at least two **Mergers Team** members voted ``+1`` (only one if the submitter is part of the Mergers team) and no Core @@ -179,12 +201,20 @@ A pull request **can be merged** if: ``+1`` (if the submitter is part of the Mergers team, two *other* members) and no Core member voted ``-1`` (via GitHub reviews or as comments). +.. _core-team_unsubstantial-changes: + +.. note:: + + Unsubstantial changes comprise typos, DocBlock fixes, code standards + fixes, comment, exception message tweaks, and minor CSS, JavaScript and + HTML modifications. + Pull Request Merging Process ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -All code must be committed to the repository through pull requests, except for -:ref:`minor change ` which can be committed directly -to the repository. +All code must be committed to the repository through pull requests, except +for :ref:`unsubstantial change ` which can be +committed directly to the repository. **Mergers** must always use the command-line ``gh`` tool provided by the **Project Leader** to merge pull requests. @@ -206,6 +236,90 @@ following these rules: Getting the right category is important as it is used by automated tools to generate the CHANGELOG files when releasing new versions. +.. tip:: + + Core team members are part of the ``mergers`` group on the ``symfony`` + Github organization. This gives them write-access to many repositories, + including the main ``symfony/symfony`` mono-repository. + + To avoid unintentional pushes to the main project (which in turn creates + new versions on Packagist), Core team members are encouraged to have + two clones of the project locally: + + #. A clone for their own contributions, which they use to push to their + fork on GitHub. Clear out the push URL for the Symfony repository using + ``git remote set-url --push origin dev://null`` (change ``origin`` + to the Git remote poiting to the Symfony repository); + #. A clone for merging, which they use in combination with ``gh`` and + allows them to push to the main repository. + +Upmerging Version Branches +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To synchronize changes in all versions, version branches are regularly +merged from oldest to latest, called "upmerging". This is a manual process. +There is no strict policy on when this occurs, but usually not more than +once a day and at least once before monthly releases. + +Before starting the upmerge, Git must be configured to provide a merge +summary by running: + +.. code-block:: terminal + + # Run command in the "symfony" repository + $ git config merge.stat true + +The upmerge should always be done on all maintained versions at the same +time. Refer to `the releases page`_ to find all actively maintained +versions (indicated by a green color). + +The process follows these steps: + +#. Start on the oldest version and make sure it's up to date with the + upstream repository; +#. Check-out the second oldest version, update from upstream and merge the + previous version from the local branch; +#. Continue this process until you reached the latest version; +#. Push the branches to the repository and monitor the test suite. Failure + might indicate hidden/missed merge conflicts. + +.. code-block:: terminal + + # 'origin' is refered to as the main upstream project + $ git fetch origin + + # update the local branches + $ git checkout 6.4 + $ git reset --hard origin/6.4 + $ git checkout 7.2 + $ git reset --hard origin/7.2 + $ git checkout 7.3 + $ git reset --hard origin/7.3 + + # upmerge 6.4 into 7.2 + $ git checkout 7.2 + $ git merge --no-ff 6.4 + # ... resolve conflicts + $ git commit + + # upmerge 7.2 into 7.3 + $ git checkout 7.3 + $ git merge --no-ff 7.2 + # ... resolve conflicts + $ git commit + + $ git push origin 7.3 7.2 6.4 + +.. warning:: + + Upmerges must be explicit, i.e. no fast-forward merges. + +.. tip:: + + Solving merge conflicts can be challenging. You can always ping other + Core team members to help you in the process (e.g. members that merged + a specific conflicting change). + Release Policy ~~~~~~~~~~~~~~ @@ -217,13 +331,6 @@ Symfony Core Rules and Protocol Amendments The rules described in this document may be amended at any time at the discretion of the **Project Leader**. -.. _core-team_minor-changes: - -.. note:: - - Minor changes comprise typos, DocBlock fixes, code standards - violations, and minor CSS, JavaScript and HTML modifications. - .. _`symfony-docs repository`: https://github.com/symfony/symfony-docs .. _`UX repositories`: https://github.com/symfony/ux .. _`fabpot`: https://github.com/fabpot/ @@ -264,4 +371,5 @@ discretion of the **Project Leader**. .. _`mtarld`: https://github.com/mtarld/ .. _`spomky`: https://github.com/spomky/ .. _`alexandre-daubois`: https://github.com/alexandre-daubois/ -.. _`tucksaun`: https://github.com/tucksaun/ \ No newline at end of file +.. _`tucksaun`: https://github.com/tucksaun/ +.. _`the releases page`: https://symfony.com/releases From 89e37c14121ed155d84a295554343f1ff1674057 Mon Sep 17 00:00:00 2001 From: MrYamous Date: Fri, 28 Mar 2025 08:28:17 +0100 Subject: [PATCH 358/642] messenger: allow to close connection --- messenger.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/messenger.rst b/messenger.rst index 0f1eba49a9f..06ad586407d 100644 --- a/messenger.rst +++ b/messenger.rst @@ -2254,6 +2254,20 @@ on a case-by-case basis via the :class:`Symfony\\Component\\Messenger\\Stamp\\Se provides that control. See `SymfonyCasts' message serializer tutorial`_ for details. +Closing connection +~~~~~~~~~~~~~~~~~~ + +When using a transport that requires a connection, you can close it using +the :method:`Symfony\\Component\\Messenger\\Transport\\CloseableTransportInterface::close` +method to allow free resources for long-running processes. + +This interface is implemented by the following transports: AmazonSqs, Amqp and Redis. +If you want to close a Doctrine connection, this can be achieved :ref:`using middleware `. + +.. versionadded:: 7.3 + + The ``CloseableTransportInterface`` and ``close`` method were introduced in Symfony 7.3. + Running Commands And External Processes --------------------------------------- From 76f7e6e9506ebc8f04d956af63d9c4b723eca446 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 28 Mar 2025 16:59:53 +0100 Subject: [PATCH 359/642] Minor tweak --- 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 7986140e050..3f34f792630 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1025,8 +1025,8 @@ exceptions **type**: ``array`` -Defines the :ref:`log level `, :ref:`log channel ` and HTTP status code applied to the -exceptions that match the given exception class: +Defines the :ref:`log level `, :ref:`log channel ` +and HTTP status code applied to the exceptions that match the given exception class: .. configuration-block:: From 5b9b62ea671b4894c733dccd4a49e1acc5d20686 Mon Sep 17 00:00:00 2001 From: Tugdual Saunier Date: Sun, 30 Mar 2025 10:45:20 +0200 Subject: [PATCH 360/642] Add CLI team description --- contributing/core_team.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contributing/core_team.rst b/contributing/core_team.rst index 88c9edf45ab..f895dcd00d8 100644 --- a/contributing/core_team.rst +++ b/contributing/core_team.rst @@ -69,6 +69,7 @@ In addition, there are other groups created to manage specific topics: * **Security Team**: manages the whole security process (triaging reported vulnerabilities, fixing the reported issues, coordinating the release of security fixes, etc.); * **Symfony UX Team**: manages the `UX repositories`_; +* **Symfony CLI Team**: manages the `CLI repositories`_; * **Documentation Team**: manages the whole `symfony-docs repository`_. Active Core Members @@ -100,7 +101,6 @@ Active Core Members * **Berislav Balogović** (`hypemc`_); * **Mathias Arlaud** (`mtarld`_); * **Florent Morselli** (`spomky`_); - * **Tugdual Saunier** (`tucksaun`_); * **Alexandre Daubois** (`alexandre-daubois`_). * **Security Team** (``@symfony/security`` on GitHub): @@ -116,6 +116,11 @@ Active Core Members * **Hugo Alliaume** (`kocal`_); * **Matheo Daninos** (`webmamba`_). +* **Symfony CLI Team** (``@symfony-cli/core`` on GitHub): + + * **Fabien Potencier** (`fabpot`_); + * **Tugdual Saunier** (`tucksaun`_). + * **Documentation Team** (``@symfony/team-symfony-docs`` on GitHub): * **Fabien Potencier** (`fabpot`_); @@ -333,6 +338,7 @@ discretion of the **Project Leader**. .. _`symfony-docs repository`: https://github.com/symfony/symfony-docs .. _`UX repositories`: https://github.com/symfony/ux +.. _`CLI repositories`: https://github.com/symfony-cli .. _`fabpot`: https://github.com/fabpot/ .. _`webmozart`: https://github.com/webmozart/ .. _`Tobion`: https://github.com/Tobion/ From 1d02e0ca8e4be30395bcd45b183b3b1129de4cd6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 31 Mar 2025 08:02:46 +0200 Subject: [PATCH 361/642] Minor tweaks --- messenger.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/messenger.rst b/messenger.rst index 06ad586407d..fc8f9491022 100644 --- a/messenger.rst +++ b/messenger.rst @@ -2254,19 +2254,21 @@ on a case-by-case basis via the :class:`Symfony\\Component\\Messenger\\Stamp\\Se provides that control. See `SymfonyCasts' message serializer tutorial`_ for details. -Closing connection -~~~~~~~~~~~~~~~~~~ +Closing Connections +~~~~~~~~~~~~~~~~~~~ -When using a transport that requires a connection, you can close it using -the :method:`Symfony\\Component\\Messenger\\Transport\\CloseableTransportInterface::close` -method to allow free resources for long-running processes. +When using a transport that requires a connection, you can close it by calling the +:method:`Symfony\\Component\\Messenger\\Transport\\CloseableTransportInterface::close` +method to free up resources in long-running processes. -This interface is implemented by the following transports: AmazonSqs, Amqp and Redis. -If you want to close a Doctrine connection, this can be achieved :ref:`using middleware `. +This interface is implemented by the following transports: AmazonSqs, Amqp, and Redis. +If you need to close a Doctrine connection, you can do so +:ref:`using middleware `. .. versionadded:: 7.3 - The ``CloseableTransportInterface`` and ``close`` method were introduced in Symfony 7.3. + The ``CloseableTransportInterface`` and its ``close()`` method were introduced + in Symfony 7.3. Running Commands And External Processes --------------------------------------- From bffad6daf493a377863a6389799623defd92de52 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 1 Apr 2025 09:05:00 +0200 Subject: [PATCH 362/642] Minor tweaks --- serializer.rst | 61 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/serializer.rst b/serializer.rst index 5f9144abae0..1fca42a3572 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1544,13 +1544,13 @@ Named Serializers Named serializers were introduced in Symfony 7.2. -Sometimes, you may need multiple configurations for the serializer, such -as different default contexts, name converters, or sets of normalizers and -encoders, depending on the use case. For example, when your application -communicates with multiple APIs, each with its own set of rules. +Sometimes, you may need multiple configurations for the serializer, such as +different default contexts, name converters, or sets of normalizers and encoders, +depending on the use case. For example, when your application communicates with +multiple APIs, each of which follows its own set of serialization rules. -This can be achieved by configuring multiple instances of the serializer -using the ``named_serializers`` option: +You can achieve this by configuring multiple serializer instances using +the ``named_serializers`` option: .. configuration-block:: @@ -1633,7 +1633,7 @@ using :ref:`named aliases `:: class PersonController extends AbstractController { public function index( - SerializerInterface $serializer, // Default serializer + SerializerInterface $serializer, // default serializer SerializerInterface $apiClient1Serializer, // api_client1 serializer #[Target('apiClient2.serializer')] // api_client2 serializer SerializerInterface $customName, @@ -1642,10 +1642,10 @@ using :ref:`named aliases `:: } } -Named serializers are configured with the default set of normalizers and encoders. - -You can register additional normalizers and encoders with a specific named -serializer by adding a ``serializer`` attribute to +By default, named serializers use the built-in set of normalizers and encoders, +just like the main serializer service. However, you can customize them by +registering additional normalizers or encoders for a specific named serializer. +To do that, add a ``serializer`` attribute to the :ref:`serializer.normalizer ` or :ref:`serializer.encoder ` tags: @@ -1658,14 +1658,14 @@ or :ref:`serializer.encoder ` tags: # ... Symfony\Component\Serializer\Normalizer\CustomNormalizer: - # Prevent this normalizer from automatically being included in the default serializer + # prevent this normalizer from being automatically added to the default serializer autoconfigure: false tags: - # Include this normalizer in a single serializer + # add this normalizer only to a specific named serializer - serializer.normalizer: { serializer: 'api_client1' } - # Include this normalizer in multiple serializers + # add this normalizer to several named serializers - serializer.normalizer: { serializer: [ 'api_client1', 'api_client2' ] } - # Include this normalizer in all serializers (including the default one) + # add this normalizer to all serializers, including the default one - serializer.normalizer: { serializer: '*' } .. code-block:: xml @@ -1680,20 +1680,19 @@ or :ref:`serializer.encoder ` tags: - - + - + - + - + @@ -1710,32 +1709,32 @@ or :ref:`serializer.encoder ` tags: // ... $services->set(CustomNormalizer::class) - // Prevent this normalizer from automatically being included in the default serializer + // prevent this normalizer from being automatically added to the default serializer ->autoconfigure(false) - // Include this normalizer in a single serializer + // add this normalizer only to a specific named serializer ->tag('serializer.normalizer', ['serializer' => 'api_client1']) - // Include this normalizer in multiple serializers + // add this normalizer to several named serializers ->tag('serializer.normalizer', ['serializer' => ['api_client1', 'api_client2']]) - // Include this normalizer in all serializers (including the default one) + // add this normalizer to all serializers, including the default one ->tag('serializer.normalizer', ['serializer' => '*']) ; }; -When the ``serializer`` attribute is not set, the service is registered with +When the ``serializer`` attribute is not set, the service is registered only with the default serializer. -Each normalizer and encoder used in a named serializer is tagged with -a ``serializer.normalizer.`` or ``serializer.encoder.`` tag, -which can be used to list their priorities using the following command: +Each normalizer or encoder used in a named serializer is tagged with a +``serializer.normalizer.`` or ``serializer.encoder.`` tag. +You can inspect their priorities using the following command: .. code-block:: terminal $ php bin/console debug:container --tag serializer.. -Additionally, you can exclude the default set of normalizers and encoders by -setting the ``include_built_in_normalizers`` and ``include_built_in_encoders`` -options to ``false``: +Additionally, you can exclude the default set of normalizers and encoders from a +named serializer by setting the ``include_built_in_normalizers`` and +``include_built_in_encoders`` options to ``false``: .. configuration-block:: From 8d35db9a1f83b31f7828f8910c744a91f60a82c3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 1 Apr 2025 09:57:29 +0200 Subject: [PATCH 363/642] Tweaks --- .../service_subscribers_locators.rst | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index d154f2dce3d..14cdb010152 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -298,10 +298,10 @@ This is done by having ``getSubscribedServices()`` return an array of The above example requires using ``3.2`` version or newer of ``symfony/service-contracts``. .. _service-locator_autowire-locator: -.. _service-locator_autowire-iterator: +.. _the-autowirelocator-and-autowireiterator-attributes: -The AutowireLocator and AutowireIterator Attributes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The AutowireLocator Attribute +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Another way to define a service locator is to use the :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator` @@ -381,16 +381,17 @@ attribute:: :class:`Symfony\\Component\\DependencyInjection\\Attribute\\AutowireLocator` attribute was introduced in Symfony 6.4. +.. _service-locator_autowire-iterator: + The AutowireIterator Attribute ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Variant of the ``AutowireLocator`` that specifically provides an iterable of services -based on a tag. This allows you to collect all services with a particular tag into -an iterable, which can be useful when you need to iterate over a set of services -rather than retrieving them individually. -For example, if you want to collect all the handlers for different command types, -you can use the ``AutowireIterator`` attribute to automatically inject all services -tagged with a specific tag:: +A variant of ``AutowireLocator`` that injects an iterable of services tagged +with a specific :doc:`tag `. This is useful to loop +over a set of tagged services instead of retrieving them individually. + +For example, to collect all handlers for different command types, use the +``AutowireIterator`` attribute and pass the tag used by those services:: // src/CommandBus.php namespace App; @@ -404,7 +405,7 @@ tagged with a specific tag:: { public function __construct( #[AutowireIterator('command_handler')] - private iterable $handlers, // Collects all services tagged with 'command_handler' + private iterable $handlers, // collects all services tagged with 'command_handler' ) { } From 6fd2b0e53a6bf8700152c05ec384f5b27bc14d85 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 1 Apr 2025 16:06:08 +0200 Subject: [PATCH 364/642] Minor tweaks --- translation.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/translation.rst b/translation.rst index 231969f9487..5565c65bdba 100644 --- a/translation.rst +++ b/translation.rst @@ -497,9 +497,9 @@ Symfony looks for message files (i.e. translations) in the following default loc ``Resources/translations/`` directory, which is no longer recommended for bundles). The locations are listed here with the highest priority first. That is, you can -override the translation messages of a bundle in the first directory. Bundles are processed -in the order they are given in the ``config/bundles.php`` file, so bundles listed first have -higher priority. +override the translation messages of a bundle in the first directory. Bundles are +processed in the order in which they are listed in the ``config/bundles.php`` file, +so bundles appearing earlier have higher priority. The override mechanism works at a key level: only the overridden keys need to be listed in a higher priority message file. When a key is not found From 4742f5d77f54ed153beefbb510b8591c0830bae4 Mon Sep 17 00:00:00 2001 From: Fabian Freiburg Date: Wed, 5 Feb 2025 15:14:38 +0100 Subject: [PATCH 365/642] Readd handling of fallback of bundles directory in Apache config --- setup/web_server_configuration.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/setup/web_server_configuration.rst b/setup/web_server_configuration.rst index 0612f609721..fdedfc81794 100644 --- a/setup/web_server_configuration.rst +++ b/setup/web_server_configuration.rst @@ -178,6 +178,14 @@ directive to pass requests for PHP files to PHP FPM: # Options FollowSymlinks # + # optionally disable the fallback resource for the asset directories + # which will allow Apache to return a 404 error when files are + # not found instead of passing the request to Symfony + # + # DirectoryIndex disabled + # FallbackResource disabled + # + ErrorLog /var/log/apache2/project_error.log CustomLog /var/log/apache2/project_access.log combined From 671eba4dc083e285978a5fb723f2bad4b93976e6 Mon Sep 17 00:00:00 2001 From: MrYamous Date: Wed, 2 Apr 2025 05:45:39 +0200 Subject: [PATCH 366/642] [FrameworkBundle] Deprecate setting the collect_serializer_data to false --- reference/configuration/framework.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 3f34f792630..bcf99d989c7 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -2352,12 +2352,16 @@ Combine it with the ``collect`` option to enable/disable the profiler on demand: collect_serializer_data ....................... -**type**: ``boolean`` **default**: ``false`` +**type**: ``boolean`` **default**: ``true`` -Set this option to ``true`` to enable the serializer data collector and its -profiler panel. When this option is ``true``, all normalizers and encoders are +When this option is ``true``, all normalizers and encoders are decorated by traceable implementations that collect profiling information about them. +.. deprecated:: 7.3 + + Setting the ``collect_serializer_data`` option to ``false`` is deprecated + since Symfony 7.3. + .. _profiler-dsn: dsn From 7b9ffb28b276674ae330e113c07e578b87055200 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 2 Apr 2025 17:00:44 +0200 Subject: [PATCH 367/642] Tweaks --- service_container/tags.rst | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/service_container/tags.rst b/service_container/tags.rst index ae10f8ef51a..fab25ea910a 100644 --- a/service_container/tags.rst +++ b/service_container/tags.rst @@ -155,24 +155,8 @@ In a Symfony application, call this method in your kernel class:: } } -In a Symfony bundle, call this method in the ``load()`` method of the -:doc:`bundle extension class `:: - - // src/DependencyInjection/MyBundleExtension.php - class MyBundleExtension extends Extension - { - // ... - - public function load(array $configs, ContainerBuilder $container): void - { - $container->registerForAutoconfiguration(CustomInterface::class) - ->addTag('app.custom_tag') - ; - } - } - -or if you are following the recommended way for new bundles and for bundles following the -:ref:`recommended directory structure `:: +In bundles extending the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle` +class, call this method in the ``loadExtension()`` method of the main bundle class:: // ... use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -190,6 +174,11 @@ or if you are following the recommended way for new bundles and for bundles foll } } +.. note:: + + For bundles not extending the ``AbstractBundle`` class, call this method in + the ``load()`` method of the :doc:`bundle extension class `. + Autoconfiguration registering is not limited to interfaces. It is possible to use PHP attributes to autoconfigure services by using the :method:`Symfony\\Component\\DependencyInjection\\ContainerBuilder::registerAttributeForAutoconfiguration` From b1e2c53a4bc85add8633e13b02e676961503b2a9 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 3 Apr 2025 08:49:41 +0200 Subject: [PATCH 368/642] Tweaks --- reference/configuration/security.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index b4ba6696e18..3ccea5f9026 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -1004,11 +1004,11 @@ the session must not be used when authenticating users: .. _reference-security-lazy: lazy -~~~~~~~~~ +~~~~ -Firewalls can configure a ``lazy`` boolean option in order to load the user and start the session only -if the application actually accesses the User object, -(e.g. via a is_granted() call in a template or isGranted() in a controller or service): +Firewalls can configure a ``lazy`` boolean option to load the user and start the +session only if the application actually accesses the User object, (e.g. calling +``is_granted()`` in a template or ``isGranted()`` in a controller or service): .. configuration-block:: @@ -1048,8 +1048,8 @@ if the application actually accesses the User object, use Symfony\Config\SecurityConfig; return static function (SecurityConfig $security): void { - $mainFirewall = $security->firewall('main'); - $mainFirewall->lazy(true); + $security->firewall('main') + ->lazy(true); // ... }; From 6cfc92a41d05b0034ab643ab86538cd65b353670 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 3 Apr 2025 08:57:25 +0200 Subject: [PATCH 369/642] Minor tweaks --- messenger.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/messenger.rst b/messenger.rst index c0822f97fdd..cefa6b8c0d4 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1865,9 +1865,9 @@ under the transport in ``messenger.yaml``: The Redis consumer group name ``consumer`` (default: ``consumer``) - Consumer name used in Redis. Allows to set explicit consumer name identifier. - Recommended for environments with multiple workers to prevent duplicate message processing. - Typically set via environment variable: + Consumer name used in Redis. Allows setting an explicit consumer name identifier. + Recommended in environments with multiple workers to prevent duplicate message + processing. Typically set via an environment variable: .. code-block:: yaml From 111956866e03a6fbbc015e0af60a16bbe5278554 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 3 Apr 2025 08:59:29 +0200 Subject: [PATCH 370/642] [Messenger] Add consumer name documentation --- messenger.rst | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index e86bc73d1c6..6fbfd36385d 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1698,7 +1698,20 @@ under the transport in ``messenger.yaml``: The Redis consumer group name ``consumer`` (default: ``consumer``) - Consumer name used in Redis + Consumer name used in Redis. Allows setting an explicit consumer name identifier. + Recommended in environments with multiple workers to prevent duplicate message + processing. Typically set via an environment variable: + + .. code-block:: yaml + + # config/packages/messenger.yaml + framework: + messenger: + transports: + redis: + dsn: '%env(MESSENGER_TRANSPORT_DSN)%' + options: + consumer: '%env(MESSENGER_CONSUMER_NAME)%' ``auto_setup`` (default: ``true``) Whether to create the Redis group automatically From 0ff840444f265d5492238a92df8081e01eaaf6d2 Mon Sep 17 00:00:00 2001 From: Adam Prancz Date: Wed, 18 Dec 2024 09:48:18 +0100 Subject: [PATCH 371/642] [Serializer] Update serializer.rst --- serializer.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/serializer.rst b/serializer.rst index 7f2569eb104..24ec7e325e0 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1352,6 +1352,31 @@ normalizers (in order of priority): This denormalizer converts an array of arrays to an array of objects (with the given type). See :ref:`Handling Arrays `. + ByUsing the PropertyInfoExtractor you can simply annotate the arrays by '@var Person[]' + + .. configuration-block:: + + .. code-block:: php-standalone + + use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor; + use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; + use Symfony\Component\PropertyInfo\PropertyInfoExtractor; + use Symfony\Component\Serializer\Encoder\JsonEncoder; + use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; + use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader; + use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; + use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; + use Symfony\Component\Serializer\Serializer; + + $reflectionExtractor = new ReflectionExtractor(); + $phpDocExtractor = new PhpDocExtractor(); + $propertyInfo = new PropertyInfoExtractor([], [$phpDocExtractor, $reflectionExtractor]); + + $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); + $normalizers = [new ObjectNormalizer($classMetadataFactory, null, null, $propertyInfo), new ArrayDenormalizer()]; + + $this->serializer = new Serializer($normalizers, [new JsonEncoder()]); + :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` This is the most powerful default normalizer and used for any object that could not be normalized by the other normalizers. From 5e6d3b08c2aab43c19e319928f23769764214366 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 3 Apr 2025 09:12:44 +0200 Subject: [PATCH 372/642] Minor tweaks --- serializer.rst | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/serializer.rst b/serializer.rst index 24ec7e325e0..6e20a651341 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1352,7 +1352,8 @@ normalizers (in order of priority): This denormalizer converts an array of arrays to an array of objects (with the given type). See :ref:`Handling Arrays `. - ByUsing the PropertyInfoExtractor you can simply annotate the arrays by '@var Person[]' + Use :class:`Symfony\\Component\\PropertyInfo\\PropertyInfoExtractor` to provide + hints with annotations like ``@var Person[]``: .. configuration-block:: @@ -1367,13 +1368,9 @@ normalizers (in order of priority): use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; use Symfony\Component\Serializer\Serializer; - - $reflectionExtractor = new ReflectionExtractor(); - $phpDocExtractor = new PhpDocExtractor(); - $propertyInfo = new PropertyInfoExtractor([], [$phpDocExtractor, $reflectionExtractor]); - - $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader()); - $normalizers = [new ObjectNormalizer($classMetadataFactory, null, null, $propertyInfo), new ArrayDenormalizer()]; + + $propertyInfo = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]); + $normalizers = [new ObjectNormalizer(new ClassMetadataFactory(new AttributeLoader()), null, null, $propertyInfo), new ArrayDenormalizer()]; $this->serializer = new Serializer($normalizers, [new JsonEncoder()]); From 05ff5258e27fd36bc5fdec0391b6833ab302a4f8 Mon Sep 17 00:00:00 2001 From: Ignacio Aguirre <52148018+nachoaguirre@users.noreply.github.com> Date: Tue, 10 Dec 2024 23:47:02 -0300 Subject: [PATCH 373/642] Update dynamic_form_modification.rst Update the namespace of the class to subscribe to events. Currently it points to \EventListener, but I find it more coherent to associate it with \EventSubscriber --- form/dynamic_form_modification.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/form/dynamic_form_modification.rst b/form/dynamic_form_modification.rst index 09be80ebb5a..a1f32c7c16c 100644 --- a/form/dynamic_form_modification.rst +++ b/form/dynamic_form_modification.rst @@ -138,8 +138,8 @@ For better reusability or if there is some heavy logic in your event listener, you can also move the logic for creating the ``name`` field to an :ref:`event subscriber `:: - // src/Form/EventListener/AddNameFieldSubscriber.php - namespace App\Form\EventListener; + // src/Form/EventSubscriber/AddNameFieldSubscriber.php + namespace App\Form\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\Extension\Core\Type\TextType; @@ -172,7 +172,7 @@ Great! Now use that in your form class:: namespace App\Form\Type; // ... - use App\Form\EventListener\AddNameFieldSubscriber; + use App\Form\EventSubscriber\AddNameFieldSubscriber; class ProductType extends AbstractType { From 850666f30df9503367b415410c083df1fb44da73 Mon Sep 17 00:00:00 2001 From: sarah-eit Date: Wed, 27 Nov 2024 14:59:57 +0100 Subject: [PATCH 374/642] [Twig] [twig reference] add examples to functions and filter --- reference/twig_reference.rst | 199 +++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 21f251dc6de..4b145364b0e 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -110,6 +110,22 @@ asset ``packageName`` *(optional)* **type**: ``string`` | ``null`` **default**: ``null`` +.. code-block:: yaml + + # config/packages/framework.yaml + framework: + # ... + assets: + packages: + foo_package: + base_path: /avatars + +.. code-block:: twig + + {# the image lives at "public/avatars/avatar.png" #} + {{ asset(path = 'avatar.png', packageName = 'foo_package') }} + {# output: /avatars/avatar.png #} + Returns the public path of the given asset path (which can be a CSS file, a JavaScript file, an image path, etc.). This function takes into account where the application is installed (e.g. in case the project is accessed in a host @@ -187,6 +203,30 @@ logout_path Generates a relative logout URL for the given firewall. If no key is provided, the URL is generated for the current firewall the user is logged into. +.. code-block:: yaml + + # config/packages/security.yaml + security: + # ... + + firewalls: + main: + # ... + logout: + path: '/logout' + othername: + # ... + logout: + path: '/other/logout' + +.. code-block:: twig + + {{ logout_path(key = 'main') }} + {# output: /logout #} + + {{ logout_path(key = 'othername') }} + {# output: /other/logout #} + logout_url ~~~~~~~~~~ @@ -200,6 +240,30 @@ logout_url Equal to the `logout_path`_ function, but it'll generate an absolute URL instead of a relative one. +.. code-block:: yaml + + # config/packages/security.yaml + security: + # ... + + firewalls: + main: + # ... + logout: + path: '/logout' + othername: + # ... + logout: + path: '/other/logout' + +.. code-block:: twig + + {{ logout_url(key = 'main') }} + {# output: http://example.org/logout #} + + {{ logout_url(key = 'othername') }} + {# output: http://example.org/other/logout #} + path ~~~~ @@ -217,6 +281,14 @@ path Returns the relative URL (without the scheme and host) for the given route. If ``relative`` is enabled, it'll create a path relative to the current path. +.. code-block:: twig + + {{ path(name = 'app_blog', parameters = {page: 3}, relative = false) }} + {# output (depending on the route configuration): /blog/3 or /blog?page=3 #} + + {{ path(name = 'app_blog', parameters = {page: 3}, relative = true) }} + {# output (depending on the route configuration): blog/3 or ?page=3 #} + .. seealso:: Read more about :doc:`Symfony routing ` and about @@ -239,6 +311,16 @@ url Returns the absolute URL (with scheme and host) for the given route. If ``schemeRelative`` is enabled, it'll create a scheme-relative URL. +.. code-block:: twig + + {{ url(name = 'app_blog', parameters = {page: 3}, schemeRelative = false) }} + {# output (depending on the route configuration): http://example.org/blog/3 + or http://example.org/blog?page=3 #} + + {{ url(name = 'app_blog', parameters = {page: 3}, schemeRelative = true) }} + {# output (depending on the route configuration): //example.org/blog/3 + or //example.org/blog?page=3 #} + .. seealso:: Read more about :doc:`Symfony routing ` and about @@ -290,6 +372,11 @@ expression Creates an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` related to the :doc:`ExpressionLanguage component `. +.. code-block:: twig + + {{ expression(1 + 2) }} + {# output: 3 #} + impersonation_path ~~~~~~~~~~~~~~~~~~ @@ -373,6 +460,42 @@ t Creates a ``Translatable`` object that can be passed to the :ref:`trans filter `. +.. configuration-block:: + + .. code-block:: yaml + + # translations/blog.en.yaml + message: Hello %name% + + .. code-block:: xml + + + + + + + + message + Hello %name% + + + + + + .. code-block:: php + + // translations/blog.en.php + return [ + 'message' => "Hello %name%", + ]; + +Using the filter will be rendered as: + +.. code-block:: twig + + {{ t(message = 'message', parameters = {'%name%': 'John'}, domain = 'blog')|trans }} + {# output: Hello John #} + importmap ~~~~~~~~~ @@ -452,6 +575,42 @@ trans Translates the text into the current language. More information in :ref:`Translation Filters `. +.. configuration-block:: + + .. code-block:: yaml + + # translations/messages.en.yaml + message: Hello %name% + + .. code-block:: xml + + + + + + + + message + Hello %name% + + + + + + .. code-block:: php + + // translations/messages.en.php + return [ + 'message' => "Hello %name%", + ]; + +Using the filter will be rendered as: + +.. code-block:: twig + + {{ 'message'|trans(arguments = {'%name%': 'John'}, domain = 'messages', locale = 'en') }} + {# output: Hello John #} + sanitize_html ~~~~~~~~~~~~~ @@ -593,6 +752,16 @@ abbr_class Generates an ```` element with the short name of a PHP class (the FQCN will be shown in a tooltip when a user hovers over the element). +.. code-block:: twig + + {{ 'App\\Entity\\Product'|abbr_class }} + +The above example will be rendered as: + +.. code-block:: html + + Product + abbr_method ~~~~~~~~~~~ @@ -607,6 +776,16 @@ Generates an ```` element using the ``FQCN::method()`` syntax. If ``method`` is ``Closure``, ``Closure`` will be used instead and if ``method`` doesn't have a class name, it's shown as a function (``method()``). +.. code-block:: twig + + {{ 'App\\Controller\\ProductController::list'|abbr_method }} + +The above example will be rendered as: + +.. code-block:: html + + ProductController + format_args ~~~~~~~~~~~ @@ -694,6 +873,11 @@ file_link Generates a link to the provided file and line number using a preconfigured scheme. +.. code-block:: twig + + {{ 'path/to/file/file.txt'|file_link(line = 3) }} + {# output: file://path/to/file/file.txt#L3 #} + file_relative ~~~~~~~~~~~~~ @@ -736,6 +920,21 @@ serialize Accepts any data that can be serialized by the :doc:`Serializer component ` and returns a serialized string in the specified ``format``. +For example:: + + $object = new \stdClass(); + $object->foo = 'bar'; + $object->content = []; + $object->createdAt = new \DateTime('2024-11-30'); + +.. code-block:: twig + + {{ object|serialize(format = 'json', context = { + 'datetime_format': 'D, Y-m-d', + 'empty_array_as_object': true, + }) }} + {# output: {"foo":"bar","content":{},"createdAt":"Sat, 2024-11-30"} #} + .. _reference-twig-tags: Tags From f187ace7bf11d80568cf20731aa2bf09e9e9aeb2 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 3 Apr 2025 10:33:39 +0200 Subject: [PATCH 375/642] Minor tweaks --- reference/twig_reference.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 4b145364b0e..825ad3b0af7 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -283,11 +283,13 @@ If ``relative`` is enabled, it'll create a path relative to the current path. .. code-block:: twig + {# consider that the app defines an 'app_blog' route with the path '/blog/{page}' #} + {{ path(name = 'app_blog', parameters = {page: 3}, relative = false) }} - {# output (depending on the route configuration): /blog/3 or /blog?page=3 #} + {# output: /blog/3 #} {{ path(name = 'app_blog', parameters = {page: 3}, relative = true) }} - {# output (depending on the route configuration): blog/3 or ?page=3 #} + {# output: blog/3 #} .. seealso:: @@ -313,13 +315,13 @@ Returns the absolute URL (with scheme and host) for the given route. If .. code-block:: twig + {# consider that the app defines an 'app_blog' route with the path '/blog/{page}' #} + {{ url(name = 'app_blog', parameters = {page: 3}, schemeRelative = false) }} - {# output (depending on the route configuration): http://example.org/blog/3 - or http://example.org/blog?page=3 #} + {# output: http://example.org/blog/3 #} {{ url(name = 'app_blog', parameters = {page: 3}, schemeRelative = true) }} - {# output (depending on the route configuration): //example.org/blog/3 - or //example.org/blog?page=3 #} + {# output: //example.org/blog/3 #} .. seealso:: @@ -784,7 +786,7 @@ The above example will be rendered as: .. code-block:: html - ProductController + ProductController::list() format_args ~~~~~~~~~~~ From 82fea87c3de14d6f56f7d707dd29d890f19902ab Mon Sep 17 00:00:00 2001 From: Edgar Brunet Date: Fri, 29 Nov 2024 14:45:51 +0100 Subject: [PATCH 376/642] =?UTF-8?q?[Twig]=20[twig=20reference]=20add=20exa?= =?UTF-8?q?mples=20to=20functions=20(format=5Ffile,=20file=5F=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reference/twig_reference.rst | 72 ++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 825ad3b0af7..50da312f29f 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -98,6 +98,21 @@ like :ref:`render() ` and .. _reference-twig-function-asset: +.. code-block:: html+twig + + {% set myArray = {'a': 'foo', 'b': 'bar'} %} + + + +Output: + +.. code-block:: html + + + asset ~~~~~ @@ -171,6 +186,11 @@ csrf_token Renders a CSRF token. Use this function if you want :doc:`CSRF protection ` in a regular HTML form not managed by the Symfony Form component. +.. code-block:: twig + + {{ csrf_token(intention = 'my_form') }} + {# output: generates a variable token #} + is_granted ~~~~~~~~~~ @@ -830,6 +850,28 @@ Generates an excerpt of a code file around the given ``line`` number. The ``srcContext`` argument defines the total number of lines to display around the given line number (use ``-1`` to display the whole file). +Let's assume this is the content of a file : + +.. code-block:: text + + a + b + c + d + e + +.. code-block:: twig + + {{ "/path/to/file/file.txt"|file_excerpt(line = 4, srcContext = 1) }} + {# output: + 3.c + 4.d + 5.e #} + + {{ "/path/to/file/file.txt"|file_excerpt(line = 1, srcContext = 0) }} + {# output: + 1.a #} + format_file ~~~~~~~~~~~ @@ -848,6 +890,36 @@ Generates the file path inside an ```` element. If the path is inside the kernel root directory, the kernel root directory path is replaced by ``kernel.project_dir`` (showing the full path in a tooltip on hover). +Example 1 + +.. code-block:: twig + + {{ "path/to/file/file.txt"|format_file(line = 1, text = "my_text") }} + +Output: + +.. code-block:: html + + my_text at line 1 + + +Example 2 + +.. code-block:: twig + + {{ "path/to/file/file.txt"|format_file(line = 3) }} + +Output: + +.. code-block:: html + + + file.txt + / at line 3 + + format_file_from_text ~~~~~~~~~~~~~~~~~~~~~ From 3c4f75387112e4e5f755f7925517edcbf86ab668 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 3 Apr 2025 12:33:55 +0200 Subject: [PATCH 377/642] Tweaks and rewords --- reference/configuration/framework.rst | 2 + reference/twig_reference.rst | 84 ++++++++++++--------------- 2 files changed, 39 insertions(+), 47 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 8838a6a61b2..2274addf1aa 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -271,6 +271,8 @@ The ``trusted_proxies`` option is needed to get precise information about the client (e.g. their IP address) when running Symfony behind a load balancer or a reverse proxy. See :doc:`/deployment/proxies`. +.. _reference-framework-ide: + ide ~~~ diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 50da312f29f..08ea8f1d581 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -96,22 +96,12 @@ Returns an instance of ``ControllerReference`` to be used with functions like :ref:`render() ` and :ref:`render_esi() `. -.. _reference-twig-function-asset: - .. code-block:: html+twig - {% set myArray = {'a': 'foo', 'b': 'bar'} %} - - - -Output: - -.. code-block:: html + {{ render(controller('App\\Controller\\BlogController:latest', {max: 3})) }} + {# output: the content returned by the controller method; e.g. a rendered Twig template #} - +.. _reference-twig-function-asset: asset ~~~~~ @@ -188,8 +178,9 @@ in a regular HTML form not managed by the Symfony Form component. .. code-block:: twig - {{ csrf_token(intention = 'my_form') }} - {# output: generates a variable token #} + {{ csrf_token('my_form') }} + {# output: a random alphanumeric string like: + a.YOosAd0fhT7BEuUCFbROzrvgkW8kpEmBDQ_DKRMUi2o.Va8ZQKt5_2qoa7dLW-02_PLYwDBx9nnWOluUHUFCwC5Zo0VuuVfQCqtngg #} is_granted ~~~~~~~~~~ @@ -850,7 +841,7 @@ Generates an excerpt of a code file around the given ``line`` number. The ``srcContext`` argument defines the total number of lines to display around the given line number (use ``-1`` to display the whole file). -Let's assume this is the content of a file : +Consider the following as the content of ``file.txt``: .. code-block:: text @@ -862,15 +853,21 @@ Let's assume this is the content of a file : .. code-block:: twig - {{ "/path/to/file/file.txt"|file_excerpt(line = 4, srcContext = 1) }} + {{ '/path/to/file.txt'|file_excerpt(line = 4, srcContext = 1) }} {# output: - 3.c - 4.d - 5.e #} - - {{ "/path/to/file/file.txt"|file_excerpt(line = 1, srcContext = 0) }} +
    +
  1. c
  2. +
  3. d
  4. +
  5. e
  6. +
+ #} + + {{ '/path/to/file.txt'|file_excerpt(line = 1, srcContext = 0) }} {# output: - 1.a #} +
    +
  1. a
  2. +
+ #} format_file ~~~~~~~~~~~ @@ -890,35 +887,28 @@ Generates the file path inside an ```` element. If the path is inside the kernel root directory, the kernel root directory path is replaced by ``kernel.project_dir`` (showing the full path in a tooltip on hover). -Example 1 - .. code-block:: twig - {{ "path/to/file/file.txt"|format_file(line = 1, text = "my_text") }} - -Output: - -.. code-block:: html - - my_text at line 1 - - -Example 2 - -.. code-block:: twig - - {{ "path/to/file/file.txt"|format_file(line = 3) }} + {{ '/path/to/file.txt'|format_file(line = 1, text = "my_text") }} + {# output: + my_text at line 1 + + #} -Output: + {{ "/path/to/file.txt"|format_file(line = 3) }} + {# output: + /path/to/file.txt at line 3 + + #} -.. code-block:: html +.. tip:: - - file.txt - / at line 3 - + If you set the :ref:`framework.ide ` option, the + generated links will change to open the file in that IDE/editor. For example, + when using PhpStorm, the `` Date: Thu, 3 Apr 2025 12:40:55 +0200 Subject: [PATCH 378/642] Fix some syntax issues --- reference/twig_reference.rst | 46 ++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 08ea8f1d581..8ad72575e82 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -96,7 +96,7 @@ Returns an instance of ``ControllerReference`` to be used with functions like :ref:`render() ` and :ref:`render_esi() `. -.. code-block:: html+twig +.. code-block:: twig {{ render(controller('App\\Controller\\BlogController:latest', {max: 3})) }} {# output: the content returned by the controller method; e.g. a rendered Twig template #} @@ -851,23 +851,21 @@ Consider the following as the content of ``file.txt``: d e -.. code-block:: twig +.. code-block:: html+twig {{ '/path/to/file.txt'|file_excerpt(line = 4, srcContext = 1) }} - {# output: -
    -
  1. c
  2. -
  3. d
  4. -
  5. e
  6. -
- #} + {# output: #} +
    +
  1. c
  2. +
  3. d
  4. +
  5. e
  6. +
{{ '/path/to/file.txt'|file_excerpt(line = 1, srcContext = 0) }} - {# output: -
    -
  1. a
  2. -
- #} + {# output: #} +
    +
  1. a
  2. +
format_file ~~~~~~~~~~~ @@ -887,21 +885,19 @@ Generates the file path inside an ```` element. If the path is inside the kernel root directory, the kernel root directory path is replaced by ``kernel.project_dir`` (showing the full path in a tooltip on hover). -.. code-block:: twig +.. code-block:: html+twig {{ '/path/to/file.txt'|format_file(line = 1, text = "my_text") }} - {# output: - my_text at line 1 - - #} + {# output: #} + my_text at line 1 + {{ "/path/to/file.txt"|format_file(line = 3) }} - {# output: - /path/to/file.txt at line 3 - - #} + {# output: #} + /path/to/file.txt at line 3 + .. tip:: From 534c9837e20c0049a2cdcf93f6b93a647a581bae Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 4 Apr 2025 08:31:32 +0200 Subject: [PATCH 379/642] Reword --- scheduler.rst | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/scheduler.rst b/scheduler.rst index e9c729ab986..63ec32962cf 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -290,32 +290,25 @@ defined by PHP datetime functions:: You can also define periodic tasks using :ref:`the AsPeriodicTask attribute `. -Be aware that the message isn't passed to the messenger when you start the -scheduler. The message will only be executed after the first frequency period -has passed. - -It's also possible to pass a from and until time for your schedule. For -example, if you want to execute a command every day at 13:00:: +You can also define ``from`` and ``until`` times for your schedule:: + // create a message every day at 13:00 $from = new \DateTimeImmutable('13:00', new \DateTimeZone('Europe/Paris')); - RecurringMessage::every('1 day', new Message(), from: $from); - -Or if you want to execute a message every day until a specific date:: + RecurringMessage::every('1 day', new Message(), $from); + // create a message every day until a specific date:: $until = '2023-06-12'; - RecurringMessage::every('1 day', new Message(), until: $until); - -And you can even combine the from and until parameters for more granular -control:: + RecurringMessage::every('1 day', new Message(), null, $until); + // combine from and until for more precise control $from = new \DateTimeImmutable('2023-01-01 13:47', new \DateTimeZone('Europe/Paris')); $until = '2023-06-12'; - RecurringMessage::every('first Monday of next month', new Message(), from: $from, until: $until); + RecurringMessage::every('first Monday of next month', new Message(), $from, $until); -If you don't pass a from parameter to your schedule, the first frequency period -is counted from the moment the scheduler is started. So if you start your -scheduler at 8:33 and the message is scheduled to perform every hour, it -will be executed at 9:33, 10:33, 11:33 and so on. +When starting the scheduler, the message isn't sent to the messenger immediately. +If you don't set a ``from`` parameter, the first frequency period starts from the +moment the scheduler runs. For example, if you start it at 8:33 and the message +is scheduled hourly, it will run at 9:33, 10:33, 11:33, etc. Custom Triggers ~~~~~~~~~~~~~~~ From 9c2d1d9198e70676f45a34119d7d563718840db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9?= Date: Sun, 25 Aug 2024 21:23:24 +1200 Subject: [PATCH 380/642] [Mailer] Update mailer.rst --- mailer.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mailer.rst b/mailer.rst index 53a6475a093..27a90c73191 100644 --- a/mailer.rst +++ b/mailer.rst @@ -834,6 +834,13 @@ The exceptions related to mailer transports (those which implement :class:`Symfony\\Component\\Mailer\\Exception\\TransportException`) also provide this debug information via the ``getDebug()`` method. +But you have to keep in mind that using :class:`Symfony\\Component\\Mailer\\Transport\\TransportInterface` +you can't rely on asynchronous sending emails. +It doesn't use a bus to dispatch :class:`Symfony\\Component\\Mailer\\Messenger\\SendEmailMessage`. + +Use :class:`Symfony\\Component\\Mailer\\MailerInterface` if you want to have an opportunity +to send emails asynchronously. + .. _mailer-twig: Twig: HTML & CSS From eb5f908bc97bd2b40d9bbe644d8c61674d1a7714 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Fri, 4 Apr 2025 10:44:13 -0400 Subject: [PATCH 381/642] [RateLimiter] default `lock_factory` to `auto` --- rate_limiter.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rate_limiter.rst b/rate_limiter.rst index 6c158ee52d0..1cef90828f5 100644 --- a/rate_limiter.rst +++ b/rate_limiter.rst @@ -461,9 +461,10 @@ simultaneous requests (e.g. three servers of a company hitting your API at the same time). Rate limiters use :doc:`locks ` to protect their operations against these race conditions. -By default, Symfony uses the global lock configured by ``framework.lock``, but -you can use a specific :ref:`named lock ` via the -``lock_factory`` option (or none at all): +By default, if the :doc:`lock ` component is installed, Symfony uses the +global lock configured by ``framework.lock``, but you can use a specific +:ref:`named lock ` via the ``lock_factory`` option (or none +at all): .. configuration-block:: From 74a146ee4f74fbc7dae98fa695040dd8da81dcc3 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 4 Apr 2025 16:51:36 +0200 Subject: [PATCH 382/642] Reword --- mailer.rst | 65 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/mailer.rst b/mailer.rst index 27a90c73191..52c6ee7dded 100644 --- a/mailer.rst +++ b/mailer.rst @@ -807,40 +807,59 @@ Catch that exception to recover from the error or to display some message:: Debugging Emails ---------------- -The :class:`Symfony\\Component\\Mailer\\SentMessage` object returned by the -``send()`` method of the :class:`Symfony\\Component\\Mailer\\Transport\\TransportInterface` -provides access to the original message (``getOriginalMessage()``) and to some -debug information (``getDebug()``) such as the HTTP calls done by the HTTP -transports, which is useful to debug errors. +The ``send()`` method of the mailer service injected when using ``MailerInterface`` +doesn't return anything, so you can't access the sent email information. This is because +it sends email messages **asynchronously** when the :doc:`Messenger component ` +is used in the application. -You can also access :class:`Symfony\\Component\\Mailer\\SentMessage` by listening -to the :ref:`SentMessageEvent ` and retrieve ``getDebug()`` -by listening to the :ref:`FailedMessageEvent `. +To access information about the sent email, update your code to replace the +:class:`Symfony\\Component\\Mailer\\MailerInterface` with +:class:`Symfony\\Component\\Mailer\\Transport\\TransportInterface`: -.. note:: +.. code-block:: diff + + -use Symfony\Component\Mailer\MailerInterface; + +use Symfony\Component\Mailer\Transport\TransportInterface; + // ... + + class MailerController extends AbstractController + { + #[Route('/email')] + - public function sendEmail(MailerInterface $mailer): Response + + public function sendEmail(TransportInterface $mailer): Response + { + $email = (new Email()) + // ... + + $sentEmail = $mailer->send($email); - If your code used :class:`Symfony\\Component\\Mailer\\MailerInterface`, you - need to replace it by :class:`Symfony\\Component\\Mailer\\Transport\\TransportInterface` - to have the ``SentMessage`` object returned. + // ... + } + } + +The ``send()`` method of ``TransportInterface`` returns an object of type +:class:`Symfony\\Component\\Mailer\\SentMessage`. This is because it always sends +the emails **synchronously**, even if your application uses the Messenger component. + +The ``SentMessage`` object provides access to the original message +(``getOriginalMessage()``) and to some debug information (``getDebug()``) such +as the HTTP calls done by the HTTP transports, which is useful to debug errors. + +You can also access the :class:`Symfony\\Component\\Mailer\\SentMessage` object +by listening to the :ref:`SentMessageEvent `, and retrieve +``getDebug()`` by listening to the :ref:`FailedMessageEvent `. .. note:: Some mailer providers change the ``Message-Id`` when sending the email. The - ``getMessageId()`` method from ``SentMessage`` always returns the definitive - ID of the message (being the original random ID generated by Symfony or the - new ID generated by the mailer provider). + ``getMessageId()`` method from ``SentMessage`` always returns the final ID + of the message - whether it's the original random ID generated by Symfony or + a new one generated by the provider. -The exceptions related to mailer transports (those which implement +Exceptions related to mailer transports (those implementing :class:`Symfony\\Component\\Mailer\\Exception\\TransportException`) also provide this debug information via the ``getDebug()`` method. -But you have to keep in mind that using :class:`Symfony\\Component\\Mailer\\Transport\\TransportInterface` -you can't rely on asynchronous sending emails. -It doesn't use a bus to dispatch :class:`Symfony\\Component\\Mailer\\Messenger\\SendEmailMessage`. - -Use :class:`Symfony\\Component\\Mailer\\MailerInterface` if you want to have an opportunity -to send emails asynchronously. - .. _mailer-twig: Twig: HTML & CSS From 8ed84b509d7a062e4991024d71770e0411f2f77c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 4 Apr 2025 16:53:55 +0200 Subject: [PATCH 383/642] Fix RST syntax issue --- scheduler.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scheduler.rst b/scheduler.rst index 63ec32962cf..e7d855db249 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -296,7 +296,7 @@ You can also define ``from`` and ``until`` times for your schedule:: $from = new \DateTimeImmutable('13:00', new \DateTimeZone('Europe/Paris')); RecurringMessage::every('1 day', new Message(), $from); - // create a message every day until a specific date:: + // create a message every day until a specific date $until = '2023-06-12'; RecurringMessage::every('1 day', new Message(), null, $until); From 19b99dbe1b32290052fd6d211e75836efa00f20b Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Fri, 4 Apr 2025 17:04:03 +0200 Subject: [PATCH 384/642] [Config] Add `NodeDefinition::docUrl()` --- components/config/definition.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/components/config/definition.rst b/components/config/definition.rst index 44189d9dd6f..4848af33ffe 100644 --- a/components/config/definition.rst +++ b/components/config/definition.rst @@ -546,6 +546,30 @@ and in XML: +You can also provide a URL to a full documentation page:: + + $rootNode + ->docUrl('Full documentation is available at https://example.com/docs/{version:major}.{version:minor}/reference.html') + ->children() + ->integerNode('entries_per_page') + ->defaultValue(25) + ->end() + ->end() + ; + +A few placeholders are available to customize the URL: + +* ``{version:major}``: The major version of the package currently installed +* ``{version:minor}``: The minor version of the package currently installed +* ``{package}``: The name of the package + +The placeholders will be replaced when printing the configuration tree with the +``config:dump-reference`` command. + +.. versionadded:: 7.3 + + The ``docUrl()`` method was introduced in Symfony 7.3. + Optional Sections ----------------- From 50b6ffdac765fad51e6c087a00667711840aea10 Mon Sep 17 00:00:00 2001 From: RisingSunLight Date: Mon, 7 Apr 2025 00:49:36 +0200 Subject: [PATCH 385/642] fix command explanation --- templates.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates.rst b/templates.rst index 77e10bc5596..98742058988 100644 --- a/templates.rst +++ b/templates.rst @@ -849,7 +849,8 @@ errors. It's useful to run it before deploying your application to production $ php bin/console lint:twig templates/email/ $ php bin/console lint:twig templates/article/recent_list.html.twig - # you can also show the deprecated features used in your templates + # you can also show the first deprecated feature used in your templates + # as it shows only the first one found, you may need to run it until no more deprecations are found $ php bin/console lint:twig --show-deprecations templates/email/ When running the linter inside `GitHub Actions`_, the output is automatically From 20ce1892efe75de46c9447a7a40baab5f6a5a2a0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 7 Apr 2025 08:09:16 +0200 Subject: [PATCH 386/642] Added a versionadded directive --- rate_limiter.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rate_limiter.rst b/rate_limiter.rst index 1cef90828f5..81bd7f5689e 100644 --- a/rate_limiter.rst +++ b/rate_limiter.rst @@ -535,6 +535,12 @@ at all): ; }; +.. versionadded:: 7.3 + + Before Symfony 7.3, configuring a rate limiter and using the default configured + lock factory (``lock.factory``) failed if the Symfony Lock component was not + installed in the application. + .. _`DoS attacks`: https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html .. _`Apache mod_ratelimit`: https://httpd.apache.org/docs/current/mod/mod_ratelimit.html .. _`NGINX rate limiting`: https://www.nginx.com/blog/rate-limiting-nginx/ From 03321a8e0246365b1a17ac998f1de895f28b94ad Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 7 Apr 2025 08:36:00 +0200 Subject: [PATCH 387/642] Minor reword --- templates.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates.rst b/templates.rst index 98742058988..5e34b930d0e 100644 --- a/templates.rst +++ b/templates.rst @@ -849,8 +849,8 @@ errors. It's useful to run it before deploying your application to production $ php bin/console lint:twig templates/email/ $ php bin/console lint:twig templates/article/recent_list.html.twig - # you can also show the first deprecated feature used in your templates - # as it shows only the first one found, you may need to run it until no more deprecations are found + # you can also show the deprecated features used in your templates + # (only the first deprecation is shown, so run multiple times to catch all) $ php bin/console lint:twig --show-deprecations templates/email/ When running the linter inside `GitHub Actions`_, the output is automatically From b5efbcd9a777ac3e913a93fdfb96e1c0ef934fc7 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 7 Apr 2025 08:41:41 +0200 Subject: [PATCH 388/642] [Templates] Add a versionadded diretive for a command change --- templates.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/templates.rst b/templates.rst index a93bbe742c6..c6312abb33c 100644 --- a/templates.rst +++ b/templates.rst @@ -870,7 +870,6 @@ errors. It's useful to run it before deploying your application to production $ php bin/console lint:twig templates/article/recent_list.html.twig # you can also show the deprecated features used in your templates - # (only the first deprecation is shown, so run multiple times to catch all) $ php bin/console lint:twig --show-deprecations templates/email/ # you can also excludes directories @@ -880,6 +879,11 @@ errors. It's useful to run it before deploying your application to production The option to exclude directories was introduced in Symfony 7.1. +.. versionadded:: 7.3 + + Before Symfony 7.3, the ``--show-deprecations`` option only displayed the + first deprecation found, so you had to run the command repeatedly. + When running the linter inside `GitHub Actions`_, the output is automatically adapted to the format required by GitHub, but you can force that format too: From 97de6eb32ef3f5d8d1b7d2c1fca5fffe04806c1f Mon Sep 17 00:00:00 2001 From: Antoine Makdessi Date: Mon, 7 Apr 2025 08:42:34 +0200 Subject: [PATCH 389/642] [Translator] document global variables feature configuration --- translation.rst | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/translation.rst b/translation.rst index c8996c08b48..7800161d77b 100644 --- a/translation.rst +++ b/translation.rst @@ -1160,6 +1160,67 @@ service argument with the :class:`Symfony\\Component\\Translation\\LocaleSwitche class to inject the locale switcher service. Otherwise, configure your services manually and inject the ``translation.locale_switcher`` service. +.. _translation-global-variables: + +Global Variables +~~~~~~~~~~~~~~~~ + +The translator allows you to automatically configure global variables that will be available +for the translation process. These global variables are defined in the ``translations.globals`` +option inside the main configuration file: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/translator.yaml + translator: + # ... + globals: + '%%app_name%%': 'My application' + '{app_version}': '1.2.3' + '{url}': { message: 'url', parameters: { scheme: 'https://' }, domain: 'global' } + + .. code-block:: xml + + + + + + + + + My application + + + https:// + + + + + + .. code-block:: php + + // config/packages/translator.php + use Symfony\Config\TwigConfig; + + return static function (TwigConfig $translator): void { + // ... + $translator->globals('%%app_name%%')->value('My application'); + $translator->globals('{app_version}')->value('1.2.3'); + $translator->globals('{url}')->value(['message' => 'url', 'parameters' => ['scheme' => 'https://']); + }; + +.. versionadded:: 7.3 + + The ``translator:globals`` option was introduced in Symfony 7.3. + .. _translation-debug: How to Find Missing or Unused Translation Messages From 97d301a5c1c4a69f3958777b95d9c8ffcd4b928b Mon Sep 17 00:00:00 2001 From: Pierre Ambroise Date: Wed, 2 Apr 2025 11:41:05 +0200 Subject: [PATCH 390/642] [Messenger] Document stamps in HandleTrait::handle --- messenger.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/messenger.rst b/messenger.rst index fc8f9491022..159dd567a34 100644 --- a/messenger.rst +++ b/messenger.rst @@ -2512,6 +2512,20 @@ wherever you need a query bus behavior instead of the ``MessageBusInterface``:: } } +You also can also add stamps when handling a message. For example, you can +add an ``DoctrineFlushStamp`` to flush the entity manager after handling the message:: + + $this->handle(new SomeMessage($data), [new DoctrineFlushStamp()]); + +.. note:: + + If adding a stamp of the same type that already exists in the envelope, + both stamps will be kept (see `Envelopes & Stamps`_). + +.. versionadded:: 7.3 + + The ``$stamps`` parameter on the handle method was introduced in Symfony 7.3. + Customizing Handlers -------------------- From c2c14fd74b50128ffd986919aec8c1fc7cf49cca Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 7 Apr 2025 09:40:08 +0200 Subject: [PATCH 391/642] Minor tweak --- messenger.rst | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/messenger.rst b/messenger.rst index 47e0ad61906..b05056243c2 100644 --- a/messenger.rst +++ b/messenger.rst @@ -2525,19 +2525,15 @@ wherever you need a query bus behavior instead of the ``MessageBusInterface``:: } } -You also can also add stamps when handling a message. For example, you can -add an ``DoctrineFlushStamp`` to flush the entity manager after handling the message:: +You can also add new stamps when handling a message; they will be appended +to the existing ones. For example, you can add a ``DoctrineFlushStamp`` to +flush the entity manager after handling the message:: $this->handle(new SomeMessage($data), [new DoctrineFlushStamp()]); -.. note:: - - If adding a stamp of the same type that already exists in the envelope, - both stamps will be kept (see `Envelopes & Stamps`_). - .. versionadded:: 7.3 - The ``$stamps`` parameter on the handle method was introduced in Symfony 7.3. + The ``$stamps`` parameter of the ``handle()`` method was introduced in Symfony 7.3. Customizing Handlers -------------------- From 7f9f20fcd83889b0725ec7d6e7192af8dc2391c8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 7 Apr 2025 10:16:39 +0200 Subject: [PATCH 392/642] [Mesenger] Fix code example --- messenger.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/messenger.rst b/messenger.rst index b05056243c2..9078a500d11 100644 --- a/messenger.rst +++ b/messenger.rst @@ -2526,10 +2526,9 @@ wherever you need a query bus behavior instead of the ``MessageBusInterface``:: } You can also add new stamps when handling a message; they will be appended -to the existing ones. For example, you can add a ``DoctrineFlushStamp`` to -flush the entity manager after handling the message:: +to the existing ones:: - $this->handle(new SomeMessage($data), [new DoctrineFlushStamp()]); + $this->handle(new SomeMessage($data), [new SomeStamp(), new AnotherStamp()]); .. versionadded:: 7.3 From 11b256998374bdfbfa7a0155cf1274661a15811b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 8 Apr 2025 11:03:16 +0200 Subject: [PATCH 393/642] Tweaks and rewords --- translation.rst | 139 +++++++++++++++++++++++++++--------------------- 1 file changed, 78 insertions(+), 61 deletions(-) diff --git a/translation.rst b/translation.rst index 7800161d77b..35751d7db5f 100644 --- a/translation.rst +++ b/translation.rst @@ -416,6 +416,84 @@ You can also specify the message domain and pass some additional variables: major difference: automatic output escaping is **not** applied to translations using a tag. +Global Translation Parameters +----------------------------- + +.. versionadded:: 7.3 + + The global translation parameters feature was introduced in Symfony 7.3. + +If the content of a translation parameter is repeated across multiple +translation messages (e.g. a company name, or a version number), you can define +it as a global translation parameter. This helps you avoid repeating the same +values manually in each message. + +You can configure these global parameters in the ``translations.globals`` option +of your main configuration file using either ``%...%`` or ``{...}`` syntax: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/translator.yaml + translator: + # ... + globals: + # when using the '%' wrapping characters, you must escape them + '%%app_name%%': 'My application' + '{app_version}': '1.2.3' + '{url}': { message: 'url', parameters: { scheme: 'https://' }, domain: 'global' } + + .. code-block:: xml + + + + + + + + + + My application + + + https:// + + + + + + .. code-block:: php + + // config/packages/translator.php + use Symfony\Config\TwigConfig; + + return static function (TwigConfig $translator): void { + // ... + // when using the '%' wrapping characters, you must escape them + $translator->globals('%%app_name%%')->value('My application'); + $translator->globals('{app_version}')->value('1.2.3'); + $translator->globals('{url}')->value(['message' => 'url', 'parameters' => ['scheme' => 'https://']]); + }; + +Once defined, you can use these parameters in translation messages anywhere in +your application: + +.. code-block:: twig + + {{ 'Application version: {version}'|trans }} + {# output: "Application version: 1.2.3" #} + + {# parameters passed to the message override global parameters #} + {{ 'Package version: {version}'|trans({'{version}': '2.3.4'}) }} + # Displays "Package version: 2.3.4" + Forcing the Translator Locale ----------------------------- @@ -1160,67 +1238,6 @@ service argument with the :class:`Symfony\\Component\\Translation\\LocaleSwitche class to inject the locale switcher service. Otherwise, configure your services manually and inject the ``translation.locale_switcher`` service. -.. _translation-global-variables: - -Global Variables -~~~~~~~~~~~~~~~~ - -The translator allows you to automatically configure global variables that will be available -for the translation process. These global variables are defined in the ``translations.globals`` -option inside the main configuration file: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/translator.yaml - translator: - # ... - globals: - '%%app_name%%': 'My application' - '{app_version}': '1.2.3' - '{url}': { message: 'url', parameters: { scheme: 'https://' }, domain: 'global' } - - .. code-block:: xml - - - - - - - - - My application - - - https:// - - - - - - .. code-block:: php - - // config/packages/translator.php - use Symfony\Config\TwigConfig; - - return static function (TwigConfig $translator): void { - // ... - $translator->globals('%%app_name%%')->value('My application'); - $translator->globals('{app_version}')->value('1.2.3'); - $translator->globals('{url}')->value(['message' => 'url', 'parameters' => ['scheme' => 'https://']); - }; - -.. versionadded:: 7.3 - - The ``translator:globals`` option was introduced in Symfony 7.3. - .. _translation-debug: How to Find Missing or Unused Translation Messages From fff52938f54f32cb6b246f73423affbe1365660b Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 8 Apr 2025 07:23:44 -0400 Subject: [PATCH 394/642] [RateLimiter] deprecate injecting `RateLimiterFactory` --- rate_limiter.rst | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/rate_limiter.rst b/rate_limiter.rst index 81bd7f5689e..1036e80e682 100644 --- a/rate_limiter.rst +++ b/rate_limiter.rst @@ -230,6 +230,12 @@ prevents that number from being higher than 5,000). Rate Limiting in Action ----------------------- +.. versionadded:: 7.3 + + :class:`Symfony\\Component\\RateLimiter\\RateLimiterFactoryInterface` was + added and should now be used for autowiring instead of + :class:`Symfony\\Component\\RateLimiter\\RateLimiterFactory`. + After having installed and configured the rate limiter, inject it in any service or controller and call the ``consume()`` method to try to consume a given number of tokens. For example, this controller uses the previous rate limiter to control @@ -242,13 +248,13 @@ the number of requests to the API:: use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; - use Symfony\Component\RateLimiter\RateLimiterFactory; + use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; class ApiController extends AbstractController { // if you're using service autowiring, the variable name must be: // "rate limiter name" (in camelCase) + "Limiter" suffix - public function index(Request $request, RateLimiterFactory $anonymousApiLimiter): Response + public function index(Request $request, RateLimiterFactoryInterface $anonymousApiLimiter): Response { // create a limiter based on a unique identifier of the client // (e.g. the client's IP address, a username/email, an API key, etc.) @@ -291,11 +297,11 @@ using the ``reserve()`` method:: use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\RateLimiter\RateLimiterFactory; + use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; class ApiController extends AbstractController { - public function registerUser(Request $request, RateLimiterFactory $authenticatedApiLimiter): Response + public function registerUser(Request $request, RateLimiterFactoryInterface $authenticatedApiLimiter): Response { $apiKey = $request->headers->get('apikey'); $limiter = $authenticatedApiLimiter->create($apiKey); @@ -350,11 +356,11 @@ the :class:`Symfony\\Component\\RateLimiter\\Reservation` object returned by the use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\RateLimiter\RateLimiterFactory; + use Symfony\Component\RateLimiter\RateLimiterFactoryInterface; class ApiController extends AbstractController { - public function index(Request $request, RateLimiterFactory $anonymousApiLimiter): Response + public function index(Request $request, RateLimiterFactoryInterface $anonymousApiLimiter): Response { $limiter = $anonymousApiLimiter->create($request->getClientIp()); $limit = $limiter->consume(); From 35349c17b05de94e156ca0247b2f4d9f887aaa3a Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 8 Apr 2025 07:45:37 -0400 Subject: [PATCH 395/642] [RateLimiter] compound rate limiter --- rate_limiter.rst | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/rate_limiter.rst b/rate_limiter.rst index 81bd7f5689e..906e787e7e1 100644 --- a/rate_limiter.rst +++ b/rate_limiter.rst @@ -541,6 +541,123 @@ at all): lock factory (``lock.factory``) failed if the Symfony Lock component was not installed in the application. +Compound Rate Limiter +--------------------- + +.. versionadded:: 7.3 + + Configuring compound rate limiters was added in 7.3. + +You can configure multiple rate limiters to work together: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/rate_limiter.yaml + framework: + rate_limiter: + two_per_minute: + policy: 'fixed_window' + limit: 2 + interval: '1 minute' + five_per_hour: + policy: 'fixed_window' + limit: 5 + interval: '1 hour' + contact_form: + policy: 'compound' + limiters: [two_per_minute, five_per_hour] + + .. code-block:: xml + + + + + + + + + + + + + two_per_minute + five_per_hour + + + + + + .. code-block:: php + + // config/packages/rate_limiter.php + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework): void { + $framework->rateLimiter() + ->limiter('two_per_minute') + ->policy('fixed_window') + ->limit(2) + ->interval('1 minute') + ; + + $framework->rateLimiter() + ->limiter('two_per_minute') + ->policy('fixed_window') + ->limit(5) + ->interval('1 hour') + ; + + $framework->rateLimiter() + ->limiter('contact_form') + ->policy('compound') + ->limiters(['two_per_minute', 'five_per_hour']) + ; + }; + +Then, inject and use as normal:: + + // src/Controller/ContactController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\RateLimiter\RateLimiterFactory; + + class ContactController extends AbstractController + { + public function registerUser(Request $request, RateLimiterFactoryInterface $contactFormLimiter): Response + { + $limiter = $contactFormLimiter->create($request->getClientIp()); + + if (false === $limiter->consume(1)->isAccepted()) { + // either of the two limiters has been reached + } + + // ... + } + + // ... + } + .. _`DoS attacks`: https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html .. _`Apache mod_ratelimit`: https://httpd.apache.org/docs/current/mod/mod_ratelimit.html .. _`NGINX rate limiting`: https://www.nginx.com/blog/rate-limiting-nginx/ From 70716ab76d3e4066668c81ad1be465e3dc1a772e Mon Sep 17 00:00:00 2001 From: Hubert Lenoir Date: Tue, 8 Apr 2025 17:57:53 +0200 Subject: [PATCH 396/642] Update service_container.rst to 7.3 --- service_container.rst | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/service_container.rst b/service_container.rst index 30b69b8aa14..9a024e5e320 100644 --- a/service_container.rst +++ b/service_container.rst @@ -162,10 +162,6 @@ each time you ask for it. # this creates a service per class whose id is the fully-qualified class name App\: resource: '../src/' - exclude: - - '../src/DependencyInjection/' - - '../src/Entity/' - - '../src/Kernel.php' # order is important in this file because service definitions # always *replace* previous ones; add your own service configuration below @@ -187,7 +183,7 @@ each time you ask for it. - + @@ -212,8 +208,7 @@ each time you ask for it. // makes classes in src/ available to be used as services // this creates a service per class whose id is the fully-qualified class name - $services->load('App\\', '../src/') - ->exclude('../src/{DependencyInjection,Entity,Kernel.php}'); + $services->load('App\\', '../src/'); // order is important in this file because service definitions // always *replace* previous ones; add your own service configuration below @@ -221,9 +216,7 @@ each time you ask for it. .. tip:: - The value of the ``resource`` and ``exclude`` options can be any valid - `glob pattern`_. The value of the ``exclude`` option can also be an - array of glob patterns. + The value of the ``resource`` option can be any valid `glob pattern`_. Thanks to this configuration, you can automatically use any classes from the ``src/`` directory as a service, without needing to manually configure From c0f12c7408489bb8031b77d2caea5e3ce547781f Mon Sep 17 00:00:00 2001 From: "hubert.lenoir" Date: Tue, 8 Apr 2025 20:36:41 +0200 Subject: [PATCH 397/642] [Translation] Fix Lint (DOCtor-RST) --- translation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translation.rst b/translation.rst index 35751d7db5f..86282090801 100644 --- a/translation.rst +++ b/translation.rst @@ -461,7 +461,7 @@ of your main configuration file using either ``%...%`` or ``{...}`` syntax: My application - + https:// From ac9e20cad5195c101d843bcb6cb391f0b9316011 Mon Sep 17 00:00:00 2001 From: Mokhtar Tlili Date: Tue, 8 Apr 2025 21:05:43 +0200 Subject: [PATCH 398/642] Add docs for bridge twig validator #20836 --- bridge/twig/validation.rst | 63 ++++++++++++++++++++++++++++++++++++++ index.rst | 1 + twig_bridge.rst | 9 ++++++ 3 files changed, 73 insertions(+) create mode 100644 bridge/twig/validation.rst create mode 100644 twig_bridge.rst diff --git a/bridge/twig/validation.rst b/bridge/twig/validation.rst new file mode 100644 index 00000000000..e7b887f5588 --- /dev/null +++ b/bridge/twig/validation.rst @@ -0,0 +1,63 @@ +Validating Twig Template Syntax +=============================== + +The Twig Bridge provides a custom `Twig` constraint that allows validating +whether a given string contains valid Twig syntax. + +This is particularly useful when template content is user-generated or +configurable, and you want to ensure it can be safely rendered by the Twig engine. + +Installation +------------ + +This constraint is part of the `symfony/twig-bridge` package. Make sure it's installed: + +.. code-block:: terminal + + $ composer require symfony/twig-bridge + +Usage +----- + +To use the `Twig` constraint, annotate the property that should contain a valid +Twig template:: + + use Symfony\Bridge\Twig\Validator\Constraints\Twig; + + class Template + { + #[Twig] + private string $templateCode; + } + +If the template contains a syntax error, a validation error will be thrown. + +Constraint Options +------------------ + +**message** +Customize the default error message when the template is invalid:: + + // ... + class Template + { + #[Twig(message: 'Your template contains a syntax error.')] + private string $templateCode; + } + +**skipDeprecations** +By default, this option is set to `true`, which means Twig deprecation warnings +are ignored during validation. + +If you want validation to fail when deprecated features are used in the template, +set this to `false`:: + + // ... + class Template + { + #[Twig(skipDeprecations: false)] + private string $templateCode; + } + +This can be helpful when enforcing stricter template rules or preparing for major +Twig version upgrades. diff --git a/index.rst b/index.rst index c566e5f8671..2bd09c8c0dc 100644 --- a/index.rst +++ b/index.rst @@ -60,6 +60,7 @@ Topics web_link webhook workflow + twig_bridge Components ---------- diff --git a/twig_bridge.rst b/twig_bridge.rst new file mode 100644 index 00000000000..bb7d84f7e9a --- /dev/null +++ b/twig_bridge.rst @@ -0,0 +1,9 @@ +Twig Bridge +=========== + +This bridge integrates Symfony with the Twig templating engine. + +.. toctree:: + :maxdepth: 1 + + bridge/twig/validation From a7a6c1d23db3430752cbdf59354aad47aaa4ef32 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Tue, 8 Apr 2025 07:13:46 -0400 Subject: [PATCH 399/642] [Scheduler][Webhook] add screencast links --- scheduler.rst | 6 ++++++ webhook.rst | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/scheduler.rst b/scheduler.rst index a09fabfc4ca..1630fd8dada 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -1,6 +1,11 @@ Scheduler ========= +.. admonition:: Screencast + :class: screencast + + Like video tutorials? Check out this `Scheduler quick-start screencast`_. + The scheduler component manages task scheduling within your PHP application, like running a task each night at 3 AM, every two weeks except for holidays or any other custom schedule you might need. @@ -1008,3 +1013,4 @@ helping you identify those messages when needed. .. _`cron command-line utility`: https://en.wikipedia.org/wiki/Cron .. _`crontab.guru website`: https://crontab.guru/ .. _`relative formats`: https://www.php.net/manual/en/datetime.formats.php#datetime.formats.relative +.. _`Scheduler quick-start screencast`: https://symfonycasts.com/screencast/mailtrap/bonus-symfony-scheduler diff --git a/webhook.rst b/webhook.rst index aba95cffb04..9d6b56c7ef0 100644 --- a/webhook.rst +++ b/webhook.rst @@ -15,6 +15,11 @@ Installation Usage in Combination with the Mailer Component ---------------------------------------------- +.. admonition:: Screencast + :class: screencast + + Like video tutorials? Check out the `Webhook Component for Email Events screencast`_. + When using a third-party mailer provider, you can use the Webhook component to receive webhook calls from this provider. @@ -207,3 +212,4 @@ Creating a Custom Webhook Webhook. .. _`MakerBundle`: https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html +.. _`Webhook Component for Email Events screencast`: https://symfonycasts.com/screencast/mailtrap/email-event-webhook From 59fd3a3f167c8c799fad3256ace24dc8a72501de Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 9 Apr 2025 12:31:13 +0200 Subject: [PATCH 400/642] Tweaks --- service_container.rst | 44 ++++++++++++++++++++++++++++++++++++ service_container/import.rst | 7 ++---- 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/service_container.rst b/service_container.rst index 9a024e5e320..6ae601cf724 100644 --- a/service_container.rst +++ b/service_container.rst @@ -223,6 +223,50 @@ each time you ask for it. it. Later, you'll learn how to :ref:`import many services at once ` with resource. + If some files or directories in your project should not become services, you + can exclude them using the ``exclude`` option: + + .. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + App\: + resource: '../src/' + exclude: + - '../src/SomeDirectory/' + - '../src/AnotherDirectory/' + - '../src/SomeFile.php' + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + return function(ContainerConfigurator $container): void { + // ... + + $services->load('App\\', '../src/') + ->exclude('../src/{SomeDirectory,AnotherDirectory,Kernel.php}'); + }; + If you'd prefer to manually wire your service, you can :ref:`use explicit configuration `. diff --git a/service_container/import.rst b/service_container/import.rst index d5056032115..293cb5b97c2 100644 --- a/service_container/import.rst +++ b/service_container/import.rst @@ -82,7 +82,6 @@ a relative or absolute path to the imported file: App\: resource: '../src/*' - exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' # ... @@ -104,8 +103,7 @@ a relative or absolute path to the imported file: - + @@ -127,8 +125,7 @@ a relative or absolute path to the imported file: ->autoconfigure() ; - $services->load('App\\', '../src/*') - ->exclude('../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'); + $services->load('App\\', '../src/*'); }; When loading a configuration file, Symfony loads first the imported files and From 518fc3a24cfd03eff976cf296997edc8e2aea2f8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 9 Apr 2025 16:22:11 +0200 Subject: [PATCH 401/642] Minor tweak --- rate_limiter.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rate_limiter.rst b/rate_limiter.rst index 4bd213d2363..3a517c37bd4 100644 --- a/rate_limiter.rst +++ b/rate_limiter.rst @@ -552,7 +552,7 @@ Compound Rate Limiter .. versionadded:: 7.3 - Configuring compound rate limiters was added in 7.3. + Support for configuring compound rate limiters was introduced in Symfony 7.3. You can configure multiple rate limiters to work together: From fcfcacb6afdd7b6fcbdf5587f9c59140b9d5f887 Mon Sep 17 00:00:00 2001 From: Van Truong PHAN Date: Thu, 10 Apr 2025 04:44:59 +0200 Subject: [PATCH 402/642] Franken PHP can perform some action after the response has been streamed to the user Franken also implements fastcgi_finish_request PHP function. kernel.terminate event works well with franken. --- components/http_kernel.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/http_kernel.rst b/components/http_kernel.rst index 351a2123b90..cf16de9fe61 100644 --- a/components/http_kernel.rst +++ b/components/http_kernel.rst @@ -497,8 +497,8 @@ as possible to the client (e.g. sending emails). .. warning:: Internally, the HttpKernel makes use of the :phpfunction:`fastcgi_finish_request` - PHP function. This means that at the moment, only the `PHP FPM`_ server - API is able to send a response to the client while the server's PHP process + PHP function. This means that at the moment, only the `PHP FPM`_ and `FrankenPHP`_ servers + API are able to send a response to the client while the server's PHP process still performs some tasks. With all other server APIs, listeners to ``kernel.terminate`` are still executed, but the response is not sent to the client until they are all completed. @@ -770,3 +770,4 @@ Learn more .. _FOSRestBundle: https://github.com/friendsofsymfony/FOSRestBundle .. _`PHP FPM`: https://www.php.net/manual/en/install.fpm.php .. _variadic: https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list +.. _`FrankenPHP`: https://frankenphp.dev From ccd113706035d4d5c09342c2d8de207d138cf53c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 10 Apr 2025 08:03:25 +0200 Subject: [PATCH 403/642] Minor tweak --- components/http_kernel.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/http_kernel.rst b/components/http_kernel.rst index cf16de9fe61..91643086a18 100644 --- a/components/http_kernel.rst +++ b/components/http_kernel.rst @@ -497,8 +497,8 @@ as possible to the client (e.g. sending emails). .. warning:: Internally, the HttpKernel makes use of the :phpfunction:`fastcgi_finish_request` - PHP function. This means that at the moment, only the `PHP FPM`_ and `FrankenPHP`_ servers - API are able to send a response to the client while the server's PHP process + PHP function. This means that at the moment, only the `PHP FPM`_ API and the + `FrankenPHP`_ server are able to send a response to the client while the server's PHP process still performs some tasks. With all other server APIs, listeners to ``kernel.terminate`` are still executed, but the response is not sent to the client until they are all completed. From 344d8baac24247f9a0637bec61a2896e9404c43b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 10 Apr 2025 09:12:48 +0200 Subject: [PATCH 404/642] Reorganized the contents of the new constraint --- bridge/twig/validation.rst | 63 --------------- index.rst | 1 - reference/constraints/Twig.rst | 130 ++++++++++++++++++++++++++++++ reference/constraints/map.rst.inc | 1 + twig_bridge.rst | 9 --- 5 files changed, 131 insertions(+), 73 deletions(-) delete mode 100644 bridge/twig/validation.rst create mode 100644 reference/constraints/Twig.rst delete mode 100644 twig_bridge.rst diff --git a/bridge/twig/validation.rst b/bridge/twig/validation.rst deleted file mode 100644 index e7b887f5588..00000000000 --- a/bridge/twig/validation.rst +++ /dev/null @@ -1,63 +0,0 @@ -Validating Twig Template Syntax -=============================== - -The Twig Bridge provides a custom `Twig` constraint that allows validating -whether a given string contains valid Twig syntax. - -This is particularly useful when template content is user-generated or -configurable, and you want to ensure it can be safely rendered by the Twig engine. - -Installation ------------- - -This constraint is part of the `symfony/twig-bridge` package. Make sure it's installed: - -.. code-block:: terminal - - $ composer require symfony/twig-bridge - -Usage ------ - -To use the `Twig` constraint, annotate the property that should contain a valid -Twig template:: - - use Symfony\Bridge\Twig\Validator\Constraints\Twig; - - class Template - { - #[Twig] - private string $templateCode; - } - -If the template contains a syntax error, a validation error will be thrown. - -Constraint Options ------------------- - -**message** -Customize the default error message when the template is invalid:: - - // ... - class Template - { - #[Twig(message: 'Your template contains a syntax error.')] - private string $templateCode; - } - -**skipDeprecations** -By default, this option is set to `true`, which means Twig deprecation warnings -are ignored during validation. - -If you want validation to fail when deprecated features are used in the template, -set this to `false`:: - - // ... - class Template - { - #[Twig(skipDeprecations: false)] - private string $templateCode; - } - -This can be helpful when enforcing stricter template rules or preparing for major -Twig version upgrades. diff --git a/index.rst b/index.rst index 2bd09c8c0dc..c566e5f8671 100644 --- a/index.rst +++ b/index.rst @@ -60,7 +60,6 @@ Topics web_link webhook workflow - twig_bridge Components ---------- diff --git a/reference/constraints/Twig.rst b/reference/constraints/Twig.rst new file mode 100644 index 00000000000..8435c191855 --- /dev/null +++ b/reference/constraints/Twig.rst @@ -0,0 +1,130 @@ +Twig Constraint +=============== + +.. versionadded:: 7.3 + + The ``Twig`` constraint was introduced in Symfony 7.3. + +Validates that a given string contains valid :ref:`Twig syntax `. +This is particularly useful when template content is user-generated or +configurable, and you want to ensure it can be rendered by the Twig engine. + +.. note:: + + Using this constraint requires having the ``symfony/twig-bridge`` package + installed in your application (e.g. by running ``composer require symfony/twig-bridge``). + +========== =================================================================== +Applies to :ref:`property or method ` +Class :class:`Symfony\\Bridge\\Twig\\Validator\\Constraints\\Twig` +Validator :class:`Symfony\\Bridge\\Twig\\Validator\\Constraints\\TwigValidator` +========== =================================================================== + +Basic Usage +----------- + +Apply the ``Twig`` constraint to validate the contents of any property or the +returned value of any method: + + use Symfony\Bridge\Twig\Validator\Constraints\Twig; + + class Template + { + #[Twig] + private string $templateCode; + } + +.. configuration-block:: + + .. code-block:: php-attributes + + // src/Entity/Page.php + namespace App\Entity; + + use Symfony\Bridge\Twig\Validator\Constraints\Twig; + + class Page + { + #[Twig] + private string $templateCode; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Entity\Page: + properties: + templateCode: + - Symfony\Bridge\Twig\Validator\Constraints\Twig: ~ + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // src/Entity/Page.php + namespace App\Entity; + + use Symfony\Bridge\Twig\Validator\Constraints\Twig; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class Page + { + // ... + + public static function loadValidatorMetadata(ClassMetadata $metadata): void + { + $metadata->addPropertyConstraint('templateCode', new Twig()); + } + } + +Constraint Options +------------------ + +``message`` +~~~~~~~~~~~ + +**type**: ``message`` **default**: ``This value is not a valid Twig template.`` + +This is the message displayed when the given string does *not* contain valid Twig syntax:: + + // ... + + class Page + { + #[Twig(message: 'Check this Twig code; it contains errors.')] + private string $templateCode; + } + +This message has no parameters. + +``skipDeprecations`` +~~~~~~~~~~~~~~~~~~~~ + +**type**: ``boolean`` **default**: ``true`` + +If ``true``, Twig deprecation warnings are ignored during validation. Set it to +``false`` to trigger validation errors when the given Twig code contains any deprecations:: + + // ... + + class Page + { + #[Twig(skipDeprecations: false)] + private string $templateCode; + } + +This can be helpful when enforcing stricter template rules or preparing for major +Twig version upgrades. diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 58801a8c653..c2396ae3af7 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -34,6 +34,7 @@ String Constraints * :doc:`PasswordStrength ` * :doc:`Regex ` * :doc:`Slug ` +* :doc:`Twig ` * :doc:`Ulid ` * :doc:`Url ` * :doc:`UserPassword ` diff --git a/twig_bridge.rst b/twig_bridge.rst deleted file mode 100644 index bb7d84f7e9a..00000000000 --- a/twig_bridge.rst +++ /dev/null @@ -1,9 +0,0 @@ -Twig Bridge -=========== - -This bridge integrates Symfony with the Twig templating engine. - -.. toctree:: - :maxdepth: 1 - - bridge/twig/validation From b5f3bf13ed6858bb79901593bac9b3571ad53388 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Fri, 11 Apr 2025 14:43:37 +0200 Subject: [PATCH 405/642] Update end_to_end.rst --- testing/end_to_end.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/end_to_end.rst b/testing/end_to_end.rst index bb4a316f347..83d61b1e60f 100644 --- a/testing/end_to_end.rst +++ b/testing/end_to_end.rst @@ -26,7 +26,7 @@ to install the needed dependencies: .. code-block:: terminal - $ composer require symfony/panther + $ composer require --dev symfony/panther .. include:: /components/require_autoload.rst.inc From ce286296c47b9d53358a75df75f3f267b0286a75 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Thu, 10 Apr 2025 19:43:53 +0200 Subject: [PATCH 406/642] [Serializer] (re)document PRESERVE_EMPTY_OBJECTS --- serializer.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/serializer.rst b/serializer.rst index 6e20a651341..b7deb6f5bff 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1571,6 +1571,13 @@ to ``true``:: ]); // $jsonContent contains {"name":"Jane Doe"} +Preserving Empty Objetcs +~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, the Serializer will transform an empty array to `[]`. +You can change this behavior by setting the ``AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS`` context option +to ``true``, when the value is `\ArrayObject()` the serialization would be `{}`. + Handling Uninitialized Properties ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From e384239df47030f05c3547139678c37218400ab2 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 14 Apr 2025 10:40:54 +0200 Subject: [PATCH 407/642] Tweaks --- serializer.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/serializer.rst b/serializer.rst index b7deb6f5bff..440494d0be1 100644 --- a/serializer.rst +++ b/serializer.rst @@ -1571,12 +1571,13 @@ to ``true``:: ]); // $jsonContent contains {"name":"Jane Doe"} -Preserving Empty Objetcs +Preserving Empty Objects ~~~~~~~~~~~~~~~~~~~~~~~~ -By default, the Serializer will transform an empty array to `[]`. -You can change this behavior by setting the ``AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS`` context option -to ``true``, when the value is `\ArrayObject()` the serialization would be `{}`. +By default, the Serializer transforms an empty array to ``[]``. You can change +this behavior by setting the ``AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS`` +context option to ``true``. When the value is an instance of ``\ArrayObject()``, +the serialized data will be ``{}``. Handling Uninitialized Properties ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 3a572358ae6cfafa90748178292a6e24c9ab9321 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 14 Apr 2025 10:59:37 +0200 Subject: [PATCH 408/642] Update the build script to mention that it does not support Windows --- _build/build.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/_build/build.php b/_build/build.php index 454553d459e..b80d8e0e51e 100755 --- a/_build/build.php +++ b/_build/build.php @@ -15,6 +15,13 @@ ->addOption('generate-fjson-files', null, InputOption::VALUE_NONE, 'Use this option to generate docs both in HTML and JSON formats') ->addOption('disable-cache', null, InputOption::VALUE_NONE, 'Use this option to force a full regeneration of all doc contents') ->setCode(function(InputInterface $input, OutputInterface $output) { + // the doc building app doesn't work on Windows + if ('\\' === DIRECTORY_SEPARATOR) { + $output->writeln('ERROR: The application that builds Symfony Docs does not support Windows. You can try using a Linux distribution via WSL (Windows Subsystem for Linux).'); + + return 1; + } + $io = new SymfonyStyle($input, $output); $io->text('Building all Symfony Docs...'); From 5ca3039e8bf1344368e098a83ac47749aeff7748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Hump=C3=A1l?= Date: Mon, 14 Apr 2025 12:04:35 +0200 Subject: [PATCH 409/642] Specify explicitly what a priority of message handlers mean --- messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index 484a5cbdaff..f504a470336 100644 --- a/messenger.rst +++ b/messenger.rst @@ -2468,7 +2468,7 @@ Possible options to configure with tags are: Name of the method that will process the message. ``priority`` - Priority of the handler when multiple handlers can process the same message. + Priority of the handler when multiple handlers can process the same message; higher values will be processed first. .. _handler-subscriber-options: From cb186580124e06ed65024404856642ffb29ab1f9 Mon Sep 17 00:00:00 2001 From: jdevinemt <65512359+jdevinemt@users.noreply.github.com> Date: Thu, 20 Mar 2025 17:07:39 -0600 Subject: [PATCH 410/642] [Deployment] - More accurate local .env file recommendation --- deployment.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/deployment.rst b/deployment.rst index 864ebc7a963..87e88f10ff8 100644 --- a/deployment.rst +++ b/deployment.rst @@ -134,14 +134,13 @@ B) Configure your Environment Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Most Symfony applications read their configuration from environment variables. -While developing locally, you'll usually store these in ``.env`` and ``.env.local`` -(for local overrides). On production, you have two options: +While developing locally, you'll usually store these in :ref:`.env files `. On production, you have two options: 1. Create "real" environment variables. How you set environment variables, depends on your setup: they can be set at the command line, in your Nginx configuration, or via other methods provided by your hosting service; -2. Or, create a ``.env.local`` file like your local development. +2. Or, create a ``.env.prod.local`` file containing values specific to your production environment. There is no significant advantage to either of the two options: use whatever is most natural in your hosting environment. From 059ecfa24b88db20e962a8e6a4cee88244de65ec Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 14 Apr 2025 17:42:27 +0200 Subject: [PATCH 411/642] Minor tweaks --- deployment.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/deployment.rst b/deployment.rst index 87e88f10ff8..07187f53cba 100644 --- a/deployment.rst +++ b/deployment.rst @@ -134,16 +134,18 @@ B) Configure your Environment Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Most Symfony applications read their configuration from environment variables. -While developing locally, you'll usually store these in :ref:`.env files `. On production, you have two options: +While developing locally, you'll usually store these in :ref:`.env files `. +On production, you have two options: 1. Create "real" environment variables. How you set environment variables, depends on your setup: they can be set at the command line, in your Nginx configuration, or via other methods provided by your hosting service; -2. Or, create a ``.env.prod.local`` file containing values specific to your production environment. +2. Or, create a ``.env.prod.local`` file that contains values specific to your + production environment. -There is no significant advantage to either of the two options: use whatever is -most natural in your hosting environment. +There is no significant advantage to either option: use whichever is most natural +for your hosting environment. .. tip:: From 0ce5ffbbbd24e0230e7ec527b372958e0cb53e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 14 Apr 2025 17:23:22 +0200 Subject: [PATCH 412/642] [HttpFoundation] Add FrankenPHP docs for X-Sendfile --- components/http_foundation.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/http_foundation.rst b/components/http_foundation.rst index 9a4f0b61516..5a0c24ef618 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -854,7 +854,7 @@ Alternatively, if you are serving a static file, you can use a The ``BinaryFileResponse`` will automatically handle ``Range`` and ``If-Range`` headers from the request. It also supports ``X-Sendfile`` -(see for `nginx`_ and `Apache`_). To make use of it, you need to determine +(see for `FrankenPHP`_, `nginx`_ and `Apache`_). To make use of it, you need to determine whether or not the ``X-Sendfile-Type`` header should be trusted and call :method:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse::trustXSendfileTypeHeader` if it should:: @@ -1061,6 +1061,7 @@ Learn More /session /http_cache/* +.. _FrankenPHP: https://frankenphp.dev/docs/x-sendfile/ .. _nginx: https://mattbrictson.com/blog/accelerated-rails-downloads .. _Apache: https://tn123.org/mod_xsendfile/ .. _`JSON Hijacking`: https://haacked.com/archive/2009/06/25/json-hijacking.aspx/ From afc84e2242147e1c7137a4d380b9e0736d86626c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 14 Apr 2025 17:58:24 +0200 Subject: [PATCH 413/642] Update the links related to X-Sendfile and X-Accel-Redirect --- components/http_foundation.rst | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/components/http_foundation.rst b/components/http_foundation.rst index 5a0c24ef618..14843bab346 100644 --- a/components/http_foundation.rst +++ b/components/http_foundation.rst @@ -854,9 +854,10 @@ Alternatively, if you are serving a static file, you can use a The ``BinaryFileResponse`` will automatically handle ``Range`` and ``If-Range`` headers from the request. It also supports ``X-Sendfile`` -(see for `FrankenPHP`_, `nginx`_ and `Apache`_). To make use of it, you need to determine -whether or not the ``X-Sendfile-Type`` header should be trusted and call -:method:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse::trustXSendfileTypeHeader` +(see `FrankenPHP X-Sendfile and X-Accel-Redirect headers`_, +`nginx X-Accel-Redirect header`_ and `Apache mod_xsendfile module`_). To make use +of it, you need to determine whether or not the ``X-Sendfile-Type`` header should +be trusted and call :method:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse::trustXSendfileTypeHeader` if it should:: BinaryFileResponse::trustXSendfileTypeHeader(); @@ -1061,9 +1062,9 @@ Learn More /session /http_cache/* -.. _FrankenPHP: https://frankenphp.dev/docs/x-sendfile/ -.. _nginx: https://mattbrictson.com/blog/accelerated-rails-downloads -.. _Apache: https://tn123.org/mod_xsendfile/ +.. _`FrankenPHP X-Sendfile and X-Accel-Redirect headers`: https://frankenphp.dev/docs/x-sendfile/ +.. _`nginx X-Accel-Redirect header`: https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_headers +.. _`Apache mod_xsendfile module`: https://github.com/nmaier/mod_xsendfile .. _`JSON Hijacking`: https://haacked.com/archive/2009/06/25/json-hijacking.aspx/ .. _`valid JSON top-level value`: https://www.json.org/json-en.html .. _OWASP guidelines: https://cheatsheetseries.owasp.org/cheatsheets/AJAX_Security_Cheat_Sheet.html#always-return-json-with-an-object-on-the-outside From a8a4523bcdfd889b2f9afee9342a655a879e090c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 15 Apr 2025 17:18:46 +0200 Subject: [PATCH 414/642] Fix a syntax issue in Coding Standards --- contributing/code/standards.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst index 3a00343faf1..ebfde7dfab4 100644 --- a/contributing/code/standards.rst +++ b/contributing/code/standards.rst @@ -211,7 +211,7 @@ Naming Conventions * Use `camelCase`_ for PHP variables, function and method names, arguments (e.g. ``$acceptableContentTypes``, ``hasSession()``); -Use `snake_case`_ for configuration parameters, route names and Twig template +* Use `snake_case`_ for configuration parameters, route names and Twig template variables (e.g. ``framework.csrf_protection``, ``http_status_code``); * Use SCREAMING_SNAKE_CASE for constants (e.g. ``InputArgument::IS_ARRAY``); From 4f50290a46361fdf8907ecfa53beb8c6864330d8 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Tue, 15 Apr 2025 11:33:07 +0200 Subject: [PATCH 415/642] feat: swap from event constant to event class --- event_dispatcher.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/event_dispatcher.rst b/event_dispatcher.rst index 7372d58b8d0..b854488de52 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -246,14 +246,13 @@ methods could be called before or after the methods defined in other listeners and subscribers. To learn more about event subscribers, read :doc:`/components/event_dispatcher`. The following example shows an event subscriber that defines several methods which -listen to the same ``kernel.exception`` event:: +listen to the same ``kernel.exception`` event which has an ``ExceptionEvent``:: // src/EventSubscriber/ExceptionSubscriber.php namespace App\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ExceptionEvent; - use Symfony\Component\HttpKernel\KernelEvents; class ExceptionSubscriber implements EventSubscriberInterface { @@ -261,7 +260,7 @@ listen to the same ``kernel.exception`` event:: { // return the subscribed events, their methods and priorities return [ - KernelEvents::EXCEPTION => [ + ExceptionEvent::class => [ ['processException', 10], ['logException', 0], ['notifyException', -10], From a6e0879c81c9009fcd376a144c0f97702b60213f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 15 Apr 2025 17:36:40 +0200 Subject: [PATCH 416/642] Minor tweak --- event_dispatcher.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/event_dispatcher.rst b/event_dispatcher.rst index b854488de52..8e5c3d092e0 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -246,7 +246,8 @@ methods could be called before or after the methods defined in other listeners and subscribers. To learn more about event subscribers, read :doc:`/components/event_dispatcher`. The following example shows an event subscriber that defines several methods which -listen to the same ``kernel.exception`` event which has an ``ExceptionEvent``:: +listen to the same :ref:`kernel.exception event `_ +via its ``ExceptionEvent`` class:: // src/EventSubscriber/ExceptionSubscriber.php namespace App\EventSubscriber; From 90def249610950e1811cd70d5bc1a94caf09ad15 Mon Sep 17 00:00:00 2001 From: Ousmane NDIAYE Date: Tue, 15 Apr 2025 16:47:27 +0200 Subject: [PATCH 417/642] Update embedded.rst Comment added to specify the name of the fil to edit. (As previous examples) --- form/embedded.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/form/embedded.rst b/form/embedded.rst index dd163235f03..9e20164c3a4 100644 --- a/form/embedded.rst +++ b/form/embedded.rst @@ -87,6 +87,7 @@ inside the task form itself. To accomplish this, add a ``category`` field to the ``TaskType`` object whose type is an instance of the new ``CategoryType`` class:: + // src/Form/TaskType.php use App\Form\CategoryType; use Symfony\Component\Form\FormBuilderInterface; From 89ff8f3485003be9c8c2f774a7acb588f7d192aa Mon Sep 17 00:00:00 2001 From: Bastien Jaillot Date: Wed, 16 Apr 2025 12:20:24 +0200 Subject: [PATCH 418/642] Update routing.rst --- routing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routing.rst b/routing.rst index 79a513ba8dd..f34205de98e 100644 --- a/routing.rst +++ b/routing.rst @@ -2715,7 +2715,7 @@ same URL, but with the HTTPS scheme. If you want to force a group of routes to use HTTPS, you can define the default scheme when importing them. The following example forces HTTPS on all routes -defined as annotations: +defined as attributes: .. configuration-block:: From 32aec360f5fce14a1f7b680b8e5695a36cbad263 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 16 Apr 2025 17:43:23 +0200 Subject: [PATCH 419/642] [EventDispatcher] Fix a minor syntax issue in a reference --- event_dispatcher.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event_dispatcher.rst b/event_dispatcher.rst index 8e5c3d092e0..d9b913ed49f 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -246,7 +246,7 @@ methods could be called before or after the methods defined in other listeners and subscribers. To learn more about event subscribers, read :doc:`/components/event_dispatcher`. The following example shows an event subscriber that defines several methods which -listen to the same :ref:`kernel.exception event `_ +listen to the same :ref:`kernel.exception event ` via its ``ExceptionEvent`` class:: // src/EventSubscriber/ExceptionSubscriber.php From 7fc2bc7c4c2268e5f27acbc7954497a859c96a3a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 16 Apr 2025 16:22:47 +0200 Subject: [PATCH 420/642] [Console] Minor tweak in the article introduction --- console.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/console.rst b/console.rst index baab4aff4a7..82bafd4d640 100644 --- a/console.rst +++ b/console.rst @@ -18,14 +18,14 @@ the ``list`` command to view all available commands in the application: ... Available commands: - about Display information about the current project - completion Dump the shell completion script - help Display help for a command - list List commands + about Display information about the current project + completion Dump the shell completion script + help Display help for a command + list List commands assets - assets:install Install bundle's web assets under a public directory + assets:install Install bundle's web assets under a public directory cache - cache:clear Clear the cache + cache:clear Clear the cache ... .. note:: From c4cf202b2614a726be6694161b6eec7aba253588 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 17 Apr 2025 08:34:15 +0200 Subject: [PATCH 421/642] fix configuration block indentation --- service_container.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service_container.rst b/service_container.rst index 6ae601cf724..6086ae1d946 100644 --- a/service_container.rst +++ b/service_container.rst @@ -226,7 +226,7 @@ each time you ask for it. If some files or directories in your project should not become services, you can exclude them using the ``exclude`` option: - .. configuration-block:: + .. configuration-block:: .. code-block:: yaml From fa8f5b191bf11cd1c8e29f56fea743855d597e54 Mon Sep 17 00:00:00 2001 From: Alexander Hofbauer Date: Thu, 17 Apr 2025 09:15:22 +0200 Subject: [PATCH 422/642] [Mailer] Document email.image name parameter --- mailer.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mailer.rst b/mailer.rst index a6b5eb819d8..034f9bdfe97 100644 --- a/mailer.rst +++ b/mailer.rst @@ -1093,6 +1093,14 @@ the email contents:

Welcome {{ email.toName }}!

{# ... #} +By default this will create an attachment using the file path as filename: +``Content-Disposition: inline; name="cid..."; filename="@images/logo.png"``. +This behavior can be overridden by passing a name (the third argument): + +.. code-block:: html+twig + + My Logo + .. _mailer-inline-css: Inlining CSS Styles From 316c8ac333d822e1b5d68aa8bdbc25f8923c453c Mon Sep 17 00:00:00 2001 From: Pierre Ambroise Date: Thu, 17 Apr 2025 16:39:24 +0200 Subject: [PATCH 423/642] Add when argument to AsAlias --- service_container/alias_private.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/service_container/alias_private.rst b/service_container/alias_private.rst index f99f7cb5f3e..84632c1d89b 100644 --- a/service_container/alias_private.rst +++ b/service_container/alias_private.rst @@ -181,6 +181,27 @@ This means that when using the container directly, you can access the # ... app.mailer: '@App\Mail\PhpMailer' +The ``#[AsAlias]`` attribute also support per-environment configuration +via the ``when`` argument:: + + // src/Mail/PhpMailer.php + namespace App\Mail; + + // ... + use Symfony\Component\DependencyInjection\Attribute\AsAlias; + + #[AsAlias(id: 'app.mailer', when: 'dev')] + class PhpMailer + { + // ... + } + +You can pass either a string or an array of strings to the ``when`` argument. + +.. versionadded:: 7.3 + + The ``when`` argument on the ``#[AsAlias]`` attribute was introduced in Symfony 7.3. + .. tip:: When using ``#[AsAlias]`` attribute, you may omit passing ``id`` argument From 5482c8281ad9bf346362e06f7988e83dd4282c6a Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Thu, 17 Apr 2025 20:14:01 -0400 Subject: [PATCH 424/642] [Routing] add tip about using `symfony/clock` with `UriSigner` --- routing.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/routing.rst b/routing.rst index a9ada1fc44b..0e1a46a505b 100644 --- a/routing.rst +++ b/routing.rst @@ -2815,6 +2815,16 @@ argument of :method:`Symfony\\Component\\HttpFoundation\\UriSigner::sign`:: Starting with Symfony 7.3, signed URI hashes no longer include the ``/`` or ``+`` characters, as these may cause issues with certain clients. +.. tip:: + + If ``symfony/clock`` is installed, it is used for creating and verifying the + expiration. This allows you to :ref:`mock the current time in your tests + `. + +.. versionadded:: 7.3 + + ``symfony/clock`` support was added to ``UriSigner`` in Symfony 7.3. + Troubleshooting --------------- From aa3b1674ea0202c0d3966ee82f04836fe33479ec Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Fri, 18 Apr 2025 19:34:27 -0400 Subject: [PATCH 425/642] [Routing] document `UriSigner::verify()` --- routing.rst | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/routing.rst b/routing.rst index a9ada1fc44b..19c2ae1a5a6 100644 --- a/routing.rst +++ b/routing.rst @@ -2810,10 +2810,30 @@ argument of :method:`Symfony\\Component\\HttpFoundation\\UriSigner::sign`:: The feature to add an expiration date for a signed URI was introduced in Symfony 7.1. +If you need to know the reason why a signed URI is invalid, you can use the +``verify()`` method which throws exceptions on failure:: + + use Symfony\Component\HttpFoundation\Exception\ExpiredSignedUriException; + use Symfony\Component\HttpFoundation\Exception\UnsignedUriException; + use Symfony\Component\HttpFoundation\Exception\UnverifiedSignedUriException; + + // ... + + try { + $uriSigner->verify($uri); // $uri can be a string or Request object + + // the URI is valid + } catch (UnsignedUriException) { + // the URI isn't signed + } catch (UnverifiedSignedUriException) { + // the URI is signed but the signature is invalid + } catch (ExpiredSignedUriException) { + // the URI is signed but expired + } + .. versionadded:: 7.3 - Starting with Symfony 7.3, signed URI hashes no longer include the ``/`` or - ``+`` characters, as these may cause issues with certain clients. + The ``verify()`` method was introduced in Symfony 7.3. Troubleshooting --------------- From e7613b0e2af243005b9fb66c9d696f74005a645a Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Sat, 19 Apr 2025 19:03:15 +0200 Subject: [PATCH 426/642] [Validator] Add filenameCharset and filenameCountUnit options to File constraint --- reference/constraints/File.rst | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 495c19f9cbe..62efa6cc08e 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -274,6 +274,31 @@ You can find a list of existing mime types on the `IANA website`_. If set, the validator will check that the filename of the underlying file doesn't exceed a certain length. +``filenameCountUnit`` +~~~~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``File::FILENAME_COUNT_BYTES`` + +The character count unit to use for the filename max length check. +By default :phpfunction:`strlen` is used, which counts the length of the string in bytes. + +Can be one of the following constants of the +:class:`Symfony\\Component\\Validator\\Constraints\\File` class: + +* ``FILENAME_COUNT_BYTES``: Uses :phpfunction:`strlen` counting the length of the + string in bytes. +* ``FILENAME_COUNT_CODEPOINTS``: Uses :phpfunction:`mb_strlen` counting the length + of the string in Unicode code points. Simple (multibyte) Unicode characters count + as 1 character, while for example ZWJ sequences of composed emojis count as + multiple characters. +* ``FILENAME_COUNT_GRAPHEMES``: Uses :phpfunction:`grapheme_strlen` counting the + length of the string in graphemes, i.e. even emojis and ZWJ sequences of composed + emojis count as 1 character. + +.. versionadded:: 7.3 + + The ``filenameCountUnit`` option was introduced in Symfony 7.3. + ``filenameTooLongMessage`` ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -290,6 +315,35 @@ Parameter Description ``{{ filename_max_length }}`` Maximum number of characters allowed ============================== ============================================================== +``filenameCharset`` +~~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``null`` + +The charset to be used when computing value's filename max length with the +:phpfunction:`mb_check_encoding` and :phpfunction:`mb_strlen` +PHP functions. + +``filenameCharsetMessage`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This filename does not match the expected charset.`` + +The message that will be shown if the value is not using the given `filenameCharsetMessage`_. + +You can use the following parameters in this message: + +================= ============================================================ +Parameter Description +================= ============================================================ +``{{ charset }}`` The expected charset +``{{ name }}`` The current (invalid) value +================= ============================================================ + +.. versionadded:: 7.3 + + The ``filenameCharset`` and ``filenameCharsetMessage`` options were introduced in Symfony 7.3. + ``extensionsMessage`` ~~~~~~~~~~~~~~~~~~~~~ From 76c4720d47de37c43d0a87cc1c61fe8d1216605c Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 22 Apr 2025 17:17:11 +0200 Subject: [PATCH 427/642] Minor tweaks --- service_container/alias_private.rst | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/service_container/alias_private.rst b/service_container/alias_private.rst index 84632c1d89b..22bf649d861 100644 --- a/service_container/alias_private.rst +++ b/service_container/alias_private.rst @@ -181,8 +181,8 @@ This means that when using the container directly, you can access the # ... app.mailer: '@App\Mail\PhpMailer' -The ``#[AsAlias]`` attribute also support per-environment configuration -via the ``when`` argument:: +The ``#[AsAlias]`` attribute can also be limited to one or more specific +:ref:`config environments ` using the ``when`` argument:: // src/Mail/PhpMailer.php namespace App\Mail; @@ -196,11 +196,16 @@ via the ``when`` argument:: // ... } -You can pass either a string or an array of strings to the ``when`` argument. + // pass an array to apply it in multiple config environments + #[AsAlias(id: 'app.mailer', when: ['dev', 'test'])] + class PhpMailer + { + // ... + } .. versionadded:: 7.3 - The ``when`` argument on the ``#[AsAlias]`` attribute was introduced in Symfony 7.3. + The ``when`` argument of the ``#[AsAlias]`` attribute was introduced in Symfony 7.3. .. tip:: From 6350e2ea71430fa106bd7873a4c384153fab134e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 22 Apr 2025 17:49:02 +0200 Subject: [PATCH 428/642] Minor tweaks --- routing.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/routing.rst b/routing.rst index 672f331291b..663e8518504 100644 --- a/routing.rst +++ b/routing.rst @@ -2837,13 +2837,14 @@ If you need to know the reason why a signed URI is invalid, you can use the .. tip:: - If ``symfony/clock`` is installed, it is used for creating and verifying the - expiration. This allows you to :ref:`mock the current time in your tests + If ``symfony/clock`` is installed, it will be used to create and verify + expirations. This allows you to :ref:`mock the current time in your tests `. .. versionadded:: 7.3 - ``symfony/clock`` support was added to ``UriSigner`` in Symfony 7.3. + Support for :doc:`Symfony Clock ` in ``UriSigner`` was + introduced in Symfony 7.3. Troubleshooting --------------- From a5308819ac679da412d48acbb3d45f245b23f1e3 Mon Sep 17 00:00:00 2001 From: aurac Date: Wed, 23 Apr 2025 19:38:45 +0200 Subject: [PATCH 429/642] fix(doctrine): default configuration --- reference/configuration/doctrine.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index 8a1062a54ae..46877e9f84c 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -176,7 +176,7 @@ that the ORM resolves to: doctrine: orm: - auto_mapping: true + auto_mapping: false # the standard distribution overrides this to be true in debug, false otherwise auto_generate_proxy_classes: false proxy_namespace: Proxies From 3abba12e6fe84c031dcae54569f35e8225775b76 Mon Sep 17 00:00:00 2001 From: Hmache Abdellah <11814824+Hmache@users.noreply.github.com> Date: Sun, 20 Apr 2025 23:31:32 +0200 Subject: [PATCH 430/642] [uid] Fix Uuid::v8() usage example to show it requires a UUID string This update corrects the usage example of Uuid::v8() to prevent misuse and confusion --- components/uid.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/uid.rst b/components/uid.rst index b286329151d..2fa04251f90 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -140,7 +140,7 @@ of the UUID value is specific to each implementation and no format should be ass use Symfony\Component\Uid\Uuid; // $uuid is an instance of Symfony\Component\Uid\UuidV8 - $uuid = Uuid::v8(); + $uuid = Uuid::v8('d9e7a184-5d5b-11ea-a62a-3499710062d0'); .. versionadded:: 6.2 From 4464d375341f6d333fff2dd91e2b48aeb3589591 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 24 Apr 2025 08:57:36 +0200 Subject: [PATCH 431/642] Tweaks --- components/uid.rst | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/components/uid.rst b/components/uid.rst index 2fa04251f90..b031348f85a 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -32,13 +32,13 @@ to create each type of UUID: **UUID v1** (time-based) Generates the UUID using a timestamp and the MAC address of your device -(`read UUIDv1 spec `__). +(`read the UUIDv1 spec `__). Both are obtained automatically, so you don't have to pass any constructor argument:: use Symfony\Component\Uid\Uuid; - // $uuid is an instance of Symfony\Component\Uid\UuidV1 $uuid = Uuid::v1(); + // $uuid is an instance of Symfony\Component\Uid\UuidV1 .. tip:: @@ -48,7 +48,7 @@ Both are obtained automatically, so you don't have to pass any constructor argum **UUID v2** (DCE security) Similar to UUIDv1 but with a very high likelihood of ID collision -(`read UUIDv2 spec `__). +(`read the UUIDv2 spec `__). It's part of the authentication mechanism of DCE (Distributed Computing Environment) and the UUID includes the POSIX UIDs (user/group ID) of the user who generated it. This UUID variant is **not implemented** by the Uid component. @@ -56,7 +56,7 @@ This UUID variant is **not implemented** by the Uid component. **UUID v3** (name-based, MD5) Generates UUIDs from names that belong, and are unique within, some given namespace -(`read UUIDv3 spec `__). +(`read the UUIDv3 spec `__). This variant is useful to generate deterministic UUIDs from arbitrary strings. It works by populating the UUID contents with the``md5`` hash of concatenating the namespace and the name:: @@ -69,8 +69,8 @@ the namespace and the name:: // $namespace = Uuid::v4(); // $name can be any arbitrary string - // $uuid is an instance of Symfony\Component\Uid\UuidV3 $uuid = Uuid::v3($namespace, $name); + // $uuid is an instance of Symfony\Component\Uid\UuidV3 These are the default namespaces defined by the standard: @@ -81,20 +81,20 @@ These are the default namespaces defined by the standard: **UUID v4** (random) -Generates a random UUID (`read UUIDv4 spec `__). +Generates a random UUID (`read the UUIDv4 spec `__). Because of its randomness, it ensures uniqueness across distributed systems without the need for a central coordinating entity. It's privacy-friendly because it doesn't contain any information about where and when it was generated:: use Symfony\Component\Uid\Uuid; - // $uuid is an instance of Symfony\Component\Uid\UuidV4 $uuid = Uuid::v4(); + // $uuid is an instance of Symfony\Component\Uid\UuidV4 **UUID v5** (name-based, SHA-1) It's the same as UUIDv3 (explained above) but it uses ``sha1`` instead of -``md5`` to hash the given namespace and name (`read UUIDv5 spec `__). +``md5`` to hash the given namespace and name (`read the UUIDv5 spec `__). This makes it more secure and less prone to hash collisions. .. _uid-uuid-v6: @@ -103,12 +103,12 @@ This makes it more secure and less prone to hash collisions. It rearranges the time-based fields of the UUIDv1 to make it lexicographically sortable (like :ref:`ULIDs `). It's more efficient for database indexing -(`read UUIDv6 spec `__):: +(`read the UUIDv6 spec `__):: use Symfony\Component\Uid\Uuid; - // $uuid is an instance of Symfony\Component\Uid\UuidV6 $uuid = Uuid::v6(); + // $uuid is an instance of Symfony\Component\Uid\UuidV6 .. tip:: @@ -121,26 +121,28 @@ sortable (like :ref:`ULIDs `). It's more efficient for database indexing Generates time-ordered UUIDs based on a high-resolution Unix Epoch timestamp source (the number of milliseconds since midnight 1 Jan 1970 UTC, leap seconds excluded) -(`read UUIDv7 spec `__). +(`read the UUIDv7 spec `__). It's recommended to use this version over UUIDv1 and UUIDv6 because it provides better entropy (and a more strict chronological order of UUID generation):: use Symfony\Component\Uid\Uuid; - // $uuid is an instance of Symfony\Component\Uid\UuidV7 $uuid = Uuid::v7(); + // $uuid is an instance of Symfony\Component\Uid\UuidV7 **UUID v8** (custom) -Provides an RFC-compatible format for experimental or vendor-specific use cases -(`read UUIDv8 spec `__). -The only requirement is to set the variant and version bits of the UUID. The rest -of the UUID value is specific to each implementation and no format should be assumed:: +Provides an RFC-compatible format intended for experimental or vendor-specific use cases +(`read the UUIDv8 spec `__). +You must generate the UUID value yourself. The only requirement is to set the +variant and version bits of the UUID correctly. The rest of the UUID content is +implementation-specific, and no particular format should be assumed:: use Symfony\Component\Uid\Uuid; - // $uuid is an instance of Symfony\Component\Uid\UuidV8 + // pass your custom UUID value as the argument $uuid = Uuid::v8('d9e7a184-5d5b-11ea-a62a-3499710062d0'); + // $uuid is an instance of Symfony\Component\Uid\UuidV8 .. versionadded:: 6.2 From d8ac2923438cd19b28bc3a51577e5b505102eb64 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 24 Apr 2025 09:24:02 +0200 Subject: [PATCH 432/642] fix path to Panther web server router file The path is used as an argument for the `php` binary which treats it relative to the document root directory that is passed with the `-t` option (in a typical Symfony application the document root directory is `public` which is located in the same parent directory as the `tests` directory). --- testing/end_to_end.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/end_to_end.rst b/testing/end_to_end.rst index 83d61b1e60f..80e970bd2cd 100644 --- a/testing/end_to_end.rst +++ b/testing/end_to_end.rst @@ -878,7 +878,7 @@ Then declare it as a router for Panther server in ``phpunit.xml.dist`` using the - + From 9f7cc9d5d899a2afc46ca1be67f2242a21326168 Mon Sep 17 00:00:00 2001 From: Arnaud Thibaudet Date: Thu, 24 Apr 2025 10:17:29 +0200 Subject: [PATCH 433/642] Fix typo on parameter name Signed-off-by: Arnaud Thibaudet --- translation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/translation.rst b/translation.rst index 86282090801..23949b7e67f 100644 --- a/translation.rst +++ b/translation.rst @@ -487,11 +487,11 @@ your application: .. code-block:: twig - {{ 'Application version: {version}'|trans }} + {{ 'Application version: {app_version}'|trans }} {# output: "Application version: 1.2.3" #} {# parameters passed to the message override global parameters #} - {{ 'Package version: {version}'|trans({'{version}': '2.3.4'}) }} + {{ 'Package version: {app_version}'|trans({'{app_version}': '2.3.4'}) }} # Displays "Package version: 2.3.4" Forcing the Translator Locale From ce4cd4db8dc7154a63c9951f5a276b11f93cc29e Mon Sep 17 00:00:00 2001 From: Jorick Pepin Date: Thu, 24 Apr 2025 17:11:47 +0200 Subject: [PATCH 434/642] [BrowserKit] Fix `submitForm` behaviour --- components/browser_kit.rst | 2 +- testing.rst | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/browser_kit.rst b/components/browser_kit.rst index c744837d7b1..21ceaf5a095 100644 --- a/components/browser_kit.rst +++ b/components/browser_kit.rst @@ -147,7 +147,7 @@ field values, etc.) before submitting it:: $crawler = $client->request('GET', 'https://github.com/login'); // find the form with the 'Log in' button and submit it - // 'Log in' can be the text content, id, value or name of a @@ -229,7 +237,7 @@ generate a CSRF token in the template and store it as a hidden form field: Then, get the value of the CSRF token in the controller action and use the :method:`Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController::isCsrfTokenValid` -method to check its validity:: +method to check its validity, passing the same token ID used in the template:: use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -302,6 +310,166 @@ targeted parts of the plaintext. To mitigate these attacks, and prevent an attacker from guessing the CSRF tokens, a random mask is prepended to the token and used to scramble it. +Stateless CSRF Tokens +--------------------- + +.. versionadded:: 7.2 + + Stateless anti-CSRF protection was introduced in Symfony 7.2. + +By default CSRF tokens are stateful, which means they're stored in the session. +But some token ids can be declared as stateless using the ``stateless_token_ids`` +option: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/csrf.yaml + framework: + # ... + csrf_protection: + stateless_token_ids: ['submit', 'authenticate', 'logout'] + + .. code-block:: xml + + + + + + + + submit + authenticate + logout + + + + + .. code-block:: php + + // config/packages/csrf.php + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework): void { + $framework->csrfProtection() + ->statelessTokenIds(['submit', 'authenticate', 'logout']) + ; + }; + +Stateless CSRF tokens use a CSRF protection that doesn't need the session. This +means that you can cache the entire page and still have CSRF protection. + +When a stateless CSRF token is checked for validity, Symfony verifies the +``Origin`` and the ``Referer`` headers of the incoming HTTP request. + +If either of these headers match the target origin of the application (its domain +name), the CSRF token is considered valid. This relies on the app being able to +know its own target origin. Don't miss configuring your reverse proxy if you're +behind one. See :doc:`/deployment/proxies`. + +Using a Default Token ID +~~~~~~~~~~~~~~~~~~~~~~~~ + +While stateful CSRF tokens are better seggregated per form or action, stateless +ones don't need many token identifiers. In the previous example, ``authenticate`` +and ``logout`` are listed because they're the default identifiers used by the +Symfony Security component. The ``submit`` identifier is then listed so that +form types defined by the application can use it by default. The following +configuration - which applies only to form types declared using autofiguration +(the default way to declare *your* services) - will make your form types use the +``submit`` token identifier by default: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/csrf.yaml + framework: + form: + csrf_protection: + token_id: 'submit' + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // config/packages/csrf.php + use Symfony\Config\FrameworkConfig; + + return static function (FrameworkConfig $framework): void { + $framework->form() + ->csrfProtection() + ->tokenId('submit') + ; + }; + +Forms configured with a token identifier listed in the above ``stateless_token_ids`` +option will use the stateless CSRF protection. + +Generating CSRF Token Using Javascript +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In addition to the ``Origin`` and ``Referer`` headers, stateless CSRF protection +also checks a cookie and a header (named ``csrf-token`` by default, see the +:ref:`CSRF configuration reference `). + +These extra checks are part of defense-in-depth strategies provided by the +stateless CSRF protection. They are optional and they require +`some JavaScript`_ to be activated. This JavaScript is responsible for generating +a crypto-safe random token when a form is submitted, then putting the token in +the hidden CSRF field of the form and submitting it also as a cookie and header. +On the server-side, the CSRF token is validated by checking the cookie and header +values. This "double-submit" protection relies on the same-origin policy +implemented by browsers and is strengthened by regenerating the token at every +form submission - which prevents cookie fixation issues - and by using +``samesite=strict`` and ``__Host-`` cookies, which make them domain-bound and +HTTPS-only. + +Note that the default snippet of JavaScript provided by Symfony requires that +the hidden CSRF form field is either named ``_csrf_token``, or that it has the +``data-controller="csrf-protection"`` attribute. You can of course take +inspiration from this snippet to write your own, provided you follow the same +protocol. + +As a last measure, a behavioral check is added on the server-side to ensure that +the validation method cannot be downgraded: if and only if a session is already +available, successful "double-submit" is remembered and is then required for +subsequent requests. This prevents attackers from exploiting potentially reduced +validation checks once cookie and/or header validation has been confirmed as +effective (they're optional by default as explained above). + +.. note:: + + Enforcing successful "double-submit" for every requests is not recommended as + as it could lead to a broken user experience. The opportunistic approach + described above is preferred because it allows the application to gracefully + degrade to ``Origin`` / ``Referer`` checks when JavaScript is not available. + .. _`Cross-site request forgery`: https://en.wikipedia.org/wiki/Cross-site_request_forgery .. _`BREACH`: https://en.wikipedia.org/wiki/BREACH .. _`CRIME`: https://en.wikipedia.org/wiki/CRIME +.. _`some JavaScript`: https://github.com/symfony/recipes/blob/main/symfony/stimulus-bundle/2.20/assets/controllers/csrf_protection_controller.js diff --git a/session.rst b/session.rst index 0c5348ec9e6..4c14638fc68 100644 --- a/session.rst +++ b/session.rst @@ -115,7 +115,7 @@ sessions for anonymous users, you must *completely* avoid accessing the session. .. note:: Sessions will also be started when using features that rely on them internally, - such as the :ref:`CSRF protection in forms `. + such as the :ref:`stateful CSRF protection in forms `. .. _flash-messages: From e591c5903958e63eaf1bc7d9b470431b29904f8c Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 12 May 2025 15:16:58 +0200 Subject: [PATCH 496/642] [Routing] Tell about {foo:bar} mapping syntax --- doctrine.rst | 31 +++++++++++-------------------- routing.rst | 43 ++++++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/doctrine.rst b/doctrine.rst index 171f8a3348a..270c59a08c4 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -680,7 +680,7 @@ will automatically fetch them:: /** * Perform a findOneBy() where the slug property matches {slug}. */ - #[Route('/product/{slug}')] + #[Route('/product/{slug:product}')] public function showBySlug(Product $product): Response { } @@ -694,14 +694,17 @@ Automatic fetching works in these situations: *all* of the wildcards in your route that are actually properties on your entity (non-properties are ignored). -This behavior is enabled by default on all controllers. If you prefer, you can -restrict this feature to only work on route wildcards called ``id`` to look for -entities by primary key. To do so, set the option -``doctrine.orm.controller_resolver.auto_mapping`` to ``false``. +The ``{slug:product}`` syntax maps the route parameter named ``slug`` to the +controller argument named ``$product``. It also hints the resolver to lookup +by slug when loading the corresponding ``Product`` object from the database. -When ``auto_mapping`` is disabled, you can configure the mapping explicitly for -any controller argument with the ``MapEntity`` attribute. You can even control -the ``EntityValueResolver`` behavior by using the `MapEntity options`_ :: +.. versionadded:: 7.1 + + Route parameter mapping was introduced in Symfony 7.1. + +You can also configure the mapping explicitly for any controller argument +with the ``MapEntity`` attribute. You can even control the +``EntityValueResolver`` behavior by using the `MapEntity options`_ :: // src/Controller/ProductController.php namespace App\Controller; @@ -812,18 +815,6 @@ control behavior: ): 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:: - - #[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. diff --git a/routing.rst b/routing.rst index 24218eaf74c..587a35f269b 100644 --- a/routing.rst +++ b/routing.rst @@ -22,8 +22,7 @@ Creating Routes as Attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PHP attributes allow to define routes next to the code of the -:doc:`controllers ` associated to those routes. Attributes are -native in PHP 8 and higher versions, so you can use them right away. +:doc:`controllers ` associated to those routes. You need to add a bit of configuration to your project before using them. If your project uses :ref:`Symfony Flex `, this file is already created for you. @@ -707,12 +706,6 @@ URL Route Parameters matches any uppercase character in any language, ``\p{Greek}`` matches any Greek characters, etc. -.. note:: - - When using regular expressions in route parameters, you can set the ``utf8`` - route option to ``true`` to make any ``.`` character match any UTF-8 - characters instead of just a single byte. - If you prefer, requirements can be inlined in each parameter using the syntax ``{parameter_name}``. This feature makes configuration more concise, but it can decrease route readability when requirements are complex: @@ -998,7 +991,7 @@ controller action. Instead of ``string $slug``, add ``BlogPost $post``:: { // ... - #[Route('/blog/{slug}', name: 'blog_show')] + #[Route('/blog/{slug:post}', name: 'blog_show')] public function show(BlogPost $post): Response { // $post is the object whose slug matches the routing parameter @@ -1012,9 +1005,37 @@ this case), the "param converter" makes a database request to find the object using the request parameters (``slug`` in this case). If no object is found, Symfony generates a 404 response automatically. +The ``{slug:post}`` syntax maps the route parameter named ``slug`` to the controller +argument named ``$post``. It also hints the "param converter" to lookup by slug +when loading the corresponding ``BlogPost`` object from the database. + +.. versionadded:: 7.1 + + Route parameter mapping was introduced in Symfony 7.1. + +When more than one entity needs to be derived from route parameters, collisions can happen. +In the following example, the route tries to define two mappings: one to load an author by +name, two to load a category by name. But this is not allowed because from the side of the +route definition, this declares a parameter named "name" twice:: + + #[Route('/search-book/{name:author}/{name:category}')] + +Such routes should instead be defined using the following syntax:: + + #[Route('/search-book/{authorName:author.name}/{categoryName:category.name}')] + +This way, the route parameter names are unique (``authorName`` and ``categoryName``) and +the "param converter" can correctly map them to controller arguments (``$author`` and +``$category``), loading them both by their name. + +.. versionadded:: 7.3 + + This more advanced style of route parameter mapping was introduced in Symfony 7.3. + +More advanced mappings can be achieved using the ``#[MapEntity]`` attribute. Check out the :ref:`Doctrine param conversion documentation ` -to learn about the ``#[MapEntity]`` attribute that can be used to customize the -database queries used to fetch the object from the route parameter. +to learn how to customize the database queries used to fetch the object from the route +parameter. Backed Enum Parameters ~~~~~~~~~~~~~~~~~~~~~~ From 62b7149405447a29a11f8f8e93da99dfb4afac85 Mon Sep 17 00:00:00 2001 From: HypeMC Date: Sat, 17 May 2025 17:55:29 +0200 Subject: [PATCH 497/642] [Doctrine] Remove redundant example with attribute --- doctrine/events.rst | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/doctrine/events.rst b/doctrine/events.rst index 9507316eb5b..3f8f93bc2ee 100644 --- a/doctrine/events.rst +++ b/doctrine/events.rst @@ -304,23 +304,6 @@ listener in the Symfony application by creating a new service for it and .. configuration-block:: - .. code-block:: php-attributes - - // src/EventListener/SearchIndexer.php - namespace App\EventListener; - - use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; - use Doctrine\ORM\Event\PostPersistEventArgs; - - #[AsDoctrineListener('postPersist'/*, 500, 'default'*/)] - class SearchIndexer - { - public function postPersist(PostPersistEventArgs $event): void - { - // ... - } - } - .. code-block:: yaml # config/services.yaml From c6ae20a8fcdd5c5452fe4a3321244773dca04e1e Mon Sep 17 00:00:00 2001 From: Nazar Mammedov Date: Fri, 16 May 2025 15:54:01 -0400 Subject: [PATCH 498/642] Update messenger.rst Typo fix from "may looks" to "may look" --- messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index bf5a8180adc..18c7be913b5 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1372,7 +1372,7 @@ RabbitMQ. Install it by running: $ composer require symfony/amqp-messenger -The AMQP transport DSN may looks like this: +The AMQP transport DSN may look like this: .. code-block:: env From 3474a09b856214ca239af989d221faa05fe69c4a Mon Sep 17 00:00:00 2001 From: "Benjamin D." Date: Wed, 16 Apr 2025 11:31:41 +0200 Subject: [PATCH 499/642] Highligh 'extensions' instead of 'mimeTypes' for File constraint You should always use the extensions option instead of mimeTypes except if you explicitly don't want to check that the extension of the file is consistent with its content (this can be a security issue). By default, the extensions option also checks the media type of the file. --- controller/upload_file.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/controller/upload_file.rst b/controller/upload_file.rst index dff5453509a..941bd911a78 100644 --- a/controller/upload_file.rst +++ b/controller/upload_file.rst @@ -77,11 +77,8 @@ so Symfony doesn't try to get/set its value from the related entity:: 'constraints' => [ new File([ 'maxSize' => '1024k', - 'mimeTypes' => [ - 'application/pdf', - 'application/x-pdf', - ], - 'mimeTypesMessage' => 'Please upload a valid PDF document', + 'extensions' => ['pdf'], + 'extensionsMessage' => 'Please upload a valid PDF document', ]) ], ]) From e8128a60528c45de2833c9eb1bf692daf963fadf Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Tue, 20 May 2025 08:55:56 +0200 Subject: [PATCH 500/642] remove the Slug constraint --- reference/constraints/Slug.rst | 119 ------------------------------ reference/constraints/map.rst.inc | 1 - 2 files changed, 120 deletions(-) delete mode 100644 reference/constraints/Slug.rst diff --git a/reference/constraints/Slug.rst b/reference/constraints/Slug.rst deleted file mode 100644 index 2eb82cd9c10..00000000000 --- a/reference/constraints/Slug.rst +++ /dev/null @@ -1,119 +0,0 @@ -Slug -==== - -.. versionadded:: 7.3 - - The ``Slug`` constraint was introduced in Symfony 7.3. - -Validates that a value is a slug. By default, a slug is a string that matches -the following regular expression: ``/^[a-z0-9]+(?:-[a-z0-9]+)*$/``. - -.. include:: /reference/constraints/_empty-values-are-valid.rst.inc - -========== =================================================================== -Applies to :ref:`property or method ` -Class :class:`Symfony\\Component\\Validator\\Constraints\\Slug` -Validator :class:`Symfony\\Component\\Validator\\Constraints\\SlugValidator` -========== =================================================================== - -Basic Usage ------------ - -The ``Slug`` constraint can be applied to a property or a getter method: - -.. configuration-block:: - - .. code-block:: php-attributes - - // src/Entity/Author.php - namespace App\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Author - { - #[Assert\Slug] - protected string $slug; - } - - .. code-block:: yaml - - # config/validator/validation.yaml - App\Entity\Author: - properties: - slug: - - Slug: ~ - - .. code-block:: xml - - - - - - - - - - - - - .. code-block:: php - - // src/Entity/Author.php - namespace App\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - use Symfony\Component\Validator\Mapping\ClassMetadata; - - class Author - { - // ... - - public static function loadValidatorMetadata(ClassMetadata $metadata): void - { - $metadata->addPropertyConstraint('slug', new Assert\Slug()); - } - } - -Examples of valid values: - -* foobar -* foo-bar -* foo123 -* foo-123bar - -Uppercase characters would result in an violation of this constraint. - -Options -------- - -``regex`` -~~~~~~~~~ - -**type**: ``string`` default: ``/^[a-z0-9]+(?:-[a-z0-9]+)*$/`` - -This option allows you to modify the regular expression pattern that the input -will be matched against via the :phpfunction:`preg_match` PHP function. - -If you need to use it, you might also want to take a look at the :doc:`Regex constraint `. - -``message`` -~~~~~~~~~~~ - -**type**: ``string`` **default**: ``This value is not a valid slug`` - -This is the message that will be shown if this validator fails. - -You can use the following parameters in this message: - -================= ============================================================== -Parameter Description -================= ============================================================== -``{{ value }}`` The current (invalid) value -================= ============================================================== - -.. include:: /reference/constraints/_groups-option.rst.inc - -.. include:: /reference/constraints/_payload-option.rst.inc diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index c2396ae3af7..06680e42207 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -33,7 +33,6 @@ String Constraints * :doc:`NotCompromisedPassword ` * :doc:`PasswordStrength ` * :doc:`Regex ` -* :doc:`Slug ` * :doc:`Twig ` * :doc:`Ulid ` * :doc:`Url ` From 88a7eb25c60453e95dd9cf782238f2855b9e4738 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 20 May 2025 09:17:27 +0200 Subject: [PATCH 501/642] Minor tweaks --- doctrine.rst | 8 ++++---- routing.rst | 23 ++--------------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/doctrine.rst b/doctrine.rst index 270c59a08c4..a55ff8fe600 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -695,16 +695,16 @@ Automatic fetching works in these situations: on your entity (non-properties are ignored). The ``{slug:product}`` syntax maps the route parameter named ``slug`` to the -controller argument named ``$product``. It also hints the resolver to lookup -by slug when loading the corresponding ``Product`` object from the database. +controller argument named ``$product``. It also hints the resolver to look up +the corresponding ``Product`` object from the database using the slug. .. versionadded:: 7.1 Route parameter mapping was introduced in Symfony 7.1. You can also configure the mapping explicitly for any controller argument -with the ``MapEntity`` attribute. You can even control the -``EntityValueResolver`` behavior by using the `MapEntity options`_ :: +using the ``MapEntity`` attribute. You can even control the behavior of the +``EntityValueResolver`` by using the `MapEntity options`_ :: // src/Controller/ProductController.php namespace App\Controller; diff --git a/routing.rst b/routing.rst index 587a35f269b..810c753ec3a 100644 --- a/routing.rst +++ b/routing.rst @@ -1006,32 +1006,13 @@ using the request parameters (``slug`` in this case). If no object is found, Symfony generates a 404 response automatically. The ``{slug:post}`` syntax maps the route parameter named ``slug`` to the controller -argument named ``$post``. It also hints the "param converter" to lookup by slug -when loading the corresponding ``BlogPost`` object from the database. +argument named ``$post``. It also hints the "param converter" to look up the +corresponding ``BlogPost`` object from the database using the slug. .. versionadded:: 7.1 Route parameter mapping was introduced in Symfony 7.1. -When more than one entity needs to be derived from route parameters, collisions can happen. -In the following example, the route tries to define two mappings: one to load an author by -name, two to load a category by name. But this is not allowed because from the side of the -route definition, this declares a parameter named "name" twice:: - - #[Route('/search-book/{name:author}/{name:category}')] - -Such routes should instead be defined using the following syntax:: - - #[Route('/search-book/{authorName:author.name}/{categoryName:category.name}')] - -This way, the route parameter names are unique (``authorName`` and ``categoryName``) and -the "param converter" can correctly map them to controller arguments (``$author`` and -``$category``), loading them both by their name. - -.. versionadded:: 7.3 - - This more advanced style of route parameter mapping was introduced in Symfony 7.3. - More advanced mappings can be achieved using the ``#[MapEntity]`` attribute. Check out the :ref:`Doctrine param conversion documentation ` to learn how to customize the database queries used to fetch the object from the route From d71927f9e9eb83a697416e76f5928af85bbbe38a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 20 May 2025 09:21:53 +0200 Subject: [PATCH 502/642] [Routing] Readd the 7.3 docs about advance param mapping --- routing.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/routing.rst b/routing.rst index 5df9694296d..47a3e9f3190 100644 --- a/routing.rst +++ b/routing.rst @@ -1020,6 +1020,25 @@ corresponding ``BlogPost`` object from the database using the slug. Route parameter mapping was introduced in Symfony 7.1. +When mapping multiple entities from route parameters, name collisions can occur. +In this example, the route tries to define two mappings: one for an author and one +for a category; both using the same ``name`` parameter. This isn't allowed because +the route ends up declaring ``name`` twice:: + + #[Route('/search-book/{name:author}/{name:category}')] + +Such routes should instead be defined using the following syntax:: + + #[Route('/search-book/{authorName:author.name}/{categoryName:category.name}')] + +This way, the route parameter names are unique (``authorName`` and ``categoryName``), +and the "param converter" can correctly map them to controller arguments (``$author`` +and ``$category``), loading them both by their name. + +.. versionadded:: 7.3 + + This more advanced style of route parameter mapping was introduced in Symfony 7.3. + More advanced mappings can be achieved using the ``#[MapEntity]`` attribute. Check out the :ref:`Doctrine param conversion documentation ` to learn how to customize the database queries used to fetch the object from the route From a28b614c27bdca774025babeca5f8393a318ff86 Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Tue, 25 Feb 2025 10:04:59 +0100 Subject: [PATCH 503/642] [Routing] Clarify route aliases examples with explicit route names --- routing.rst | 123 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 100 insertions(+), 23 deletions(-) diff --git a/routing.rst b/routing.rst index a41b9bba6a6..18a25dd3018 100644 --- a/routing.rst +++ b/routing.rst @@ -1336,15 +1336,18 @@ A possible solution is to change the parameter requirements to be more permissiv Route Aliasing -------------- -Route alias allow you to have multiple name for the same route: +Route alias allows you to have multiple names for the same route +and can be used to provide backward compatibility for routes that +have been renamed. Let's say you have a route called ``product_show``: .. configuration-block:: .. code-block:: yaml # config/routes.yaml - new_route_name: - alias: original_route_name + product_show: + path: /product/{id} + controller: App\Controller\ProductController::show .. code-block:: xml @@ -1355,7 +1358,7 @@ Route alias allow you to have multiple name for the same route: xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> - + .. code-block:: php @@ -1364,10 +1367,55 @@ Route alias allow you to have multiple name for the same route: use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; return static function (RoutingConfigurator $routes): void { - $routes->alias('new_route_name', 'original_route_name'); + $routes->add('product_show', '/product/{id}') + ->controller('App\Controller\ProductController::show'); }; -In this example, both ``original_route_name`` and ``new_route_name`` routes can +Now, let's say you want to create a new route called ``product_details`` +that acts exactly the same as ``product_show``. + +Instead of duplicating the original route, you can create an alias for it. + +.. configuration-block:: + + .. code-block:: yaml + + # config/routes.yaml + product_show: + path: /product/{id} + controller: App\Controller\ProductController::show + + product_details: + # "alias" option refers to the name of the route declared above + alias: product_show + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/routes.php + use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; + + return static function (RoutingConfigurator $routes): void { + $routes->add('product_show', '/product/{id}') + ->controller('App\Controller\ProductController::show'); + // second argument refers to the name of the route declared above + $routes->alias('product_details', 'product_show'); + }; + +In this example, both ``product_show`` and ``product_details`` routes can be used in the application and will produce the same result. .. _routing-alias-deprecation: @@ -1375,27 +1423,45 @@ be used in the application and will produce the same result. Deprecating Route Aliases ~~~~~~~~~~~~~~~~~~~~~~~~~ -If some route alias should no longer be used (because it is outdated or -you decided not to maintain it anymore), you can deprecate its definition: +Route aliases can be used to provide backward compatibility for routes that +have been renamed. + +Now, let's say you want to replace the ``product_show`` route in favor of +``product_details`` and mark the old one as deprecated. + +In the previous example, the alias ``product_details`` was pointing to +``product_show`` route. + +To mark the ``product_show`` route as deprecated, you need to "switch" the alias. +The ``product_show`` become the alias, and will now point to the ``product_details`` route. +This way, the ``product_show`` alias could be deprecated. .. configuration-block:: .. code-block:: yaml - new_route_name: - alias: original_route_name + # Move the concrete route definition under ``product_details`` + product_details: + path: /product/{id} + controller: App\Controller\ProductController::show + + # Define the alias and the deprecation under the ``product_show`` definition + product_show: + alias: product_details # this outputs the following generic deprecation message: - # Since acme/package 1.2: The "new_route_name" route alias is deprecated. You should stop using it, as it will be removed in the future. + # Since acme/package 1.2: The "product_show" route alias is deprecated. You should stop using it, as it will be removed in the future. deprecated: package: 'acme/package' version: '1.2' - # you can also define a custom deprecation message (%alias_id% placeholder is available) + # or + + # you can define a custom deprecation message (%alias_id% placeholder is available) deprecated: package: 'acme/package' version: '1.2' - message: 'The "%alias_id%" route alias is deprecated. Do not use it anymore.' + message: 'The "%alias_id%" route alias is deprecated. Please use "product_details" instead.' .. code-block:: xml @@ -1405,35 +1471,46 @@ you decided not to maintain it anymore), you can deprecate its definition: xsi:schemaLocation="http://symfony.com/schema/routing https://symfony.com/schema/routing/routing-1.0.xsd"> - + + + + + + Since acme/package 1.2: The "product_show" route alias is deprecated. You should stop using it, as it will be removed in the future. --> - + + + - The "%alias_id%" route alias is deprecated. Do not use it anymore. + The "%alias_id%" route alias is deprecated. Please use "product_details" instead. .. code-block:: php - $routes->alias('new_route_name', 'original_route_name') + $routes->add('product_details', '/product/{id}') + ->controller('App\Controller\ProductController::show'); + + $routes->alias('product_show', 'product_details') // this outputs the following generic deprecation message: - // Since acme/package 1.2: The "new_route_name" route alias is deprecated. You should stop using it, as it will be removed in the future. + // Since acme/package 1.2: The "product_show" route alias is deprecated. You should stop using it, as it will be removed in the future. ->deprecate('acme/package', '1.2', '') - // you can also define a custom deprecation message (%alias_id% placeholder is available) + // or + + // you can define a custom deprecation message (%alias_id% placeholder is available) ->deprecate( 'acme/package', '1.2', - 'The "%alias_id%" route alias is deprecated. Do not use it anymore.' + 'The "%alias_id%" route alias is deprecated. Please use "product_details" instead.' ) ; -In this example, every time the ``new_route_name`` alias is used, a deprecation -warning is triggered, advising you to stop using that alias. +In this example, every time the ``product_show`` alias is used, a deprecation +warning is triggered, advising you to stop using this route and prefer using ``product_details``. The message is actually a message template, which replaces occurrences of the ``%alias_id%`` placeholder by the route alias name. You **must** have From def8d6b58f1786121227b053cd884257d742a628 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Tue, 20 May 2025 16:11:40 +0200 Subject: [PATCH 504/642] Add runtime schedule modification feature to docs Document the new ability introduced in Symfony 6.4 to modify schedules dynamically at runtime. This includes recalculating the internal trigger heap for better control over recurring tasks. --- scheduler.rst | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/scheduler.rst b/scheduler.rst index 3f2f8d7d2ac..4c5955a9470 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -890,6 +890,46 @@ code:: use the ``messenger:consume`` command as explained in the previous section. +Modifying the Schedule at Runtime +--------------------------------- + +.. versionadded:: 6.4 + + Modifying the schedule at runtime and recalculating the heap was introduced in Symfony 6.4. + +When a recurring message is added to or removed from the schedule, +the scheduler automatically restarts and recalculates the internal trigger heap. +This allows dynamic control over scheduled tasks during runtime. +code:: + + // src/Scheduler/DynamicScheduleProvider.php + namespace App\Scheduler; + + #[AsSchedule('uptoyou')] + class DynamicScheduleProvider implements ScheduleProviderInterface + { + private ?Schedule $schedule = null; + + public function getSchedule(): Schedule + { + return $this->schedule ??= (new Schedule()) + ->with( + // ... + ) + ; + } + + public function clearAndAddMessages(): void + { + // Clear the current schedule (if any) and add new recurring messages + $this->schedule?->clear(); + $this->schedule?->add( + RecurringMessage::cron('@hourly', new DoActionMessage()), + RecurringMessage::cron('@daily', new DoAnotherActionMessage()), + ); + } + } + Debugging the Schedule ---------------------- From 0d4b75575c53dd9a4bb846051462ece4d95c3c3e Mon Sep 17 00:00:00 2001 From: Jacob Dreesen Date: Tue, 20 May 2025 22:56:29 +0200 Subject: [PATCH 505/642] Try fixing a list in object_mapper.rst --- object_mapper.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/object_mapper.rst b/object_mapper.rst index fe7db2365cf..0eed176c67d 100644 --- a/object_mapper.rst +++ b/object_mapper.rst @@ -149,7 +149,7 @@ You can apply the ``#[Map]`` attribute to properties to customize their mapping * ``target``: Specifies the name of the property in the target object; * ``source``: Specifies the name of the property in the source object (useful - when mapping is defined on the target, see below); + when mapping is defined on the target, see below); * ``if``: Defines a condition for mapping the property; * ``transform``: Applies a transformation to the value before mapping. From 38c015ca7f845deda55076f6738f2fe6c1804091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9rage=20K=C3=A9vin?= Date: Tue, 20 May 2025 11:15:12 +0200 Subject: [PATCH 506/642] Clarifying LocaleSwitcher's behavior --- translation.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/translation.rst b/translation.rst index 5565c65bdba..8505b916587 100644 --- a/translation.rst +++ b/translation.rst @@ -1110,6 +1110,13 @@ just to run some code. Imagine a console command that renders Twig templates of emails in different languages. You need to change the locale only to render those templates. +.. note:: + + The LocaleSwitcher will apply the locale at a request level + this means that it will be available only for that request. A + redirect, for example, will cancel the LocaleSwitcher's effect. + For a permanent locale switch between requests see https://symfony.com/doc/current/session.html#making-the-locale-sticky-during-a-user-s-session. + The ``LocaleSwitcher`` class allows you to change at once the locale of: From aa698f8069b4bfc73a2bc3b866e368bcd2304e48 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 21 May 2025 10:12:34 +0200 Subject: [PATCH 507/642] Rewords --- translation.rst | 55 ++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/translation.rst b/translation.rst index 8505b916587..8d66c21f39b 100644 --- a/translation.rst +++ b/translation.rst @@ -1105,25 +1105,11 @@ Switch Locale Programmatically The ``LocaleSwitcher`` was introduced in Symfony 6.1. -Sometimes you need to change the locale of the application dynamically -just to run some code. Imagine a console command that renders Twig templates -of emails in different languages. You need to change the locale only to -render those templates. +Sometimes you need to change the application's locale dynamically while running +some code. For example, a console command that renders email templates in +different languages. In such cases, you only need to switch the locale temporarily. -.. note:: - - The LocaleSwitcher will apply the locale at a request level - this means that it will be available only for that request. A - redirect, for example, will cancel the LocaleSwitcher's effect. - For a permanent locale switch between requests see https://symfony.com/doc/current/session.html#making-the-locale-sticky-during-a-user-s-session. - -The ``LocaleSwitcher`` class allows you to change at once the locale -of: - -* All the services that are tagged with ``kernel.locale_aware``; -* ``\Locale::setDefault()``; -* If the ``RequestContext`` service is available, the ``_locale`` - parameter (so urls are generated with the new locale):: +The ``LocaleSwitcher`` class allows you to do that:: use Symfony\Component\Translation\LocaleSwitcher; @@ -1136,28 +1122,23 @@ of: public function someMethod(): void { - // you can get the current application locale like this: $currentLocale = $this->localeSwitcher->getLocale(); - // you can set the locale for the entire application like this: - // (from now on, the application will use 'fr' (French) as the - // locale; including the default locale used to translate Twig templates) + // set the application locale programmatically to 'fr' (French): + // this affects translation, URL generation, etc. $this->localeSwitcher->setLocale('fr'); - // reset the current locale of your application to the configured default locale - // in config/packages/translation.yaml, by option 'default_locale' + // reset the locale to the default one configured via the + // 'default_locale' option in config/packages/translation.yaml $this->localeSwitcher->reset(); - // you can also run some code with a certain locale, without + // run some code with a specific locale, temporarily, without // changing the locale for the rest of the application $this->localeSwitcher->runWithLocale('es', function() { - - // e.g. render here some Twig templates using 'es' (Spanish) locale - + // e.g. render templates, send emails, etc. using the 'es' (Spanish) locale }); - // you can optionally declare an argument in your callback to receive the - // injected locale + // optionally, receive the current locale as an argument: $this->localeSwitcher->runWithLocale('es', function(string $locale) { // here, the $locale argument will be set to 'es' @@ -1175,6 +1156,20 @@ of: :method:`Symfony\\Component\\Translation\\LocaleSwitcher::runWithLocale` method was introduced in Symfony 6.4. +The ``LocaleSwitcher`` class changes the locale of: + +* All services tagged with ``kernel.locale_aware``; +* The default locale set via ``\Locale::setDefault()``; +* The ``_locale`` parameter of the ``RequestContext`` service (if available), + so generated URLs reflect the new locale. + +.. note:: + + The LocaleSwitcher applies the new locale only for the current request, + and its effect is lost on subsequent requests, such as after a redirect. + + See :ref:`how to make the locale persist across requests `. + When using :ref:`autowiring `, type-hint any controller or service argument with the :class:`Symfony\\Component\\Translation\\LocaleSwitcher` class to inject the locale switcher service. Otherwise, configure your services From 07ef56369b8b519999fa51abf16feb8156804785 Mon Sep 17 00:00:00 2001 From: Antoine M Date: Wed, 21 May 2025 10:28:05 +0200 Subject: [PATCH 508/642] fix: no bundle involved --- doctrine.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doctrine.rst b/doctrine.rst index 103ba869611..6abf5fa7928 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -649,7 +649,7 @@ automatically! You can simplify the controller to:: } } -That's it! The bundle uses the ``{id}`` from the route to query for the ``Product`` +That's it! The attribute 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. .. tip:: From e12256ce2df50fdf97ca28eaa866c28a2255ba97 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 22 May 2025 10:33:40 +0200 Subject: [PATCH 509/642] Rewords --- reference/configuration/framework.rst | 28 +++---- security/csrf.rst | 108 +++++++++++++------------- 2 files changed, 71 insertions(+), 65 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 23baa21642e..464b61dd69b 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -857,36 +857,38 @@ stateless_token_ids **type**: ``array`` **default**: ``[]`` -The list of CSRF token ids that will use stateless CSRF protection. +The list of CSRF token ids that will use :ref:`stateless CSRF protection `. .. versionadded:: 7.2 - This option was added in Symfony 7.2 to aid in configuring stateless CSRF protection. + The ``stateless_token_ids`` option was introduced in Symfony 7.2. check_header ............ **type**: ``integer`` or ``bool`` **default**: ``false`` -Whether to check the CSRF token in a header in addition to a cookie when using stateless protection. -Can be set to ``2`` (the value of the ``CHECK_ONLY_HEADER`` constant on the -:class:`Symfony\\Component\\Security\\Csrf\\SameOriginCsrfTokenManager` class) to check only the header -and not the cookie. +Whether to check the CSRF token in an HTTP header in addition to the cookie when +using :ref:`stateless CSRF protection `. You can also set +this to ``2`` (the value of the ``CHECK_ONLY_HEADER`` constant on the +:class:`Symfony\\Component\\Security\\Csrf\\SameOriginCsrfTokenManager` class) +to check only the header and ignore the cookie. .. versionadded:: 7.2 - This option was added in Symfony 7.2 to aid in configuring stateless CSRF protection. + The ``check_header`` option was introduced in Symfony 7.2. cookie_name ........... **type**: ``string`` **default**: ``csrf-token`` -The name of the cookie (and header) to use for the double-submit when using stateless protection. +The name of the cookie (and HTTP header) to use for the double-submit when using +:ref:`stateless CSRF protection `. .. versionadded:: 7.2 - This option was added in Symfony 7.2 to aid in configuring stateless CSRF protection. + The ``cookie_name`` option was introduced in Symfony 7.2. .. _config-framework-default_locale: @@ -1213,16 +1215,16 @@ field_attr **type**: ``array`` **default**: ``['data-controller' => 'csrf-protection']`` -This is the HTML attributes that should be added to the CSRF token field of your forms. +HTML attributes to add to the CSRF token field of your forms. token_id '''''''' **type**: ``string`` **default**: ``null`` -This is the CSRF token id that should be used for validating the CSRF tokens of your forms. -Note that this setting applies only to autoconfigured form types, which usually means only -to your own form types and not to form types registered by third-party bundles. +The CSRF token ID used to validate the CSRF tokens of your forms. This setting +applies only to form types that use :ref:`service autoconfiguration `, +which typically means your own form types, not those registered by third-party bundles. fragments ~~~~~~~~~ diff --git a/security/csrf.rst b/security/csrf.rst index 2e7369d170f..b303af9511b 100644 --- a/security/csrf.rst +++ b/security/csrf.rst @@ -34,9 +34,9 @@ unique tokens added to forms as hidden fields. The legit server validates them t ensure that the request originated from the expected source and not some other malicious website. -Anti-CSRF tokens can be managed either in a stateful way: they're put in the -session and are unique for each user and for each kind of action, or in a -stateless way: they're generated on the client-side. +Anti-CSRF tokens can be managed in two ways: using a **stateful** approach, +where tokens are stored in the session and are unique per user and action; or a +**stateless** approach, where tokens are generated on the client side. Installation ------------ @@ -106,7 +106,7 @@ protected forms, among them: field value with it. The most effective way to cache pages that need CSRF protected forms is to use -stateless CSRF tokens, see below. +:ref:`stateless CSRF tokens `, as explained below. .. _csrf-protection-forms: @@ -310,6 +310,8 @@ targeted parts of the plaintext. To mitigate these attacks, and prevent an attacker from guessing the CSRF tokens, a random mask is prepended to the token and used to scramble it. +.. _csrf-stateless-tokens: + Stateless CSRF Tokens --------------------- @@ -363,28 +365,31 @@ option: ; }; -Stateless CSRF tokens use a CSRF protection that doesn't need the session. This -means that you can cache the entire page and still have CSRF protection. +Stateless CSRF tokens provide protection without relying on the session. This +allows you to fully cache pages while still protecting against CSRF attacks. -When a stateless CSRF token is checked for validity, Symfony verifies the -``Origin`` and the ``Referer`` headers of the incoming HTTP request. +When validating a stateless CSRF token, Symfony checks the ``Origin`` and +``Referer`` headers of the incoming HTTP request. If either header matches the +application's target origin (i.e. its domain), the token is considered valid. -If either of these headers match the target origin of the application (its domain -name), the CSRF token is considered valid. This relies on the app being able to -know its own target origin. Don't miss configuring your reverse proxy if you're -behind one. See :doc:`/deployment/proxies`. +This mechanism relies on the application being able to determine its own origin. +If you're behind a reverse proxy, make sure it's properly configured. See +:doc:`/deployment/proxies`. Using a Default Token ID ~~~~~~~~~~~~~~~~~~~~~~~~ -While stateful CSRF tokens are better seggregated per form or action, stateless -ones don't need many token identifiers. In the previous example, ``authenticate`` -and ``logout`` are listed because they're the default identifiers used by the -Symfony Security component. The ``submit`` identifier is then listed so that -form types defined by the application can use it by default. The following -configuration - which applies only to form types declared using autofiguration -(the default way to declare *your* services) - will make your form types use the -``submit`` token identifier by default: +Stateful CSRF tokens are typically scoped per form or action, while stateless +tokens don't require many identifiers. + +In the example above, the ``authenticate`` and ``logout`` identifiers are listed +because they are used by default in the Symfony Security component. The ``submit`` +identifier is included so that form types defined by the application can also use +CSRF protection by default. + +The following configuration applies only to form types registered via +:ref:`autoconfiguration ` (which is the default for your +own services), and it sets ``submit`` as their default token identifier: .. configuration-block:: @@ -433,41 +438,40 @@ option will use the stateless CSRF protection. Generating CSRF Token Using Javascript ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In addition to the ``Origin`` and ``Referer`` headers, stateless CSRF protection -also checks a cookie and a header (named ``csrf-token`` by default, see the -:ref:`CSRF configuration reference `). - -These extra checks are part of defense-in-depth strategies provided by the -stateless CSRF protection. They are optional and they require -`some JavaScript`_ to be activated. This JavaScript is responsible for generating -a crypto-safe random token when a form is submitted, then putting the token in -the hidden CSRF field of the form and submitting it also as a cookie and header. -On the server-side, the CSRF token is validated by checking the cookie and header -values. This "double-submit" protection relies on the same-origin policy -implemented by browsers and is strengthened by regenerating the token at every -form submission - which prevents cookie fixation issues - and by using -``samesite=strict`` and ``__Host-`` cookies, which make them domain-bound and -HTTPS-only. - -Note that the default snippet of JavaScript provided by Symfony requires that -the hidden CSRF form field is either named ``_csrf_token``, or that it has the -``data-controller="csrf-protection"`` attribute. You can of course take -inspiration from this snippet to write your own, provided you follow the same -protocol. - -As a last measure, a behavioral check is added on the server-side to ensure that -the validation method cannot be downgraded: if and only if a session is already -available, successful "double-submit" is remembered and is then required for -subsequent requests. This prevents attackers from exploiting potentially reduced -validation checks once cookie and/or header validation has been confirmed as -effective (they're optional by default as explained above). +In addition to the ``Origin`` and ``Referer`` HTTP headers, stateless CSRF protection +can also validate tokens using a cookie and a header (named ``csrf-token`` by +default; see the :ref:`CSRF configuration reference `). + +These additional checks are part of the **defense-in-depth** strategy provided by +stateless CSRF protection. They are optional and require `some JavaScript`_ to +be enabled. This JavaScript generates a cryptographically secure random token +when a form is submitted. It then inserts the token into the form's hidden CSRF +field and sends it in both a cookie and a request header. + +On the server side, CSRF token validation compares the values in the cookie and +the header. This "double-submit" protection relies on the browser's same-origin +policy and is further hardened by: + +* generating a new token for each submission (to prevent cookie fixation); +* using ``samesite=strict`` and ``__Host-`` cookie attributes (to enforce HTTPS + and limit the cookie to the current domain). + +By default, the Symfony JavaScript snippet expects the hidden CSRF field to be +named ``_csrf_token`` or to include the ``data-controller="csrf-protection"`` +attribute. You can adapt this logic to your needs as long as the same protocol +is followed. + +To prevent validation from being downgraded, an extra behavioral check is performed: +if (and only if) a session already exists, successful "double-submit" is remembered +and becomes required for future requests. This ensures that once the optional cookie/header +validation has been proven effective, it remains enforced for that session. .. note:: - Enforcing successful "double-submit" for every requests is not recommended as - as it could lead to a broken user experience. The opportunistic approach - described above is preferred because it allows the application to gracefully - degrade to ``Origin`` / ``Referer`` checks when JavaScript is not available. + Enforcing "double-submit" validation on all requests is not recommended, + as it may lead to a broken user experience. The opportunistic approach + described above is preferred, allowing the application to gracefully + fall back to ``Origin`` / ``Referer`` checks when JavaScript is unavailable. .. _`Cross-site request forgery`: https://en.wikipedia.org/wiki/Cross-site_request_forgery .. _`BREACH`: https://en.wikipedia.org/wiki/BREACH From f97b8d59f092fcbc14ea54daa9b59adfb33093ce Mon Sep 17 00:00:00 2001 From: Santiago San Martin Date: Sun, 18 May 2025 11:52:46 -0300 Subject: [PATCH 510/642] [Security] iscsrftokenvalid-attribute-controller-usage --- security/csrf.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/security/csrf.rst b/security/csrf.rst index b303af9511b..cc1ab42482d 100644 --- a/security/csrf.rst +++ b/security/csrf.rst @@ -281,6 +281,20 @@ Suppose you want a CSRF token per item, so in the template you have something li +In addition :class:`Symfony\\Component\\Security\\Http\\Attribute\\IsCsrfTokenValid` +attribute can be applied to a controller class. +This will cause the CSRF token validation to be executed for all routes defined within the controller:: + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Security\Http\Attribute\IsCsrfTokenValid; + // ... + + #[IsCsrfTokenValid('controller')] + final class FooController extends AbstractController + { + // ... + } + The :class:`Symfony\\Component\\Security\\Http\\Attribute\\IsCsrfTokenValid` attribute also accepts an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` object evaluated to the id:: From 83b30927029e9f60cc4c4a77edba2bbd6bda5f2f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 22 May 2025 17:45:51 +0200 Subject: [PATCH 511/642] Minor tweaks --- security/csrf.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/security/csrf.rst b/security/csrf.rst index cc1ab42482d..b72c7cc2526 100644 --- a/security/csrf.rst +++ b/security/csrf.rst @@ -281,16 +281,16 @@ Suppose you want a CSRF token per item, so in the template you have something li -In addition :class:`Symfony\\Component\\Security\\Http\\Attribute\\IsCsrfTokenValid` -attribute can be applied to a controller class. -This will cause the CSRF token validation to be executed for all routes defined within the controller:: +This attribute can also be applied to a controller class. When used this way, +the CSRF token validation will be applied to **all actions** defined in that +controller:: use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Security\Http\Attribute\IsCsrfTokenValid; // ... - #[IsCsrfTokenValid('controller')] - final class FooController extends AbstractController + #[IsCsrfTokenValid('the token ID')] + final class SomeController extends AbstractController { // ... } From 53e3b0dece9748ae7f45e1b59ef3b7fd37bc9975 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 23 May 2025 08:43:43 +0200 Subject: [PATCH 512/642] Mention that backward compatibility promise doesn't cover translations --- contributing/code/bc.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/contributing/code/bc.rst b/contributing/code/bc.rst index 497c70fb01d..ad394d2720c 100644 --- a/contributing/code/bc.rst +++ b/contributing/code/bc.rst @@ -176,6 +176,13 @@ covered by our backward compatibility promise: | Use a public, protected or private method | Yes | +-----------------------------------------------+-----------------------------+ +Using our Translations +~~~~~~~~~~~~~~~~~~~~~~ + +All translations provided by Symfony for security and validation errors are +intended for internal use only. They may be changed or removed at any time. +Symfony's Backward Compatibility Promise does not apply to internal translations. + Working on Symfony Code ----------------------- From 2c33c3623d631fc631bf75d3a5dcfbfb3d2c259f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 23 May 2025 09:12:33 +0200 Subject: [PATCH 513/642] [FrameworkBundle] Enable controller service with #[Route] attribute --- controller/service.rst | 84 +++++++++++++++++++++++++++++------------- routing.rst | 2 + 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/controller/service.rst b/controller/service.rst index 88af093ff29..cf83e066a19 100644 --- a/controller/service.rst +++ b/controller/service.rst @@ -7,11 +7,65 @@ and your controllers extend the `AbstractController`_ class, they *are* automati registered as services. This means you can use dependency injection like any other normal service. -If your controllers don't extend the `AbstractController`_ class, you must -explicitly mark your controller services as ``public``. Alternatively, you can -apply the ``controller.service_arguments`` tag to your controller services. This -will make the tagged services ``public`` and will allow you to inject services -in method parameters: +If you prefer to not extend the ``AbstractController`` class, you can register +your controllers as services in several ways: + +#. Using the ``#[Route]`` attribute; +#. Using the ``#[AsController]`` attribute; +#. Using the ``controller.service_arguments`` service tag. + +Using the ``#[Route]`` Attribute +-------------------------------- + +When using :ref:`the #[Route] attribute ` to define +routes on any PHP class, Symfony treats that class as a controller. It registers +it as a public, non-lazy service and enables service argument injection in all +its methods. + +This is the simplest and recommended way to register controllers as services +when not extending the base controller class. + +.. versionadded:: 7.3 + + The feature to register controllers as services when using the ``#[Route]`` + attribute was introduced in Symfony 7.3. + +Using the ``#[AsController]`` Attribute +--------------------------------------- + +If you prefer, you can use the ``#[AsController]`` PHP attribute to automatically +apply the ``controller.service_arguments`` tag to your controller services:: + + // src/Controller/HelloController.php + namespace App\Controller; + + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\HttpKernel\Attribute\AsController; + use Symfony\Component\Routing\Attribute\Route; + + #[AsController] + class HelloController + { + #[Route('/hello', name: 'hello', methods: ['GET'])] + public function index(): Response + { + // ... + } + } + +.. tip:: + + When using the ``#[Route]`` attribute, Symfony already registers the controller + class as a service, so using the ``#[AsController]`` attribute is redundant. + +Using the ``controller.service_arguments`` Service Tag +------------------------------------------------------ + +If your controllers don't extend the `AbstractController`_ class and you don't +use the ``#[AsController]`` or ``#[Route]`` attributes, you must register the +controllers as public services manually and apply the ``controller.service_arguments`` +:doc:`service tag ` to enable service injection in +controller actions: .. configuration-block:: @@ -58,26 +112,6 @@ in method parameters: calls: - [setContainer, ['@abstract_controller.locator']] -If you prefer, you can use the ``#[AsController]`` PHP attribute to automatically -apply the ``controller.service_arguments`` tag to your controller services:: - - // src/Controller/HelloController.php - namespace App\Controller; - - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\HttpKernel\Attribute\AsController; - use Symfony\Component\Routing\Attribute\Route; - - #[AsController] - class HelloController - { - #[Route('/hello', name: 'hello', methods: ['GET'])] - public function index(): Response - { - // ... - } - } - Registering your controller as a service is the first step, but you also need to update your routing config to reference the service properly, so that Symfony knows to use it. diff --git a/routing.rst b/routing.rst index e634a410c37..b8b2437ee3e 100644 --- a/routing.rst +++ b/routing.rst @@ -18,6 +18,8 @@ your favorite. :ref:`Symfony recommends attributes ` because it's convenient to put the route and controller in the same place. +.. _routing-route-attributes: + Creating Routes as Attributes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 310783b4c0a9f1b329c010b1c20dcdfed5643400 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 23 May 2025 09:55:29 +0200 Subject: [PATCH 514/642] [Translation] Don't mention the abandoned Doctrine Translatable behavior --- translation.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/translation.rst b/translation.rst index b9fbdd7e0f6..f41d0cb6489 100644 --- a/translation.rst +++ b/translation.rst @@ -599,8 +599,7 @@ Translations of Doctrine Entities Unlike the contents of templates, it's not practical to translate the contents stored in Doctrine Entities using translation catalogs. Instead, use the -Doctrine `Translatable Extension`_ or the `Translatable Behavior`_. For more -information, read the documentation of those libraries. +Doctrine `Translatable Extension`_. Custom Translation Resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1599,7 +1598,6 @@ Learn more .. _`ISO 3166-1 alpha-2`: https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes .. _`ISO 639-1`: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes .. _`Translatable Extension`: https://github.com/doctrine-extensions/DoctrineExtensions/blob/main/doc/translatable.md -.. _`Translatable Behavior`: https://github.com/KnpLabs/DoctrineBehaviors .. _`Custom Language Name setting`: https://docs.lokalise.com/en/articles/1400492-uploading-files#custom-language-codes .. _`ICU resource bundle`: https://github.com/unicode-org/icu-docs/blob/main/design/bnf_rb.txt .. _`Portable object format`: https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html From 203c0f8e21071ad1396ef12686a344e39f47a1bc Mon Sep 17 00:00:00 2001 From: MrYamous Date: Tue, 25 Feb 2025 20:45:57 +0100 Subject: [PATCH 515/642] [Security] Add ability for voters to explain their vote --- security.rst | 8 +++++++- security/voters.rst | 30 +++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/security.rst b/security.rst index ca910765be0..5a6fc15ae2e 100644 --- a/security.rst +++ b/security.rst @@ -2704,13 +2704,14 @@ anonymous users access by checking if there is no user set on the token:: // ... use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authentication\User\UserInterface; + use Symfony\Component\Security\Core\Authorization\Voter\Vote; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class PostVoter extends Voter { // ... - protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token): bool + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token, ?Vote $vote = null): bool { // ... @@ -2722,6 +2723,11 @@ anonymous users access by checking if there is no user set on the token:: } } +.. versionadded:: 7.3 + + The `$vote` parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method + was introduced in Symfony 7.3. + Setting Individual User Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/security/voters.rst b/security/voters.rst index e7452fadf99..ba4c96fe6fd 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -40,14 +40,20 @@ or extend :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\Vote which makes creating a voter even easier:: use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\Authorization\Voter\Vote; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; abstract class Voter implements VoterInterface { abstract protected function supports(string $attribute, mixed $subject): bool; - abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool; + abstract protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool; } +.. versionadded:: 7.3 + + The `$vote` parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method + was introduced in Symfony 7.3. + .. _how-to-use-the-voter-in-a-controller: .. tip:: @@ -140,6 +146,7 @@ would look like this:: use App\Entity\Post; use App\Entity\User; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\Authorization\Voter\Vote; use Symfony\Component\Security\Core\Authorization\Voter\Voter; class PostVoter extends Voter @@ -163,12 +170,14 @@ would look like this:: return true; } - protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool + protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool { $user = $token->getUser(); + $vote ??= new Vote(); if (!$user instanceof User) { // the user must be logged in; if not, deny access + $vote->reasons[] = 'The user is not logged in.'; return false; } @@ -197,7 +206,13 @@ would look like this:: private function canEdit(Post $post, User $user): bool { // this assumes that the Post object has a `getOwner()` method - return $user === $post->getOwner(); + if ($user === $post->getOwner()) { + return true; + } + + $vote->reasons[] = 'You are not the owner of the Post.'; + + return false; } } @@ -215,11 +230,12 @@ To recap, here's what's expected from the two abstract methods: return ``true`` if the attribute is ``view`` or ``edit`` and if the object is a ``Post`` instance. -``voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token)`` +``voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null)`` If you return ``true`` from ``supports()``, then this method is called. Your job is to return ``true`` to allow access and ``false`` to deny access. - The ``$token`` can be used to find the current user object (if any). In this - example, all of the complex business logic is included to determine access. + The ``$token`` can be used to find the current user object (if any). The ``$vote`` + argument can be used to add a reason to the vote. In this example, all of the + complex business logic is included to determine access. .. _declaring-the-voter-as-a-service: @@ -256,7 +272,7 @@ with ``ROLE_SUPER_ADMIN``:: ) { } - protected function voteOnAttribute($attribute, mixed $subject, TokenInterface $token): bool + protected function voteOnAttribute($attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool { // ... From ef9c945fe180c9a7a459b37e6a13fd0f63c9678d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 23 May 2025 10:24:48 +0200 Subject: [PATCH 516/642] Minor tweaks --- security.rst | 4 ++-- security/voters.rst | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/security.rst b/security.rst index 8b1c8a96e69..9d2df6165d0 100644 --- a/security.rst +++ b/security.rst @@ -2715,8 +2715,8 @@ anonymous users access by checking if there is no user set on the token:: .. versionadded:: 7.3 - The `$vote` parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method - was introduced in Symfony 7.3. + The ``$vote`` argument of the ``voteOnAttribute()`` method was introduced + in Symfony 7.3. Setting Individual User Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/security/voters.rst b/security/voters.rst index ba4c96fe6fd..2b4a5af54e2 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -51,8 +51,8 @@ which makes creating a voter even easier:: .. versionadded:: 7.3 - The `$vote` parameter in the :method:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\VoterInterface::voteOnAttribute` method - was introduced in Symfony 7.3. + The ``$vote`` argument of the ``voteOnAttribute()`` method was introduced + in Symfony 7.3. .. _how-to-use-the-voter-in-a-controller: @@ -173,11 +173,10 @@ would look like this:: protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null): bool { $user = $token->getUser(); - $vote ??= new Vote(); if (!$user instanceof User) { // the user must be logged in; if not, deny access - $vote->reasons[] = 'The user is not logged in.'; + $vote?->addReason('The user is not logged in.'); return false; } @@ -205,12 +204,15 @@ would look like this:: private function canEdit(Post $post, User $user): bool { - // this assumes that the Post object has a `getOwner()` method - if ($user === $post->getOwner()) { + // this assumes that the Post object has a `getAuthor()` method + if ($user === $post->getAuthor()) { return true; } - $vote->reasons[] = 'You are not the owner of the Post.'; + $vote?->addReason(sprintf( + 'The logged in user (username: %s) is not the author of this post (id: %d).', + $user->getUsername(), $post->getId() + )); return false; } @@ -233,9 +235,9 @@ To recap, here's what's expected from the two abstract methods: ``voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token, ?Vote $vote = null)`` If you return ``true`` from ``supports()``, then this method is called. Your job is to return ``true`` to allow access and ``false`` to deny access. - The ``$token`` can be used to find the current user object (if any). The ``$vote`` - argument can be used to add a reason to the vote. In this example, all of the - complex business logic is included to determine access. + The ``$token`` can be used to find the current user object (if any). + The ``$vote`` argument can be used to provide an explanation for the vote. + This explanation is included in log messages and on exception pages. .. _declaring-the-voter-as-a-service: From a2c83bfb2fc02162dcc2e13a5d67d6f187c861a8 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 23 May 2025 10:50:10 +0200 Subject: [PATCH 517/642] Minor tweaks --- workflow.rst | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/workflow.rst b/workflow.rst index 544f775cce0..482fe8ed273 100644 --- a/workflow.rst +++ b/workflow.rst @@ -1306,11 +1306,11 @@ In Twig templates, metadata is available via the ``workflow_metadata()`` functio

-Adding Custom Definition Validators -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Validating Workflow Definitions +------------------------------- -Sometimes, you may want to add custom logics to validate your workflow definition. -To do this, you need to implement the +Symfony allows you to validate workflow definitions using your own custom logic. +To do so, create a class that implements the :class:`Symfony\\Component\\Workflow\\Validator\\DefinitionValidatorInterface`:: namespace App\Workflow\Validator; @@ -1326,11 +1326,12 @@ To do this, you need to implement the if (!$definition->getMetadataStore()->getMetadata('title')) { throw new InvalidDefinitionException(sprintf('The workflow metadata title is missing in Workflow "%s".', $name)); } + + // ... } } -Once your definition validator is implemented, you can configure your workflow to use -it: +After implementing your validator, configure your workflow to use it: .. configuration-block:: @@ -1340,7 +1341,7 @@ it: framework: workflows: blog_publishing: - # ... previous configuration + # ... definition_validators: - App\Workflow\Validator\BlogPublishingValidator @@ -1357,7 +1358,7 @@ it: > - + App\Workflow\Validator\BlogPublishingValidator @@ -1370,7 +1371,7 @@ it: return static function (FrameworkConfig $framework): void { $blogPublishing = $framework->workflows()->workflows('blog_publishing'); - // ... previous configuration + // ... $blogPublishing->definitionValidators([ App\Workflow\Validator\BlogPublishingValidator::class @@ -1379,11 +1380,12 @@ it: // ... }; -The ``BlogPublishingValidator`` definition validator will be executed during the container compilation. +The ``BlogPublishingValidator`` will be executed during container compilation +to validate the workflow definition. .. versionadded:: 7.3 - Support for defining custom workflow definition validators was introduced in Symfony 7.3. + Support for workflow definition validators was introduced in Symfony 7.3. Learn more ---------- From 6a908963b4574972ce644893138b76e5a52609c2 Mon Sep 17 00:00:00 2001 From: Romain Card <47689092+Synxgz@users.noreply.github.com> Date: Fri, 23 May 2025 10:57:44 +0200 Subject: [PATCH 518/642] [Security] Fix type in `upgradePassword` --- security/passwords.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/security/passwords.rst b/security/passwords.rst index fe20187b3a0..4bf481fa827 100644 --- a/security/passwords.rst +++ b/security/passwords.rst @@ -500,13 +500,14 @@ the user provider:: namespace App\Security; // ... + use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; class UserProvider implements UserProviderInterface, PasswordUpgraderInterface { // ... - public function upgradePassword(UserInterface $user, string $newHashedPassword): void + public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void { // set the new hashed password on the User object $user->setPassword($newHashedPassword); From faa30fc32ec1a0360baa201db0afd7edcf4861bb Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Sun, 25 May 2025 11:52:44 +0200 Subject: [PATCH 519/642] [Security] Stateless CSRF is enabled by default in 7.2 Page: https://symfony.com/doc/current/security/csrf.html#stateless-csrf-tokens Info is taken from https://github.com/symfony/recipes/blob/main/symfony/form/7.2/config/packages/csrf.yaml --- security/csrf.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/csrf.rst b/security/csrf.rst index b72c7cc2526..07e0671f07b 100644 --- a/security/csrf.rst +++ b/security/csrf.rst @@ -331,9 +331,9 @@ Stateless CSRF Tokens .. versionadded:: 7.2 - Stateless anti-CSRF protection was introduced in Symfony 7.2. + Stateless anti-CSRF protection was introduced in Symfony 7.2, and set as default. -By default CSRF tokens are stateful, which means they're stored in the session. +Traditionally CSRF tokens are stateful, which means they're stored in the session. But some token ids can be declared as stateless using the ``stateless_token_ids`` option: From a73b542aa43ab744336cfbf8e341467f7746d13e Mon Sep 17 00:00:00 2001 From: Hamza Makraz <19323431+makraz@users.noreply.github.com> Date: Sat, 24 May 2025 22:25:52 +0100 Subject: [PATCH 520/642] Update AsTaggedItem parameters name in the file value_resolver.rst --- controller/value_resolver.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller/value_resolver.rst b/controller/value_resolver.rst index 48d0ba17bdb..f0f0db9aff1 100644 --- a/controller/value_resolver.rst +++ b/controller/value_resolver.rst @@ -382,7 +382,7 @@ but you can set it yourself to change its ``priority`` or ``name`` attributes. use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem; use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; - #[AsTaggedItem(name: 'booking_id', priority: 150)] + #[AsTaggedItem(index: 'booking_id', priority: 150)] class BookingIdValueResolver implements ValueResolverInterface { // ... From d7eae45b90ca74ab0fed17efc2708a543c11c046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Andr=C3=A9?= Date: Sat, 24 May 2025 15:06:55 +0200 Subject: [PATCH 521/642] [Form] Remove link to abandoned collection package --- form/form_collections.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/form/form_collections.rst b/form/form_collections.rst index 2a0ba99657f..3c8a2050690 100644 --- a/form/form_collections.rst +++ b/form/form_collections.rst @@ -699,13 +699,12 @@ the relationship between the removed ``Tag`` and ``Task`` object. The Symfony community has created some JavaScript packages that provide the functionality needed to add, edit and delete elements of the collection. - Check out the `@a2lix/symfony-collection`_ package for modern browsers and - the `symfony-collection`_ package based on jQuery for the rest of browsers. + Check out the `@a2lix/symfony-collection`_ or search on GitHub for other + recent packages. .. _`Owning Side and Inverse Side`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/unitofwork-associations.html .. _`JSFiddle`: https://jsfiddle.net/ey8ozh6n/ .. _`@a2lix/symfony-collection`: https://github.com/a2lix/symfony-collection -.. _`symfony-collection`: https://github.com/ninsuo/symfony-collection .. _`ArrayCollection`: https://www.doctrine-project.org/projects/doctrine-collections/en/1.6/index.html .. _`Symfony UX Demo of Form Collections`: https://ux.symfony.com/live-component/demos/form-collection-type .. _`Stimulus`: https://symfony.com/doc/current/frontend/encore/simple-example.html#stimulus-symfony-ux From f8cdbc89d9aadb4b509a314d28d093fb65f02557 Mon Sep 17 00:00:00 2001 From: miqrogroove <1371835+miqrogroove@users.noreply.github.com> Date: Sun, 25 May 2025 11:15:12 -0400 Subject: [PATCH 522/642] Fixed typo in event_dispatcher.rst --- event_dispatcher.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event_dispatcher.rst b/event_dispatcher.rst index d9b913ed49f..13ef1624370 100644 --- a/event_dispatcher.rst +++ b/event_dispatcher.rst @@ -149,7 +149,7 @@ Defining Event Listeners with PHP Attributes An alternative way to define an event listener is to use the :class:`Symfony\\Component\\EventDispatcher\\Attribute\\AsEventListener` -PHP attribute. This allows to configure the listener inside its class, without +PHP attribute. This allows you to configure the listener inside its class, without having to add any configuration in external files:: namespace App\EventListener; From ff3a594325e29c00b95519ef2f422c3749e2531f Mon Sep 17 00:00:00 2001 From: Ilya Bakhlin Lebedev Date: Sun, 25 May 2025 12:11:13 +0200 Subject: [PATCH 523/642] Updating the web_server_configuration.rst File In the documentation, it's being said that Symfony requires the `symfony/apache-pack`, which adds a `.htaccess` file to the `public/` directory. However, it's nowhere specified to change the `AllowOverride` setting from `None` to `All`. --- setup/web_server_configuration.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup/web_server_configuration.rst b/setup/web_server_configuration.rst index fdedfc81794..27bcc67aab0 100644 --- a/setup/web_server_configuration.rst +++ b/setup/web_server_configuration.rst @@ -195,7 +195,8 @@ directive to pass requests for PHP files to PHP FPM: If you are doing some quick tests with Apache, you can also run ``composer require symfony/apache-pack``. This package creates an ``.htaccess`` file in the ``public/`` directory with the necessary rewrite rules needed to serve - the Symfony application. However, in production, it's recommended to move these + the Symfony application. Don't forget to change the Apache's ``AllowOverride None`` setting to + ``AllowOverride All``. However, in production, it's recommended to move these rules to the main Apache configuration file (as shown above) to improve performance. Caddy From 712707d07a4a322f8e387f9209279c949b50ae1a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 26 May 2025 12:59:37 +0200 Subject: [PATCH 524/642] Minor tweaks --- setup/web_server_configuration.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/setup/web_server_configuration.rst b/setup/web_server_configuration.rst index 27bcc67aab0..43bd79c10dd 100644 --- a/setup/web_server_configuration.rst +++ b/setup/web_server_configuration.rst @@ -192,12 +192,14 @@ directive to pass requests for PHP files to PHP FPM: .. note:: - If you are doing some quick tests with Apache, you can also run - ``composer require symfony/apache-pack``. This package creates an ``.htaccess`` - file in the ``public/`` directory with the necessary rewrite rules needed to serve - the Symfony application. Don't forget to change the Apache's ``AllowOverride None`` setting to - ``AllowOverride All``. However, in production, it's recommended to move these - rules to the main Apache configuration file (as shown above) to improve performance. + If you're running some quick tests with Apache, you can run + ``composer require symfony/apache-pack`` to create an ``.htaccess`` file in + the ``public/`` directory with the rewrite rules needed to serve the Symfony + application. Make sure Apache's ``AllowOverride`` setting is set to ``All`` + for that directory; otherwise, the ``.htaccess`` file will be ignored. + + In production, however, it's recommended to move these rules to the main + Apache configuration file (as shown above) to improve performance. Caddy ----- From ff3f1a1a6ba524581818259645d2ab99d46d7c07 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 26 May 2025 15:29:35 +0200 Subject: [PATCH 525/642] [HttpClient] Update the concurrent requests section --- http_client.rst | 75 +++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 34 deletions(-) diff --git a/http_client.rst b/http_client.rst index f4be8b2460b..e2b0b4f41bf 100644 --- a/http_client.rst +++ b/http_client.rst @@ -1314,65 +1314,72 @@ remaining to do. Concurrent Requests ------------------- -Thanks to responses being lazy, requests are always managed concurrently. -On a fast enough network, the following code makes 379 requests in less than -half a second when cURL is used:: +Symfony's HTTP client makes asynchronous HTTP requests by default. This means +you don't need to configure anything special to send multiple requests in parallel +and process them efficiently. +Here's a practical example that fetches metadata about several Symfony +components from the Packagist API in parallel:: + + $packages = ['console', 'http-kernel', '...', 'routing', 'yaml']; $responses = []; - for ($i = 0; $i < 379; ++$i) { - $uri = "https://http2.akamai.com/demo/tile-$i.png"; - $responses[] = $client->request('GET', $uri); + foreach ($packages as $package) { + $uri = sprintf('https://repo.packagist.org/p2/symfony/%s.json', $package); + // send all requests concurrently (they won't block until response content is read) + $responses[$package] = $client->request('GET', $uri); } - foreach ($responses as $response) { - $content = $response->getContent(); - // ... + $results = []; + // iterate through the responses and read their content + foreach ($responses as $package => $response) { + // process response data somehow ... + $results[$package] = $response->toArray(); } -As you can read in the first "for" loop, requests are issued but are not consumed -yet. That's the trick when concurrency is desired: requests should be sent -first and be read later on. This will allow the client to monitor all pending -requests while your code waits for a specific one, as done in each iteration of -the above "foreach" loop. +As you can see, the requests are sent in the first loop, but their responses +aren't consumed until the second one. This is the key to achieving parallel and +concurrent execution: dispatch all requests first, and read them later. +This allows the client to handle all pending responses efficiently while your +code waits only when necessary. .. note:: - The maximum number of concurrent requests that you can perform depends on - the resources of your machine (e.g. your operating system may limit the - number of simultaneous reads of the file that stores the certificates - file). Make your requests in batches to avoid these issues. + The maximum number of concurrent requests depends on your system's resources + (e.g. the operating system might limit the number of simultaneous connections + or access to certificate files). To avoid hitting these limits, consider + processing requests in batches. Multiplexing Responses ~~~~~~~~~~~~~~~~~~~~~~ -If you look again at the snippet above, responses are read in requests' order. -But maybe the 2nd response came back before the 1st? Fully asynchronous operations -require being able to deal with the responses in whatever order they come back. +In the previous example, responses are read in the same order as the requests +were sent. However, it's possible that, for instance, the second response arrives +before the first. To handle such cases efficiently, you need fully asynchronous +processing, which allows responses to be handled in whatever order they arrive. -In order to do so, the -:method:`Symfony\\Contracts\\HttpClient\\HttpClientInterface::stream` -accepts a list of responses to monitor. As mentioned +To achieve this, the +:method:`Symfony\\Contracts\\HttpClient\\HttpClientInterface::stream` method +can be used to monitor a list of responses. As mentioned :ref:`previously `, this method yields response -chunks as they arrive from the network. By replacing the "foreach" in the -snippet with this one, the code becomes fully async:: +chunks as soon as they arrive over the network. Replacing the standard ``foreach`` +loop with the following version enables true asynchronous behavior:: foreach ($client->stream($responses) as $response => $chunk) { if ($chunk->isFirst()) { - // headers of $response just arrived - // $response->getHeaders() is now a non-blocking call + // the $response headers just arrived + // $response->getHeaders() is now non-blocking } elseif ($chunk->isLast()) { - // the full content of $response just completed - // $response->getContent() is now a non-blocking call + // the full $response body has been received + // $response->getContent() is now non-blocking } else { - // $chunk->getContent() will return a piece - // of the response body that just arrived + // $chunk->getContent() returns a piece of the body that just arrived } } .. tip:: - Use the ``user_data`` option combined with ``$response->getInfo('user_data')`` - to track the identity of the responses in your foreach loops. + Use the ``user_data`` option along with ``$response->getInfo('user_data')`` + to identify each response during streaming. Dealing with Network Timeouts ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 1ea48c70da87d7cebe252c7fb94cb0fce499834f Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Wed, 21 May 2025 18:15:40 -0400 Subject: [PATCH 526/642] [Security] remove `plaintext` password hasher usage --- security/passwords.rst | 90 +++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 50 deletions(-) diff --git a/security/passwords.rst b/security/passwords.rst index 4bf481fa827..9ebfa122ab2 100644 --- a/security/passwords.rst +++ b/security/passwords.rst @@ -124,25 +124,22 @@ Further in this article, you can find a .. code-block:: yaml - # config/packages/test/security.yaml - 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 + # config/packages/security.yaml + when@test: + security: + # ... + + password_hashers: + # Use your user class name here + App\Entity\User: + algorithm: auto + 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 - + - - - - - - - - - - - - + + + + + + + + + .. code-block:: php - // config/packages/test/security.php + // config/packages/security.php use App\Entity\User; + use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Symfony\Config\SecurityConfig; - return static function (SecurityConfig $security): void { + return static function (SecurityConfig $security, ContainerConfigurator $container): void { // ... - // Use your user class name here - $security->passwordHasher(User::class) - ->algorithm('plaintext'); // disable hashing (only do this in tests!) - - // or use the lowest possible values - $security->passwordHasher(User::class) - ->algorithm('auto') // This should be the same value as in config/packages/security.yaml - ->cost(4) // Lowest possible value for bcrypt - ->timeCost(2) // Lowest possible value for argon - ->memoryCost(10) // Lowest possible value for argon - ; + if ('test' === $container->env()) { + // Use your user class name here + $security->passwordHasher(User::class) + ->algorithm('auto') // This should be the same value as in config/packages/security.yaml + ->cost(4) // Lowest possible value for bcrypt + ->timeCost(2) // Lowest possible value for argon + ->memoryCost(10) // Lowest possible value for argon + ; + } }; Hashing the Password From 1851eae15c33913d2b483098b132ffb5ef548c5f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 08:13:25 +0200 Subject: [PATCH 527/642] Remove the wrong XML config sample --- security/passwords.rst | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/security/passwords.rst b/security/passwords.rst index 9ebfa122ab2..7f05bc3acb9 100644 --- a/security/passwords.rst +++ b/security/passwords.rst @@ -137,33 +137,6 @@ Further in this article, you can find a time_cost: 3 # Lowest possible value for argon memory_cost: 10 # Lowest possible value for argon - .. code-block:: xml - - - - - - - - - - - - - - - - .. code-block:: php // config/packages/security.php From 7d13bac560ff6506c3eaaf734aa388d7f7d3bf19 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 08:28:54 +0200 Subject: [PATCH 528/642] Minor tweak --- security/csrf.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/security/csrf.rst b/security/csrf.rst index 07e0671f07b..29fe96fa689 100644 --- a/security/csrf.rst +++ b/security/csrf.rst @@ -331,11 +331,12 @@ Stateless CSRF Tokens .. versionadded:: 7.2 - Stateless anti-CSRF protection was introduced in Symfony 7.2, and set as default. + Stateless anti-CSRF protection was introduced in Symfony 7.2. -Traditionally CSRF tokens are stateful, which means they're stored in the session. -But some token ids can be declared as stateless using the ``stateless_token_ids`` -option: +Traditionally, CSRF tokens are stateful, meaning they're stored in the session. +However, some token IDs can be declared as stateless using the +``stateless_token_ids`` option. Stateless CSRF tokens are enabled by default +in applications using :ref:`Symfony Flex `. .. configuration-block:: From 881051cce0d0c678da689bcdd268f52e29c2a8de Mon Sep 17 00:00:00 2001 From: Dustin Meiner <168725484+curenect-meiner@users.noreply.github.com> Date: Sun, 25 May 2025 14:39:42 +0200 Subject: [PATCH 529/642] Update uid.rst with the string almost their is failure cause symfony dont find the class. This prevents it form it. --- components/uid.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/uid.rst b/components/uid.rst index b031348f85a..fe15f964f44 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -527,6 +527,7 @@ entity primary keys:: namespace App\Entity; use Doctrine\ORM\Mapping as ORM; + use Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator; use Symfony\Bridge\Doctrine\Types\UlidType; use Symfony\Component\Uid\Ulid; @@ -535,7 +536,7 @@ entity primary keys:: #[ORM\Id] #[ORM\Column(type: UlidType::NAME, unique: true)] #[ORM\GeneratedValue(strategy: 'CUSTOM')] - #[ORM\CustomIdGenerator(class: 'doctrine.ulid_generator')] + #[ORM\CustomIdGenerator(class: UlidGenerator::class)] private ?Ulid $id; public function getId(): ?Ulid From f98e8dc1d1853dd88d0c40c28e441952b90a3538 Mon Sep 17 00:00:00 2001 From: chx Date: Tue, 22 Apr 2025 08:47:55 +0200 Subject: [PATCH 530/642] Clarify the code flow a little bit more --- components/runtime.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/runtime.rst b/components/runtime.rst index 4eb75de2a75..336a8573227 100644 --- a/components/runtime.rst +++ b/components/runtime.rst @@ -36,6 +36,8 @@ So how does this front-controller work? At first, the special the component. This file runs the following logic: #. It instantiates a :class:`Symfony\\Component\\Runtime\\RuntimeInterface`; +#. The runtime includes the front-controller script -- in this case + ``public/index.php`` -- making it run again. Make sure this doesn't cause problems. #. The callable (returned by ``public/index.php``) is passed to the Runtime, whose job is to resolve the arguments (in this example: ``array $context``); #. Then, this callable is called to get the application (``App\Kernel``); From 7ada520c4c6856701cd7e4a30df9314da859c086 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 09:11:49 +0200 Subject: [PATCH 531/642] Minor tweak --- components/runtime.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/runtime.rst b/components/runtime.rst index 336a8573227..770ea102563 100644 --- a/components/runtime.rst +++ b/components/runtime.rst @@ -36,8 +36,9 @@ So how does this front-controller work? At first, the special the component. This file runs the following logic: #. It instantiates a :class:`Symfony\\Component\\Runtime\\RuntimeInterface`; -#. The runtime includes the front-controller script -- in this case - ``public/index.php`` -- making it run again. Make sure this doesn't cause problems. +#. The front-controller script (e.g. ``public/index.php``) is included by the + runtime, making it run again. Ensure this doesn't produce any side effects + in your code; #. The callable (returned by ``public/index.php``) is passed to the Runtime, whose job is to resolve the arguments (in this example: ``array $context``); #. Then, this callable is called to get the application (``App\Kernel``); From 4e67b7324679f9fbffa24aad0d0635c41be8285a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 09:21:16 +0200 Subject: [PATCH 532/642] Reword --- messenger.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index e37f075518a..97b95565fa7 100644 --- a/messenger.rst +++ b/messenger.rst @@ -2468,7 +2468,8 @@ Possible options to configure with tags are: Name of the method that will process the message. ``priority`` - Priority of the handler when multiple handlers can process the same message; higher values will be processed first. + Defines the order in which the handler is executed when multiple handlers + can process the same message; those with higher priority run first. .. _handler-subscriber-options: From 9323d4717cbbbdf31108d122982e821330a75e89 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 09:22:09 +0200 Subject: [PATCH 533/642] [Messenger] Backoport an improvement in the priority description --- messenger.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index 18c7be913b5..cc0b13c3467 100644 --- a/messenger.rst +++ b/messenger.rst @@ -2416,7 +2416,8 @@ Possible options to configure with tags are: Name of the method that will process the message. ``priority`` - Priority of the handler when multiple handlers can process the same message. + Defines the order in which the handler is executed when multiple handlers + can process the same message; those with higher priority run first. .. _handler-subscriber-options: From 01f381305940492ddbbfc0ab54a30db383971ac4 Mon Sep 17 00:00:00 2001 From: ifiroth <97906657+ifiroth@users.noreply.github.com> Date: Tue, 27 May 2025 11:11:06 +0200 Subject: [PATCH 534/642] Update voters.rst I think you're missing an argument here --- security/voters.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/voters.rst b/security/voters.rst index 2b4a5af54e2..f20e1de62af 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -186,7 +186,7 @@ would look like this:: return match($attribute) { self::VIEW => $this->canView($post, $user), - self::EDIT => $this->canEdit($post, $user), + self::EDIT => $this->canEdit($post, $user, $vote), default => throw new \LogicException('This code should not be reached!') }; } @@ -202,7 +202,7 @@ would look like this:: return !$post->isPrivate(); } - private function canEdit(Post $post, User $user): bool + private function canEdit(Post $post, User $user, ?Vote $vote): bool { // this assumes that the Post object has a `getAuthor()` method if ($user === $post->getAuthor()) { From 91a952566fc2a93c08cb162eeddb1fc83edef0ab Mon Sep 17 00:00:00 2001 From: NanoSector Date: Mon, 24 Mar 2025 21:31:11 +0100 Subject: [PATCH 535/642] [Doctrine] entityvalueresolver target entities --- doctrine.rst | 22 +++++++++++++++++ doctrine/resolve_target_entity.rst | 39 +++++++++++++++++------------- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/doctrine.rst b/doctrine.rst index 171f8a3348a..876aaa3065d 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -780,6 +780,28 @@ variable. Let's say you want the first or the last comment of a product dependin Comment $comment ): Response { } + +.. _doctrine-entity-value-resolver-resolve-target-entities: + +Fetch via Interfaces +~~~~~~~~~~~~~~~~~~~~ + +Suppose your ``Product`` object implements an interface called ``ProductInterface``. +If you want to decouple your controllers from your entity implementations, you can instead reference entities via an interface. +To do this, first you need to configure the :doc:`resolve_target_entities option `. + +Your controller can then reference the Product entity by its interface instead:: + + public function show( + #[MapEntity] + ProductInterface $product + ): Response { + // ... + } + +.. versionadded:: 7.3 + + Support for ``resolve_target_entites`` in the ``EntityValueResolver`` was introduced in Symfony 7.3. MapEntity Options ~~~~~~~~~~~~~~~~~ diff --git a/doctrine/resolve_target_entity.rst b/doctrine/resolve_target_entity.rst index 5ae6475a957..c55b27f6463 100644 --- a/doctrine/resolve_target_entity.rst +++ b/doctrine/resolve_target_entity.rst @@ -1,30 +1,35 @@ -How to Define Relationships with Abstract Classes and Interfaces -================================================================ +Referencing Entities with Abstract Classes and Interfaces +========================================================= -One of the goals of bundles is to create discrete bundles of functionality -that do not have many (if any) dependencies, allowing you to use that -functionality in other applications without including unnecessary items. +In applications where functionality is segregated with minimal concrete dependencies +between the various layers, such as monoliths which are split into multiple modules, +it might be hard to prevent hard dependencies on entities between modules. Doctrine 2.2 includes a new utility called the ``ResolveTargetEntityListener``, that functions by intercepting certain calls inside Doctrine and rewriting ``targetEntity`` parameters in your metadata mapping at runtime. It means that -in your bundle you are able to use an interface or abstract class in your -mappings and expect correct mapping to a concrete entity at runtime. +you are able to use an interface or abstract class in your mappings and expect +correct mapping to a concrete entity at runtime. This functionality allows you to define relationships between different entities without making them hard dependencies. +.. tip:: + + Starting with Symfony 7.3, this functionality also works with the ``EntityValueResolver``. + See :ref:`doctrine-entity-value-resolver-resolve-target-entities` for more details. + Background ---------- -Suppose you have an InvoiceBundle which provides invoicing functionality -and a CustomerBundle that contains customer management tools. You want -to keep these separated, because they can be used in other systems without -each other, but for your application you want to use them together. +Suppose you have an application which provides two modules; an Invoice module which +provides invoicing functionality, and a Customer module that contains customer management +tools. You want to keep dependencies between these modules separated, because they should +not be aware of the other module's implementation details. -In this case, you have an ``Invoice`` entity with a relationship to a -non-existent object, an ``InvoiceSubjectInterface``. The goal is to get -the ``ResolveTargetEntityListener`` to replace any mention of the interface +In this case, you have an ``Invoice`` entity with a relationship to the interface +``InvoiceSubjectInterface``. This is not recognized as a valid entity by Doctrine. +The goal is to get the ``ResolveTargetEntityListener`` to replace any mention of the interface with a real object that implements that interface. Set up @@ -89,7 +94,7 @@ An InvoiceSubjectInterface:: public function getName(): string; } -Next, you need to configure the listener, which tells the DoctrineBundle +Next, you need to configure the ``resolve_target_entities`` option, which tells the DoctrineBundle about the replacement: .. configuration-block:: @@ -141,6 +146,6 @@ Final Thoughts -------------- With the ``ResolveTargetEntityListener``, you are able to decouple your -bundles, keeping them usable by themselves, but still being able to +modules, keeping them usable by themselves, but still being able to define relationships between different objects. By using this method, -your bundles will end up being easier to maintain independently. +your modules will end up being easier to maintain independently. From e6ba3a96ec8c28f692dedb1bc22a423868aad574 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 12:02:55 +0200 Subject: [PATCH 536/642] Rewords --- doctrine.rst | 28 +++++++------ doctrine/resolve_target_entity.rst | 67 ++++++++++++++---------------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/doctrine.rst b/doctrine.rst index e66488beefd..ecaf675df08 100644 --- a/doctrine.rst +++ b/doctrine.rst @@ -789,22 +789,26 @@ variable. Let's say you want the first or the last comment of a product dependin Fetch via Interfaces ~~~~~~~~~~~~~~~~~~~~ -Suppose your ``Product`` object implements an interface called ``ProductInterface``. -If you want to decouple your controllers from your entity implementations, you can instead reference entities via an interface. -To do this, first you need to configure the :doc:`resolve_target_entities option `. - -Your controller can then reference the Product entity by its interface instead:: +Suppose your ``Product`` class implements an interface called ``ProductInterface``. +If you want to decouple your controllers from the concrete entity implementation, +you can reference the entity by its interface instead. - public function show( - #[MapEntity] - ProductInterface $product - ): Response { - // ... - } +To enable this, first configure the +:doc:`resolve_target_entities option `. +Then, your controller can type-hint the interface, and the entity will be +resolved automatically:: + + public function show( + #[MapEntity] + ProductInterface $product + ): Response { + // ... + } .. versionadded:: 7.3 - Support for ``resolve_target_entites`` in the ``EntityValueResolver`` was introduced in Symfony 7.3. + Support for target entity resolution in the ``EntityValueResolver`` was + introduced Symfony 7.3 MapEntity Options ~~~~~~~~~~~~~~~~~ diff --git a/doctrine/resolve_target_entity.rst b/doctrine/resolve_target_entity.rst index c55b27f6463..1495f475628 100644 --- a/doctrine/resolve_target_entity.rst +++ b/doctrine/resolve_target_entity.rst @@ -1,44 +1,45 @@ Referencing Entities with Abstract Classes and Interfaces ========================================================= -In applications where functionality is segregated with minimal concrete dependencies -between the various layers, such as monoliths which are split into multiple modules, -it might be hard to prevent hard dependencies on entities between modules. +In applications where functionality is organized in layers or modules with +minimal concrete dependencies, such as monoliths split into multiple modules, +it can be challenging to avoid tight coupling between entities. -Doctrine 2.2 includes a new utility called the ``ResolveTargetEntityListener``, -that functions by intercepting certain calls inside Doctrine and rewriting -``targetEntity`` parameters in your metadata mapping at runtime. It means that -you are able to use an interface or abstract class in your mappings and expect -correct mapping to a concrete entity at runtime. +Doctrine provides a utility called the ``ResolveTargetEntityListener`` to solve +this issue. It works by intercepting certain calls within Doctrine and rewriting +``targetEntity`` parameters in your metadata mapping at runtime. This allows you +to reference an interface or abstract class in your mappings and have it resolved +to a concrete entity at runtime. -This functionality allows you to define relationships between different entities -without making them hard dependencies. +This makes it possible to define relationships between entities without +creating hard dependencies. This feature also works with the ``EntityValueResolver`` +:ref:`as explained in the main Doctrine article `. -.. tip:: +.. versionadded:: 7.3 - Starting with Symfony 7.3, this functionality also works with the ``EntityValueResolver``. - See :ref:`doctrine-entity-value-resolver-resolve-target-entities` for more details. + Support for target entity resolution in the ``EntityValueResolver`` was + introduced Symfony 7.3 Background ---------- -Suppose you have an application which provides two modules; an Invoice module which -provides invoicing functionality, and a Customer module that contains customer management -tools. You want to keep dependencies between these modules separated, because they should -not be aware of the other module's implementation details. +Suppose you have an application with two modules: an Invoice module that +provides invoicing functionality, and a Customer module that handles customer +management. You want to keep these modules decoupled, so that neither is aware +of the other's implementation details. -In this case, you have an ``Invoice`` entity with a relationship to the interface -``InvoiceSubjectInterface``. This is not recognized as a valid entity by Doctrine. -The goal is to get the ``ResolveTargetEntityListener`` to replace any mention of the interface -with a real object that implements that interface. +In this case, your ``Invoice`` entity has a relationship to the interface +``InvoiceSubjectInterface``. Since interfaces are not valid Doctrine entities, +the goal is to use the ``ResolveTargetEntityListener`` to replace all +references to this interface with a concrete class that implements it. Set up ------ -This article uses the following two basic entities (which are incomplete for -brevity) to explain how to set up and use the ``ResolveTargetEntityListener``. +This article uses two basic (incomplete) entities to demonstrate how to set up +and use the ``ResolveTargetEntityListener``. -A Customer entity:: +A ``Customer`` entity:: // src/Entity/Customer.php namespace App\Entity; @@ -55,7 +56,7 @@ A Customer entity:: // are already implemented in the BaseCustomer } -An Invoice entity:: +An ``Invoice`` entity:: // src/Entity/Invoice.php namespace App\Entity; @@ -63,9 +64,6 @@ An Invoice entity:: use App\Model\InvoiceSubjectInterface; use Doctrine\ORM\Mapping as ORM; - /** - * Represents an Invoice. - */ #[ORM\Entity] #[ORM\Table(name: 'invoice')] class Invoice @@ -74,7 +72,7 @@ An Invoice entity:: protected InvoiceSubjectInterface $subject; } -An InvoiceSubjectInterface:: +The interface representing the subject used in the invoice:: // src/Model/InvoiceSubjectInterface.php namespace App\Model; @@ -94,8 +92,8 @@ An InvoiceSubjectInterface:: public function getName(): string; } -Next, you need to configure the ``resolve_target_entities`` option, which tells the DoctrineBundle -about the replacement: +Now configure the ``resolve_target_entities`` option to tell Doctrine +how to replace the interface with the concrete class: .. configuration-block:: @@ -145,7 +143,6 @@ about the replacement: Final Thoughts -------------- -With the ``ResolveTargetEntityListener``, you are able to decouple your -modules, keeping them usable by themselves, but still being able to -define relationships between different objects. By using this method, -your modules will end up being easier to maintain independently. +Using ``ResolveTargetEntityListener`` allows you to decouple your modules +while still defining relationships between their entities. This makes your +codebase more modular and easier to maintain over time. From a94507c576e7bebbb126116b3fb9bfe6e6dbd55e Mon Sep 17 00:00:00 2001 From: Dustin Meiner <168725484+curenect-meiner@users.noreply.github.com> Date: Tue, 27 May 2025 11:36:02 +0200 Subject: [PATCH 537/642] Update uid.rst --- components/uid.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/uid.rst b/components/uid.rst index fe15f964f44..4b6938e98d2 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -331,6 +331,7 @@ entity primary keys:: namespace App\Entity; use Doctrine\ORM\Mapping as ORM; + use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator; use Symfony\Bridge\Doctrine\Types\UuidType; use Symfony\Component\Uid\Uuid; @@ -339,7 +340,7 @@ entity primary keys:: #[ORM\Id] #[ORM\Column(type: UuidType::NAME, unique: true)] #[ORM\GeneratedValue(strategy: 'CUSTOM')] - #[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')] + #[ORM\CustomIdGenerator(class: UuidGenerator::class)] private ?Uuid $id; public function getId(): ?Uuid From 15728d3dbe8e79d47d0cef98b5c784673ed72619 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 13:05:49 +0200 Subject: [PATCH 538/642] Minor tweaks --- security/access_token.rst | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/security/access_token.rst b/security/access_token.rst index 0370949e584..4d358aec526 100644 --- a/security/access_token.rst +++ b/security/access_token.rst @@ -411,15 +411,15 @@ and retrieve the user info: ; }; -To enable the `OpenID Connect Discovery`_, the ``OidcUserInfoTokenHandler`` +To enable `OpenID Connect Discovery`_, the ``OidcUserInfoTokenHandler`` requires the ``symfony/cache`` package to store the OIDC configuration in -cache. If you haven't installed it yet, run this command: +the cache. If you haven't installed it yet, run the following command: .. code-block:: terminal $ composer require symfony/cache -Then, configure the ``base_uri`` and ``discovery`` keys: +Next, configure the ``base_uri`` and ``discovery`` options: .. configuration-block:: @@ -477,6 +477,10 @@ Then, configure the ``base_uri`` and ``discovery`` keys: ; }; +.. versionadded:: 7.3 + + Support for OpenID Connect Discovery was introduced in Symfony 7.3. + Following the `OpenID Connect Specification`_, the ``sub`` claim is used as user identifier by default. To use another claim, specify it on the configuration: @@ -691,16 +695,16 @@ it and retrieve the user info from it: The support of multiple algorithms to sign the JWS was introduced in Symfony 7.1. In previous versions, only the ``ES256`` algorithm was supported. -To enable the `OpenID Connect Discovery`_, the ``OidcTokenHandler`` -requires the ``symfony/cache`` package to store the OIDC configuration in -cache. If you haven't installed it yet, run this command: +To enable `OpenID Connect Discovery`_, the ``OidcTokenHandler`` requires the +``symfony/cache`` package to store the OIDC configuration in the cache. If you +haven't installed it yet, run the following command: .. code-block:: terminal $ composer require symfony/cache -Then, you can remove the ``keyset`` configuration key (it will be imported from -the OpenID Connect Discovery), and configure the ``discovery`` key: +Then, you can remove the ``keyset`` configuration option (it will be imported +from the OpenID Connect Discovery), and configure the ``discovery`` option: .. configuration-block:: From 8b403f7948d9ce2bcec4f63b2783d8a055436a90 Mon Sep 17 00:00:00 2001 From: Florent Morselli Date: Sun, 9 Feb 2025 10:44:02 +0100 Subject: [PATCH 539/642] Add support for encrypted access tokens (JWE) in OIDC This update introduces support for decrypting encrypted access tokens (JWE) in Symfony 7.3. It includes configuration options for enabling encryption, enforcing it, specifying decryption algorithms, and providing decryption keysets. The feature extends flexibility in handling secure tokens alongside existing signing mechanisms. --- security/access_token.rst | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/security/access_token.rst b/security/access_token.rst index 4d358aec526..70c9e21980e 100644 --- a/security/access_token.rst +++ b/security/access_token.rst @@ -615,8 +615,8 @@ If you haven't installed it yet, run this command: $ composer require web-token/jwt-library -Symfony provides a generic ``OidcTokenHandler`` to decode your token, validate -it and retrieve the user info from it: +Symfony provides a generic ``OidcTokenHandler`` that decodes the token, validates +it, and retrieves the user information from it. Optionally, the token can be encrypted (JWE): .. configuration-block:: @@ -637,6 +637,11 @@ it and retrieve the user info from it: audience: 'api-example' # Issuers (`iss` claim): required for validation purpose issuers: ['https://oidc.example.com'] + encryption: + enabled: true # Default to false + enforce: false # Default to false, requires an encrypted token when true + algorithms: ['ECDH-ES', 'A128GCM'] + keyset: '{"keys": [...]}' # Encryption private keyset .. code-block:: xml @@ -662,6 +667,10 @@ it and retrieve the user info from it: ES256 RS256 https://oidc.example.com + + ECDH-ES + A128GCM + @@ -681,12 +690,20 @@ it and retrieve the user info from it: ->oidc() // Algorithm used to sign the JWS ->algorithms(['ES256', 'RS256']) - // A JSON-encoded JWK + // A JSON-encoded JWKSet (public keys) ->keyset('{"keys":[{"kty":"...","k":"..."}]}') // Audience (`aud` claim): required for validation purpose ->audience('api-example') // Issuers (`iss` claim): required for validation purpose ->issuers(['https://oidc.example.com']) + ->encryption() + ->enabled(true) //Default to false + ->enforce(false) //Default to false, requires an encrypted token when true + // Algorithm used to decrypt the JWE + ->algorithms(['ECDH-ES', 'A128GCM']) + // A JSON-encoded JWKSet (private keys) + ->keyset('{"keys":[...]}') + ; }; @@ -695,6 +712,10 @@ it and retrieve the user info from it: The support of multiple algorithms to sign the JWS was introduced in Symfony 7.1. In previous versions, only the ``ES256`` algorithm was supported. +.. versionadded:: 7.3 + + Support for encryption algorithms to decrypt JWEs was introduced in Symfony 7.3. + To enable `OpenID Connect Discovery`_, the ``OidcTokenHandler`` requires the ``symfony/cache`` package to store the OIDC configuration in the cache. If you haven't installed it yet, run the following command: From 22cd58eb815c7c672ab3d4f7164ed8a51dcca7bd Mon Sep 17 00:00:00 2001 From: Mathieu Santostefano Date: Sun, 9 Feb 2025 17:56:57 +0100 Subject: [PATCH 540/642] [Routing] Add Attribute code examples for alias in `#[Route]` attribute --- routing.rst | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/routing.rst b/routing.rst index e634a410c37..2f18b121472 100644 --- a/routing.rst +++ b/routing.rst @@ -1340,6 +1340,23 @@ have been renamed. Let's say you have a route called ``product_show``: .. configuration-block:: + .. code-block:: php-attributes + + // src/Controller/ProductController.php + namespace App\Controller; + + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Routing\Attribute\Route; + + class ProductController + { + #[Route('/product/{id}', name: 'product_show')] + public function show(): Response + { + // ... + } + } + .. code-block:: yaml # config/routes.yaml @@ -1376,6 +1393,25 @@ Instead of duplicating the original route, you can create an alias for it. .. configuration-block:: + .. code-block:: php-attributes + + // src/Controller/ProductController.php + namespace App\Controller; + + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Routing\Attribute\Route; + + class ProductController + { + // "alias" named argument indicates the name of the alias you want to create. + // The alias will point to the actual route "product_show" + #[Route('/product/{id}', name: 'product_show', alias: ['product_details'])] + public function show(): Response + { + // ... + } + } + .. code-block:: yaml # config/routes.yaml @@ -1416,6 +1452,15 @@ Instead of duplicating the original route, you can create an alias for it. In this example, both ``product_show`` and ``product_details`` routes can be used in the application and will produce the same result. +.. note:: + + Using non-attributes formats (YAML, XML and PHP) is the only way + to define an alias pointing to a route that you don't own. + + So that you can use your own route name for URL generation, + while actually using a route defined by a third-party bundle as the target of that URL generation, + as the 2 definitions are not required to be in the same config file (or even in the same format). + .. _routing-alias-deprecation: Deprecating Route Aliases @@ -1436,6 +1481,42 @@ This way, the ``product_show`` alias could be deprecated. .. configuration-block:: + .. code-block:: php-attributes + + // src/Controller/ProductController.php + namespace App\Controller; + + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Routing\Attribute\Route; + + class ProductController + { + // this outputs the following generic deprecation message: + // Since acme/package 1.2: The "product_show" route alias is deprecated. You should stop using it, as it will be removed in the future. + #[Route('/product/{id}', + name: 'product_details', + alias: new DeprecatedAlias( + aliasName: 'product_show', + package: 'acme/package', + version: '1.2', + ), + )] + // Or, you can also define a custom deprecation message (%alias_id% placeholder is available) + #[Route('/product/{id}', + name: 'product_details', + alias: new DeprecatedAlias( + aliasName: 'product_show', + package: 'acme/package', + version: '1.2', + message: 'The "%alias_id%" route alias is deprecated. Please use "product_details" instead.', + ), + )] + public function show(): Response + { + // ... + } + } + .. code-block:: yaml # Move the concrete route definition under ``product_details`` From 32e93b8b32003bd7bbe123f21137a19d87c5c185 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 15:33:25 +0200 Subject: [PATCH 541/642] Tweaks and added the versionadded directive --- routing.rst | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/routing.rst b/routing.rst index ee913f07b4c..bf3946b343d 100644 --- a/routing.rst +++ b/routing.rst @@ -1405,8 +1405,8 @@ Instead of duplicating the original route, you can create an alias for it. class ProductController { - // "alias" named argument indicates the name of the alias you want to create. - // The alias will point to the actual route "product_show" + // the "alias" argument assigns an alternate name to this route; + // the alias will point to the actual route "product_show" #[Route('/product/{id}', name: 'product_show', alias: ['product_details'])] public function show(): Response { @@ -1451,17 +1451,21 @@ Instead of duplicating the original route, you can create an alias for it. $routes->alias('product_details', 'product_show'); }; +.. versionadded:: 7.3 + + Support for route aliases in PHP attributes was introduced in Symfony 7.3. + In this example, both ``product_show`` and ``product_details`` routes can be used in the application and will produce the same result. .. note:: - Using non-attributes formats (YAML, XML and PHP) is the only way - to define an alias pointing to a route that you don't own. + YAML, XML, and PHP configuration formats are the only ways to define an alias + for a route that you do not own. You can't do this when using PHP attributes. - So that you can use your own route name for URL generation, - while actually using a route defined by a third-party bundle as the target of that URL generation, - as the 2 definitions are not required to be in the same config file (or even in the same format). + This allows you for example to use your own route name for URL generation, + while still targeting a route defined by a third-party bundle. The alias and + the original route do not need to be declared in the same file or format. .. _routing-alias-deprecation: From c15467e0abef4c60c6e2265994fc5761127f7b2c Mon Sep 17 00:00:00 2001 From: Matthieu Lempereur Date: Mon, 31 Mar 2025 13:32:59 +0200 Subject: [PATCH 542/642] [Messenger] [Amqp] Add default exchange support --- messenger.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index fc8f9491022..f4c3f683627 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1645,11 +1645,15 @@ The transport has a number of options: Exchange flags ``exchange[name]`` - Name of the exchange + Name of the exchange, an empty string can be used to use the default exchange ``exchange[type]`` (default: ``fanout``) Type of exchange +.. versionadded:: 7.3 + + Empty string support for ``exchange[name]`` was introduced in Symfony 7.3. + You can also configure AMQP-specific settings on your message by adding :class:`Symfony\\Component\\Messenger\\Bridge\\Amqp\\Transport\\AmqpStamp` to your Envelope:: From b17f770615cfe7beb1291746bb186cc07c4a8f8e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 16:10:17 +0200 Subject: [PATCH 543/642] Minor tweak --- messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index 08fdcf7f4dc..46ee188d68d 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1645,7 +1645,7 @@ The transport has a number of options: Exchange flags ``exchange[name]`` - Name of the exchange, an empty string can be used to use the default exchange + Name of the exchange. Use an empty string to use the default exchange. ``exchange[type]`` (default: ``fanout``) Type of exchange From 326e7e52585777d299865e5b788b9c00519aabe4 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 27 May 2025 16:45:15 +0200 Subject: [PATCH 544/642] Tweaks and rewords --- security/custom_authenticator.rst | 73 +++++++++++++++---------------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/security/custom_authenticator.rst b/security/custom_authenticator.rst index 5877194ab4e..ed22ab96d19 100644 --- a/security/custom_authenticator.rst +++ b/security/custom_authenticator.rst @@ -219,10 +219,10 @@ identifier (e.g. the username or email):: User Identifier ~~~~~~~~~~~~~~~ -The user identifier is a unique string that identifies the user. It is used -to load the user using :ref:`the user provider `. -This identifier is often something like the user's email address or username, -but it could be any unique value associated with the user. +The user identifier is a unique string that identifies the user. It is often +something like their email address or username, but it can be any unique value +associated with the user. It allows loading the user through the configured +:ref:`user provider `. .. note:: @@ -262,21 +262,39 @@ but it could be any unique value associated with the user. } } -It is a good practice to normalize the user identifier before using it. -For example, this ensures that variations such as "john.doe", "John.Doe", -or "JOHN.DOE" refer to the same user. -Normalization can include converting the identifier to lowercase -and trimming unnecessary spaces. -You can optionally pass a user identifier normalizer as third argument to the -``UserBadge``. This callable receives the ``$userIdentifier`` -and must return a normalized user identifier as a string. +It's a good practice to normalize the user identifier before using it. This +ensures that variations like "john.doe", "John.Doe", or "JOHN.DOE" are treated +as the same user. + +Normalization typically involves converting the identifier to lowercase and +trimming extra spaces. For example, Google considers the following email +addresses equivalent: ``john.doe@gmail.com``, ``j.hon.d.oe@gmail.com``, and +``johndoe@gmail.com``. This is due to normalization rules that remove dots and +lowercase the address. + +In enterprise environments, users might authenticate using different identifier +formats, such as: + +* ``john.doe@acme.com`` +* ``acme.com\jdoe`` +* ``https://acme.com/+jdoe`` +* ``acct:jdoe@acme.com`` + +Applying normalization (e.g. lowercasing, trimming, or unifying formats) helps +ensure consistent identity resolution and prevents duplication caused by +format differences. + +In Symfony applications, you can optionally pass a user identifier normalizer as +the third argument to the ``UserBadge``. This callable receives the ``$userIdentifier`` +and must return a normalized string. .. versionadded:: 7.3 - The support of the user identifier normalizer was introduced in Symfony 7.3. + Support for user identifier normalizers was introduced in Symfony 7.3. -For instance, the example below uses a normalizer that converts usernames to a normalized, ASCII-only, lowercase format, -suitable for consistent comparison and storage. +For instance, the example below uses a normalizer that converts usernames to +a normalized, ASCII-only, lowercase format suitable for consistent comparison +and storage:: // src/Security/NormalizedUserBadge.php namespace App\Security; @@ -319,33 +337,12 @@ suitable for consistent comparison and storage. } } -.. note:: - - For example, Google treats the following email addresses as equivalent: - ``john.doe@gmail.com``, ``j.hon.d.oe@gmail.com``, and ``johndoe@gmail.com``. - This is because Google applies normalization rules that remove dots - and convert the address to lowercase (though behavior varies across services). - -.. note:: - - In enterprise environments, a user may authenticate using different formats - of their identifier, such as: - - - ``john.doe@acme.com`` - - ``acme.com\jdoe`` - - ``https://acme.com/+jdoe`` - - ``acct:jdoe@acme.com`` - - Applying normalization (e.g., trimming, lowercasing, or format unification) - helps ensure consistent identity recognition across systems and prevents - duplicates caused by format variations. - User Credential ~~~~~~~~~~~~~~~ -The user credential is used to authenticate the user i.e. to verify +The user credential is used to authenticate the user; that is, to verify the validity of the provided information (such as a password, an API token, -or other custom credentials). +or custom credentials). The following credential classes are supported by default: From 7dfeaba1252a9b2d50280f667c87cad7a7e390dc Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 28 May 2025 10:45:42 +0200 Subject: [PATCH 545/642] Fixed a minor RST syntax issue --- setup/_update_dep_errors.rst.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/_update_dep_errors.rst.inc b/setup/_update_dep_errors.rst.inc index 49ae97067e4..5dc7e6745bc 100644 --- a/setup/_update_dep_errors.rst.inc +++ b/setup/_update_dep_errors.rst.inc @@ -24,7 +24,7 @@ versions of other libraries. Check your error message to debug. Another issue that may happen is that the project dependencies can be installed on your local computer but not on the remote server. This usually happens when the PHP versions are different on each machine. The solution is to add the -`platform`_ config option to your `composer.json` file to define the highest +`platform`_ config option to your ``composer.json`` file to define the highest PHP version allowed for the dependencies (set it to the server's PHP version). .. _`platform`: https://getcomposer.org/doc/06-config.md#platform From ece1723cd9f4a3bf908159742444cd8c12ae5971 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 28 May 2025 10:46:47 +0200 Subject: [PATCH 546/642] Fixed a minor RST syntax issue --- components/cache/adapters/redis_adapter.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/cache/adapters/redis_adapter.rst b/components/cache/adapters/redis_adapter.rst index 3362f4cc2db..23dd8d948a1 100644 --- a/components/cache/adapters/redis_adapter.rst +++ b/components/cache/adapters/redis_adapter.rst @@ -222,7 +222,7 @@ Available Options .. versionadded:: 7.1 - The option `sentinel_master` as an alias for `redis_sentinel` was introduced + The option ``sentinel_master`` as an alias for ``redis_sentinel`` was introduced in Symfony 7.1. .. note:: From fa9df566b994fe1ee54f851e087f2bdf0a4b30b0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 29 May 2025 07:54:14 +0200 Subject: [PATCH 547/642] [Security] Fixed a code example --- security/custom_authenticator.rst | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/security/custom_authenticator.rst b/security/custom_authenticator.rst index ed22ab96d19..fd7fa0b139e 100644 --- a/security/custom_authenticator.rst +++ b/security/custom_authenticator.rst @@ -300,14 +300,13 @@ and storage:: namespace App\Security; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; - use Symfony\Component\String\UnicodeString; use function Symfony\Component\String\u; final class NormalizedUserBadge extends UserBadge { public function __construct(string $identifier) { - $callback = static fn (string $identifier) => u($identifier)->normalize(UnicodeString::NFKC)->ascii()->lower()->toString(); + $callback = static fn (string $identifier): string => u($identifier)->normalize(UnicodeString::NFKC)->ascii()->lower()->toString(); parent::__construct($identifier, null, $callback); } @@ -318,7 +317,7 @@ and storage:: final class PasswordAuthenticator extends AbstractLoginFormAuthenticator { - // Simplified for brievety + // simplified for brevity public function authenticate(Request $request): Passport { $username = (string) $request->request->get('username', ''); @@ -331,7 +330,7 @@ and storage:: new NormalizedUserBadge($username), new PasswordCredentials($password), [ - //All other useful badges + // all other useful badges ] ); } From d923acb02ff743309fe3b2be2a69c95aee704928 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 28 May 2025 08:43:53 +0200 Subject: [PATCH 548/642] [Security] Document the new `expose_security_errors` option --- reference/configuration/security.rst | 31 +++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index c8609c20c9c..8bae363d9cf 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -23,7 +23,8 @@ key in your application configuration. * `access_denied_url`_ * `erase_credentials`_ -* `hide_user_not_found`_ +* `expose_security_errors`_ +* `hide_user_not_found`_ (deprecated) * `session_fixation_strategy`_ **Advanced Options**: @@ -71,11 +72,39 @@ after authentication:: Since Symfony 7.3, ``eraseCredentials()`` methods are deprecated and are not called if they have the ``#[\Deprecated]`` attribute. +expose_security_errors +---------------------- + +**type**: ``string`` **default**: ``'none'`` + +.. deprecated:: 7.3 + + The ``expose_security_errors`` option was introduced in Symfony 7.3 + +User enumeration is a common security issue where attackers infer valid usernames +based on error messages. For example, a message like "This user does not exist" +shown by your login form reveals whether a username exists. + +This option lets you hide some or all errors related to user accounts +(e.g. blocked or expired accounts) to prevent this issue. Instead, these +errors will trigger a generic ``BadCredentialsException``. The value of this +option can be one of the following: + +* ``'none'``: hides all user-related security exceptions; +* ``'account_status'``: shows account-related exceptions (e.g. blocked or expired + accounts) but only for users who provided the correct password; +* ``'all'``: shows all security-related exceptions. + hide_user_not_found ------------------- **type**: ``boolean`` **default**: ``true`` +.. deprecated:: 7.3 + + The ``hide_user_not_found`` option was deprecated in favor of the + ``expose_security_errors`` option in Symfony 7.3. + If ``true``, when a user is not found a generic exception of type :class:`Symfony\\Component\\Security\\Core\\Exception\\BadCredentialsException` is thrown with the message "Bad credentials". From c1b44e71348da9b97d9a5aac119dfa9dc1507ac9 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 28 May 2025 10:16:02 +0200 Subject: [PATCH 549/642] [Cache][Lock] Add support for `Relay\Cluster` in docs --- components/cache/adapters/redis_adapter.rst | 32 +++++++++++++++++++-- components/lock.rst | 10 +++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/components/cache/adapters/redis_adapter.rst b/components/cache/adapters/redis_adapter.rst index 9f29eae84e7..c0295b487a0 100644 --- a/components/cache/adapters/redis_adapter.rst +++ b/components/cache/adapters/redis_adapter.rst @@ -20,9 +20,9 @@ to utilize a cluster of servers to provide redundancy and/or fail-over is also a **Requirements:** At least one `Redis server`_ must be installed and running to use this adapter. Additionally, this adapter requires a compatible extension or library that implements - ``\Redis``, ``\RedisArray``, ``RedisCluster``, ``\Relay\Relay`` or ``\Predis``. + ``\Redis``, ``\RedisArray``, ``RedisCluster``, ``\Relay\Relay``, ``\Relay\Cluster`` or ``\Predis``. -This adapter expects a `Redis`_, `RedisArray`_, `RedisCluster`_, `Relay`_ or `Predis`_ instance to be +This adapter expects a `Redis`_, `RedisArray`_, `RedisCluster`_, `Relay`_, `RelayCluster`_ or `Predis`_ instance to be passed as the first parameter. A namespace and default cache lifetime can optionally be passed as the second and third parameters:: @@ -48,6 +48,10 @@ as the second and third parameters:: ?MarshallerInterface $marshaller = null ); +.. versionadded:: 7.3 + + Support for ``Relay\Cluster`` was introduced in Symfony 7.3. + Configure the Connection ------------------------ @@ -226,11 +230,34 @@ Available Options ``ssl`` (type: ``array``, default: ``null``) SSL context options. See `php.net/context.ssl`_ for more information. +``relay_cluster_context`` (type: ``array``, default: ``[]``) + Defines configuration options specific to ``\Relay\Cluster``. For example, to + user a self-signed certificate for testing in local environment:: + + $options = [ + // ... + 'relay_cluster_context' => [ + // ... + 'stream' => [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'allow_self_signed' => true, + 'local_cert' => '/valkey.crt', + 'local_pk' => '/valkey.key', + 'cafile' => '/valkey.crt', + ], + ], + ]; + .. versionadded:: 7.1 The option ``sentinel_master`` as an alias for ``redis_sentinel`` was introduced in Symfony 7.1. +.. versionadded:: 7.3 + + The ``relay_cluster_context`` option was introduced in Symfony 7.3. + .. note:: When using the `Predis`_ library some additional Predis-specific options are available. @@ -359,6 +386,7 @@ Supports key rotation, ensuring secure decryption with both old and new keys:: .. _`RedisArray`: https://github.com/phpredis/phpredis/blob/develop/arrays.md .. _`RedisCluster`: https://github.com/phpredis/phpredis/blob/develop/cluster.md .. _`Relay`: https://relay.so/ +.. _`RelayCluster`: https://relay.so/docs/1.x/connections#cluster .. _`Predis`: https://packagist.org/packages/predis/predis .. _`Predis Connection Parameters`: https://github.com/nrk/predis/wiki/Connection-Parameters#list-of-connection-parameters .. _`TCP-keepalive`: https://redis.io/topics/clients#tcp-keepalive diff --git a/components/lock.rst b/components/lock.rst index b8ba38c8fc7..2403763bd4a 100644 --- a/components/lock.rst +++ b/components/lock.rst @@ -612,9 +612,9 @@ RedisStore ~~~~~~~~~~ The RedisStore saves locks on a Redis server, it requires a Redis connection -implementing the ``\Redis``, ``\RedisArray``, ``\RedisCluster``, ``\Relay\Relay`` or -``\Predis`` classes. This store does not support blocking, and expects a TTL to -avoid stalled locks:: +implementing the ``\Redis``, ``\RedisArray``, ``\RedisCluster``, ``\Relay\Relay``, +``\Relay\Cluster`` or ``\Predis`` classes. This store does not support blocking, +and expects a TTL to avoid stalled locks:: use Symfony\Component\Lock\Store\RedisStore; @@ -623,6 +623,10 @@ avoid stalled locks:: $store = new RedisStore($redis); +.. versionadded:: 7.3 + + Support for ``Relay\Cluster`` was introduced in Symfony 7.3. + .. _lock-store-semaphore: SemaphoreStore From e37d364158f6f8e3793b39b866cc59dde4aa48fb Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 29 May 2025 08:11:20 +0200 Subject: [PATCH 550/642] Minor tweaks --- scheduler.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scheduler.rst b/scheduler.rst index 4c5955a9470..f88d9e4f328 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -895,12 +895,12 @@ Modifying the Schedule at Runtime .. versionadded:: 6.4 - Modifying the schedule at runtime and recalculating the heap was introduced in Symfony 6.4. + Support for modifying the schedule at runtime and recalculating the heap + was introduced in Symfony 6.4. When a recurring message is added to or removed from the schedule, the scheduler automatically restarts and recalculates the internal trigger heap. -This allows dynamic control over scheduled tasks during runtime. -code:: +This enables dynamic control of scheduled tasks at runtime:: // src/Scheduler/DynamicScheduleProvider.php namespace App\Scheduler; @@ -921,7 +921,7 @@ code:: public function clearAndAddMessages(): void { - // Clear the current schedule (if any) and add new recurring messages + // clear the current schedule and add new recurring messages $this->schedule?->clear(); $this->schedule?->add( RecurringMessage::cron('@hourly', new DoActionMessage()), From 9ef310b4f6201f1e43250ab7705a845c6353b5a6 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 29 May 2025 08:35:11 +0200 Subject: [PATCH 551/642] Removed an unneeded versionadded directive --- scheduler.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/scheduler.rst b/scheduler.rst index cec0fe25ede..de2e448f5af 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -864,11 +864,6 @@ code:: Modifying the Schedule at Runtime --------------------------------- -.. versionadded:: 6.4 - - Support for modifying the schedule at runtime and recalculating the heap - was introduced in Symfony 6.4. - When a recurring message is added to or removed from the schedule, the scheduler automatically restarts and recalculates the internal trigger heap. This enables dynamic control of scheduled tasks at runtime:: From 47988f995bf4d180cbcb9cbe73d11692f49c4baa Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 29 May 2025 13:19:16 +0200 Subject: [PATCH 552/642] [Serializer] Fix YAML normalizationContext and denormalizationContext names --- serializer.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/serializer.rst b/serializer.rst index 440494d0be1..cc514684bab 100644 --- a/serializer.rst +++ b/serializer.rst @@ -520,8 +520,8 @@ You can also specify a context specific to normalization or denormalization: attributes: createdAt: contexts: - - normalizationContext: { datetime_format: 'Y-m-d' } - denormalizationContext: { datetime_format: !php/const \DateTime::RFC3339 } + - normalization_context: { datetime_format: 'Y-m-d' } + denormalization_context: { datetime_format: !php/const \DateTime::RFC3339 } .. code-block:: xml @@ -1371,7 +1371,7 @@ normalizers (in order of priority): $propertyInfo = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]); $normalizers = [new ObjectNormalizer(new ClassMetadataFactory(new AttributeLoader()), null, null, $propertyInfo), new ArrayDenormalizer()]; - + $this->serializer = new Serializer($normalizers, [new JsonEncoder()]); :class:`Symfony\\Component\\Serializer\\Normalizer\\ObjectNormalizer` From b356a5b6d86cf9a04cc3f438be00150c3ec437c5 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 29 May 2025 13:36:20 +0200 Subject: [PATCH 553/642] [EventDispatcher] Document event name is optionnal --- components/event_dispatcher.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/event_dispatcher.rst b/components/event_dispatcher.rst index 8cd676dd5fe..62a3707bb39 100644 --- a/components/event_dispatcher.rst +++ b/components/event_dispatcher.rst @@ -277,8 +277,9 @@ Dispatch the Event The :method:`Symfony\\Component\\EventDispatcher\\EventDispatcher::dispatch` method notifies all listeners of the given event. It takes two arguments: -the ``Event`` instance to pass to each listener of that event and the name -of the event to dispatch:: +the ``Event`` instance to pass to each listener of that event and optionally the +name of the event to dispatch. If it's not defined, the class of the ``Event`` +instance will be used:: use Acme\Store\Event\OrderPlacedEvent; use Acme\Store\Order; From 65599c4cec1f2bc3661fad1fe9e3594146bd65f7 Mon Sep 17 00:00:00 2001 From: Antoine Lamirault Date: Thu, 29 May 2025 14:07:18 +0200 Subject: [PATCH 554/642] Replace get_class() calls by ::class --- components/options_resolver.rst | 2 +- components/property_info.rst | 2 +- components/yaml.rst | 2 +- console/verbosity.rst | 2 +- doctrine/associations.rst | 2 +- security/user_providers.rst | 2 +- service_container/service_subscribers_locators.rst | 6 +++--- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/components/options_resolver.rst b/components/options_resolver.rst index 6b033a1b69c..35e443d3d94 100644 --- a/components/options_resolver.rst +++ b/components/options_resolver.rst @@ -922,7 +922,7 @@ can change your code to do the configuration only once per class:: public function __construct(array $options = []) { // What type of Mailer is this, a Mailer, a GoogleMailer, ... ? - $class = get_class($this); + $class = $this::class; // Was configureOptions() executed before for this class? if (!isset(self::$resolversByClass[$class])) { diff --git a/components/property_info.rst b/components/property_info.rst index 6d57c1bb274..238da6396ab 100644 --- a/components/property_info.rst +++ b/components/property_info.rst @@ -131,7 +131,7 @@ class exposes public methods to extract several types of information: $propertyInfo->getProperties($awesomeObject); // Good! - $propertyInfo->getProperties(get_class($awesomeObject)); + $propertyInfo->getProperties($awesomeObject::class); $propertyInfo->getProperties('Example\Namespace\YourAwesomeClass'); $propertyInfo->getProperties(YourAwesomeClass::class); diff --git a/components/yaml.rst b/components/yaml.rst index 268ad5c5452..700ebd22379 100644 --- a/components/yaml.rst +++ b/components/yaml.rst @@ -296,7 +296,7 @@ You can make it convert to a ``DateTime`` instance by using the ``PARSE_DATETIME flag:: $date = Yaml::parse('2016-05-27', Yaml::PARSE_DATETIME); - var_dump(get_class($date)); // DateTime + var_dump($date::class); // DateTime Dumping Multi-line Literal Blocks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/console/verbosity.rst b/console/verbosity.rst index f7a1a1e5e59..b822e3d7534 100644 --- a/console/verbosity.rst +++ b/console/verbosity.rst @@ -60,7 +60,7 @@ level. For example:: // available methods: ->isQuiet(), ->isVerbose(), ->isVeryVerbose(), ->isDebug() if ($output->isVerbose()) { - $output->writeln('User class: '.get_class($user)); + $output->writeln('User class: '.$user::class); } // alternatively you can pass the verbosity level PHP constant to writeln() diff --git a/doctrine/associations.rst b/doctrine/associations.rst index 2279d02a851..bb670eeee52 100644 --- a/doctrine/associations.rst +++ b/doctrine/associations.rst @@ -447,7 +447,7 @@ by adding JOINs. $category = $product->getCategory(); // prints "Proxies\AppEntityCategoryProxy" - dump(get_class($category)); + dump($category::class); die(); This proxy object extends the true ``Category`` object, and looks and diff --git a/security/user_providers.rst b/security/user_providers.rst index 7e9de36eff1..73b723faaaf 100644 --- a/security/user_providers.rst +++ b/security/user_providers.rst @@ -425,7 +425,7 @@ command will generate a nice skeleton to get you started:: public function refreshUser(UserInterface $user): UserInterface { if (!$user instanceof User) { - throw new UnsupportedUserException(sprintf('Invalid user class "%s".', get_class($user))); + throw new UnsupportedUserException(sprintf('Invalid user class "%s".', $user::class)); } // Return a User object after making sure its data is "fresh". diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index 14cdb010152..d67384d50dd 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -36,7 +36,7 @@ to handle their respective command when it is asked for:: public function handle(Command $command): mixed { - $commandClass = get_class($command); + $commandClass = $command::class; if (!$handler = $this->handlerMap[$commandClass] ?? null) { return; @@ -94,7 +94,7 @@ in the service subscriber:: public function handle(Command $command): mixed { - $commandClass = get_class($command); + $commandClass = $command::class; if ($this->locator->has($commandClass)) { $handler = $this->locator->get($commandClass); @@ -328,7 +328,7 @@ attribute:: public function handle(Command $command): mixed { - $commandClass = get_class($command); + $commandClass = $command::class; if ($this->handlers->has($commandClass)) { $handler = $this->handlers->get($commandClass); From cb8e2ff4f85289bf6be15ab40ad4736397f7fa0d Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Fri, 21 Mar 2025 11:13:12 +0100 Subject: [PATCH 555/642] =?UTF-8?q?[AssetMapper]=20Adding=20info=20that=20?= =?UTF-8?q?always=20*all*=20entrypoints=20are=20included=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/asset_mapper.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index 061c4598bfa..bab55035271 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -1081,8 +1081,10 @@ loads the ``app.js`` file *and* the ``checkout.js`` file. It's important to *not* call ``parent()`` in the ``importmap`` block. Each page can only have *one* importmap, so ``importmap()`` must be called exactly once. -If, for some reason, you want to execute *only* ``checkout.js`` +If you want to execute *only* ``checkout.js`` and *not* ``app.js``, pass only ``checkout`` to ``importmap()``. +In this case, still **both** files are added to ````). Using a Content Security Policy (CSP) ------------------------------------- From 45c11e5a0f5690eb8a30cc87ba447e96939056ab Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 29 May 2025 16:57:15 +0200 Subject: [PATCH 556/642] Tweaks and rewords --- frontend/asset_mapper.rst | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst index be013514a8f..ca500a9686d 100644 --- a/frontend/asset_mapper.rst +++ b/frontend/asset_mapper.rst @@ -1095,16 +1095,19 @@ both ``app`` and ``checkout``: {{ importmap(['app', 'checkout']) }} {% endblock %} -By passing both ``app`` and ``checkout``, the ``importmap()`` function will -output the ``importmap`` and also add a ````). +The ``importmap()`` function always includes the full import map to ensure all +module definitions are available on the page. It also adds a `` @@ -332,7 +332,7 @@ as follows: .. code-block:: html+twig - Symfony! + Symfony! @@ -973,7 +973,7 @@ following code to display the user information is repeated in several places: {# ... #} @@ -1258,7 +1258,7 @@ In practice, the ``base.html.twig`` template would look like this: {% block title %}My Application{% endblock %} {% block stylesheets %} - + {% endblock %} From 01915d0a3448db4b8ba415abe35793498a51f43f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 24 Jun 2025 09:19:05 +0200 Subject: [PATCH 622/642] [Templating] [Templates] Removing self-closing slashes --- templates.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates.rst b/templates.rst index 0041077dfed..bc249024f05 100644 --- a/templates.rst +++ b/templates.rst @@ -304,10 +304,10 @@ You can now use the ``asset()`` function: .. code-block:: html+twig {# the image lives at "public/images/logo.png" #} - Symfony! + Symfony! {# the CSS file lives at "public/css/blog.css" #} - + {# the JS file lives at "public/bundles/acme/js/loader.js" #} @@ -332,7 +332,7 @@ as follows: .. code-block:: html+twig - Symfony! + Symfony! @@ -946,7 +946,7 @@ following code to display the user information is repeated in several places: {# ... #} @@ -1231,7 +1231,7 @@ In practice, the ``base.html.twig`` template would look like this: {% block title %}My Application{% endblock %} {% block stylesheets %} - + {% endblock %} From 9ad93fa3eda5d3786b50d98f965315c18412ba0a Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 24 Jun 2025 09:29:00 +0200 Subject: [PATCH 623/642] Fix some RST issues --- http_client.rst | 10 +++++----- reference/configuration/framework.rst | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/http_client.rst b/http_client.rst index 3904368d50f..36ed66a1506 100644 --- a/http_client.rst +++ b/http_client.rst @@ -167,8 +167,9 @@ Some options are described in this guide: Check out the full :ref:`http_client config reference ` to learn about all the options. -The HTTP client also has one configuration option called -``max_host_connections``, this option can not be overridden by a request: +The HTTP client also has a configuration option called +:ref:`max_host_connections `. +This option cannot be overridden per request: .. configuration-block:: @@ -1355,9 +1356,8 @@ code waits only when necessary. or access to certificate files). To avoid hitting these limits, consider processing requests in batches. - There is however a maximum amount of concurrent connections that can be open - per host (6 by default). See - :doc:`max_host_connections `. + There is, however, a maximum amount of concurrent connections that can be open + per host (``6`` by default). See :ref:`max_host_connections `. Multiplexing Responses ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 6376c057940..d97c59dd9e9 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -1129,6 +1129,8 @@ max_duration The maximum execution time, in seconds, that the request and the response are allowed to take. A value lower than or equal to 0 means it is unlimited. +.. _reference-http-client-max-host-connections: + max_host_connections .................... From 117be6865e342379601d7c7dba32d06bc1774b55 Mon Sep 17 00:00:00 2001 From: Thomas Landauer Date: Tue, 24 Jun 2025 11:21:37 +0200 Subject: [PATCH 624/642] [Templating] Adding AssetMapper to `asset()` function Page: https://symfony.com/doc/6.4/templates.html#linking-to-css-javascript-and-image-assets There is another link to AssetMapper just one screen further down - maybe you want to merge them somehow: https://symfony.com/doc/6.4/templates.html#build-versioning-more-advanced-css-javascript-and-image-handling My point here is that the main advantage of the `asset()` function IMO is not the subdirectory scenario, but rather: If you *later* switch to AssetMapper, it saves you a lot of time if you've already used `asset()` everywhere. --- templates.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/templates.rst b/templates.rst index bc249024f05..5f6ddea8195 100644 --- a/templates.rst +++ b/templates.rst @@ -312,8 +312,12 @@ You can now use the ``asset()`` function: {# the JS file lives at "public/bundles/acme/js/loader.js" #} -The ``asset()`` function's main purpose is to make your application more portable. -If your application lives at the root of your host (e.g. ``https://example.com``), +Using the ``asset()`` function is recommended for two reasons: + +* It automatically takes care of versioning your assets with + :doc:`Symfony's AssetMapper ` + +* If your application lives at the root of your host (e.g. ``https://example.com``), then the rendered path should be ``/images/logo.png``. But if your application lives in a subdirectory (e.g. ``https://example.com/my_app``), each asset path should render with the subdirectory (e.g. ``/my_app/images/logo.png``). The From e56a427ab53d52fbb0ee1b96554648a540855219 Mon Sep 17 00:00:00 2001 From: Pieter Oliver Date: Tue, 24 Jun 2025 12:22:57 +0100 Subject: [PATCH 625/642] Fix typo --- reference/constraints/Url.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst index 74f0d750dfd..1e9bb8ac62e 100644 --- a/reference/constraints/Url.rst +++ b/reference/constraints/Url.rst @@ -323,7 +323,7 @@ also relative URLs that contain no protocol (e.g. ``//example.com``). and will default to ``true`` in Symfony 8.0. By default, URLs like ``https://aaa`` or ``https://foobar`` are considered valid -because they are tecnically correct according to the `URL spec`_. If you set this option +because they are technically correct according to the `URL spec`_. If you set this option to ``true``, the host part of the URL will have to include a TLD (top-level domain name): e.g. ``https://example.com`` will be valid but ``https://example`` won't. From 124be08d977cac7ef6296b6ed35496abf8443e05 Mon Sep 17 00:00:00 2001 From: Peter Potrowl Date: Thu, 19 Jun 2025 21:53:06 +0200 Subject: [PATCH 626/642] [Testing] Indicate how to use InMemoryUser in WebTestCase --- testing.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/testing.rst b/testing.rst index 9356f2013a7..862d66d59b8 100644 --- a/testing.rst +++ b/testing.rst @@ -714,6 +714,27 @@ stores in the session of the test client. If you need to define custom attributes in this token, you can use the ``tokenAttributes`` argument of the :method:`Symfony\\Bundle\\FrameworkBundle\\KernelBrowser::loginUser` method. +You can also use :class:`Symfony\\Component\\Security\\Core\\User\\InMemoryUser` but you have to define those in the configuration first:: + +.. code-block:: yaml + + # config/packages/security.yaml + when@test: + security: + users_in_memory: + memory: + users: + admin: { password: password, roles: ROLE_ADMIN } + +and then log the user with:: + + // tests/Controller/ProfileControllerTest.php + use Symfony\Component\Security\Core\User\InMemoryUser; + + $client = static::createClient(); + $testUser = new InMemoryUser('admin', 'password', ['ROLE_ADMIN']); + $client->loginUser($testUser); + To set a specific firewall (``main`` is set by default):: $client->loginUser($testUser, 'my_firewall'); From c42aead2daa69a6c5422955569f75eb09b8a24e9 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 27 Jun 2025 09:28:03 +0200 Subject: [PATCH 627/642] Minor reword --- testing.rst | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/testing.rst b/testing.rst index 862d66d59b8..09cddfa55bb 100644 --- a/testing.rst +++ b/testing.rst @@ -714,7 +714,18 @@ stores in the session of the test client. If you need to define custom attributes in this token, you can use the ``tokenAttributes`` argument of the :method:`Symfony\\Bundle\\FrameworkBundle\\KernelBrowser::loginUser` method. -You can also use :class:`Symfony\\Component\\Security\\Core\\User\\InMemoryUser` but you have to define those in the configuration first:: +You can also use an :ref:`in-memory user ` in your tests +by instantiating :class:`Symfony\\Component\\Security\\Core\\User\\InMemoryUser` directly:: + + // tests/Controller/ProfileControllerTest.php + use Symfony\Component\Security\Core\User\InMemoryUser; + + $client = static::createClient(); + $testUser = new InMemoryUser('admin', 'password', ['ROLE_ADMIN']); + $client->loginUser($testUser); + +Before doing this, you must define the in-memory user in your test environment +configuration to ensure it exists and can be authenticated:: .. code-block:: yaml @@ -726,15 +737,6 @@ You can also use :class:`Symfony\\Component\\Security\\Core\\User\\InMemoryUser` users: admin: { password: password, roles: ROLE_ADMIN } -and then log the user with:: - - // tests/Controller/ProfileControllerTest.php - use Symfony\Component\Security\Core\User\InMemoryUser; - - $client = static::createClient(); - $testUser = new InMemoryUser('admin', 'password', ['ROLE_ADMIN']); - $client->loginUser($testUser); - To set a specific firewall (``main`` is set by default):: $client->loginUser($testUser, 'my_firewall'); From bf3b463fb2ad37e9eb690c3124a8f5f109fae268 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 27 Jun 2025 15:53:52 +0200 Subject: [PATCH 628/642] Fix a reference to third-party bundles templates dir --- templates.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates.rst b/templates.rst index bc249024f05..ab4e8da6680 100644 --- a/templates.rst +++ b/templates.rst @@ -1486,8 +1486,8 @@ Bundle Templates ~~~~~~~~~~~~~~~~ If you :ref:`install packages/bundles ` in your application, they -may include their own Twig templates (in the ``Resources/views/`` directory of -each bundle). To avoid messing with your own templates, Symfony adds bundle +may include their own Twig templates (in the ``templates/`` directory of each +bundle). To avoid messing with your own templates, Symfony adds bundle templates under an automatic namespace created after the bundle name. For example, the templates of a bundle called ``AcmeBlogBundle`` are available From 9ef642bcd9a564de473e71622dd872bd4bf6e1ab Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 1 Jul 2025 09:35:45 +0200 Subject: [PATCH 629/642] Document the `Path::join()` method - Add clear documentation for path joining behavior - Show examples for common use cases - Clarify that leading slashes in subsequent arguments are removed - Include examples for empty parts, trailing slashes, and multiple arguments --- components/filesystem.rst | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/components/filesystem.rst b/components/filesystem.rst index 8cdc2a34884..1d8e2452fd0 100644 --- a/components/filesystem.rst +++ b/components/filesystem.rst @@ -359,6 +359,44 @@ Malformed paths are returned unchanged:: echo Path::canonicalize('C:Programs/PHP/php.ini'); // => C:Programs/PHP/php.ini +Joining Paths +~~~~~~~~~~~~~ + +The :method:`Symfony\\Component\\Filesystem\\Path::join` method concatenates +the given paths and normalizes separators. It's a cleaner alternative to +string concatenation for building file paths:: + + echo Path::join('/var/www', 'vhost', 'config.ini'); + // => /var/www/vhost/config.ini + + echo Path::join('C:\\Program Files', 'PHP', 'php.ini'); + // => C:/Program Files/PHP/php.ini + +The method handles multiple scenarios correctly: + +- Empty parts are ignored:: + + echo Path::join('/var/www', '', 'config.ini'); + // => /var/www/config.ini + +- Leading slashes in subsequent arguments are removed:: + + echo Path::join('/var/www', '/etc', 'config.ini'); + // => /var/www/etc/config.ini + +- Trailing slashes are preserved only for root paths:: + + echo Path::join('/var/www', 'vhost/'); + // => /var/www/vhost + + echo Path::join('/', ''); + // => / + +- Works with any number of arguments:: + + echo Path::join('/var', 'www', 'vhost', 'symfony', 'config', 'config.ini'); + // => /var/www/vhost/symfony/config/config.ini + Converting Absolute/Relative Paths ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 01161e984187e837db72a63bc5b3781d8853e6d3 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 1 Jul 2025 11:24:50 +0200 Subject: [PATCH 630/642] Rename and refocus Symfony Local Web Server docs to Symfony CLI - Rename `symfony_server.rst` to `symfony_cli.rst` - Refocus documentation on the Symfony CLI as a comprehensive tool - Add new sections for project creation and PHP management - Reorganize content with better structure and flow - Update all references throughout the documentation - Add redirect from old URL to maintain backward compatibility - Improve troubleshooting section - Keep all existing anchors for backward compatibility --- _build/redirection_map | 1 + configuration/micro_kernel_trait.rst | 4 +- console.rst | 2 +- contributing/code/reproducer.rst | 2 +- create_framework/front_controller.rst | 2 +- create_framework/introduction.rst | 2 +- frontend/encore/dev-server.rst | 2 +- mailer.rst | 2 +- mercure.rst | 2 +- page_creation.rst | 2 +- quick_tour/the_big_picture.rst | 2 +- setup.rst | 2 +- setup/symfony_cli.rst | 431 ++++++++++++++++++++ setup/symfony_server.rst | 563 -------------------------- setup/web_server_configuration.rst | 2 +- 15 files changed, 445 insertions(+), 576 deletions(-) create mode 100644 setup/symfony_cli.rst delete mode 100644 setup/symfony_server.rst diff --git a/_build/redirection_map b/_build/redirection_map index 896c58a3022..184ec11aa1f 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -435,6 +435,7 @@ /setup/composer /setup /security/security_checker /setup /setup/built_in_web_server /setup/symfony_server +/setup/symfony_server /setup/symfony_cli /service_container/parameters /configuration /routing/generate_url_javascript /routing /routing/slash_in_parameter /routing diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index 20b6544181c..71a4a94e596 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -131,7 +131,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: options inside the ``configureContainer()`` method). That's it! To test it, start the :doc:`Symfony Local Web Server -`: +`: .. code-block:: terminal @@ -470,7 +470,7 @@ this: └─ composer.lock As before you can use the :doc:`Symfony Local Web Server -`: +`: .. code-block:: terminal diff --git a/console.rst b/console.rst index 82bafd4d640..a3e1abbf34b 100644 --- a/console.rst +++ b/console.rst @@ -109,7 +109,7 @@ completion (by default, by pressing the Tab key). .. tip:: If you are using the :doc:`Symfony local web server - `, it is recommended to use the built-in completion + `, it is recommended to use the built-in completion script that will ensure the right PHP version and configuration are used when running the Console Completion. Run ``symfony completion --help`` for the installation instructions for your shell. The Symfony CLI will provide diff --git a/contributing/code/reproducer.rst b/contributing/code/reproducer.rst index 3392ca87035..914ab2fd3f2 100644 --- a/contributing/code/reproducer.rst +++ b/contributing/code/reproducer.rst @@ -65,7 +65,7 @@ to a route definition. Then, after creating your project: of controllers, actions, etc. as in your original application. #. Create a small controller and add your routing definition that shows the bug. #. Don't create or modify any other file. -#. Install the :doc:`local web server ` provided by Symfony +#. Install the :doc:`local web server ` provided by Symfony and use the ``symfony server:start`` command to browse to the new route and see if the bug appears or not. #. If you can see the bug, you're done and you can already share the code with us. diff --git a/create_framework/front_controller.rst b/create_framework/front_controller.rst index fded71a7b1c..f2843b9067a 100644 --- a/create_framework/front_controller.rst +++ b/create_framework/front_controller.rst @@ -154,7 +154,7 @@ Now, configure your web server root directory to point to ``web/`` and all other files will no longer be accessible from the client. To test your changes in a browser (``http://localhost:4321/hello?name=Fabien``), -run the :doc:`Symfony Local Web Server `: +run the :doc:`Symfony Local Web Server `: .. code-block:: terminal diff --git a/create_framework/introduction.rst b/create_framework/introduction.rst index 7a1e6b2ad50..d65bb4fe85e 100644 --- a/create_framework/introduction.rst +++ b/create_framework/introduction.rst @@ -101,7 +101,7 @@ start with the simplest web application we can think of in PHP:: printf('Hello %s', $name); -You can use the :doc:`Symfony Local Web Server ` to test +You can use the :doc:`Symfony Local Web Server ` to test this great application in a browser (``http://localhost:8000/index.php?name=Fabien``): diff --git a/frontend/encore/dev-server.rst b/frontend/encore/dev-server.rst index 01501178caf..c3287a7bc2f 100644 --- a/frontend/encore/dev-server.rst +++ b/frontend/encore/dev-server.rst @@ -53,7 +53,7 @@ method in your ``webpack.config.js`` file: Enabling HTTPS using the Symfony Web Server ------------------------------------------- -If you're using the :doc:`Symfony web server ` locally with HTTPS, +If you're using the :doc:`Symfony web server ` locally with HTTPS, you'll need to also tell the dev-server to use HTTPS. To do this, you can reuse the Symfony web server SSL certificate: diff --git a/mailer.rst b/mailer.rst index 5ba85056d27..30e50fbd359 100644 --- a/mailer.rst +++ b/mailer.rst @@ -1825,7 +1825,7 @@ 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 +`, the mailer DSN is automatically exposed via the :ref:`symfony binary Docker integration `. Sending Test Emails diff --git a/mercure.rst b/mercure.rst index ed89fe034f7..fd32d01c01f 100644 --- a/mercure.rst +++ b/mercure.rst @@ -72,7 +72,7 @@ Thanks to :doc:`the Docker integration of Symfony `, :ref:`Flex ` proposes to install a Mercure hub for development. Run ``docker-compose up`` to start the hub if you have chosen this option. -If you use the :doc:`Symfony Local Web Server `, +If you use the :doc:`Symfony Local Web Server `, you must start it with the ``--no-tls`` option to prevent mixed content and invalid TLS certificate issues: diff --git a/page_creation.rst b/page_creation.rst index 8ab89baa9f6..ce7a2b1a935 100644 --- a/page_creation.rst +++ b/page_creation.rst @@ -80,7 +80,7 @@ metadata to code): } } -That's it! If you are using :doc:`the Symfony web server `, +That's it! If you are using :doc:`the Symfony web server `, try it out by going to: http://localhost:8000/lucky/number .. tip:: diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index b069cb4f716..997bb05904e 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -42,7 +42,7 @@ Symfony application: Can we already load the project in a browser? Yes! You can set up :doc:`Nginx or Apache ` and configure their document root to be the ``public/`` directory. But, for development, it's better -to :doc:`install the Symfony local web server ` and run +to :doc:`install the Symfony local web server ` and run it as follows: .. code-block:: terminal diff --git a/setup.rst b/setup.rst index 889df729466..b2b184114d0 100644 --- a/setup.rst +++ b/setup.rst @@ -121,7 +121,7 @@ development. .. _symfony-binary-web-server: However for local development, the most convenient way of running Symfony is by -using the :doc:`local web server ` provided by the +using the :doc:`local web server ` provided by the ``symfony`` binary. This local server provides among other things support for HTTP/2, concurrent requests, TLS/SSL and automatic generation of security certificates. diff --git a/setup/symfony_cli.rst b/setup/symfony_cli.rst new file mode 100644 index 00000000000..6bd6e047341 --- /dev/null +++ b/setup/symfony_cli.rst @@ -0,0 +1,431 @@ +.. _symfony-server: +.. _symfony-local-web-server: + +Symfony CLI +=========== + +The Symfony CLI is a developer tool to help you build, run, and manage your +Symfony applications directly from your terminal. It's designed to boost your +productivity with smart features like: + +* Local web server optimized for development +* Seamless integration with Platform.sh services +* Docker support with automatic environment variable management +* Multiple PHP version management +* Background worker management +* And much more! + +Installation +------------ + +The Symfony CLI is available as a standalone executable that supports Linux, +macOS, and Windows. + +Download and install it following the instructions on `symfony.com/download`_. + +Shell Autocompletion +~~~~~~~~~~~~~~~~~~~~ + +The Symfony CLI supports autocompletion for Bash, Zsh, and Fish shells. This +helps you type commands faster and discover available options: + +.. code-block:: terminal + + # Install autocompletion (do this only once) + $ symfony completion bash | sudo tee /etc/bash_completion.d/symfony + + # For Zsh users + $ symfony completion zsh > ~/.symfony_completion && echo "source ~/.symfony_completion" >> ~/.zshrc + + # For Fish users + $ symfony completion fish | source + +After installation, restart your terminal to enable autocompletion. The CLI will +also provide autocompletion for ``composer`` and ``console`` commands when it +detects a Symfony project. + +.. note:: + + You can view and contribute to the Symfony CLI source in the + `symfony-cli/symfony-cli GitHub repository`_. + +Creating New Symfony Applications +--------------------------------- + +The Symfony CLI includes a powerful project creation command that helps you +start new projects quickly: + +.. code-block:: terminal + + # Create a new Symfony project (latest stable version) + $ symfony new my_project + + # Create a project with the latest LTS (Long Term Support) version + $ symfony new my_project --version=lts + + # Create a project based on a specific Symfony version + $ symfony new my_project --version=6.4 + + # Create a project using the development version + $ symfony new my_project --version=next + + # Create a minimal project (microkernel) + $ symfony new my_project --version=6.4 + + # Create a demo application + $ symfony new my_project --demo + +.. tip:: + + Pass the ``--cloud`` option to initialize a Platform.sh project at the same + time the Symfony project is created. Pass the ``--webapp`` option if you + prefer a project where Twig is already configured. + +Running the Local Web Server +---------------------------- + +The Symfony CLI includes a powerful local web server for development. This +server is not intended for production use but provides features that make +development more productive: + +* HTTPS support with automatic certificate generation +* HTTP/2 support +* Automatic PHP version selection +* Integration with Docker services +* Built-in proxy for custom domain names + +Getting Started +~~~~~~~~~~~~~~~ + +To serve a Symfony project with the local server: + +.. code-block:: terminal + + $ cd my-project/ + $ symfony server:start + + [OK] Web server listening on http://127.0.0.1:8000 + ... + + # Browse the site using your default browser + $ symfony open:local + +Running the server this way displays log messages in the console. To run it in +the background: + +.. code-block:: terminal + + $ symfony server:start -d + + # Continue working and running other commands... + + # View the latest log messages + $ symfony server:log + + # Stop the background server + $ symfony server:stop + +.. tip:: + + On macOS, you might see a warning about accepting incoming network + connections. To fix this, sign the Symfony binary: + + .. code-block:: terminal + + $ sudo codesign --force --deep --sign - $(whereis -q symfony) + +Enabling HTTPS/TLS +~~~~~~~~~~~~~~~~~~ + +Running your application over HTTPS locally helps detect mixed content issues +early and allows using features that require secure connections: + +.. code-block:: terminal + + # Install the certificate authority (only once) + $ symfony server:ca:install + + # Now start your server - it will use HTTPS automatically + $ symfony server:start + +.. tip:: + + For WSL (Windows Subsystem for Linux) users, manually import the certificate + authority in Windows: + + .. code-block:: terminal + + $ explorer.exe `wslpath -w $HOME/.symfony5/certs` + + Then double-click on ``default.p12`` to import it. + +PHP Management +-------------- + +The Symfony CLI provides powerful PHP management features, allowing you to use +different PHP versions for different projects. + +Selecting PHP Version +~~~~~~~~~~~~~~~~~~~~~ + +Create a ``.php-version`` file at your project root: + +.. code-block:: terminal + + $ cd my-project/ + + # Use a specific PHP version + $ echo 8.2 > .php-version + + # Use any PHP 8.x version available + $ echo 8 > .php-version + +To see all available PHP versions: + +.. code-block:: terminal + + $ symfony local:php:list + +.. tip:: + + You can create a ``.php-version`` file in a parent directory to set the same + PHP version for multiple projects. + +Custom PHP Configuration +~~~~~~~~~~~~~~~~~~~~~~~~ + +Override PHP settings per project by creating a ``php.ini`` file at the project +root: + +.. code-block:: ini + + ; php.ini + [Date] + date.timezone = Europe/Paris + + [PHP] + memory_limit = 256M + +Using PHP Commands +~~~~~~~~~~~~~~~~~~ + +Use ``symfony php`` to ensure commands run with the correct PHP version: + +.. code-block:: terminal + + # Runs with the system's default PHP + $ php -v + + # Runs with the project's PHP version + $ symfony php -v + + # This also works for Composer + $ symfony composer install + +Local Domain Names +------------------ + +Instead of using ``127.0.0.1:8000``, you can use custom domain names like +``https://my-app.wip``. + +Setting up the Local Proxy +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Configure your system to use the Symfony proxy: + + * Open your system's proxy settings + * Set ``http://127.0.0.1:7080/proxy.pac`` as the automatic proxy configuration URL + +2. Start the proxy: + + .. code-block:: terminal + + $ symfony proxy:start + +3. Attach a domain to your project: + + .. code-block:: terminal + + $ cd my-project/ + $ symfony proxy:domain:attach my-app + + Your application is now available at ``https://my-app.wip`` + +.. tip:: + + View all local domains and their configuration at http://127.0.0.1:7080 + +You can also use wildcards: + +.. code-block:: terminal + + $ symfony proxy:domain:attach "*.my-app" + +This allows accessing subdomains like ``https://api.my-app.wip`` or +``https://admin.my-app.wip``. + +.. _symfony-server-docker: + +Docker Integration +------------------ + +The local server automatically detects Docker services and exposes their +connection information as environment variables. + +Automatic Service Detection +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +With this ``compose.yaml``: + +.. code-block:: yaml + + services: + database: + image: mysql:8 + ports: [3306] + +The server automatically creates these environment variables: + +* ``DATABASE_URL`` +* ``DATABASE_HOST`` +* ``DATABASE_PORT`` + +Supported services include MySQL, PostgreSQL, Redis, RabbitMQ, Elasticsearch, +MongoDB, and more. + +.. tip:: + + Run ``symfony var:export`` to see all exposed environment variables. + +Service Naming +~~~~~~~~~~~~~~ + +If your service names don't match Symfony conventions, use labels: + +.. code-block:: yaml + + services: + db: + image: postgres:15 + ports: [5432] + labels: + com.symfony.server.service-prefix: 'DATABASE' + +Managing Long-Running Processes +------------------------------- + +Use the Symfony CLI to manage long-running processes like Webpack watchers: + +.. code-block:: terminal + + # Start webpack watcher in the background + $ symfony run -d npx encore dev --watch + + # View logs + $ symfony server:log + + # Check status + $ symfony server:status + +.. _symfony-server_configuring-workers: + +Configuring Workers +~~~~~~~~~~~~~~~~~~~ + +Define processes that should start automatically with the server in +``.symfony.local.yaml``: + +.. code-block:: yaml + + # .symfony.local.yaml + workers: + # Built-in Encore integration + npm_encore_watch: ~ + + # Messenger consumer with file watching + messenger_consume_async: + cmd: ['symfony', 'console', 'messenger:consume', 'async'] + watch: ['config', 'src', 'templates', 'vendor'] + + # Custom commands + build_spa: + cmd: ['npm', 'run', 'watch'] + + # Auto-start Docker Compose + docker_compose: ~ + +Advanced Configuration +---------------------- + +The ``.symfony.local.yaml`` file provides advanced configuration options: + +.. code-block:: yaml + + # Custom domains + proxy: + domains: + - app + - admin.app + + # HTTP server settings + http: + document_root: public/ + passthru: index.php + port: 8000 + preferred_port: 8001 + allow_http: false + no_tls: false + p12: path/to/custom-cert.p12 + +Platform.sh Integration +----------------------- + +The Symfony CLI provides seamless integration with `Platform.sh`_: + +.. code-block:: terminal + + # Open Platform.sh web UI + $ symfony cloud:web + + # Deploy to Platform.sh + $ symfony cloud:deploy + + # Create a new environment + $ symfony cloud:env:create feature-xyz + +For more Platform.sh features, see the `Platform.sh documentation`_. + +Troubleshooting +--------------- + +**Server doesn't start**: Check if the port is already in use: + +.. code-block:: terminal + + $ symfony server:status + $ symfony server:stop # If a server is already running + +**HTTPS not working**: Ensure the CA is installed: + +.. code-block:: terminal + + $ symfony server:ca:install + +**Docker services not detected**: Check that Docker is running and environment +variables are properly exposed: + +.. code-block:: terminal + + $ docker compose ps + $ symfony var:export --debug + +**Proxy domains not working**: + +* Clear your browser cache +* Check proxy settings in your system +* For Chrome, visit ``chrome://net-internals/#proxy`` and click "Re-apply settings" + +.. _`symfony.com/download`: https://symfony.com/download +.. _`symfony-cli/symfony-cli GitHub repository`: https://github.com/symfony-cli/symfony-cli +.. _`Platform.sh`: https://symfony.com/cloud/ +.. _`Platform.sh documentation`: https://docs.platform.sh/frameworks/symfony.html \ No newline at end of file diff --git a/setup/symfony_server.rst b/setup/symfony_server.rst deleted file mode 100644 index 2ea4da543fe..00000000000 --- a/setup/symfony_server.rst +++ /dev/null @@ -1,563 +0,0 @@ -Symfony Local Web Server -======================== - -You can run Symfony applications with any web server (Apache, nginx, the -internal PHP web server, etc.). However, Symfony provides its own web server to -make you more productive while developing your applications. - -Although this server is not intended for production use, it supports HTTP/2, -TLS/SSL, automatic generation of security certificates, local domains, and many -other features that sooner or later you'll need when developing web projects. -Moreover, the server is not tied to Symfony and you can also use it with any -PHP application and even with HTML or single page applications. - -Installation ------------- - -The Symfony server is part of the ``symfony`` binary created when you -`install Symfony`_ and has support for Linux, macOS and Windows. - -.. tip:: - - The Symfony CLI supports auto completion for Bash, Zsh, or Fish shells. You - have to install the completion script *once*. Run ``symfony completion - --help`` for the installation instructions for your shell. After installing - and restarting your terminal, you're all set to use completion (by default, - by pressing the Tab key). - - The Symfony CLI will also provide completion for the ``composer`` command - and for the ``console`` command if it detects a Symfony project. - -.. note:: - - You can view and contribute to the Symfony CLI source in the - `symfony-cli/symfony-cli GitHub repository`_. - -Getting Started ---------------- - -The Symfony server is started once per project, so you may end up with several -instances (each of them listening to a different port). This is the common -workflow to serve a Symfony project: - -.. code-block:: terminal - - $ cd my-project/ - $ symfony server:start - - [OK] Web server listening on http://127.0.0.1:.... - ... - - # Now, browse the given URL, or run this command: - $ symfony open:local - -Running the server this way makes it display the log messages in the console, so -you won't be able to run other commands at the same time. If you prefer, you can -run the Symfony server in the background: - -.. code-block:: terminal - - $ cd my-project/ - - # start the server in the background - $ symfony server:start -d - - # continue working and running other commands... - - # show the latest log messages - $ symfony server:log - -.. tip:: - - On macOS, when starting the Symfony server you might see a warning dialog asking - *"Do you want the application to accept incoming network connections?"*. - This happens when running unsigned applications that are not listed in the - firewall list. The solution is to run this command that signs the Symfony binary: - - .. code-block:: terminal - - $ sudo codesign --force --deep --sign - $(whereis -q symfony) - -Enabling PHP-FPM ----------------- - -.. note:: - - PHP-FPM must be installed locally for the Symfony server to utilize. - -When the server starts, it checks for ``web/index_dev.php``, ``web/index.php``, -``public/app_dev.php``, ``public/app.php`` in that order. If one is found, the -server will automatically start with PHP-FPM enabled. Otherwise the server will -start without PHP-FPM and will show a ``Page not found`` page when trying to -access a ``.php`` file in the browser. - -.. tip:: - - When an ``index.html`` and a front controller like e.g. ``index.php`` are - both present the server will still start with PHP-FPM enabled but the - ``index.html`` will take precedence over the front controller. This means - when an ``index.html`` file is present in ``public`` or ``web``, it will be - displayed instead of the ``index.php`` which would show e.g. the Symfony - application. - -Enabling TLS ------------- - -Browsing the secure version of your applications locally is important to detect -problems with mixed content early, and to run libraries that only run in HTTPS. -Traditionally this has been painful and complicated to set up, but the Symfony -server automates everything. First, run this command: - -.. code-block:: terminal - - $ symfony server:ca:install - -This command creates a local certificate authority, registers it in your system -trust store, registers it in Firefox (this is required only for that browser) -and creates a default certificate for ``localhost`` and ``127.0.0.1``. In other -words, it does everything for you. - -.. tip:: - - If you are doing this in WSL (Windows Subsystem for Linux), the newly created - local certificate authority needs to be manually imported in Windows. The file - is located in ``wsl`` at ``~/.symfony5/certs/default.p12``. The easiest way to - do so is to run the following command from ``wsl``: - - .. code-block:: terminal - - $ explorer.exe `wslpath -w $HOME/.symfony5/certs` - - In the file explorer window that just opened, double-click on the file - called ``default.p12``. - -Before browsing your local application with HTTPS instead of HTTP, restart its -server stopping and starting it again. - -Different PHP Settings Per Project ----------------------------------- - -Selecting a Different PHP Version -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you have multiple PHP versions installed on your computer, you can tell -Symfony which one to use creating a file called ``.php-version`` at the project -root directory: - -.. code-block:: terminal - - $ cd my-project/ - - # use a specific PHP version - $ echo 7.4 > .php-version - - # use any PHP 8.x version available - $ echo 8 > .php-version - -.. tip:: - - The Symfony server traverses the directory structure up to the root - directory, so you can create a ``.php-version`` file in some parent - directory to set the same PHP version for a group of projects under that - directory. - -Run the command below if you don't remember all the PHP versions installed on your -computer: - -.. code-block:: terminal - - $ symfony local:php:list - - # You'll see all supported SAPIs (CGI, FastCGI, etc.) for each version. - # FastCGI (php-fpm) is used when possible; then CGI (which acts as a FastCGI - # server as well), and finally, the server falls back to plain CGI. - -Overriding PHP Config Options Per Project -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can change the value of any PHP runtime config option per project by creating a -file called ``php.ini`` at the project root directory. Add only the options you want -to override: - -.. code-block:: terminal - - $ cd my-project/ - - # this project only overrides the default PHP timezone - $ cat php.ini - [Date] - date.timezone = Asia/Tokyo - -Running Commands with Different PHP Versions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When running different PHP versions, it is useful to use the main ``symfony`` -command as a wrapper for the ``php`` command. This allows you to always select -the most appropriate PHP version according to the project which is running the -commands. It also loads the env vars automatically, which is important when -running non-Symfony commands: - -.. code-block:: terminal - - # runs the command with the default PHP version - $ php -r "..." - - # runs the command with the PHP version selected by the project - # (or the default PHP version if the project didn't select one) - $ symfony php -r "..." - -Local Domain Names ------------------- - -By default, projects are accessible at some random port of the ``127.0.0.1`` -local IP. However, sometimes it is preferable to associate a domain name to them: - -* It's more convenient when you work continuously on the same project because - port numbers can change but domains don't; -* The behavior of some applications depend on their domains/subdomains; -* To have stable endpoints, such as the local redirection URL for OAuth2. - -Setting up the Local Proxy -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Local domains are possible thanks to a local proxy provided by the Symfony server. -If this is the first time you run the proxy, you must configure it as follows: - -#. Open the **proxy settings** of your operating system: - - * `Proxy settings in Windows`_; - * `Proxy settings in macOS`_; - * `Proxy settings in Ubuntu`_. - -#. Set the following URL as the value of the **Automatic Proxy Configuration**: - - ``http://127.0.0.1:7080/proxy.pac`` - -Now run this command to start the proxy: - -.. code-block:: terminal - - $ symfony proxy:start - -If the proxy doesn't work as explained in the following sections, check these: - -* Some browsers (e.g. Chrome) require to re-apply proxy settings (clicking on - ``Re-apply settings`` button on the ``chrome://net-internals/#proxy`` page) - or a full restart after starting the proxy. Otherwise, you'll see a - *"This webpage is not available"* error (``ERR_NAME_NOT_RESOLVED``); -* Some Operating Systems (e.g. macOS) don't apply by default the proxy settings - to local hosts and domains. You may need to remove ``*.local`` and/or other - IP addresses from that list. -* Windows Operating System **requires** ``localhost`` instead of ``127.0.0.1`` - when configuring the automatic proxy, otherwise you won't be able to access - your local domain from your browser running in Windows. - -Defining the Local Domain -~~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, Symfony proposes ``.wip`` (for *Work in Progress*) for the local -domains. You can define a local domain for your project as follows: - -.. code-block:: terminal - - $ cd my-project/ - $ symfony proxy:domain:attach my-domain - -If you have installed the local proxy as explained in the previous section, you -can now browse ``https://my-domain.wip`` to access your local project with the -new custom domain. - -.. tip:: - - 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: - -.. code-block:: terminal - - # 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 - -.. warning:: - - Although env var names are always defined in uppercase, the ``https_proxy`` - env var `is treated differently`_ than other env vars and its name must be - spelled in lowercase. - -.. tip:: - - 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. - -Long-Running Commands ---------------------- - -Long-running commands, such as the ones that compile front-end web assets, block -the terminal and you can't run other commands at the same time. The Symfony -server provides a ``run`` command to wrap them as follows: - -.. code-block:: terminal - - # compile Webpack assets using Symfony Encore ... but do that in the - # background to not block the terminal - $ symfony run -d npx encore dev --watch - - # continue working and running other commands... - - # from time to time, check the command logs if you want - $ symfony server:log - - # and you can also check if the command is still running - $ symfony server:status - Web server listening on ... - Command "npx ..." running with PID ... - - # stop the web server (and all the associated commands) when you are finished - $ symfony server:stop - -Configuration file ------------------- - -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 - no_workers: true # Do not start workers - -.. warning:: - - 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. - -.. _symfony-server_configuring-workers: - -Configuring Workers -~~~~~~~~~~~~~~~~~~~ - -If you like some processes to start automatically, along with the webserver -(``symfony server:start``), you can set them in the YAML configuration file: - -.. code-block:: yaml - - # .symfony.local.yaml - workers: - # built-in command that builds and watches front-end assets - # npm_encore_watch: - # cmd: ['npx', 'encore', 'dev', '--watch'] - npm_encore_watch: ~ - - # built-in command that starts messenger consumer - # messenger_consume_async: - # cmd: ['symfony', 'console', 'messenger:consume', 'async'] - # watch: ['config', 'src', 'templates', 'vendor'] - messenger_consume_async: ~ - - # you can also add your own custom commands - build_spa: - cmd: ['npm', '--cwd', './spa/', 'dev'] - - # auto start Docker compose when starting server (available since Symfony CLI 5.7.0) - docker_compose: ~ - -.. tip:: - - You may want to not start workers on some environments like CI. You can use the - ``--no-workers`` option to start the server without starting workers. - -.. _symfony-server-docker: - -Docker Integration ------------------- - -The local Symfony server provides full `Docker`_ integration for projects that -use it. To learn more about Docker & Symfony, see :doc:`docker`. - -When the web server detects that Docker Compose is running for the project, it -automatically exposes some environment variables. - -Via the ``docker-compose`` API, it looks for exposed ports used for common -services. When it detects one it knows about, it uses the service name to -expose environment variables. - -Consider the following configuration: - -.. code-block:: yaml - - # compose.yaml - services: - database: - ports: [3306] - -The web server detects that a service exposing port ``3306`` is running for the -project. It understands that this is a MySQL service and creates environment -variables accordingly with the service name (``database``) as a prefix: -``DATABASE_URL``, ``DATABASE_HOST``, ... - -If the service is not in the supported list below, generic environment -variables are set: ``PORT``, ``IP``, and ``HOST``. - -If the ``compose.yaml`` names do not match Symfony's conventions, add a -label to override the environment variables prefix: - -.. code-block:: yaml - - # compose.yaml - services: - db: - ports: [3306] - labels: - com.symfony.server.service-prefix: 'DATABASE' - -In this example, the service is named ``db``, so environment variables would be -prefixed with ``DB_``, but as the ``com.symfony.server.service-prefix`` is set -to ``DATABASE``, the web server creates environment variables starting with -``DATABASE_`` instead as expected by the default Symfony configuration. - -Here is the list of supported services with their ports and default Symfony -prefixes: - -============= ========= ====================== -Service Port Symfony default prefix -============= ========= ====================== -MySQL 3306 ``DATABASE_`` -PostgreSQL 5432 ``DATABASE_`` -Redis 6379 ``REDIS_`` -Memcached 11211 ``MEMCACHED_`` -RabbitMQ 5672 ``RABBITMQ_`` (set user and pass via Docker ``RABBITMQ_DEFAULT_USER`` and ``RABBITMQ_DEFAULT_PASS`` env var) -Elasticsearch 9200 ``ELASTICSEARCH_`` -MongoDB 27017 ``MONGODB_`` (set the database via a Docker ``MONGO_DATABASE`` env var) -Kafka 9092 ``KAFKA_`` -MailCatcher 1025/1080 ``MAILER_`` - or 25/80 -Blackfire 8707 ``BLACKFIRE_`` -Mercure 80 Always exposes ``MERCURE_PUBLIC_URL`` and ``MERCURE_URL`` (only works with the ``dunglas/mercure`` Docker image) -============= ========= ====================== - -You can open web management interfaces for the services that expose them: - -.. code-block:: bash - - $ symfony open:local:webmail - $ symfony open:local:rabbitmq - -Or click on the links in the "Server" section of the web debug toolbar. - -.. tip:: - - To debug and list all exported environment variables, run ``symfony - var:export --debug``. - -.. tip:: - - For some services, the web server also exposes environment variables - understood by CLI tools related to the service. For instance, running - ``symfony run psql`` will connect you automatically to the PostgreSQL server - running in a container without having to specify the username, password, or - database name. - -When Docker services are running, browse a page of your Symfony application and -check the "Symfony Server" section in the web debug toolbar; you'll see that -"Docker Compose" is "Up". - -.. note:: - - If you don't want environment variables to be exposed for a service, set - the ``com.symfony.server.service-ignore`` label to ``true``: - - .. code-block:: yaml - - # compose.yaml - services: - db: - ports: [3306] - labels: - com.symfony.server.service-ignore: true - -If your Docker Compose file is not at the root of the project, use the -``COMPOSE_FILE`` and ``COMPOSE_PROJECT_NAME`` environment variables to define -its location, same as for ``docker-compose``: - -.. code-block:: bash - - # start your containers: - COMPOSE_FILE=docker/compose.yaml COMPOSE_PROJECT_NAME=project_name docker-compose up -d - - # run any Symfony CLI command: - COMPOSE_FILE=docker/compose.yaml COMPOSE_PROJECT_NAME=project_name symfony var:export - -.. note:: - - If you have more than one Docker Compose file, you can provide them all - separated by ``:`` as explained in the `Docker compose CLI env var reference`_. - -.. warning:: - - When using the Symfony binary with ``php bin/console`` (``symfony console ...``), - the binary will **always** use environment variables detected via Docker and will - ignore local environment variables. - For example if you set up a different database name in your ``.env.test`` file - (``DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/test``) and if you run - ``symfony console doctrine:database:drop --force --env=test``, the command will drop the database - defined in your Docker configuration and not the "test" one. - -.. warning:: - - Similar to other web servers, this tool automatically exposes all environment - variables available in the CLI context. Ensure that this local server is not - accessible on your local network without consent to avoid security issues. - -Platform.sh Integration ------------------------ - -The local Symfony server provides full, but optional, integration with -`Platform.sh`_, a service optimized to run your Symfony applications on the -cloud. It provides features such as creating environments, backups/snapshots, -and even access to a copy of the production data from your local machine to -help debug any issues. - -`Read Platform.sh for Symfony technical docs`_. - -.. _`install Symfony`: https://symfony.com/download -.. _`symfony-cli/symfony-cli GitHub repository`: https://github.com/symfony-cli/symfony-cli -.. _`Docker`: https://en.wikipedia.org/wiki/Docker_(software) -.. _`Platform.sh`: https://symfony.com/cloud/ -.. _`Read Platform.sh for Symfony technical docs`: https://symfony.com/doc/current/cloud/index.html -.. _`Proxy settings in Windows`: https://www.dummies.com/computers/operating-systems/windows-10/how-to-set-up-a-proxy-in-windows-10/ -.. _`Proxy settings in macOS`: https://support.apple.com/guide/mac-help/enter-proxy-server-settings-on-mac-mchlp2591/mac -.. _`Proxy settings in Ubuntu`: https://help.ubuntu.com/stable/ubuntu-help/net-proxy.html.en -.. _`is treated differently`: https://superuser.com/a/1799209 -.. _`Docker compose CLI env var reference`: https://docs.docker.com/compose/reference/envvars/ diff --git a/setup/web_server_configuration.rst b/setup/web_server_configuration.rst index 43bd79c10dd..b202adc2443 100644 --- a/setup/web_server_configuration.rst +++ b/setup/web_server_configuration.rst @@ -2,7 +2,7 @@ Configuring a Web Server ======================== The preferred way to develop your Symfony application is to use -:doc:`Symfony Local Web Server `. +:doc:`Symfony Local Web Server `. However, when running the application in the production environment, you'll need to use a fully-featured web server. This article describes how to use Symfony From 0546a2ce4855254b05b7c95cc47c74e547381502 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 1 May 2025 11:52:20 -0400 Subject: [PATCH 631/642] [Console] Document invokable command --- .../console/changing_default_command.rst | 14 +- components/console/events.rst | 28 ++- components/console/helpers/cursor.rst | 8 +- .../console/helpers/debug_formatter.rst | 9 +- .../console/helpers/formatterhelper.rst | 12 +- components/console/helpers/processhelper.rst | 2 +- components/console/helpers/questionhelper.rst | 62 ++++--- components/console/helpers/table.rst | 12 +- components/console/helpers/tree.rst | 11 +- components/console/logger.rst | 5 +- components/console/single_command_tool.rst | 11 +- components/process.rst | 9 +- console.rst | 170 ++++++------------ console/calling_commands.rst | 12 +- console/commands_as_services.rst | 19 +- console/hide_commands.rst | 3 +- console/lockable_trait.rst | 15 +- console/style.rst | 53 +++--- console/verbosity.rst | 13 +- logging/monolog_console.rst | 14 +- reference/attributes.rst | 2 +- routing.rst | 18 +- scheduler.rst | 6 +- 23 files changed, 203 insertions(+), 305 deletions(-) diff --git a/components/console/changing_default_command.rst b/components/console/changing_default_command.rst index c69995ea395..2195bbd2697 100644 --- a/components/console/changing_default_command.rst +++ b/components/console/changing_default_command.rst @@ -9,20 +9,14 @@ name to the ``setDefaultCommand()`` method:: use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; - use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Output\OutputInterface; + use Symfony\Component\Console\Style\SymfonyStyle; - #[AsCommand(name: 'hello:world')] + #[AsCommand(name: 'hello:world', description: 'Outputs "Hello World"')] class HelloWorldCommand extends Command { - protected function configure(): void + public function __invoke(SymfonyStyle $io): int { - $this->setDescription('Outputs "Hello World"'); - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $output->writeln('Hello World'); + $io->writeln('Hello World'); return Command::SUCCESS; } diff --git a/components/console/events.rst b/components/console/events.rst index e550025b7dd..699ba444747 100644 --- a/components/console/events.rst +++ b/components/console/events.rst @@ -209,36 +209,32 @@ method:: for these constants to be available. If you use the Console component inside a Symfony application, commands can -handle signals themselves. To do so, implement the -:class:`Symfony\\Component\\Console\\Command\\SignalableCommandInterface` and subscribe to one or more signals:: +handle signals themselves by subscribing to the :class:`Symfony\\Component\\Console\\Event\\ConsoleSignalEvent` event:: - // src/Command/SomeCommand.php + // src/Command/MyCommand.php namespace App\Command; - use Symfony\Component\Console\Command\Command; - use Symfony\Component\Console\Command\SignalableCommandInterface; + use Symfony\Component\Console\Attribute\AsCommand; + use Symfony\Component\EventDispatcher\Attribute\AsEventListener; - class SomeCommand extends Command implements SignalableCommandInterface + #[AsCommand(name: 'app:my-command')] + class MyCommand { // ... - public function getSubscribedSignals(): array + #[AsEventListener(ConsoleSignalEvent::class)] + public function handleSignal(ConsoleSignalEvent $event): void { - // return here any of the constants defined by PCNTL extension - return [\SIGINT, \SIGTERM]; - } - - public function handleSignal(int $signal): int|false - { - if (\SIGINT === $signal) { + // set here any of the constants defined by PCNTL extension + if (in_array($event->getHandlingSignal(), [\SIGINT, \SIGTERM], true)) { // ... } // ... - // return an integer to set the exit code, or + // set an integer exit code, or // false to continue normal execution - return 0; + $event->setExitCode(0); } } diff --git a/components/console/helpers/cursor.rst b/components/console/helpers/cursor.rst index c5cab6c6d0b..63045f178ad 100644 --- a/components/console/helpers/cursor.rst +++ b/components/console/helpers/cursor.rst @@ -13,16 +13,16 @@ of the output: // src/Command/MyCommand.php namespace App\Command; - use Symfony\Component\Console\Command\Command; + use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Cursor; - use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - class MyCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(OutputInterface $output): int { // ... diff --git a/components/console/helpers/debug_formatter.rst b/components/console/helpers/debug_formatter.rst index 10d3c67a79a..8fa59c319c9 100644 --- a/components/console/helpers/debug_formatter.rst +++ b/components/console/helpers/debug_formatter.rst @@ -10,15 +10,14 @@ this: .. image:: /_images/components/console/debug_formatter.png :alt: Console output, with the first line showing "RUN Running figlet", followed by lines showing the output of the command prefixed with "OUT" and "RES Finished the command" as last line in the output. -Using the debug_formatter +Using the Debug Formatter ------------------------- -The formatter is included in the default helper set and you can get it by -calling :method:`Symfony\\Component\\Console\\Command\\Command::getHelper`:: +The debug formatter helper can be instantiated directly as shown:: - $debugFormatter = $this->getHelper('debug_formatter'); + $debugFormatter = new DebugFormatterHelper(); -The formatter accepts strings and returns a formatted string, which you then +It accepts strings and returns a formatted string, which you then output to the console (or even log the information or do anything else). All methods of this helper have an identifier as the first argument. This is a diff --git a/components/console/helpers/formatterhelper.rst b/components/console/helpers/formatterhelper.rst index d2b19915a3a..4f83e66eecc 100644 --- a/components/console/helpers/formatterhelper.rst +++ b/components/console/helpers/formatterhelper.rst @@ -1,15 +1,11 @@ Formatter Helper ================ -The Formatter helper provides functions to format the output with colors. -You can do more advanced things with this helper than you can in -:doc:`/console/coloring`. +The :class:`Symfony\\Component\\Console\\Helper\\FormatterHelper` helper provides +functions to format the output with colors. You can do more advanced things with +this helper than you can in :doc:`/console/coloring`:: -The :class:`Symfony\\Component\\Console\\Helper\\FormatterHelper` is included -in the default helper set and you can get it by calling -:method:`Symfony\\Component\\Console\\Command\\Command::getHelper`:: - - $formatter = $this->getHelper('formatter'); + $formatter = new FormatterHelper(); The methods return a string, which you'll usually render to the console by passing it to the diff --git a/components/console/helpers/processhelper.rst b/components/console/helpers/processhelper.rst index b46d9f2e95f..df9a8efe45b 100644 --- a/components/console/helpers/processhelper.rst +++ b/components/console/helpers/processhelper.rst @@ -11,7 +11,7 @@ a very verbose verbosity (e.g. ``-vv``):: use Symfony\Component\Process\Process; - $helper = $this->getHelper('process'); + $helper = new ProcessHelper(); $process = new Process(['figlet', 'Symfony']); $helper->run($output, $process); diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst index c7e064b16ca..6d22a2de2af 100644 --- a/components/console/helpers/questionhelper.rst +++ b/components/console/helpers/questionhelper.rst @@ -2,11 +2,9 @@ Question Helper =============== The :class:`Symfony\\Component\\Console\\Helper\\QuestionHelper` provides -functions to ask the user for more information. It is included in the default -helper set and you can get it by calling -:method:`Symfony\\Component\\Console\\Command\\Command::getHelper`:: +functions to ask the user for more information:: - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); The Question Helper has a single method :method:`Symfony\\Component\\Console\\Helper\\QuestionHelper::ask` that needs an @@ -27,18 +25,18 @@ Suppose you want to confirm an action before actually executing it. Add the following to your command:: // ... + use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\ConfirmationQuestion; - class YourCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - // ... - - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $question = new ConfirmationQuestion('Continue with this action?', false); if (!$helper->ask($input, $output, $question)) { @@ -91,7 +89,7 @@ if you want to know a bundle name, you can add this to your command:: use Symfony\Component\Console\Question\Question; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... $question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle'); @@ -121,10 +119,10 @@ but ``red`` could be set instead (could be more explicit):: use Symfony\Component\Console\Question\ChoiceQuestion; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $question = new ChoiceQuestion( 'Please select your favorite color (defaults to red)', // choices can also be PHP objects that implement __toString() method @@ -184,10 +182,10 @@ this use :method:`Symfony\\Component\\Console\\Question\\ChoiceQuestion::setMult use Symfony\Component\Console\Question\ChoiceQuestion; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $question = new ChoiceQuestion( 'Please select your favorite colors (defaults to red and blue)', ['red', 'blue', 'yellow'], @@ -218,10 +216,10 @@ will be autocompleted as the user types:: use Symfony\Component\Console\Question\Question; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $bundles = ['AcmeDemoBundle', 'AcmeBlogBundle', 'AcmeStoreBundle']; $question = new Question('Please enter the name of a bundle', 'FooBundle'); @@ -241,9 +239,9 @@ provide a callback function to dynamically generate suggestions:: use Symfony\Component\Console\Question\Question; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); // This function is called whenever the input changes and new // suggestions are needed. @@ -282,10 +280,10 @@ You can also specify if you want to not trim the answer by setting it directly w use Symfony\Component\Console\Question\Question; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $question = new Question('What is the name of the child?'); $question->setTrimmable(false); @@ -308,10 +306,10 @@ the response to a question should allow multiline answers by passing ``true`` to use Symfony\Component\Console\Question\Question; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $question = new Question('How do you solve world peace?'); $question->setMultiline(true); @@ -335,10 +333,10 @@ convenient for passwords:: use Symfony\Component\Console\Question\Question; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $question = new Question('What is the database password?'); $question->setHidden(true); @@ -372,10 +370,10 @@ convenient for passwords:: use Symfony\Component\Console\Question\ChoiceQuestion; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); QuestionHelper::disableStty(); // ... @@ -396,10 +394,10 @@ method:: use Symfony\Component\Console\Question\Question; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle'); $question->setNormalizer(function (string $value): string { @@ -434,10 +432,10 @@ method:: use Symfony\Component\Console\Question\Question; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle'); $question->setValidator(function (string $answer): string { @@ -494,10 +492,10 @@ You can also use a validator with a hidden question:: use Symfony\Component\Console\Question\Question; // ... - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { // ... - $helper = $this->getHelper('question'); + $helper = new QuestionHelper(); $question = new Question('Please enter your password'); $question->setNormalizer(function (?string $value): string { diff --git a/components/console/helpers/table.rst b/components/console/helpers/table.rst index fe267599caf..e36b1570b70 100644 --- a/components/console/helpers/table.rst +++ b/components/console/helpers/table.rst @@ -10,15 +10,16 @@ features, use the ``Table`` console helper explained in this article. To display a table, use :class:`Symfony\\Component\\Console\\Helper\\Table`, set the headers, set the rows and then render the table:: + use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\Table; - use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; // ... - class SomeCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(OutputInterface $output): int { $table = new Table($output); $table @@ -472,9 +473,10 @@ The only requirement to append rows is that the table must be rendered inside a use Symfony\Component\Console\Helper\Table; // ... - class SomeCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(OutputInterface $output): int { $section = $output->section(); $table = new Table($section); diff --git a/components/console/helpers/tree.rst b/components/console/helpers/tree.rst index b5839b74a26..3c41377ac37 100644 --- a/components/console/helpers/tree.rst +++ b/components/console/helpers/tree.rst @@ -26,22 +26,17 @@ inside your console command:: namespace App\Command; use Symfony\Component\Console\Attribute\AsCommand; - use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\TreeHelper; use Symfony\Component\Console\Helper\TreeNode; - use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; - #[AsCommand(name: 'app:some-command', description: '...')] - class SomeCommand extends Command + #[AsCommand(name: 'app:my-command', description: '...')] + class MyCommand { // ... - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(SymfonyStyle $io): int { - $io = new SymfonyStyle($input, $output); - $node = TreeNode::fromValues([ 'config/', 'public/', diff --git a/components/console/logger.rst b/components/console/logger.rst index c3d5c447a89..cc182821a0a 100644 --- a/components/console/logger.rst +++ b/components/console/logger.rst @@ -34,7 +34,6 @@ You can rely on the logger to use this dependency inside a command:: use Acme\MyDependency; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; - use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Logger\ConsoleLogger; use Symfony\Component\Console\Output\OutputInterface; @@ -42,9 +41,9 @@ You can rely on the logger to use this dependency inside a command:: name: 'my:command', description: 'Use an external dependency requiring a PSR-3 logger' )] - class MyCommand extends Command + class MyCommand { - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(OutputInterface $output): int { $logger = new ConsoleLogger($output); diff --git a/components/console/single_command_tool.rst b/components/console/single_command_tool.rst index 97cb09bf030..9c6b06537e2 100644 --- a/components/console/single_command_tool.rst +++ b/components/console/single_command_tool.rst @@ -9,19 +9,18 @@ it is possible to remove this need by declaring a single command application:: setName('My Super Command') // Optional ->setVersion('1.0.0') // Optional - ->addArgument('foo', InputArgument::OPTIONAL, 'The directory') - ->addOption('bar', null, InputOption::VALUE_REQUIRED) - ->setCode(function (InputInterface $input, OutputInterface $output): int { + ->setCode(function (OutputInterface $output, #[Argument] string $foo = 'The directory', #[Option] string $bar = ''): int { // output arguments and options + + return 0; }) ->run(); diff --git a/components/process.rst b/components/process.rst index 7552537e82e..9c25c931510 100644 --- a/components/process.rst +++ b/components/process.rst @@ -430,11 +430,14 @@ However, if you run the command via the Symfony ``Process`` class, PHP will use the settings defined in the ``php.ini`` file. You can solve this issue by using the :class:`Symfony\\Component\\Process\\PhpSubprocess` class to run the command:: + use Symfony\Component\Console\Attribute\AsCommand; + use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Process\Process; - class MyCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(SymfonyStyle $io): int { // the memory_limit (and any other config option) of this command is // the one defined in php.ini instead of the new values (optionally) @@ -444,6 +447,8 @@ the :class:`Symfony\\Component\\Process\\PhpSubprocess` class to run the command // the memory_limit (and any other config option) of this command takes // into account the values (optionally) passed via the '-d' command option $childProcess = new PhpSubprocess(['bin/console', 'cache:pool:prune']); + + return 0; } } diff --git a/console.rst b/console.rst index 24fab9885da..4a4161a2014 100644 --- a/console.rst +++ b/console.rst @@ -107,26 +107,25 @@ completion (by default, by pressing the Tab key). installation instructions for your shell. The Symfony CLI will provide completion for the ``console`` and ``composer`` commands. +.. _console_creating-command: + Creating a Command ------------------ -Commands are defined in classes extending -:class:`Symfony\\Component\\Console\\Command\\Command`. For example, you may -want a command to create a user:: +Commands are defined in classes, for example, you may want a command to create a user. Use +the ``#[AsCommand]`` attribute to auto-register it:: // src/Command/CreateUserCommand.php namespace App\Command; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; - use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Output\OutputInterface; // the name of the command is what users type after "php bin/console" #[AsCommand(name: 'app:create-user')] - class CreateUserCommand extends Command + class CreateUserCommand { - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(): int { // ... put here the code to create the user @@ -147,104 +146,54 @@ want a command to create a user:: } } -Configuring the Command -~~~~~~~~~~~~~~~~~~~~~~~ - -You can optionally define a description, help message and the -:doc:`input options and arguments ` by overriding the -``configure()`` method:: +If you can't use PHP attributes, register the command as a service and +:doc:`tag it ` with the ``console.command`` tag. If you're using the +:ref:`default services.yaml configuration `, +this is already done for you, thanks to :ref:`autoconfiguration `. - // src/Command/CreateUserCommand.php +You can also use ``#[AsCommand]`` to add a description and longer help text for the command:: - // ... - class CreateUserCommand extends Command + #[AsCommand( + name: 'app:create-user', + description: 'Creates a new user.', // the command description shown when running "php bin/console list" + help: 'This command allows you to create a user...', // the command help shown when running the command with the "--help" option + )] + class CreateUserCommand { - // ... - protected function configure(): void + public function __invoke(): int { - $this - // the command description shown when running "php bin/console list" - ->setDescription('Creates a new user.') - // the command help shown when running the command with the "--help" option - ->setHelp('This command allows you to create a user...') - ; + // ... } } -.. tip:: - - Using the ``#[AsCommand]`` attribute to define a description instead of - using the ``setDescription()`` method allows to get the command description without - instantiating its class. This makes the ``php bin/console list`` command run - much faster. - - If you want to always run the ``list`` command fast, add the ``--short`` option - to it (``php bin/console list --short``). This will avoid instantiating command - classes, but it won't show any description for commands that use the - ``setDescription()`` method instead of the attribute to define the command - description. - -The ``configure()`` method is called automatically at the end of the command -constructor. If your command defines its own constructor, set the properties -first and then call to the parent constructor, to make those properties -available in the ``configure()`` method:: +Additionally, you can extend the :class:`Symfony\\Component\\Console\\Command\\Command` class to +leverage advanced features like lifecycle hooks (e.g. :method:`Symfony\\Component\\Console\\Command\\Command::initialize` and +and :method:`Symfony\\Component\\Console\\Command\\Command::interact`):: - // ... + use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; - use Symfony\Component\Console\Input\InputArgument; + use Symfony\Component\Console\Input\InputInterface; + use Symfony\Component\Console\Output\OutputInterface; + #[AsCommand(name: 'app:create-user')] class CreateUserCommand extends Command { - // ... - - public function __construct(bool $requirePassword = false) + public function initialize(InputInterface $input, OutputInterface $output): void { - // best practices recommend to call the parent constructor first and - // then set your own properties. That wouldn't work in this case - // because configure() needs the properties set in this constructor - $this->requirePassword = $requirePassword; - - parent::__construct(); + // ... } - protected function configure(): void + public function interact(InputInterface $input, OutputInterface $output): void { - $this - // ... - ->addArgument('password', $this->requirePassword ? InputArgument::REQUIRED : InputArgument::OPTIONAL, 'User password') - ; + // ... } - } - -.. _console_registering-the-command: - -Registering the Command -~~~~~~~~~~~~~~~~~~~~~~~ -You can register the command by adding the ``AsCommand`` attribute to it:: - - // src/Command/CreateUserCommand.php - namespace App\Command; - - use Symfony\Component\Console\Attribute\AsCommand; - use Symfony\Component\Console\Command\Command; - - #[AsCommand( - name: 'app:create-user', - description: 'Creates a new user.', - hidden: false, - aliases: ['app:add-user'] - )] - class CreateUserCommand extends Command - { - // ... + public function __invoke(): int + { + // ... + } } -If you can't use PHP attributes, register the command as a service and -:doc:`tag it ` with the ``console.command`` tag. If you're using the -:ref:`default services.yaml configuration `, -this is already done for you, thanks to :ref:`autoconfiguration `. - Running the Command ~~~~~~~~~~~~~~~~~~~ @@ -255,16 +204,16 @@ After configuring and registering the command, you can run it in the terminal: $ php bin/console app:create-user As you might expect, this command will do nothing as you didn't write any logic -yet. Add your own logic inside the ``execute()`` method. +yet. Add your own logic inside the ``__invoke()`` method. Console Output -------------- -The ``execute()`` method has access to the output stream to write messages to +The ``__invoke()`` method has access to the output stream to write messages to the console:: // ... - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(OutputInterface $output): int { // outputs multiple lines to the console (adding "\n" at the end of each line) $output->writeln([ @@ -315,9 +264,10 @@ method, which returns an instance of // ... use Symfony\Component\Console\Output\ConsoleOutputInterface; - class MyCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(OutputInterface $output): int { if (!$output instanceof ConsoleOutputInterface) { throw new \LogicException('This command accepts only an instance of "ConsoleOutputInterface".'); @@ -376,20 +326,12 @@ Console Input Use input options or arguments to pass information to the command:: - use Symfony\Component\Console\Input\InputArgument; + use Symfony\Component\Console\Attribute\Argument; - // ... - protected function configure(): void - { - $this - // configure an argument - ->addArgument('username', InputArgument::REQUIRED, 'The username of the user.') - // ... - ; - } - - // ... - public function execute(InputInterface $input, OutputInterface $output): int + // The #[Argument] attribute configures $username as a + // required input argument and its value is automatically + // passed to this parameter + public function __invoke(#[Argument('The username of the user.')] string $username, OutputInterface $output): int { $output->writeln([ 'User Creator', @@ -397,8 +339,7 @@ Use input options or arguments to pass information to the command:: '', ]); - // retrieve the argument value using getArgument() - $output->writeln('Username: '.$input->getArgument('username')); + $output->writeln('Username: '.$username); return Command::SUCCESS; } @@ -428,23 +369,22 @@ as a service, you can use normal dependency injection. Imagine you have a // ... use App\Service\UserManager; - use Symfony\Component\Console\Command\Command; + use Symfony\Component\Console\Attribute\Argument; + use Symfony\Component\Console\Attribute\AsCommand; - class CreateUserCommand extends Command + #[AsCommand(name: 'app:create-user')] + class CreateUserCommand { public function __construct( - private UserManager $userManager, - ){ - parent::__construct(); + private UserManager $userManager + ) { } - // ... - - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(#[Argument] string $username, OutputInterface $output): int { // ... - $this->userManager->create($input->getArgument('username')); + $this->userManager->create($username); $output->writeln('User successfully generated!'); @@ -472,7 +412,7 @@ command: Note that it will not be called when the command is run without interaction (e.g. when passing the ``--no-interaction`` global option flag). -:method:`Symfony\\Component\\Console\\Command\\Command::execute` *(required)* +``__invoke()`` *(required)* This method is executed after ``interact()`` and ``initialize()``. It contains the logic you want the command to execute and it must return an integer which will be used as the command `exit status`_. diff --git a/console/calling_commands.rst b/console/calling_commands.rst index dd1f0b12ff9..875ead15d2d 100644 --- a/console/calling_commands.rst +++ b/console/calling_commands.rst @@ -14,20 +14,18 @@ arguments and options you want to pass to the command. The command name must be the first argument. Eventually, calling the ``doRun()`` method actually runs the command and returns -the returned code from the command (return value from command ``execute()`` +the returned code from the command (return value from command ``__invoke()`` method):: // ... - use Symfony\Component\Console\Command; + use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\ArrayInput; - use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - class CreateUserCommand extends Command + #[AsCommand(name: 'app:create-user')] + class CreateUserCommand { - // ... - - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(OutputInterface $output): int { $greetInput = new ArrayInput([ // the command name is passed as first argument diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst index 1393879a1df..ed5b99f9cb4 100644 --- a/console/commands_as_services.rst +++ b/console/commands_as_services.rst @@ -16,27 +16,16 @@ For example, suppose you want to log something from within your command:: use Psr\Log\LoggerInterface; use Symfony\Component\Console\Attribute\AsCommand; - use Symfony\Component\Console\Command\Command; - use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Output\OutputInterface; - #[AsCommand(name: 'app:sunshine')] - class SunshineCommand extends Command + #[AsCommand(name: 'app:sunshine', description: 'Good morning!')] + class SunshineCommand { public function __construct( private LoggerInterface $logger, ) { - // you *must* call the parent constructor - parent::__construct(); - } - - protected function configure(): void - { - $this - ->setDescription('Good morning!'); } - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(): int { $this->logger->info('Waking up the sun'); // ... @@ -70,7 +59,7 @@ To make your command lazily loaded, either define its name using the PHP // ... #[AsCommand(name: 'app:sunshine')] - class SunshineCommand extends Command + class SunshineCommand { // ... } diff --git a/console/hide_commands.rst b/console/hide_commands.rst index 44a69d09289..4ab9d3a6dad 100644 --- a/console/hide_commands.rst +++ b/console/hide_commands.rst @@ -15,10 +15,9 @@ the ``hidden`` property of the ``AsCommand`` attribute:: namespace App\Command; use Symfony\Component\Console\Attribute\AsCommand; - use Symfony\Component\Console\Command\Command; #[AsCommand(name: 'app:legacy', hidden: true)] - class LegacyCommand extends Command + class LegacyCommand { // ... } diff --git a/console/lockable_trait.rst b/console/lockable_trait.rst index 0f4a4900e17..2a4fd64ffaf 100644 --- a/console/lockable_trait.rst +++ b/console/lockable_trait.rst @@ -13,19 +13,17 @@ that adds two convenient methods to lock and release commands:: // ... use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\LockableTrait; - use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Output\OutputInterface; + use Symfony\Component\Console\Style\SymfonyStyle; - class UpdateContentsCommand extends Command + #[AsCommand(name: 'contents:update')] + class UpdateContentsCommand { use LockableTrait; - // ... - - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(SymfonyStyle $io): int { if (!$this->lock()) { - $output->writeln('The command is already running in another process.'); + $io->writeln('The command is already running in another process.'); return Command::SUCCESS; } @@ -52,7 +50,8 @@ a ``$lockFactory`` property with your own lock factory:: use Symfony\Component\Console\Command\LockableTrait; use Symfony\Component\Lock\LockFactory; - class UpdateContentsCommand extends Command + #[AsCommand(name: 'contents:update')] + class UpdateContentsCommand { use LockableTrait; diff --git a/console/style.rst b/console/style.rst index e1e5df38ffe..a9f71a9d7a8 100644 --- a/console/style.rst +++ b/console/style.rst @@ -7,18 +7,18 @@ questions to the user involves a lot of repetitive code. Consider for example the code used to display the title of the following command:: - // src/Command/GreetCommand.php + // src/Command/MyCommand.php namespace App\Command; + use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - class GreetCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - // ... - - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { $output->writeln([ 'Lorem Ipsum Dolor Sit Amet', @@ -42,26 +42,22 @@ which allow to create *semantic* commands and forget about their styling. Basic Usage ----------- -In your command, instantiate the :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle` -class and pass the ``$input`` and ``$output`` variables as its arguments. Then, -you can start using any of its helpers, such as ``title()``, which displays the -title of the command:: +In your ``__invoke()`` method, add an argument of type :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle`. +Then, you can start using any of its helpers, such as ``title()``, which +displays the title of the command:: - // src/Command/GreetCommand.php + // src/Command/MyCommand.php namespace App\Command; + use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; - use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; - class GreetCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - // ... - - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(SymfonyStyle $io): int { - $io = new SymfonyStyle($input, $output); $io->title('Lorem Ipsum Dolor Sit Amet'); // ... @@ -448,19 +444,17 @@ long they are. This is done to enable clickable URLs in terminals that support t If you prefer to wrap all contents, including URLs, use this method:: - // src/Command/GreetCommand.php + // src/Command/MyCommand.php namespace App\Command; // ... use Symfony\Component\Console\Style\SymfonyStyle; - class GreetCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - // ... - - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(SymfonyStyle $io): int { - $io = new SymfonyStyle($input, $output); $io->getOutputWrapper()->setAllowCutUrls(true); // ... @@ -487,7 +481,7 @@ Then, instantiate this custom class instead of the default ``SymfonyStyle`` in your commands. Thanks to the ``StyleInterface`` you won't need to change the code of your commands to change their appearance:: - // src/Command/GreetCommand.php + // src/Command/MyCommand.php namespace App\Console; use App\Console\CustomStyle; @@ -495,16 +489,11 @@ of your commands to change their appearance:: use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - class GreetCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - // ... - - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(InputInterface $input, OutputInterface $output): int { - // Before - $io = new SymfonyStyle($input, $output); - - // After $io = new CustomStyle($input, $output); // ... diff --git a/console/verbosity.rst b/console/verbosity.rst index 9910dca0c3d..cbbb1663895 100644 --- a/console/verbosity.rst +++ b/console/verbosity.rst @@ -49,21 +49,22 @@ It is possible to print a message in a command for only a specific verbosity level. For example:: // ... + use Symfony\Component\Console\Attribute\Argument; + use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - class CreateUserCommand extends Command + #[AsCommand(name: 'app:create-user')] + class CreateUserCommand { - // ... - - public function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(OutputInterface $output, #[Argument] string $username, #[Argument] string $password): int { $user = new User(...); $output->writeln([ - 'Username: '.$input->getArgument('username'), - 'Password: '.$input->getArgument('password'), + 'Username: '.$username, + 'Password: '.$password, ]); // available methods: ->isSilent(), ->isQuiet(), ->isVerbose(), ->isVeryVerbose(), ->isDebug() diff --git a/logging/monolog_console.rst b/logging/monolog_console.rst index 67bf0f5acae..4d007abe854 100644 --- a/logging/monolog_console.rst +++ b/logging/monolog_console.rst @@ -10,10 +10,9 @@ When a lot of logging has to happen, it's cumbersome to print information depending on the verbosity settings (``-v``, ``-vv``, ``-vvv``) because the calls need to be wrapped in conditions. For example:: - use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(OutputInterface $output): int { if ($output->isDebug()) { $output->writeln('Some info'); @@ -34,23 +33,22 @@ the current log level and the console verbosity. The example above could then be rewritten as:: - // src/Command/YourCommand.php + // src/Command/MyCommand.php namespace App\Command; use Psr\Log\LoggerInterface; + use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; - use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Output\OutputInterface; - class YourCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { public function __construct( private LoggerInterface $logger, ) { - parent::__construct(); } - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(): int { $this->logger->debug('Some info'); $this->logger->notice('Some more info'); diff --git a/reference/attributes.rst b/reference/attributes.rst index eb09f4aa6bc..968c7df1568 100644 --- a/reference/attributes.rst +++ b/reference/attributes.rst @@ -14,7 +14,7 @@ Doctrine Bridge Command ~~~~~~~ -* :ref:`AsCommand ` +* :ref:`AsCommand ` Contracts ~~~~~~~~~ diff --git a/routing.rst b/routing.rst index bf3946b343d..f299fb5140d 100644 --- a/routing.rst +++ b/routing.rst @@ -2669,23 +2669,23 @@ The solution is to configure the ``default_uri`` option to define the Now you'll get the expected results when generating URLs in your commands:: - // src/Command/SomeCommand.php + // src/Command/MyCommand.php namespace App\Command; - use Symfony\Component\Console\Command\Command; - use Symfony\Component\Console\Input\InputInterface; - use Symfony\Component\Console\Output\OutputInterface; + use Symfony\Component\Console\Attribute\AsCommand; + use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; // ... - class SomeCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand { - public function __construct(private UrlGeneratorInterface $urlGenerator) - { - parent::__construct(); + public function __construct( + private UrlGeneratorInterface $urlGenerator, + ) { } - protected function execute(InputInterface $input, OutputInterface $output): int + public function __invoke(SymfonyStyle $io): int { // generate a URL with no route arguments $signUpPage = $this->urlGenerator->generate('sign_up'); diff --git a/scheduler.rst b/scheduler.rst index 59dfa121361..3bf6735b1e0 100644 --- a/scheduler.rst +++ b/scheduler.rst @@ -478,7 +478,8 @@ The attribute takes more parameters to customize the trigger:: // when applying this attribute to a Symfony console command, you can pass // arguments and options to the command using the 'arguments' option: #[AsCronTask('0 0 * * *', arguments: 'some_argument --some-option --another-option=some_value')] - class MyCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand .. _scheduler-attributes-periodic-task: @@ -527,7 +528,8 @@ The ``#[AsPeriodicTask]`` attribute takes many parameters to customize the trigg // when applying this attribute to a Symfony console command, you can pass // arguments and options to the command using the 'arguments' option: #[AsPeriodicTask(frequency: '1 day', arguments: 'some_argument --some-option --another-option=some_value')] - class MyCommand extends Command + #[AsCommand(name: 'app:my-command')] + class MyCommand Managing Scheduled Messages --------------------------- From 26050d0675ef582764b497129d2be55621b562b9 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 1 Jul 2025 12:34:44 +0200 Subject: [PATCH 632/642] Minor tweaks --- components/console/helpers/formatterhelper.rst | 2 +- console.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/components/console/helpers/formatterhelper.rst b/components/console/helpers/formatterhelper.rst index 4f83e66eecc..cf9bacdeb9c 100644 --- a/components/console/helpers/formatterhelper.rst +++ b/components/console/helpers/formatterhelper.rst @@ -3,7 +3,7 @@ Formatter Helper The :class:`Symfony\\Component\\Console\\Helper\\FormatterHelper` helper provides functions to format the output with colors. You can do more advanced things with -this helper than you can in :doc:`/console/coloring`:: +this helper than you can with the :doc:`basic colors and styles `:: $formatter = new FormatterHelper(); diff --git a/console.rst b/console.rst index 4a4161a2014..a3d6701f47c 100644 --- a/console.rst +++ b/console.rst @@ -112,8 +112,8 @@ completion (by default, by pressing the Tab key). Creating a Command ------------------ -Commands are defined in classes, for example, you may want a command to create a user. Use -the ``#[AsCommand]`` attribute to auto-register it:: +Commands are defined in classes and auto-registered using the ``#[AsCommand]`` +attribute. For example, you may want a command to create a user:: // src/Command/CreateUserCommand.php namespace App\Command; @@ -412,7 +412,7 @@ command: Note that it will not be called when the command is run without interaction (e.g. when passing the ``--no-interaction`` global option flag). -``__invoke()`` *(required)* +``__invoke()`` (or :method:`Symfony\\Component\\Console\\Command\\Command::execute`) *(required)* This method is executed after ``interact()`` and ``initialize()``. It contains the logic you want the command to execute and it must return an integer which will be used as the command `exit status`_. From 6276aca10b811523cc21a7b05244cc94ae424fc7 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 1 Jul 2025 13:41:12 +0200 Subject: [PATCH 633/642] Minor tweaks --- components/filesystem.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/components/filesystem.rst b/components/filesystem.rst index 1d8e2452fd0..de1075f0b36 100644 --- a/components/filesystem.rst +++ b/components/filesystem.rst @@ -321,7 +321,8 @@ Dealing with file paths usually involves some difficulties: - Platform differences: file paths look different on different platforms. UNIX file paths start with a slash ("/"), while Windows file paths start with a system drive ("C:"). UNIX uses forward slashes, while Windows uses backslashes - by default. + by default. However, Windows also accepts forward slashes, so both types of + separators generally work. - Absolute/relative paths: web applications frequently need to deal with absolute and relative paths. Converting one to the other properly is tricky and repetitive. @@ -371,20 +372,21 @@ string concatenation for building file paths:: echo Path::join('C:\\Program Files', 'PHP', 'php.ini'); // => C:/Program Files/PHP/php.ini + // (both forward slashes and backslashes work on Windows) -The method handles multiple scenarios correctly: +The ``join()`` method handles multiple scenarios correctly: -- Empty parts are ignored:: +Empty parts are ignored:: echo Path::join('/var/www', '', 'config.ini'); // => /var/www/config.ini -- Leading slashes in subsequent arguments are removed:: +Leading slashes in subsequent arguments are removed:: echo Path::join('/var/www', '/etc', 'config.ini'); // => /var/www/etc/config.ini -- Trailing slashes are preserved only for root paths:: +Trailing slashes are preserved only for root paths:: echo Path::join('/var/www', 'vhost/'); // => /var/www/vhost @@ -392,7 +394,7 @@ The method handles multiple scenarios correctly: echo Path::join('/', ''); // => / -- Works with any number of arguments:: +Works with any number of arguments:: echo Path::join('/var', 'www', 'vhost', 'symfony', 'config', 'config.ini'); // => /var/www/vhost/symfony/config/config.ini From c836a4b8ba586b7a2d944b9584bd47f47ef0db78 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 1 Jul 2025 17:48:51 +0200 Subject: [PATCH 634/642] Tweaks and fixes --- setup/symfony_cli.rst | 404 ++++++++++++++++++++++++++++++++---------- 1 file changed, 310 insertions(+), 94 deletions(-) diff --git a/setup/symfony_cli.rst b/setup/symfony_cli.rst index 6bd6e047341..fbcdbef6d0a 100644 --- a/setup/symfony_cli.rst +++ b/setup/symfony_cli.rst @@ -4,24 +4,22 @@ Symfony CLI =========== -The Symfony CLI is a developer tool to help you build, run, and manage your -Symfony applications directly from your terminal. It's designed to boost your -productivity with smart features like: +The **Symfony CLI** is a free and `open source`_ developer tool to help you build, +run, and manage your Symfony applications directly from your terminal. It's designed +to boost your productivity with smart features like: -* Local web server optimized for development -* Seamless integration with Platform.sh services -* Docker support with automatic environment variable management -* Multiple PHP version management -* Background worker management -* And much more! +* **Web server** optimized for development, with **HTTPS support** +* **Docker** integration and automatic environment variable management +* Management of muktiple **PHP versions** +* Support for background **workers** +* Seamless integration with **Symfony Cloud** Installation ------------ The Symfony CLI is available as a standalone executable that supports Linux, -macOS, and Windows. - -Download and install it following the instructions on `symfony.com/download`_. +macOS, and Windows. Download and install it following the instructions on +`symfony.com/download`_. Shell Autocompletion ~~~~~~~~~~~~~~~~~~~~ @@ -31,62 +29,58 @@ helps you type commands faster and discover available options: .. code-block:: terminal - # Install autocompletion (do this only once) + # install autocompletion (do this only once) $ symfony completion bash | sudo tee /etc/bash_completion.d/symfony - # For Zsh users + # for Zsh users $ symfony completion zsh > ~/.symfony_completion && echo "source ~/.symfony_completion" >> ~/.zshrc - # For Fish users + # for Fish users $ symfony completion fish | source After installation, restart your terminal to enable autocompletion. The CLI will also provide autocompletion for ``composer`` and ``console`` commands when it detects a Symfony project. -.. note:: - - You can view and contribute to the Symfony CLI source in the - `symfony-cli/symfony-cli GitHub repository`_. - Creating New Symfony Applications --------------------------------- -The Symfony CLI includes a powerful project creation command that helps you -start new projects quickly: +The Symfony CLI includes a project creation command that helps you start new +projects quickly: .. code-block:: terminal - # Create a new Symfony project (latest stable version) + # create a new Symfony project based on the latest stable version $ symfony new my_project - # Create a project with the latest LTS (Long Term Support) version + # create a project with the latest LTS (Long Term Support) version $ symfony new my_project --version=lts - # Create a project based on a specific Symfony version + # create a project based on a specific Symfony version $ symfony new my_project --version=6.4 - # Create a project using the development version + # create a project using the development version $ symfony new my_project --version=next - # Create a minimal project (microkernel) - $ symfony new my_project --version=6.4 + # all the previous commands create minimal projects with the least + # amount of dependencies possible; if you are building a website or + # web application, add this option to install all the common dependencies + $ symfony new my_project --webapp - # Create a demo application + # Create a project based on the Symfony Demo application $ symfony new my_project --demo .. tip:: - Pass the ``--cloud`` option to initialize a Platform.sh project at the same - time the Symfony project is created. Pass the ``--webapp`` option if you - prefer a project where Twig is already configured. + Pass the ``--cloud`` option to initialize a Symfony Cloud project at the same + time the Symfony project is created. Running the Local Web Server ---------------------------- -The Symfony CLI includes a powerful local web server for development. This -server is not intended for production use but provides features that make -development more productive: +The Symfony CLI includes a **local web server** designed for development. It's +not intended for production use, but it provides features that improve the +developer experience: * HTTPS support with automatic certificate generation * HTTP/2 support @@ -94,8 +88,10 @@ development more productive: * Integration with Docker services * Built-in proxy for custom domain names -Getting Started -~~~~~~~~~~~~~~~ +.. _getting-started: + +Serving Your Application +~~~~~~~~~~~~~~~~~~~~~~~~ To serve a Symfony project with the local server: @@ -107,77 +103,117 @@ To serve a Symfony project with the local server: [OK] Web server listening on http://127.0.0.1:8000 ... - # Browse the site using your default browser +Now browse the given URL or run the following command to open it in the browser: + +.. code-block:: terminal + $ symfony open:local -Running the server this way displays log messages in the console. To run it in -the background: +.. tip:: + + If you work on more than one project, you can run multiple instances of the + Symfony server on your development machine. Each instance will find a different + available port. + +The ``server:start`` command blocks the current terminal to output the server +logs. To run the server in the background: .. code-block:: terminal $ symfony server:start -d - # Continue working and running other commands... +Now you can continue working in the terminal and run other commands: - # View the latest log messages +.. code-block:: terminal + + # view the latest log messages $ symfony server:log - # Stop the background server + # stop the background server $ symfony server:stop .. tip:: - On macOS, you might see a warning about accepting incoming network - connections. To fix this, sign the Symfony binary: + On macOS, when starting the Symfony server you might see a warning dialog asking + *"Do you want the application to accept incoming network connections?"*. + This happens when running unsigned applications that are not listed in the + firewall list. The solution is to run this command to sign the Symfony CLI: .. code-block:: terminal $ sudo codesign --force --deep --sign - $(whereis -q symfony) +Enabling PHP-FPM +~~~~~~~~~~~~~~~~ + +.. note:: + + PHP-FPM must be installed locally for the Symfony server to utilize. + +When the server starts, it checks for ``web/index_dev.php``, ``web/index.php``, +``public/app_dev.php``, ``public/app.php`` in that order. If one is found, the +server will automatically start with PHP-FPM enabled. Otherwise the server will +start without PHP-FPM and will show a ``Page not found`` page when trying to +access a ``.php`` file in the browser. + +.. tip:: + + When an ``index.html`` and a front controller (e.g. ``index.php``) are both + present, the server will still start with PHP-FPM enabled, but the + ``index.html`` will take precedence. This means that if an ``index.html`` + file is present in ``public/`` or ``web/``, it will be displayed instead of + the ``index.php``, which would otherwise show, for example, the Symfony + application. + Enabling HTTPS/TLS ~~~~~~~~~~~~~~~~~~ Running your application over HTTPS locally helps detect mixed content issues -early and allows using features that require secure connections: +early and allows using features that require secure connections. Traditionally, +this has been painful and complicated to set up, but the Symfony server automates +everything for you: .. code-block:: terminal - # Install the certificate authority (only once) + # install the certificate authority (run this only once on your machine) $ symfony server:ca:install - # Now start your server - it will use HTTPS automatically + # now start (or restart) your server; it will use HTTPS automatically $ symfony server:start .. tip:: - For WSL (Windows Subsystem for Linux) users, manually import the certificate - authority in Windows: + For WSL (Windows Subsystem for Linux), the newly created local certificate + authority needs to be imported manually: .. code-block:: terminal $ explorer.exe `wslpath -w $HOME/.symfony5/certs` - Then double-click on ``default.p12`` to import it. + In the file explorer window that just opened, double-click on the file + called ``default.p12``. PHP Management -------------- -The Symfony CLI provides powerful PHP management features, allowing you to use -different PHP versions for different projects. +The Symfony CLI provides PHP management features, allowing you to use different +PHP versions and/or settings for different projects. Selecting PHP Version ~~~~~~~~~~~~~~~~~~~~~ -Create a ``.php-version`` file at your project root: +If you have multiple PHP versions installed on your computer, you can tell +Symfony which one to use creating a file called ``.php-version`` at the project +root directory: .. code-block:: terminal $ cd my-project/ - # Use a specific PHP version + # use a specific PHP version $ echo 8.2 > .php-version - # Use any PHP 8.x version available + # use any PHP 8.x version available $ echo 8 > .php-version To see all available PHP versions: @@ -201,7 +237,7 @@ root: ; php.ini [Date] - date.timezone = Europe/Paris + date.timezone = Asia/Tokyo [PHP] memory_limit = 256M @@ -213,43 +249,74 @@ Use ``symfony php`` to ensure commands run with the correct PHP version: .. code-block:: terminal - # Runs with the system's default PHP + # runs with the system's default PHP $ php -v - # Runs with the project's PHP version + # runs with the project's PHP version $ symfony php -v - # This also works for Composer + # this also works for Composer $ symfony composer install Local Domain Names ------------------ -Instead of using ``127.0.0.1:8000``, you can use custom domain names like -``https://my-app.wip``. +By default, projects are accessible at a random port on the ``127.0.0.1`` +local IP. However, sometimes it is preferable to associate a domain name +(e.g. ``my-app.wip``) with them: + +* it's more convenient when working continuously on the same project because + port numbers can change but domains don't; +* the behavior of some applications depends on their domains/subdomains; +* to have stable endpoints, such as the local redirection URL for OAuth2. Setting up the Local Proxy ~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. Configure your system to use the Symfony proxy: +The Symfony CLI includes a proxy that allows using custom local domains. The +first time you use it, you must configure it as follows: - * Open your system's proxy settings - * Set ``http://127.0.0.1:7080/proxy.pac`` as the automatic proxy configuration URL +#. Open the **proxy settings** of your operating system: -2. Start the proxy: + * `Proxy settings in Windows`_; + * `Proxy settings in macOS`_; + * `Proxy settings in Ubuntu`_. - .. code-block:: terminal +#. Set the following URL as the value of the **Automatic Proxy Configuration**: - $ symfony proxy:start + ``http://127.0.0.1:7080/proxy.pac`` -3. Attach a domain to your project: +Now run this command to start the proxy: + +.. code-block:: terminal - .. code-block:: terminal + $ symfony proxy:start - $ cd my-project/ - $ symfony proxy:domain:attach my-app +If the proxy doesn't work as explained in the following sections, check the following: - Your application is now available at ``https://my-app.wip`` +* Some browsers (e.g. Chrome) require reapplying proxy settings (clicking on + ``Re-apply settings`` button on the ``chrome://net-internals/#proxy`` page) + or a full restart after starting the proxy. Otherwise, you'll see a + *"This webpage is not available"* error (``ERR_NAME_NOT_RESOLVED``); +* Some Operating Systems (e.g. macOS) don't apply proxy settings to local hosts + and domains by default. You may need to remove ``*.local`` and/or other + IP addresses from that list. +* Windows **requires** using ``localhost`` instead of ``127.0.0.1`` when + configuring the automatic proxy, otherwise you won't be able to access + your local domain from your browser running in Windows. + +Defining the Local Domain +~~~~~~~~~~~~~~~~~~~~~~~~~ + +By default, Symfony uses ``.wip`` (for *Work in Progress*) as the local TLD for +custom domains. You can define a local domain for your project as follows: + +.. code-block:: terminal + + $ cd my-project/ + $ symfony proxy:domain:attach my-app + +Your application is now available at ``https://my-app.wip`` .. tip:: @@ -264,11 +331,38 @@ You can also use wildcards: This allows accessing subdomains like ``https://api.my-app.wip`` or ``https://admin.my-app.wip``. +When running console commands, set the ``https_proxy`` environment variable +to make custom domains work: + +.. code-block:: terminal + + # 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 + +.. warning:: + + Although environment variable names are typically uppercase, the ``https_proxy`` + variable `is treated differently`_ and must be written in lowercase. + +.. tip:: + + 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. + .. _symfony-server-docker: Docker Integration ------------------ +The Symfony CLI provides full `Docker`_ integration for projects that +use it. To learn more about Docker and Symfony, see :doc:`docker`. The local server automatically detects Docker services and exposes their connection information as environment variables. @@ -284,18 +378,108 @@ With this ``compose.yaml``: image: mysql:8 ports: [3306] -The server automatically creates these environment variables: +The web server detects that a service exposing port ``3306`` is running for the +project. It understands that this is a MySQL service and creates environment +variables accordingly, using the service name (``database``) as a prefix: * ``DATABASE_URL`` * ``DATABASE_HOST`` * ``DATABASE_PORT`` -Supported services include MySQL, PostgreSQL, Redis, RabbitMQ, Elasticsearch, -MongoDB, and more. +Here is a list of supported services with their ports and default Symfony prefixes: + +============= ========= ====================== +Service Port Symfony default prefix +============= ========= ====================== +MySQL 3306 ``DATABASE_`` +PostgreSQL 5432 ``DATABASE_`` +Redis 6379 ``REDIS_`` +Memcached 11211 ``MEMCACHED_`` +RabbitMQ 5672 ``RABBITMQ_`` (set user and pass via Docker ``RABBITMQ_DEFAULT_USER`` and ``RABBITMQ_DEFAULT_PASS`` env var) +Elasticsearch 9200 ``ELASTICSEARCH_`` +MongoDB 27017 ``MONGODB_`` (set the database via a Docker ``MONGO_DATABASE`` env var) +Kafka 9092 ``KAFKA_`` +MailCatcher 1025/1080 ``MAILER_`` + or 25/80 +Blackfire 8707 ``BLACKFIRE_`` +Mercure 80 Always exposes ``MERCURE_PUBLIC_URL`` and ``MERCURE_URL`` (only works with the ``dunglas/mercure`` Docker image) +============= ========= ====================== + +If the service is not supported, generic environment variables are set: +``PORT``, ``IP``, and ``HOST``. + +You can open web management interfaces for the services that expose them +by clicking on the links in the "Server" section of the web debug toolbar +or by running these commands: + +.. code-block:: bash + + $ symfony open:local:webmail + $ symfony open:local:rabbitmq .. tip:: - Run ``symfony var:export`` to see all exposed environment variables. + To debug and list all exported environment variables, run: + ``symfony var:export --debug``. + +.. tip:: + + For some services, the local web server also exposes environment variables + understood by CLI tools related to the service. For instance, running + ``symfony run psql`` will connect you automatically to the PostgreSQL server + running in a container without having to specify the username, password, or + database name. + +When Docker services are running, browse a page of your Symfony application and +check the "Symfony Server" section in the web debug toolbar. You'll see that +"Docker Compose" is marked as "Up". + +.. note:: + + If you don't want environment variables to be exposed for a service, set + the ``com.symfony.server.service-ignore`` label to ``true``: + + .. code-block:: yaml + + # compose.yaml + services: + db: + ports: [3306] + labels: + com.symfony.server.service-ignore: true + +If your Docker Compose file is not at the root of the project, use the +``COMPOSE_FILE`` and ``COMPOSE_PROJECT_NAME`` environment variables to define +its location, same as for ``docker-compose``: + +.. code-block:: bash + + # start your containers: + COMPOSE_FILE=docker/compose.yaml COMPOSE_PROJECT_NAME=project_name docker-compose up -d + + # run any Symfony CLI command: + COMPOSE_FILE=docker/compose.yaml COMPOSE_PROJECT_NAME=project_name symfony var:export + +.. note:: + + If you have more than one Docker Compose file, you can provide them all, + separated by ``:``, as explained in the `Docker Compose CLI env var reference`_. + +.. warning:: + + When using the Symfony CLI with ``php bin/console`` (``symfony console ...``), + it will **always** use environment variables detected via Docker, ignoring + any local environment variables. For example, if you set up a different database + name in your ``.env.test`` file (``DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/test``) + and run ``symfony console doctrine:database:drop --force --env=test``, + the command will drop the database defined in your Docker configuration and not the "test" one. + +.. warning:: + + Similar to other web servers, this tool automatically exposes all environment + variables available in the CLI context. Ensure that this local server is not + accessible on your local network without your explicit consent, to avoid + potential security issues. Service Naming ~~~~~~~~~~~~~~ @@ -311,20 +495,28 @@ If your service names don't match Symfony conventions, use labels: labels: com.symfony.server.service-prefix: 'DATABASE' +In this example, the service is named ``db``, so environment variables would be +prefixed with ``DB_``, but as the ``com.symfony.server.service-prefix`` is set +to ``DATABASE``, the web server creates environment variables starting with +``DATABASE_`` instead as expected by the default Symfony configuration. + Managing Long-Running Processes ------------------------------- -Use the Symfony CLI to manage long-running processes like Webpack watchers: +Use the ``run`` command provided by the Symfony CLI to manage long-running +processes like Webpack watchers: .. code-block:: terminal - # Start webpack watcher in the background + # start webpack watcher in the background to not block the terminal $ symfony run -d npx encore dev --watch - # View logs + # continue working and running other commands... + + # view logs $ symfony server:log - # Check status + # check status $ symfony server:status .. _symfony-server_configuring-workers: @@ -361,7 +553,7 @@ The ``.symfony.local.yaml`` file provides advanced configuration options: .. code-block:: yaml - # Custom domains + # sets app.wip and admin.app.wip for the current project proxy: domains: - app @@ -371,29 +563,48 @@ The ``.symfony.local.yaml`` file provides advanced configuration options: http: document_root: public/ passthru: index.php + # forces the port that will be used to run the server port: 8000 + # sets the HTTP port you prefer for this project [default: 8000] + # (only will be used if it's available; otherwise a random port is chosen) preferred_port: 8001 - allow_http: false + # used to disable the default auto-redirection from HTTP to HTTPS + allow_http: true + # force the use of HTTP instead of HTTPS no_tls: false + # path to the file containing the TLS certificate to use in p12 format p12: path/to/custom-cert.p12 + # toggle GZIP compression + use_gzip: true + # run the server in the background + daemon: true + +.. warning:: + + 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. + +.. _platform-sh-integration: -Platform.sh Integration ------------------------ +Symfony Cloud Integration +------------------------- -The Symfony CLI provides seamless integration with `Platform.sh`_: +The Symfony CLI provides seamless integration with `Symfony Cloud`_ (powered by +`Platform.sh`_): .. code-block:: terminal - # Open Platform.sh web UI + # open Platform.sh web UI $ symfony cloud:web - # Deploy to Platform.sh + # deploy your project to production $ symfony cloud:deploy - # Create a new environment + # create a new environment $ symfony cloud:env:create feature-xyz -For more Platform.sh features, see the `Platform.sh documentation`_. +For more features, see the `Symfony Cloud documentation`_. Troubleshooting --------------- @@ -419,13 +630,18 @@ variables are properly exposed: $ docker compose ps $ symfony var:export --debug -**Proxy domains not working**: +**Proxy domains not working**: * Clear your browser cache * Check proxy settings in your system * For Chrome, visit ``chrome://net-internals/#proxy`` and click "Re-apply settings" +.. _`open source`: https://github.com/symfony-cli/symfony-cli .. _`symfony.com/download`: https://symfony.com/download -.. _`symfony-cli/symfony-cli GitHub repository`: https://github.com/symfony-cli/symfony-cli -.. _`Platform.sh`: https://symfony.com/cloud/ -.. _`Platform.sh documentation`: https://docs.platform.sh/frameworks/symfony.html \ No newline at end of file +.. _`Docker`: https://en.wikipedia.org/wiki/Docker_(software) +.. _`Symfony Cloud`: https://symfony.com/cloud/ +.. _`Platform.sh`: https://platform.sh/ +.. _`Symfony Cloud documentation`: https://docs.platform.sh/frameworks/symfony.html +.. _`Proxy settings in Windows`: https://www.dummies.com/computers/operating-systems/windows-10/how-to-set-up-a-proxy-in-windows-10/ +.. _`Proxy settings in macOS`: https://support.apple.com/guide/mac-help/enter-proxy-server-settings-on-mac-mchlp2591/mac +.. _`Proxy settings in Ubuntu`: https://help.ubuntu.com/stable/ubuntu-help/net-proxy.html.en From a9e59edfd8b7771f3afaae27f696e00d75c7dd0f Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 2 Jul 2025 08:03:16 +0200 Subject: [PATCH 635/642] Add missing references --- setup/symfony_cli.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup/symfony_cli.rst b/setup/symfony_cli.rst index fbcdbef6d0a..a80dbbc4e5c 100644 --- a/setup/symfony_cli.rst +++ b/setup/symfony_cli.rst @@ -645,3 +645,5 @@ variables are properly exposed: .. _`Proxy settings in Windows`: https://www.dummies.com/computers/operating-systems/windows-10/how-to-set-up-a-proxy-in-windows-10/ .. _`Proxy settings in macOS`: https://support.apple.com/guide/mac-help/enter-proxy-server-settings-on-mac-mchlp2591/mac .. _`Proxy settings in Ubuntu`: https://help.ubuntu.com/stable/ubuntu-help/net-proxy.html.en +.. _`is treated differently`: https://superuser.com/a/1799209 +.. _`Docker compose CLI env var reference`: https://docs.docker.com/compose/reference/envvars/ From 3be34a6511def7f7d5b216eafb3f073de36b41b4 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 2 Jul 2025 08:12:03 +0200 Subject: [PATCH 636/642] Fix another reference --- setup/symfony_cli.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/symfony_cli.rst b/setup/symfony_cli.rst index a80dbbc4e5c..0dcce759d2e 100644 --- a/setup/symfony_cli.rst +++ b/setup/symfony_cli.rst @@ -646,4 +646,4 @@ variables are properly exposed: .. _`Proxy settings in macOS`: https://support.apple.com/guide/mac-help/enter-proxy-server-settings-on-mac-mchlp2591/mac .. _`Proxy settings in Ubuntu`: https://help.ubuntu.com/stable/ubuntu-help/net-proxy.html.en .. _`is treated differently`: https://superuser.com/a/1799209 -.. _`Docker compose CLI env var reference`: https://docs.docker.com/compose/reference/envvars/ +.. _`Docker Compose CLI env var reference`: https://docs.docker.com/compose/reference/envvars/ From d637bfc33dab1a36645b990bb8b95f1ab67f2b2b Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 2 Jul 2025 08:25:58 +0200 Subject: [PATCH 637/642] Update all references to Symfony CLI and web server --- configuration/micro_kernel_trait.rst | 6 ++---- console.rst | 8 ++------ contributing/code/reproducer.rst | 4 ++-- create_framework/front_controller.rst | 2 +- create_framework/introduction.rst | 2 +- frontend/encore/dev-server.rst | 6 +++--- mailer.rst | 4 ++-- mercure.rst | 2 +- page_creation.rst | 2 +- quick_tour/the_big_picture.rst | 4 ++-- setup.rst | 4 ++-- setup/symfony_cli.rst | 4 ++++ setup/web_server_configuration.rst | 2 +- 13 files changed, 24 insertions(+), 26 deletions(-) diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst index 71a4a94e596..511a15a69fd 100644 --- a/configuration/micro_kernel_trait.rst +++ b/configuration/micro_kernel_trait.rst @@ -130,8 +130,7 @@ Next, create an ``index.php`` file that defines the kernel class and runs it: ``config/`` in your project (even if it's empty because you define the configuration options inside the ``configureContainer()`` method). -That's it! To test it, start the :doc:`Symfony Local Web Server -`: +That's it! To test it, start the :ref:`Symfony local web server `: .. code-block:: terminal @@ -469,8 +468,7 @@ this: ├─ composer.json └─ composer.lock -As before you can use the :doc:`Symfony Local Web Server -`: +As before you can use the :ref:`Symfony local web server `: .. code-block:: terminal diff --git a/console.rst b/console.rst index a3e1abbf34b..24439f6a45c 100644 --- a/console.rst +++ b/console.rst @@ -108,12 +108,8 @@ completion (by default, by pressing the Tab key). .. tip:: - If you are using the :doc:`Symfony local web server - `, it is recommended to use the built-in completion - script that will ensure the right PHP version and configuration are used when - running the Console Completion. Run ``symfony completion --help`` for the - installation instructions for your shell. The Symfony CLI will provide - completion for the ``console`` and ``composer`` commands. + If you are using the :doc:`Symfony CLI ` tool, follow + :ref:`these instructions ` to enable autocompletion. Creating a Command ------------------ diff --git a/contributing/code/reproducer.rst b/contributing/code/reproducer.rst index 914ab2fd3f2..c2208b70b09 100644 --- a/contributing/code/reproducer.rst +++ b/contributing/code/reproducer.rst @@ -65,8 +65,8 @@ to a route definition. Then, after creating your project: of controllers, actions, etc. as in your original application. #. Create a small controller and add your routing definition that shows the bug. #. Don't create or modify any other file. -#. Install the :doc:`local web server ` provided by Symfony - and use the ``symfony server:start`` command to browse to the new route and +#. Install the :doc:`Symfony CLI ` tool and use the + ``symfony server:start`` command to browse to the new route and see if the bug appears or not. #. If you can see the bug, you're done and you can already share the code with us. #. If you can't see the bug, you must keep making small changes. For example, if diff --git a/create_framework/front_controller.rst b/create_framework/front_controller.rst index f2843b9067a..cc440dd8910 100644 --- a/create_framework/front_controller.rst +++ b/create_framework/front_controller.rst @@ -154,7 +154,7 @@ Now, configure your web server root directory to point to ``web/`` and all other files will no longer be accessible from the client. To test your changes in a browser (``http://localhost:4321/hello?name=Fabien``), -run the :doc:`Symfony Local Web Server `: +run the :ref:`Symfony local web server `: .. code-block:: terminal diff --git a/create_framework/introduction.rst b/create_framework/introduction.rst index d65bb4fe85e..420537a8088 100644 --- a/create_framework/introduction.rst +++ b/create_framework/introduction.rst @@ -101,7 +101,7 @@ start with the simplest web application we can think of in PHP:: printf('Hello %s', $name); -You can use the :doc:`Symfony Local Web Server ` to test +You can use the :ref:`Symfony local web server ` to test this great application in a browser (``http://localhost:8000/index.php?name=Fabien``): diff --git a/frontend/encore/dev-server.rst b/frontend/encore/dev-server.rst index c3287a7bc2f..a3adb04685a 100644 --- a/frontend/encore/dev-server.rst +++ b/frontend/encore/dev-server.rst @@ -53,9 +53,9 @@ method in your ``webpack.config.js`` file: Enabling HTTPS using the Symfony Web Server ------------------------------------------- -If you're using the :doc:`Symfony web server ` locally with HTTPS, -you'll need to also tell the dev-server to use HTTPS. To do this, you can reuse the Symfony web -server SSL certificate: +If you're using the :ref:`Symfony local web server ` locally +with HTTPS, you'll need to also tell the dev-server to use HTTPS. To do this, +you can reuse the Symfony web server SSL certificate: .. code-block:: diff diff --git a/mailer.rst b/mailer.rst index 30e50fbd359..96357e14dbb 100644 --- a/mailer.rst +++ b/mailer.rst @@ -1824,8 +1824,8 @@ 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 +configured. In addition, if you are using the :doc:`Symfony CLI ` +tool, the mailer DSN is automatically exposed via the :ref:`symfony binary Docker integration `. Sending Test Emails diff --git a/mercure.rst b/mercure.rst index fd32d01c01f..da04ea69646 100644 --- a/mercure.rst +++ b/mercure.rst @@ -72,7 +72,7 @@ Thanks to :doc:`the Docker integration of Symfony `, :ref:`Flex ` proposes to install a Mercure hub for development. Run ``docker-compose up`` to start the hub if you have chosen this option. -If you use the :doc:`Symfony Local Web Server `, +If you use the :ref:`Symfony local web server `, you must start it with the ``--no-tls`` option to prevent mixed content and invalid TLS certificate issues: diff --git a/page_creation.rst b/page_creation.rst index ce7a2b1a935..e58e99f317c 100644 --- a/page_creation.rst +++ b/page_creation.rst @@ -80,7 +80,7 @@ metadata to code): } } -That's it! If you are using :doc:`the Symfony web server `, +That's it! If you are using :ref:`the Symfony web server `, try it out by going to: http://localhost:8000/lucky/number .. tip:: diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst index 997bb05904e..ba7cc78e28b 100644 --- a/quick_tour/the_big_picture.rst +++ b/quick_tour/the_big_picture.rst @@ -42,8 +42,8 @@ Symfony application: Can we already load the project in a browser? Yes! You can set up :doc:`Nginx or Apache ` and configure their document root to be the ``public/`` directory. But, for development, it's better -to :doc:`install the Symfony local web server ` and run -it as follows: +to install the :doc:`Symfony CLI ` tool and run its +:ref:`local web server ` as follows: .. code-block:: terminal diff --git a/setup.rst b/setup.rst index b2b184114d0..4136c42d693 100644 --- a/setup.rst +++ b/setup.rst @@ -121,8 +121,8 @@ development. .. _symfony-binary-web-server: However for local development, the most convenient way of running Symfony is by -using the :doc:`local web server ` provided by the -``symfony`` binary. This local server provides among other things support for +using the :ref:`local web server ` provided by the +Symfony CLI tool. This local server provides among other things support for HTTP/2, concurrent requests, TLS/SSL and automatic generation of security certificates. diff --git a/setup/symfony_cli.rst b/setup/symfony_cli.rst index 0dcce759d2e..ccd79e4b423 100644 --- a/setup/symfony_cli.rst +++ b/setup/symfony_cli.rst @@ -21,6 +21,8 @@ The Symfony CLI is available as a standalone executable that supports Linux, macOS, and Windows. Download and install it following the instructions on `symfony.com/download`_. +.. _symfony-cli-autocompletion: + Shell Autocompletion ~~~~~~~~~~~~~~~~~~~~ @@ -75,6 +77,8 @@ projects quickly: Pass the ``--cloud`` option to initialize a Symfony Cloud project at the same time the Symfony project is created. +.. _symfony-cli-server: + Running the Local Web Server ---------------------------- diff --git a/setup/web_server_configuration.rst b/setup/web_server_configuration.rst index b202adc2443..4b562d4f79e 100644 --- a/setup/web_server_configuration.rst +++ b/setup/web_server_configuration.rst @@ -2,7 +2,7 @@ Configuring a Web Server ======================== The preferred way to develop your Symfony application is to use -:doc:`Symfony Local Web Server `. +:ref:`Symfony local web server `. However, when running the application in the production environment, you'll need to use a fully-featured web server. This article describes how to use Symfony From dd45539e7255ee3b21f78fc8a7d9e5567948da5e Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Tue, 1 Jul 2025 21:34:07 +0200 Subject: [PATCH 638/642] Fix confusing wording about redeliver_timeout duration Changed "slowest message duration" to "longest message duration" to clarify that redeliver_timeout should be greater than the maximum processing time of any message. --- messenger.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/messenger.rst b/messenger.rst index cc0b13c3467..032bdde6e91 100644 --- a/messenger.rst +++ b/messenger.rst @@ -1601,7 +1601,7 @@ The transport has a number of options: .. note:: - Set ``redeliver_timeout`` to a greater value than your slowest message + Set ``redeliver_timeout`` to a greater value than your longest message duration. Otherwise, some messages will start a second time while the first one is still being handled. From 09bb042d326305318214504f68ce24c451944fb3 Mon Sep 17 00:00:00 2001 From: Victor Dittiere Date: Thu, 3 Jul 2025 14:54:04 +0200 Subject: [PATCH 639/642] Update encoders.rst Fix classname comment in `Creating a Custom Encoder` section --- serializer/encoders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serializer/encoders.rst b/serializer/encoders.rst index d2cf1f9cab8..8238d4d057d 100644 --- a/serializer/encoders.rst +++ b/serializer/encoders.rst @@ -271,7 +271,7 @@ Creating a Custom Encoder Imagine you want to serialize and deserialize `NEON`_. For that you'll have to create your own encoder:: - // src/Serializer/YamlEncoder.php + // src/Serializer/NeonEncoder.php namespace App\Serializer; use Nette\Neon\Neon; From ebb014c0d4b2ecb521e797a89092990b86313fab Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 3 Jul 2025 15:58:32 +0200 Subject: [PATCH 640/642] [Serializer] Backport a fix in the filename of an encoder --- serializer/encoders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serializer/encoders.rst b/serializer/encoders.rst index 37f4eee5a04..4a83522c2fb 100644 --- a/serializer/encoders.rst +++ b/serializer/encoders.rst @@ -265,7 +265,7 @@ Creating a Custom Encoder Imagine you want to serialize and deserialize `NEON`_. For that you'll have to create your own encoder:: - // src/Serializer/YamlEncoder.php + // src/Serializer/NeonEncoder.php namespace App\Serializer; use Nette\Neon\Neon; From 0d09f16df5676306a35bc29d5bbac32a8b490815 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Mon, 30 Jun 2025 13:24:03 +0200 Subject: [PATCH 641/642] [Form][Validator] Merge all articles about using validation groups in forms --- _build/redirection_map | 3 + form/button_based_validation.rst | 36 ---- form/data_based_validation.rst | 72 -------- form/validation_group_service_resolver.rst | 58 ------- form/validation_groups.rst | 164 +++++++++++++++--- forms.rst | 2 - .../types/options/validation_groups.rst.inc | 57 +----- reference/forms/types/submit.rst | 24 +-- 8 files changed, 155 insertions(+), 261 deletions(-) delete mode 100644 form/button_based_validation.rst delete mode 100644 form/data_based_validation.rst delete mode 100644 form/validation_group_service_resolver.rst diff --git a/_build/redirection_map b/_build/redirection_map index 896c58a3022..ec088ad2209 100644 --- a/_build/redirection_map +++ b/_build/redirection_map @@ -570,3 +570,6 @@ /components/serializer /serializer /serializer/custom_encoder /serializer/encoders#serializer-custom-encoder /components/string /string +/form/button_based_validation /form/validation_groups +/form/data_based_validation /form/validation_groups +/form/validation_group_service_resolver /form/validation_groups diff --git a/form/button_based_validation.rst b/form/button_based_validation.rst deleted file mode 100644 index 47f2673b079..00000000000 --- a/form/button_based_validation.rst +++ /dev/null @@ -1,36 +0,0 @@ -How to Choose Validation Groups Based on the Clicked Button -=========================================================== - -When your form contains multiple submit buttons, you can change the validation -group depending on which button is used to submit the form. For example, -consider a form in a wizard that lets you advance to the next step or go back -to the previous step. Also assume that when returning to the previous step, -the data of the form should be saved, but not validated. - -First, we need to add the two buttons to the form:: - - $form = $this->createFormBuilder($task) - // ... - ->add('nextStep', SubmitType::class) - ->add('previousStep', SubmitType::class) - ->getForm(); - -Then, we configure the button for returning to the previous step to run -specific validation groups. In this example, we want it to suppress validation, -so we set its ``validation_groups`` option to false:: - - $form = $this->createFormBuilder($task) - // ... - ->add('previousStep', SubmitType::class, [ - 'validation_groups' => false, - ]) - ->getForm(); - -Now the form will skip your validation constraints. It will still validate -basic integrity constraints, such as checking whether an uploaded file was too -large or whether you tried to submit text in a number field. - -.. seealso:: - - To see how to use a service to resolve ``validation_groups`` dynamically - read the :doc:`/form/validation_group_service_resolver` article. diff --git a/form/data_based_validation.rst b/form/data_based_validation.rst deleted file mode 100644 index b01bea10b16..00000000000 --- a/form/data_based_validation.rst +++ /dev/null @@ -1,72 +0,0 @@ -How to Choose Validation Groups Based on the Submitted Data -=========================================================== - -If you need some advanced logic to determine the validation groups (e.g. -based on submitted data), you can set the ``validation_groups`` option -to an array callback:: - - use App\Entity\Client; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => [ - Client::class, - 'determineValidationGroups', - ], - ]); - } - -This will call the static method ``determineValidationGroups()`` on the -``Client`` class after the form is submitted, but before validation is -invoked. The Form object is passed as an argument to that method (see next -example). You can also define whole logic inline by using a ``Closure``:: - - use App\Entity\Client; - use Symfony\Component\Form\FormInterface; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => function (FormInterface $form): array { - $data = $form->getData(); - - if (Client::TYPE_PERSON == $data->getType()) { - return ['person']; - } - - return ['company']; - }, - ]); - } - -Using the ``validation_groups`` option overrides the default validation -group which is being used. If you want to validate the default constraints -of the entity as well you have to adjust the option as follows:: - - use App\Entity\Client; - use Symfony\Component\Form\FormInterface; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => function (FormInterface $form): array { - $data = $form->getData(); - - if (Client::TYPE_PERSON == $data->getType()) { - return ['Default', 'person']; - } - - return ['Default', 'company']; - }, - ]); - } - -You can find more information about how the validation groups and the default constraints -work in the article about :doc:`validation groups `. diff --git a/form/validation_group_service_resolver.rst b/form/validation_group_service_resolver.rst deleted file mode 100644 index 82a6f65d6ec..00000000000 --- a/form/validation_group_service_resolver.rst +++ /dev/null @@ -1,58 +0,0 @@ -How to Dynamically Configure Form Validation Groups -=================================================== - -Sometimes you need advanced logic to determine the validation groups. If they -can't be determined by a callback, you can use a service. Create a service -that implements ``__invoke()`` which accepts a ``FormInterface`` as a -parameter:: - - // src/Validation/ValidationGroupResolver.php - namespace App\Validation; - - use Symfony\Component\Form\FormInterface; - - class ValidationGroupResolver - { - public function __construct( - private object $service1, - private object $service2, - ) { - } - - public function __invoke(FormInterface $form): array - { - $groups = []; - - // ... determine which groups to apply and return an array - - return $groups; - } - } - -Then in your form, inject the resolver and set it as the ``validation_groups``:: - - // src/Form/MyClassType.php; - namespace App\Form; - - use App\Validation\ValidationGroupResolver; - use Symfony\Component\Form\AbstractType; - use Symfony\Component\OptionsResolver\OptionsResolver; - - class MyClassType extends AbstractType - { - public function __construct( - private ValidationGroupResolver $groupResolver, - ) { - } - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => $this->groupResolver, - ]); - } - } - -This will result in the form validator invoking your group resolver to set the -validation groups returned when validating. diff --git a/form/validation_groups.rst b/form/validation_groups.rst index 4addc1ba1a7..3157ef7bc5b 100644 --- a/form/validation_groups.rst +++ b/form/validation_groups.rst @@ -1,39 +1,163 @@ -How to Define the Validation Groups to Use -========================================== +Configuring Validation Groups in Forms +====================================== -Validation Groups ------------------ +If the object handled in your form uses :doc:`validation groups `, +you need to specify which validation group(s) the form should apply. -If your object takes advantage of :doc:`validation groups `, -you'll need to specify which validation group(s) your form should use. Pass -this as an option when :ref:`creating forms in controllers `:: +To define them when :ref:`creating forms in classes `, +use the ``configureOptions()`` method:: + + use Symfony\Component\OptionsResolver\OptionsResolver; + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + // ... + 'validation_groups' => ['registration'], + ]); + } + +When :ref:`creating forms in controllers `, pass +it as a form option:: $form = $this->createFormBuilder($user, [ 'validation_groups' => ['registration'], ])->add(/* ... */); -When :ref:`creating forms in classes `, add the -following to the ``configureOptions()`` method:: +In both cases, *only* the ``registration`` group will be used to validate the +object. To apply the ``registration`` group *and* all constraints not in any +other group, add the special ``Default`` group:: + + [ + // ... + 'validation_groups' => ['Default', 'registration'], + ] +.. note:: + + You can use any name for your validation groups. Symfony recommends using + "lower snake case" (e.g. ``foo_bar``), while automatically generated + groups use "UpperCamelCase" (e.g. ``Default``, ``SomeClassName``). + +Choosing Validation Groups Based on the Clicked Button +------------------------------------------------------ + +When your form has :doc:`multiple submit buttons `, you +can change the validation group based on the clicked button. For example, in a +multi-step form like the following, you might want to skip validation when +returning to a previous step:: + + $form = $this->createFormBuilder($task) + // ... + ->add('nextStep', SubmitType::class) + ->add('previousStep', SubmitType::class) + ->getForm(); + +To do so, configure the validation groups of the ``previousStep`` button to +``false``, which is a special value that skips validation:: + + $form = $this->createFormBuilder($task) + // ... + ->add('previousStep', SubmitType::class, [ + 'validation_groups' => false, + ]) + ->getForm(); + +Now the form will skip your validation constraints when that button is clicked. +It will still validate basic integrity constraints, such as checking whether an +uploaded file was too large or whether you tried to submit text in a number field. + +Choosing Validation Groups Based on Submitted Data +-------------------------------------------------- + +To determine validation groups dynamically based on submitted data, use a +callback. This is called after the form is submitted, but before validation is +invoked. The callback receives the form object as its first argument:: + + use App\Entity\Client; + use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - // ... - 'validation_groups' => ['registration'], + 'validation_groups' => function (FormInterface $form): array { + $data = $form->getData(); + + if (Client::TYPE_PERSON === $data->getType()) { + return ['Default', 'person']; + } + + return ['Default', 'company']; + }, ]); } -In both of these cases, *only* the ``registration`` validation group will -be used to validate the underlying object. To apply the ``registration`` -group *and* all constraints that are not in a group, use:: +.. note:: + + Adding ``Default`` to the list of validation groups is common but not mandatory. + See the main :doc:`article about validation groups ` to + learn more about validation groups and the default constraints. - 'validation_groups' => ['Default', 'registration'] +You can also pass a static class method callback:: -.. note:: + 'validation_groups' => [Client::class, 'determineValidationGroups'] + +Choosing Validation Groups via a Service +---------------------------------------- + +If validation group logic requires services or can't fit in a closure, use a +dedicated validation group resolver service. The class of this service must +be invokable and receives the form object as its first argument:: + + // src/Validation/ValidationGroupResolver.php + namespace App\Validation; + + use Symfony\Component\Form\FormInterface; + + class ValidationGroupResolver + { + public function __construct( + private object $service1, + private object $service2, + ) { + } + + public function __invoke(FormInterface $form): array + { + $groups = []; + + // ... determine which groups to return + + return $groups; + } + } + +Then use the service in your form type:: + + namespace App\Form; + + use App\Validation\ValidationGroupResolver; + use Symfony\Component\Form\AbstractType; + use Symfony\Component\OptionsResolver\OptionsResolver; + + class MyClassType extends AbstractType + { + public function __construct( + private ValidationGroupResolver $groupResolver, + ) { + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'validation_groups' => $this->groupResolver, + ]); + } + } + +Learn More +---------- - You can choose any name for your validation groups, but Symfony recommends - using "lower snake case" names (e.g. ``foo_bar``) in contrast with the - automatic validation groups created by Symfony, which use "upper camel case" - (e.g. ``Default``, ``SomeClassName``). +For more information about how validation groups work, see +:doc:`/validation/groups`. diff --git a/forms.rst b/forms.rst index 38006169cdb..bea93edb25d 100644 --- a/forms.rst +++ b/forms.rst @@ -1003,8 +1003,6 @@ Validation: :maxdepth: 1 /form/validation_groups - /form/validation_group_service_resolver - /form/button_based_validation /form/disabling_validation Misc.: diff --git a/reference/forms/types/options/validation_groups.rst.inc b/reference/forms/types/options/validation_groups.rst.inc index 1f5c9a597a3..6957bf203a3 100644 --- a/reference/forms/types/options/validation_groups.rst.inc +++ b/reference/forms/types/options/validation_groups.rst.inc @@ -1,59 +1,14 @@ ``validation_groups`` ~~~~~~~~~~~~~~~~~~~~~ -**type**: ``array``, ``string``, ``callable``, :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence` or ``null`` **default**: ``null`` +**type**: ``array``, ``string``, ``callable``, :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence`, or ``null`` **default**: ``null`` -This option is only valid on the root form and is used to specify which -groups will be used by the validator. +This option is only valid on the root form. It specifies which validation groups +will be used by the validator. -For ``null`` the validator will just use the ``Default`` group. - -If you specify the groups as an array or string they will be used by the -validator as they are:: - - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => 'Registration', - ]); - } - -This is equivalent to passing the group as array:: - - 'validation_groups' => ['Registration'], - -The form's data will be :doc:`validated against all given groups `. - -If the validation groups depend on the form's data a callable may be passed to -the option. Symfony will then pass the form when calling it:: - - use Symfony\Component\Form\FormInterface; - use Symfony\Component\OptionsResolver\OptionsResolver; - - // ... - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'validation_groups' => function (FormInterface $form): array { - $entity = $form->getData(); - - return $entity->isUser() ? ['User'] : ['Company']; - }, - ]); - } - -.. seealso:: - - You can read more about this in :doc:`/form/data_based_validation`. - -.. note:: - - When your form contains multiple submit buttons, you can change the - validation group depending on :doc:`which button is used ` - to submit the form. - - If you need advanced logic to determine the validation groups have - a look at :doc:`/form/validation_group_service_resolver`. +If set to ``null``, the validator will use only the ``Default`` group. For the +other possible values, see the main article about +:doc:`using validation groups in Symfony forms ` In some cases, you want to validate your groups step by step. To do this, you can pass a :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence` diff --git a/reference/forms/types/submit.rst b/reference/forms/types/submit.rst index 70fa429685a..5d863fbe8b4 100644 --- a/reference/forms/types/submit.rst +++ b/reference/forms/types/submit.rst @@ -102,28 +102,8 @@ validation_groups **type**: ``array`` **default**: ``null`` When your form contains multiple submit buttons, you can change the validation -group based on the button which was used to submit the form. Imagine a registration -form wizard with buttons to go to the previous or the next step:: - - use Symfony\Component\Form\Extension\Core\Type\SubmitType; - // ... - - $form = $this->createFormBuilder($user) - ->add('previousStep', SubmitType::class, [ - 'validation_groups' => false, - ]) - ->add('nextStep', SubmitType::class, [ - 'validation_groups' => ['Registration'], - ]) - ->getForm(); - -The special ``false`` ensures that no validation is performed when the previous -step button is clicked. When the second button is clicked, all constraints -from the "Registration" are validated. - -.. seealso:: - - You can read more about this in :doc:`/form/data_based_validation`. +group based on the clicked button. Read the article about +:doc:`using validation groups in Symfony forms `. Form Variables -------------- From bb3f806fdf90b4da2028835a5b2bf7bb3750a2b1 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 4 Jul 2025 10:02:39 +0200 Subject: [PATCH 642/642] Reword --- reference/configuration/framework.rst | 2 ++ templates.rst | 30 +++++++++++---------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index d97c59dd9e9..947e2d5a67b 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -2056,6 +2056,8 @@ enabled Whether to enable or not SSI support in your application. +.. _reference-assets: + assets ~~~~~~ diff --git a/templates.rst b/templates.rst index 880c37dae8e..6678f653790 100644 --- a/templates.rst +++ b/templates.rst @@ -312,24 +312,18 @@ You can now use the ``asset()`` function: {# the JS file lives at "public/bundles/acme/js/loader.js" #} -Using the ``asset()`` function is recommended for two reasons: - -* It automatically takes care of versioning your assets with - :doc:`Symfony's AssetMapper ` - -* If your application lives at the root of your host (e.g. ``https://example.com``), -then the rendered path should be ``/images/logo.png``. But if your application -lives in a subdirectory (e.g. ``https://example.com/my_app``), each asset path -should render with the subdirectory (e.g. ``/my_app/images/logo.png``). The -``asset()`` function takes care of this by determining how your application is -being used and generating the correct paths accordingly. - -.. tip:: - - The ``asset()`` function supports various cache busting techniques via the - :ref:`version `, - :ref:`version_format `, and - :ref:`json_manifest_path ` configuration options. +Using the ``asset()`` function is recommended for these reasons: + +* **Asset versioning**: ``asset()`` appends a version hash to asset URLs for + cache busting. This works both via :doc:`AssetMapper ` and the + :doc:`Asset component ` (see also the + :ref:`assets configuration options `, such as ``version`` + and ``version_format``). + +* **Application portability**: whether your app is hosted at the root + (e.g. ``https://example.com``) or in a subdirectory (e.g. ``https://example.com/my_app``), + ``asset()`` generates the correct path (e.g. ``/images/logo.png`` vs ``/my_app/images/logo.png``) + automatically based on your app's base URL. If you need absolute URLs for assets, use the ``absolute_url()`` Twig function as follows: