diff --git a/_build/maintainer_guide.rst b/_build/maintainer_guide.rst index d82105e9760..9758b4e7397 100644 --- a/_build/maintainer_guide.rst +++ b/_build/maintainer_guide.rst @@ -39,14 +39,14 @@ contributes again, it's OK to mention some of the minor issues to educate them. $ gh merge 11059 - Working on symfony/symfony-docs (branch 6.1) + Working on symfony/symfony-docs (branch 6.2) Merging Pull Request 11059: dmaicher/patch-3 ... # This is important!! Say NO to push the changes now Push the changes now? (Y/n) n - Now, push with: git push gh "6.1" refs/notes/github-comments + Now, push with: git push gh "6.2" refs/notes/github-comments # Now, open your editor and make the needed changes ... @@ -54,7 +54,7 @@ contributes again, it's OK to mention some of the minor issues to educate them. # Use "Minor reword", "Minor tweak", etc. as the commit message # now run the 'push' command shown above by 'gh' (it's different each time) - $ git push gh "6.1" refs/notes/github-comments + $ git push gh "6.2" refs/notes/github-comments Merging Pull Requests --------------------- diff --git a/best_practices.rst b/best_practices.rst index 79036fe04d3..588580a48d1 100644 --- a/best_practices.rst +++ b/best_practices.rst @@ -234,14 +234,6 @@ configuration. You don't need to browse several files created with different formats (YAML, XML, PHP): all the configuration is just where you need it and it only uses one format. -Don't Use Annotations to Configure the Controller Template -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``@Template`` annotation is useful, but also involves some *magic*. -Moreover, most of the time ``@Template`` is used without any parameters, which -makes it more difficult to know which template is being rendered. It also hides -the fact that a controller should always return a ``Response`` object. - Use Dependency Injection to Get Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/components/expression_language/syntax.rst b/components/expression_language/syntax.rst index 6e457acd7ea..de0fddf80bf 100644 --- a/components/expression_language/syntax.rst +++ b/components/expression_language/syntax.rst @@ -343,6 +343,23 @@ Ternary Operators * ``foo ?: 'no'`` (equal to ``foo ? foo : 'no'``) * ``foo ? 'yes'`` (equal to ``foo ? 'yes' : ''``) +Null Coalescing Operator +~~~~~~~~~~~~~~~~~~~~~~~~ + +This is the same as the PHP `null-coalescing operator`_, which combines +the ternary operator and ``isset()``. It returns the left hand-side if it exists +and it's not ``null``; otherwise it returns the right hand-side. Note that you +can chain multiple coalescing operators. + +* ``foo ?? 'no'`` +* ``foo.baz ?? 'no'`` +* ``foo[3] ?? 'no'`` +* ``foo.baz ?? foo['baz'] ?? 'no'`` + +.. versionadded:: 6.2 + + The null-coalescing operator was introduced in Symfony 6.2. + Built-in Objects and Variables ------------------------------ diff --git a/components/finder.rst b/components/finder.rst index ee6165e15bb..55f010d61b6 100644 --- a/components/finder.rst +++ b/components/finder.rst @@ -336,12 +336,17 @@ instance. The file is excluded from the result set if the Closure returns Sorting Results --------------- -Sort the results by name or by type (directories first, then files):: +Sort the results by name, extension, size or type (directories first, then files):: $finder->sortByName(); - + $finder->sortByExtension(); + $finder->sortBySize(); $finder->sortByType(); +.. versionadded:: 6.2 + + The ``sortByExtension()`` and ``sortBySize()`` methods were introduced in Symfony 6.2. + .. tip:: By default, the ``sortByName()`` method uses the :phpfunction:`strcmp` PHP diff --git a/components/http_kernel.rst b/components/http_kernel.rst index 93d2751a9be..c6284d487b8 100644 --- a/components/http_kernel.rst +++ b/components/http_kernel.rst @@ -409,12 +409,12 @@ return a ``Response``. .. sidebar:: ``kernel.view`` in the Symfony Framework - There is no default listener inside the Symfony Framework for the ``kernel.view`` - event. However, `SensioFrameworkExtraBundle`_ *does* add a listener to this - event. If your controller returns an array, and you place the `@Template`_ - annotation above the controller, then this listener renders a template, - passes the array you returned from your controller to that template, and - creates a ``Response`` containing the returned content from that template. + There is a default listener inside the Symfony Framework for the ``kernel.view`` + event. If your controller action returns an array, and you apply the + :ref:`#[Template()] attribute ` to that + controller action, then this listener renders a template, passes the array + you returned from your controller to that template, and creates a ``Response`` + containing the returned content from that template. Additionally, a popular community bundle `FOSRestBundle`_ implements a listener on this event which aims to give you a robust view layer @@ -751,5 +751,4 @@ Learn more .. _`PHP FPM`: https://www.php.net/manual/en/install.fpm.php .. _`SensioFrameworkExtraBundle`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html .. _`@ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html -.. _`@Template`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html .. _variadic: https://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list diff --git a/components/uid.rst b/components/uid.rst index d81e593f812..6e50ddfca59 100644 --- a/components/uid.rst +++ b/components/uid.rst @@ -84,6 +84,11 @@ Use these methods to transform the UUID object into different bases:: $uuid->toBase32(); // string(26) "6SWYGR8QAV27NACAHMK5RG0RPG" $uuid->toBase58(); // string(22) "TuetYWNHhmuSQ3xPoVLv9M" $uuid->toRfc4122(); // string(36) "d9e7a184-5d5b-11ea-a62a-3499710062d0" + $uuid->toHex(); // string(34) "0xd9e7a1845d5b11eaa62a3499710062d0" + +.. versionadded:: 6.2 + + The ``toHex()`` method was introduced in Symfony 6.2. Working with UUIDs ~~~~~~~~~~~~~~~~~~ @@ -258,6 +263,11 @@ Use these methods to transform the ULID object into different bases:: $ulid->toBase32(); // string(26) "01E439TP9XJZ9RPFH3T1PYBCR8" $ulid->toBase58(); // string(22) "1BKocMc5BnrVcuq2ti4Eqm" $ulid->toRfc4122(); // string(36) "0171069d-593d-97d3-8b3e-23d06de5b308" + $ulid->toHex(); // string(34) "0x0171069d593d97d38b3e23d06de5b308" + +.. versionadded:: 6.2 + + The ``toHex()`` method was introduced in Symfony 6.2. Working with ULIDs ~~~~~~~~~~~~~~~~~~ diff --git a/components/yaml.rst b/components/yaml.rst index d5675a417e9..2c463d1c731 100644 --- a/components/yaml.rst +++ b/components/yaml.rst @@ -338,6 +338,35 @@ syntax to parse them as proper PHP constants:: $parameters = Yaml::parse($yaml, Yaml::PARSE_CONSTANT); // $parameters = ['foo' => 'PHP_INT_SIZE', 'bar' => 8]; +Parsing PHP Enumerations +~~~~~~~~~~~~~~~~~~~~~~~~ + +The YAML parser supports `PHP enumerations`_, both unit and backed enums. +By default, they are parsed as regular strings. Use the ``PARSE_CONSTANT`` flag +and the special ``!php/enum`` syntax to parse them as proper PHP enums:: + + enum FooEnum: string + { + case Foo = 'foo'; + case Bar = 'bar'; + } + + // ... + + $yaml = '{ foo: FooEnum::Foo, bar: !php/enum FooEnum::Foo }'; + $parameters = Yaml::parse($yaml, Yaml::PARSE_CONSTANT); + // the value of the 'foo' key is a string because it missed the `!php/enum` syntax + // $parameters = ['foo' => 'FooEnum::Foo', 'bar' => FooEnum::Foo]; + + $yaml = '{ foo: FooEnum::Foo, bar: !php/enum FooEnum::Foo->value }'; + $parameters = Yaml::parse($yaml, Yaml::PARSE_CONSTANT); + // the value of the 'foo' key is a string because it missed the `!php/enum` syntax + // $parameters = ['foo' => 'FooEnum::Foo', 'bar' => 'foo']; + +.. versionadded:: 6.2 + + The support for PHP enumerations was introduced in Symfony 6.2. + Parsing and Dumping of Binary Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -460,3 +489,4 @@ Learn More .. _`YAML`: https://yaml.org/ .. _`YAML 1.2 version specification`: https://yaml.org/spec/1.2/spec.html .. _`ISO-8601`: https://www.iso.org/iso-8601-date-and-time-format.html +.. _`PHP enumerations`: https://www.php.net/manual/en/language.types.enumerations.php diff --git a/configuration.rst b/configuration.rst index abf7dcefb5e..bfc7008cc0a 100644 --- a/configuration.rst +++ b/configuration.rst @@ -847,6 +847,13 @@ Use the ``debug:dotenv`` command to understand how Symfony parses the different ALICE BOB BOB bob ---------- ------- ---------- ------ + # look for a specific variable passing its full or partial name as an argument + $ php bin/console debug:dotenv foo + +.. versionadded:: 6.2 + + The option to pass variable names to ``debug:dotenv`` was introduced in Symfony 6.2. + Additionally, and regardless of how you set environment variables, you can see all environment variables, with their values, referenced in Symfony's container configuration: diff --git a/configuration/env_var_processors.rst b/configuration/env_var_processors.rst index 84bccba97d5..658a05163df 100644 --- a/configuration/env_var_processors.rst +++ b/configuration/env_var_processors.rst @@ -379,6 +379,59 @@ Symfony provides the following env var processors: $framework->trustedHosts(env('TRUSTED_HOSTS')->csv()); }; +``env(shuffle:FOO)`` + Randomly shuffles values of the ``FOO`` env var, which must be an array. + + .. configuration-block:: + + .. code-block:: yaml + + # config/packages/framework.yaml + parameters: + env(REDIS_NODES): "127.0.0.1:6380,127.0.0.1:6381" + services: + RedisCluster: + class: RedisCluster + arguments: [null, "%env(shuffle:csv:REDIS_NODES)%"] + + .. code-block:: xml + + + + + + + redis://127.0.0.1:6380,redis://127.0.0.1:6381 + + + + + null + %env(shuffle:csv:REDIS_NODES)% + + + + + .. code-block:: php + + // config/services.php + use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; + + return static function (ContainerConfigurator $configurator): void { + $container = $configurator->services() + ->set(\RedisCluster::class, \RedisCluster::class)->args([null, '%env(shuffle:csv:REDIS_NODES)%']); + }; + + .. versionadded:: 6.2 + + The ``env(shuffle:...)`` env var processor was introduced in Symfony 6.2. + ``env(file:FOO)`` Returns the contents of a file whose path is the value of the ``FOO`` env var: @@ -698,6 +751,53 @@ Symfony provides the following env var processors: ], ]); +``env(enum:FooEnum:BAR)`` + Tries to convert an environment variable to an actual ``\BackedEnum`` value. + This processor takes the fully qualified name of the ``\BackedEnum`` as an argument. + + .. code-block:: php + + # App\Enum\Environment + enum Environment: string + { + case Development = 'dev'; + case Production = 'prod'; + } + + .. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + parameters: + typed_env: '%env(enum:App\Enum\Environment:APP_ENV)%' + + .. code-block:: xml + + + + + + + %env(enum:App\Enum\Environment:APP_ENV)% + + + + .. code-block:: php + + // config/services.php + $container->setParameter('typed_env', '%env(enum:App\Enum\Environment:APP_ENV)%'); + + .. versionadded:: 6.2 + + The ``env(enum:...)`` env var processor was introduced in Symfony 6.2. + It is also possible to combine any number of processors: .. configuration-block:: diff --git a/console/style.rst b/console/style.rst index cfe6502b3fd..6603fc30ffa 100644 --- a/console/style.rst +++ b/console/style.rst @@ -324,6 +324,16 @@ User Input Methods $io->choice('Select the queue to analyze', ['queue1', 'queue2', 'queue3'], 'queue1'); + Finally, you can allow users to select multiple choices. To do so, users must + separate each choice with a comma (e.g. typing ``1, 2`` will select choice 1 + and 2):: + + $io->choice('Select the queue to analyze', ['queue1', 'queue2', 'queue3'], multiSelect: true); + +.. versionadded:: 6.2 + + The ``multiSelect`` option of ``choice()`` was introduced in Symfony 6.2. + Result Methods ~~~~~~~~~~~~~~ diff --git a/controller/argument_value_resolver.rst b/controller/argument_value_resolver.rst index 3eae1924e05..8d4b4dda6d8 100644 --- a/controller/argument_value_resolver.rst +++ b/controller/argument_value_resolver.rst @@ -252,10 +252,10 @@ retrieved from the token storage:: namespace App\ArgumentResolver; use App\Entity\User; + use Symfony\Bundle\SecurityBundle\Security\Security; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; - use Symfony\Component\Security\Core\Security; class UserValueResolver implements ArgumentValueResolverInterface { diff --git a/form/dynamic_form_modification.rst b/form/dynamic_form_modification.rst index 0af5266e9a4..fcf995b50e9 100644 --- a/form/dynamic_form_modification.rst +++ b/form/dynamic_form_modification.rst @@ -233,7 +233,7 @@ The problem is now to get the current user and create a choice field that contains only this user's friends. This can be done injecting the ``Security`` service into the form type so you can get the current user object:: - use Symfony\Component\Security\Core\Security; + use Symfony\Bundle\SecurityBundle\Security\Security; // ... class FriendMessageFormType extends AbstractType @@ -260,9 +260,9 @@ security helper to fill in the listener logic:: use App\Entity\User; use Doctrine\ORM\EntityRepository; use Symfony\Bridge\Doctrine\Form\Type\EntityType; + use Symfony\Bundle\SecurityBundle\Security\Security; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextType; - use Symfony\Component\Security\Core\Security; // ... class FriendMessageFormType extends AbstractType diff --git a/http_cache.rst b/http_cache.rst index 4569cdfac48..a33a0a2e54c 100644 --- a/http_cache.rst +++ b/http_cache.rst @@ -211,24 +211,44 @@ Expiration Caching The *easiest* way to cache a response is by caching it for a specific amount of time:: - // src/Controller/BlogController.php - use Symfony\Component\HttpFoundation\Response; - // ... +.. configuration-block:: + + .. code-block:: php-attributes + + // src/Controller/BlogController.php + use Symfony\Component\HttpKernel\Attribute\Cache; + // ... + + #[Cache(public: true, maxage: 3600, mustRevalidate: true)] + public function index() + { + return $this->render('blog/index.html.twig', []); + } + + .. code-block:: php - public function index() - { - // somehow create a Response object, like by rendering a template - $response = $this->render('blog/index.html.twig', []); + // src/Controller/BlogController.php + use Symfony\Component\HttpFoundation\Response; + // ... - // cache publicly for 3600 seconds - $response->setPublic(); - $response->setMaxAge(3600); + public function index() + { + // somehow create a Response object, like by rendering a template + $response = $this->render('blog/index.html.twig', []); - // (optional) set a custom Cache-Control directive - $response->headers->addCacheControlDirective('must-revalidate', true); + // cache publicly for 3600 seconds + $response->setPublic(); + $response->setMaxAge(3600); - return $response; - } + // (optional) set a custom Cache-Control directive + $response->headers->addCacheControlDirective('must-revalidate', true); + + return $response; + } + +.. versionadded:: 6.2 + + The ``#[Cache()]`` attribute was introduced in Symfony 6.2. Thanks to this new code, your HTTP response will have the following header: @@ -336,6 +356,10 @@ Additionally, most cache-related HTTP headers can be set via the single 'etag' => 'abcdef' ]); +.. tip:: + + All these options are also available when using the ``#[Cache()]`` attribute. + Cache Invalidation ------------------ diff --git a/http_cache/cache_vary.rst b/http_cache/cache_vary.rst index 1dbbf9a0fc4..fe9026d8d83 100644 --- a/http_cache/cache_vary.rst +++ b/http_cache/cache_vary.rst @@ -32,14 +32,28 @@ trigger a different representation of the requested resource: resource based on the URI and the value of the ``Accept-Encoding`` and ``User-Agent`` request header. -The ``Response`` object offers a clean interface for managing the ``Vary`` -header:: +Set the ``Vary`` header via the ``Response`` object methods or the ``#[Cache()]`` +attribute:: - // sets one vary header - $response->setVary('Accept-Encoding'); +.. configuration-block:: - // sets multiple vary headers - $response->setVary(['Accept-Encoding', 'User-Agent']); + .. code-block:: php-attributes -The ``setVary()`` method takes a header name or an array of header names for -which the response varies. + // this attribute takes an array with the name of the header(s) + // names for which the response varies + use Symfony\Component\HttpKernel\Attribute\Cache; + // ... + + #[Cache(vary: ['Accept-Encoding'])] + #[Cache(vary: ['Accept-Encoding', 'User-Agent'])] + public function index() + { + // ... + } + + .. code-block:: php + + // this method takes a header name or an array of header names for + // which the response varies + $response->setVary('Accept-Encoding'); + $response->setVary(['Accept-Encoding', 'User-Agent']); diff --git a/http_cache/esi.rst b/http_cache/esi.rst index f05fa195a22..9b320f3bf5e 100644 --- a/http_cache/esi.rst +++ b/http_cache/esi.rst @@ -166,20 +166,41 @@ used ``render()``. The embedded action can now specify its own caching rules entirely independently of the main page:: - // src/Controller/NewsController.php - namespace App\Controller; +.. configuration-block:: - // ... - class NewsController extends AbstractController - { - public function latest($maxPerPage) + .. code-block:: php-attributes + + // src/Controller/NewsController.php + namespace App\Controller; + + use Symfony\Component\HttpKernel\Attribute\Cache; + // ... + + class NewsController extends AbstractController { - // sets to public and adds some expiration - $response->setSharedMaxAge(60); + #[Cache(smaxage: 60)] + public function latest($maxPerPage) + { + // ... + } + } - return $response; + .. code-block:: php + + // src/Controller/NewsController.php + namespace App\Controller; + + // ... + class NewsController extends AbstractController + { + public function latest($maxPerPage) + { + // sets to public and adds some expiration + $response->setSharedMaxAge(60); + + return $response; + } } - } In this example, the embedded action is cached publicly too because the contents are the same for all requests. However, in other cases you may need to make this diff --git a/http_cache/expiration.rst b/http_cache/expiration.rst index ae436e631ee..cc92f689403 100644 --- a/http_cache/expiration.rst +++ b/http_cache/expiration.rst @@ -24,10 +24,25 @@ Expiration with the ``Cache-Control`` Header Most of the time, you will use the ``Cache-Control`` header, which is used to specify many different cache directives:: - // sets the number of seconds after which the response - // should no longer be considered fresh by shared caches - $response->setPublic(); - $response->setMaxAge(600); +.. configuration-block:: + + .. code-block:: php-attributes + + use Symfony\Component\HttpKernel\Attribute\Cache; + // ... + + #[Cache(public: true, maxage: 600)] + public function index() + { + // ... + } + + .. code-block:: php + + // sets the number of seconds after which the response + // should no longer be considered fresh by shared caches + $response->setPublic(); + $response->setMaxAge(600); The ``Cache-Control`` header would take on the following format (it may have additional directives): @@ -57,13 +72,28 @@ or disadvantage to either. According to the HTTP specification, "the ``Expires`` header field gives the date/time after which the response is considered stale." The ``Expires`` -header can be set with the ``setExpires()`` ``Response`` method. It takes a -``DateTime`` instance as an argument:: +header can be set with the ``expires`` option of the ``#[Cache()]`` attribute or +the ``setExpires()`` ``Response`` method:: + +.. configuration-block:: + + .. code-block:: php-attributes + + use Symfony\Component\HttpKernel\Attribute\Cache; + // ... + + #[Cache(expires: '+600 seconds')] + public function index() + { + // ... + } + + .. code-block:: php - $date = new DateTime(); - $date->modify('+600 seconds'); + $date = new DateTime(); + $date->modify('+600 seconds'); - $response->setExpires($date); + $response->setExpires($date); The resulting HTTP header will look like this: @@ -73,8 +103,8 @@ The resulting HTTP header will look like this: .. note:: - The ``setExpires()`` method automatically converts the date to the GMT - timezone as required by the specification. + The ``expires` option and the ``setExpires()`` method automatically convert + the date to the GMT timezone as required by the specification. Note that in HTTP versions before 1.1 the origin server wasn't required to send the ``Date`` header. Consequently, the cache (e.g. the browser) might diff --git a/http_cache/ssi.rst b/http_cache/ssi.rst index 23459588a33..449edfe5e5b 100644 --- a/http_cache/ssi.rst +++ b/http_cache/ssi.rst @@ -88,28 +88,55 @@ Suppose you have a page with private content like a Profile page and you want to cache a static GDPR content block. With SSI, you can add some expiration on this block and keep the page private:: - // src/Controller/ProfileController.php - namespace App\Controller; +.. configuration-block:: + + .. code-block:: php-attributes + + // src/Controller/ProfileController.php + namespace App\Controller; + + use Symfony\Component\HttpKernel\Attribute\Cache; + // ... - // ... - class ProfileController extends AbstractController - { - public function index(): Response + class ProfileController extends AbstractController { - // by default, responses are private - return $this->render('profile/index.html.twig'); + public function index(): Response + { + // by default, responses are private + return $this->render('profile/index.html.twig'); + } + + #[Cache(smaxage: 600)] + public function gdpr(): Response + { + return $this->render('profile/gdpr.html.twig'); + } } - public function gdpr(): Response + .. code-block:: php + + // src/Controller/ProfileController.php + namespace App\Controller; + + // ... + class ProfileController extends AbstractController { - $response = $this->render('profile/gdpr.html.twig'); + public function index(): Response + { + // by default, responses are private + return $this->render('profile/index.html.twig'); + } + + public function gdpr(): Response + { + $response = $this->render('profile/gdpr.html.twig'); - // sets to public and adds some expiration - $response->setSharedMaxAge(600); + // sets to public and adds some expiration + $response->setSharedMaxAge(600); - return $response; + return $response; + } } - } The profile index page has not public caching, but the GDPR block has 10 minutes of expiration. Let's include this block into the main one: diff --git a/mailer.rst b/mailer.rst index fd079e260c3..3e769eeb535 100644 --- a/mailer.rst +++ b/mailer.rst @@ -110,9 +110,19 @@ Mailjet ``composer require symfony/mailjet-mailer`` Postmark ``composer require symfony/postmark-mailer`` SendGrid ``composer require symfony/sendgrid-mailer`` Sendinblue ``composer require symfony/sendinblue-mailer`` -OhMySMTP ``composer require symfony/oh-my-smtp-mailer`` +MailPace ``composer require symfony/mailpace-mailer`` +Infobip ``composer require symfony/infobip-mailer`` ================== ============================================== +.. versionadded:: 6.2 + + The ``MailPace`` integration was introduced in Symfony 6.2 (in previous + Symfony versions it was called ``OhMySMTP``). + +.. versionadded:: 6.2 + + The Infobip integration was introduced in Symfony 6.2. + Each library includes a :ref:`Symfony Flex recipe ` that will add a configuration example to your ``.env`` file. For example, suppose you want to use SendGrid. First, install it: @@ -158,10 +168,11 @@ Google Gmail gmail+smtp://USERNAME:PASSWORD@default n/a Mailchimp Mandrill mandrill+smtp://USERNAME:PASSWORD@default mandrill+https://KEY@default mandrill+api://KEY@default Mailgun mailgun+smtp://USERNAME:PASSWORD@default mailgun+https://KEY:DOMAIN@default mailgun+api://KEY:DOMAIN@default Mailjet mailjet+smtp://ACCESS_KEY:SECRET_KEY@default n/a mailjet+api://ACCESS_KEY:SECRET_KEY@default +MailPace mailpace+api://API_TOKEN@default n/a mailpace+api://API_TOKEN@default Postmark postmark+smtp://ID@default n/a postmark+api://KEY@default Sendgrid sendgrid+smtp://KEY@default n/a sendgrid+api://KEY@default Sendinblue sendinblue+smtp://USERNAME:PASSWORD@default n/a sendinblue+api://KEY@default -OhMySMTP ohmysmtp+smtp://API_TOKEN@default n/a ohmysmtp+api://API_TOKEN@default +Infobip infobip+smtp://KEY@default n/a infobip+api://KEY@BASE_URL ==================== ==================================================== =========================================== ======================================== .. caution:: @@ -1193,9 +1204,38 @@ you have a transport called ``async``, you can route the message there: ->senders(['async']); }; +Thanks to this, instead of being delivered immediately, messages will be sent +to the transport to be handled later (see :ref:`messenger-worker`). Note that +the "rendering" of the email (computed headers, body rendering, ...) is also +deferred and will only happen just before the email is sent by the Messenger +handler. + +.. versionadded:: 6.2 + + The following example about rendering the email before calling + ``$mailer->send($email)`` works as of Symfony 6.2. + +When sending an email asynchronously, its instance must be serializable. +This is always the case for :class:`Symfony\\Bridge\\Twig\\Mime\\Email` +instances, but when sending a +:class:`Symfony\\Bridge\\Twig\\Mime\\TemplatedEmail`, you must ensure that +the ``context`` is serializable. If you have non-serializable variables, +like Doctrine entities, either replace them with more specific variables or +render the email before calling ``$mailer->send($email)``:: -Thanks to this, instead of being delivered immediately, messages will be sent to -the transport to be handled later (see :ref:`messenger-worker`). + use Symfony\Component\Mailer\MailerInterface; + use Symfony\Component\Mime\BodyRendererInterface; + + public function action(MailerInterface $mailer, BodyRendererInterface $bodyRenderer) + { + $email = (new TemplatedEmail()) + ->htmlTemplate($template) + ->context($context) + ; + $bodyRenderer->render($email); + + $mailer->send($email); + } You can configure which bus is used to dispatch the message using the ``message_bus`` option. You can also set this to ``false`` to call the Mailer transport directly and @@ -1274,7 +1314,7 @@ The following transports currently support tags and metadata: The following transports only support tags: -* OhMySMTP +* MailPace The following transports only support metadata: diff --git a/notifier.rst b/notifier.rst index 30893558654..396a5f8dee2 100644 --- a/notifier.rst +++ b/notifier.rst @@ -59,43 +59,45 @@ to send SMS messages to mobile phones. This feature requires subscribing to a third-party service that sends SMS messages. Symfony provides integration with a couple popular SMS services: -============== ==================================== =========================================================================== -Service Package DSN -============== ==================================== =========================================================================== -46elks ``symfony/forty-six-elks-notifier`` ``forty-six-elks://API_USERNAME:API_PASSWORD@default?from=FROM`` -AllMySms ``symfony/all-my-sms-notifier`` ``allmysms://LOGIN:APIKEY@default?from=FROM`` -AmazonSns ``symfony/amazon-sns-notifier`` ``sns://ACCESS_KEY:SECRET_KEY@default?region=REGION`` -Clickatell ``symfony/clickatell-notifier`` ``clickatell://ACCESS_TOKEN@default?from=FROM`` -Esendex ``symfony/esendex-notifier`` ``esendex://USER_NAME:PASSWORD@default?accountreference=ACCOUNT_REFERENCE&from=FROM`` -FakeSms ``symfony/fake-sms-notifier`` ``fakesms+email://MAILER_SERVICE_ID?to=TO&from=FROM`` or ``fakesms+logger://default`` -FreeMobile ``symfony/free-mobile-notifier`` ``freemobile://LOGIN:PASSWORD@default?phone=PHONE`` -GatewayApi ``symfony/gateway-api-notifier`` ``gatewayapi://TOKEN@default?from=FROM`` -Infobip ``symfony/infobip-notifier`` ``infobip://AUTH_TOKEN@HOST?from=FROM`` -Iqsms ``symfony/iqsms-notifier`` ``iqsms://LOGIN:PASSWORD@default?from=FROM`` -KazInfoTeh ``symfony/kaz-info-teh-notifier`` ``kaz-info-teh://USERNAME:PASSWORD@default?sender=FROM`` -LightSms ``symfony/light-sms-notifier`` ``lightsms://LOGIN:TOKEN@default?from=PHONE`` -Mailjet ``symfony/mailjet-notifier`` ``mailjet://TOKEN@default?from=FROM`` -MessageBird ``symfony/message-bird-notifier`` ``messagebird://TOKEN@default?from=FROM`` -MessageMedia ``symfony/message-media-notifier`` ``messagemedia://API_KEY:API_SECRET@default?from=FROM`` -Mobyt ``symfony/mobyt-notifier`` ``mobyt://USER_KEY:ACCESS_TOKEN@default?from=FROM`` -Nexmo ``symfony/nexmo-notifier`` Abandoned in favor of Vonage (symfony/vonage-notifier). -Octopush ``symfony/octopush-notifier`` ``octopush://USERLOGIN:APIKEY@default?from=FROM&type=TYPE`` -OrangeSms ``symfony/orange-sms-notifier`` ``orange-sms://CLIENT_ID:CLIENT_SECRET@default?from=FROM&sender_name=SENDER_NAME`` -OvhCloud ``symfony/ovh-cloud-notifier`` ``ovhcloud://APPLICATION_KEY:APPLICATION_SECRET@default?consumer_key=CONSUMER_KEY&service_name=SERVICE_NAME&no_stop_clause=true`` -Sendberry ``symfony/sendberry-notifier`` ``sendberry://USERNAME:PASSWORD@default?auth_key=AUTH_KEY&from=FROM`` -Sendinblue ``symfony/sendinblue-notifier`` ``sendinblue://API_KEY@default?sender=PHONE`` -Sms77 ``symfony/sms77-notifier`` ``sms77://API_KEY@default?from=FROM`` -Sinch ``symfony/sinch-notifier`` ``sinch://ACCOUNT_ID:AUTH_TOKEN@default?from=FROM`` -Smsapi ``symfony/smsapi-notifier`` ``smsapi://TOKEN@default?from=FROM&test=0`` -SmsBiuras ``symfony/sms-biuras-notifier`` ``smsbiuras://UID:API_KEY@default?from=FROM&test_mode=0`` -Smsc ``symfony/smsc-notifier`` ``smsc://LOGIN:PASSWORD@default?from=FROM`` -SpotHit ``symfony/spot-hit-notifier`` ``spothit://TOKEN@default?from=FROM`` -Telnyx ``symfony/telnyx-notifier`` ``telnyx://API_KEY@default?from=FROM&messaging_profile_id=MESSAGING_PROFILE_ID`` -TurboSms ``symfony/turbo-sms-notifier`` ``turbosms://AUTH_TOKEN@default?from=FROM`` -Twilio ``symfony/twilio-notifier`` ``twilio://SID:TOKEN@default?from=FROM`` -Vonage ``symfony/vonage-notifier`` ``vonage://KEY:SECRET@default?from=FROM`` -Yunpian ``symfony/yunpian-notifier`` ``yunpian://APIKEY@default`` -============== ==================================== =========================================================================== +=============== ===================================== =========================================================================== +Service Package DSN +=============== ===================================== =========================================================================== +46elks ``symfony/forty-six-elks-notifier`` ``forty-six-elks://API_USERNAME:API_PASSWORD@default?from=FROM`` +AllMySms ``symfony/all-my-sms-notifier`` ``allmysms://LOGIN:APIKEY@default?from=FROM`` +AmazonSns ``symfony/amazon-sns-notifier`` ``sns://ACCESS_KEY:SECRET_KEY@default?region=REGION`` +Clickatell ``symfony/clickatell-notifier`` ``clickatell://ACCESS_TOKEN@default?from=FROM`` +ContactEveryone ``symfony/contact-everyone-notifier`` ``contact-everyone://TOKEN@default?&diffusionname=DIFFUSION_NAME&category=CATEGORY`` +Esendex ``symfony/esendex-notifier`` ``esendex://USER_NAME:PASSWORD@default?accountreference=ACCOUNT_REFERENCE&from=FROM`` +FakeSms ``symfony/fake-sms-notifier`` ``fakesms+email://MAILER_SERVICE_ID?to=TO&from=FROM`` or ``fakesms+logger://default`` +FreeMobile ``symfony/free-mobile-notifier`` ``freemobile://LOGIN:PASSWORD@default?phone=PHONE`` +GatewayApi ``symfony/gateway-api-notifier`` ``gatewayapi://TOKEN@default?from=FROM`` +Infobip ``symfony/infobip-notifier`` ``infobip://AUTH_TOKEN@HOST?from=FROM`` +Iqsms ``symfony/iqsms-notifier`` ``iqsms://LOGIN:PASSWORD@default?from=FROM`` +KazInfoTeh ``symfony/kaz-info-teh-notifier`` ``kaz-info-teh://USERNAME:PASSWORD@default?sender=FROM`` +LightSms ``symfony/light-sms-notifier`` ``lightsms://LOGIN:TOKEN@default?from=PHONE`` +Mailjet ``symfony/mailjet-notifier`` ``mailjet://TOKEN@default?from=FROM`` +MessageBird ``symfony/message-bird-notifier`` ``messagebird://TOKEN@default?from=FROM`` +MessageMedia ``symfony/message-media-notifier`` ``messagemedia://API_KEY:API_SECRET@default?from=FROM`` +Mobyt ``symfony/mobyt-notifier`` ``mobyt://USER_KEY:ACCESS_TOKEN@default?from=FROM`` +Nexmo ``symfony/nexmo-notifier`` Abandoned in favor of Vonage (symfony/vonage-notifier). +Octopush ``symfony/octopush-notifier`` ``octopush://USERLOGIN:APIKEY@default?from=FROM&type=TYPE`` +OrangeSms ``symfony/orange-sms-notifier`` ``orange-sms://CLIENT_ID:CLIENT_SECRET@default?from=FROM&sender_name=SENDER_NAME`` +OvhCloud ``symfony/ovh-cloud-notifier`` ``ovhcloud://APPLICATION_KEY:APPLICATION_SECRET@default?consumer_key=CONSUMER_KEY&service_name=SERVICE_NAME&no_stop_clause=true`` +Sendberry ``symfony/sendberry-notifier`` ``sendberry://USERNAME:PASSWORD@default?auth_key=AUTH_KEY&from=FROM`` +Sendinblue ``symfony/sendinblue-notifier`` ``sendinblue://API_KEY@default?sender=PHONE`` +Sms77 ``symfony/sms77-notifier`` ``sms77://API_KEY@default?from=FROM`` +Sinch ``symfony/sinch-notifier`` ``sinch://ACCOUNT_ID:AUTH_TOKEN@default?from=FROM`` +Smsapi ``symfony/smsapi-notifier`` ``smsapi://TOKEN@default?from=FROM&test=0`` +SmsBiuras ``symfony/sms-biuras-notifier`` ``smsbiuras://UID:API_KEY@default?from=FROM&test_mode=0`` +Smsc ``symfony/smsc-notifier`` ``smsc://LOGIN:PASSWORD@default?from=FROM`` +SMSFactor ``symfony/sms-factor-notifier`` ``sms-factor://TOKEN@default?sender=SENDER&push_type=PUSH_TYPE`` +SpotHit ``symfony/spot-hit-notifier`` ``spothit://TOKEN@default?from=FROM`` +Telnyx ``symfony/telnyx-notifier`` ``telnyx://API_KEY@default?from=FROM&messaging_profile_id=MESSAGING_PROFILE_ID`` +TurboSms ``symfony/turbo-sms-notifier`` ``turbosms://AUTH_TOKEN@default?from=FROM`` +Twilio ``symfony/twilio-notifier`` ``twilio://SID:TOKEN@default?from=FROM`` +Vonage ``symfony/vonage-notifier`` ``vonage://KEY:SECRET@default?from=FROM`` +Yunpian ``symfony/yunpian-notifier`` ``yunpian://APIKEY@default`` +=============== ===================================== =========================================================================== .. versionadded:: 6.1 @@ -103,6 +105,10 @@ Yunpian ``symfony/yunpian-notifier`` ``yunpian://APIKEY@default The ``no_stop_clause`` option in ``OvhCloud`` DSN was introduced in Symfony 6.1. The ``test`` option in ``Smsapi`` DSN was introduced in Symfony 6.1. +.. versionadded:: 6.2 + + The ContactEveryone and SMSFactor integrations were introduced in Symfony 6.2. + To enable a texter, add the correct DSN in your ``.env`` file and configure the ``texter_transports``: @@ -186,9 +192,14 @@ MicrosoftTeams ``symfony/microsoft-teams-notifier`` ``microsoftteams://default RocketChat ``symfony/rocket-chat-notifier`` ``rocketchat://TOKEN@ENDPOINT?channel=CHANNEL`` Slack ``symfony/slack-notifier`` ``slack://TOKEN@default?channel=CHANNEL`` Telegram ``symfony/telegram-notifier`` ``telegram://TOKEN@default?channel=CHAT_ID`` +Zendesk ``symfony/zendesk-notifier`` ``zendesk://EMAIL:TOKEN@SUBDOMAIN`` Zulip ``symfony/zulip-notifier`` ``zulip://EMAIL:TOKEN@HOST?channel=CHANNEL`` ============== ==================================== ============================================================================= +.. versionadded:: 6.2 + + The Zendesk integration was introduced in Symfony 6.2. + Chatters are configured using the ``chatter_transports`` setting: .. code-block:: bash diff --git a/reference/configuration/debug.rst b/reference/configuration/debug.rst index e77ee6e7bd6..c4040b8d8c7 100644 --- a/reference/configuration/debug.rst +++ b/reference/configuration/debug.rst @@ -11,10 +11,18 @@ key in your application configuration. .. code-block:: terminal # displays the default config values defined by Symfony - $ php bin/console config:dump-reference debug + $ php bin/console config:dump-reference framework # displays the actual config values used by your application - $ php bin/console debug:config debug + $ php bin/console debug:config framework + + # displays the config values used by your application and replaces the + # environment variables with their actual values + $ php bin/console debug:config --resolve-env framework + +.. versionadded:: 6.2 + + The ``--resolve-env`` option was introduced in Symfony 6.2. .. note:: diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index d32327c1d08..c066429e28a 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -53,6 +53,20 @@ will invalidate all signed URIs and Remember Me cookies. That's why, after changing this value, you should regenerate the application cache and log out all the application users. +catch_all_throwables +~~~~~~~~~~~~~~~~~~~~ + +**type**: ``boolean`` **default**: ``false`` + +If set to ``true``, the Symfony kernel will catch all ``\Throwable`` exceptions +thrown by the application and will turn them into HTTP reponses. + +Starting from Symfony 7.0, the default value of this option will be ``true``. + +.. versionadded:: 6.2 + + The ``catch_all_throwables`` option was introduced in Symfony 6.2. + .. _configuration-framework-http_cache: http_cache @@ -2508,6 +2522,11 @@ email_validation_mode **type**: ``string`` **default**: ``loose`` +.. deprecated:: 6.2 + + The ``loose`` default value is deprecated since Symfony 6.2. Starting from + Symfony 7.0, the default value of this option will be ``html5``. + Sets the default value for the :ref:`"mode" option of the Email validator `. diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index daaf60f8c5b..6ba99c0fe7c 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -28,7 +28,6 @@ Configuration **Basic Options**: * `access_denied_url`_ -* `delete_cookies`_ * `erase_credentials`_ * `hide_user_not_found`_ * `session_fixation_strategy`_ @@ -52,81 +51,6 @@ access_denied_url Defines the URL where the user is redirected after a ``403`` HTTP error (unless you define a custom access denial handler). Example: ``/no-permission`` -delete_cookies -~~~~~~~~~~~~~~ - -**type**: ``array`` **default**: ``[]`` - -Lists the names (and other optional features) of the cookies to delete when the -user logs out:: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/security.yaml - security: - # ... - - firewalls: - main: - # ... - logout: - delete_cookies: - cookie1-name: null - cookie2-name: - path: '/' - cookie3-name: - path: null - domain: example.com - - .. code-block:: xml - - - - - - - - - - - - - - - - - - - - .. code-block:: php - - // config/packages/security.php - $container->loadFromExtension('security', [ - // ... - 'firewalls' => [ - 'main' => [ - 'logout' => [ - 'delete_cookies' => [ - 'cookie1-name' => null, - 'cookie2-name' => [ - 'path' => '/', - ], - 'cookie3-name' => [ - 'path' => null, - 'domain' => 'example.com', - ], - ], - ], - ], - ], - ]); - erase_credentials ~~~~~~~~~~~~~~~~~ @@ -418,6 +342,81 @@ redirected to the ``default_target_path`` to avoid a redirection loop. **Options Related to Logout Configuration** +delete_cookies +~~~~~~~~~~~~~~ + +**type**: ``array`` **default**: ``[]`` + +Lists the names (and other optional features) of the cookies to delete when the +user logs out:: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + # ... + + firewalls: + main: + # ... + logout: + delete_cookies: + cookie1-name: null + cookie2-name: + path: '/' + cookie3-name: + path: null + domain: example.com + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/packages/security.php + $container->loadFromExtension('security', [ + // ... + 'firewalls' => [ + 'main' => [ + 'logout' => [ + 'delete_cookies' => [ + 'cookie1-name' => null, + 'cookie2-name' => [ + 'path' => '/', + ], + 'cookie3-name' => [ + 'path' => null, + 'domain' => 'example.com', + ], + ], + ], + ], + ], + ]); + invalidate_session ~~~~~~~~~~~~~~~~~~ @@ -449,6 +448,19 @@ redirect after logout. .. _reference-security-logout-csrf: +enable_csrf +~~~~~~~~~~~ + +**type**: ``boolean`` **default**: ``null`` + +Set this option to ``true`` to enable CSRF protection in the logout process +using Symfony's default CSRF token generator. Set also the ``csrf_token_generator`` +option if you need to use a custom CSRF token generator. + +.. versionadded:: 6.2 + + The ``enable_csrf`` option was introduced in Symfony 6.2. + csrf_parameter ~~~~~~~~~~~~~~ diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index 47477c58ef7..73a13f165ee 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -141,6 +141,11 @@ The default value used by this option is set in the :ref:`framework.validation.email_validation_mode ` configuration option. +.. deprecated:: 6.2 + + The ``loose`` value is deprecated since Symfony 6.2. Starting from + Symfony 7.0, the default value of this option will be ``html5``. + .. include:: /reference/constraints/_normalizer-option.rst.inc .. include:: /reference/constraints/_payload-option.rst.inc diff --git a/reference/constraints/Expression.rst b/reference/constraints/Expression.rst index 1df2fe73653..abd9cd5eb3b 100644 --- a/reference/constraints/Expression.rst +++ b/reference/constraints/Expression.rst @@ -143,6 +143,13 @@ expression that must return true in order for validation to pass. To learn more about the expression language syntax, see :doc:`/components/expression_language/syntax`. +Alternatively, you can set the ``negate`` option to ``false`` in order to +assert that the expression must return ``true`` for validation to fail. + +.. versionadded:: 6.2 + + The ``negate`` option was introduced in Symfony 6.2. + .. sidebar:: Mapping the Error to a Specific Field You can also attach the constraint to a specific property and still validate @@ -294,6 +301,17 @@ Parameter Description ``{{ label }}`` Corresponding form field label =============== ============================================================== +``negate`` +~~~~~~~~~~~ + +**type**: ``boolean`` **default**: ``true`` + +If ``false``, the validation fails when expression returns ``true``. + +.. versionadded:: 6.2 + + The ``negate`` option was introduced in Symfony 6.2. + .. include:: /reference/constraints/_payload-option.rst.inc ``values`` diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index c56d081bb2a..8dd942559c7 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -195,6 +195,19 @@ correct types will be assigned to the model. .. include:: /reference/forms/types/options/group_by.rst.inc +match +~~~~~ + +**type**: ``boolean`` **default**: ``true`` + +When this option is ``false``, the constraint checks that the given value is +not one of the values defined in the ``choices`` option. In practice, it makes +the ``Choice`` constraint behave like a ``NotChoice`` constraint. + +.. versionadded:: 6.2 + + The ``match`` option was introduced in Symfony 6.2. + .. include:: /reference/forms/types/options/multiple.rst.inc .. include:: /reference/forms/types/options/placeholder.rst.inc diff --git a/reference/forms/types/options/choice_label.rst.inc b/reference/forms/types/options/choice_label.rst.inc index aa83524ad88..33b5ccbfc54 100644 --- a/reference/forms/types/options/choice_label.rst.inc +++ b/reference/forms/types/options/choice_label.rst.inc @@ -29,6 +29,12 @@ more control:: }, ]); +.. versionadded:: 6.2 + + Starting from Symfony 6.2, you can use any object that implements + :class:`Symfony\\Contracts\\Translation\\TranslatableInterface` as the value + of the choice label. + This method is called for *each* choice, passing you the ``$choice`` and ``$key`` from the choices array (additional ``$value`` is related to `choice_value`_). This will give you: diff --git a/reference/forms/types/options/help.rst.inc b/reference/forms/types/options/help.rst.inc index b0686c3f82e..12398df14b1 100644 --- a/reference/forms/types/options/help.rst.inc +++ b/reference/forms/types/options/help.rst.inc @@ -1,7 +1,7 @@ help ~~~~ -**type**: ``string`` or ``TranslatableMessage`` **default**: null +**type**: ``string`` or ``TranslatableInterface`` **default**: null Allows you to define a help message for the form field, which by default is rendered below the field:: @@ -19,3 +19,8 @@ rendered below the field:: 'help' => new TranslatableMessage('order.status', ['%order_id%' => $order->getId()], 'store'), ]) ; + +.. versionadded:: 6.2 + + The support for ``TranslatableInterface`` objects as help contents was + introduced in Symfony 6.2. diff --git a/security.rst b/security.rst index 690e3c90cb0..4c9e62dcb39 100644 --- a/security.rst +++ b/security.rst @@ -474,6 +474,12 @@ You can also manually hash a password by running: Read more about all available hashers and password migration in :doc:`security/passwords`. +.. versionadded:: 6.2 + + In applications using Symfony 6.2 and PHP 8.2 or newer, the + `SensitiveParameter PHP attribute`_ is applied to all plain passwords and + sensitive tokens so they don't appear in stack traces. + .. _firewalls-authentication: .. _a-authentication-firewalls: @@ -599,8 +605,42 @@ anything else within your firewall in the :ref:`access control $ composer require --dev symfony/profiler-pack -Now that we understand our firewall, the next step is to create a way for your -users to authenticate! +Fetching the Firewall Configuration for a Request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you need to get the configuration of the firewall that matched a given request, +use the :class:`Symfony\\Bundle\\SecurityBundle\\Security\\Security` service:: + + // src/Service/ExampleService.php + // ... + + use Symfony\Bundle\SecurityBundle\Security\Security; + use Symfony\Component\HttpFoundation\RequestStack; + + class ExampleService + { + private Security $security; + + public function __construct(Security $security, RequestStack $requestStack) + { + $this->requestStack = $requestStack; + // Avoid calling getFirewallConfig() in the constructor: auth may not + // be complete yet. Instead, store the entire Security object. + $this->security = $security; + } + + public function someMethod() + { + $request = $this->requestStack->getCurrentRequest(); + $firewallName = $this->security->getFirewallConfig($request)?->getName(); + + // ... + } + } + +.. versionadded:: 6.2 + + The ``getFirewallConfig()`` method was introduced in Symfony 6.2. .. _security-authenticators: @@ -1775,12 +1815,12 @@ Fetching the User from a Service ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you need to get the logged in user from a service, use the -:class:`Symfony\\Component\\Security\\Core\\Security` service:: +:class:`Symfony\\Bundle\\SecurityBundle\\Security\\Security` service:: // src/Service/ExampleService.php // ... - use Symfony\Component\Security\Core\Security; + use Symfony\Bundle\SecurityBundle\Security\Security; class ExampleService { @@ -1802,6 +1842,12 @@ If you need to get the logged in user from a service, use the } } +.. versionadded:: 6.2 + + The :class:`Symfony\\Bundle\\SecurityBundle\\Security\\Security` class + was introduced in Symfony 6.2. In previous Symfony versions this class was + defined in ``Symfony\Component\Security\Core\Security``. + Fetch the User in a Template ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2237,7 +2283,7 @@ want to include extra details only for users that have a ``ROLE_SALES_ADMIN`` ro // ... use Symfony\Component\Security\Core\Exception\AccessDeniedException; - + use Symfony\Component\Security\Core\Security; + + use Symfony\Bundle\SecurityBundle\Security\Security; class SalesReportManager { @@ -2653,3 +2699,4 @@ Authorization (Denying Access) .. _`SymfonyCastsVerifyEmailBundle`: https://github.com/symfonycasts/verify-email-bundle .. _`HTTP Basic authentication`: https://en.wikipedia.org/wiki/Basic_access_authentication .. _`Login CSRF attacks`: https://en.wikipedia.org/wiki/Cross-site_request_forgery#Forging_login_requests +.. _`SensitiveParameter PHP attribute`: https://wiki.php.net/rfc/redact_parameters_in_back_traces diff --git a/security/custom_authenticator.rst b/security/custom_authenticator.rst index 4178e254904..7d0ea155df3 100644 --- a/security/custom_authenticator.rst +++ b/security/custom_authenticator.rst @@ -205,6 +205,11 @@ using :ref:`the user provider `:: // ... $passport = new Passport(new UserBadge($email), $credentials); +.. note:: + + The maximum length allowed for the user identifier is 4096 characters to + prevent `session storage flooding`_ attacks. + .. note:: You can optionally pass a user loader as second argument to the @@ -373,3 +378,5 @@ authenticator methods (e.g. ``createToken()``):: return new CustomOauthToken($passport->getUser(), $passport->getAttribute('scope')); } } + +.. _`session storage flooding`: https://symfony.com/blog/cve-2016-4423-large-username-storage-in-session diff --git a/security/impersonating_user.rst b/security/impersonating_user.rst index df07bbe4af8..3219802edb7 100644 --- a/security/impersonating_user.rst +++ b/security/impersonating_user.rst @@ -160,8 +160,8 @@ the impersonator user:: // src/Service/SomeService.php namespace App\Service; + use Symfony\Bundle\SecurityBundle\Security\Security; use Symfony\Component\Security\Core\Authentication\Token\SwitchUserToken; - use Symfony\Component\Security\Core\Security; // ... class SomeService @@ -306,9 +306,9 @@ logic you want:: // src/Security/Voter/SwitchToCustomerVoter.php namespace App\Security\Voter; + use Symfony\Bundle\SecurityBundle\Security\Security; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; - use Symfony\Component\Security\Core\Security; use Symfony\Component\Security\Core\User\UserInterface; class SwitchToCustomerVoter extends Voter diff --git a/security/voters.rst b/security/voters.rst index 23aa39cce41..0987d3567e0 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -229,7 +229,7 @@ with ``ROLE_SUPER_ADMIN``:: // src/Security/PostVoter.php // ... - use Symfony\Component\Security\Core\Security; + use Symfony\Bundle\SecurityBundle\Security\Security; class PostVoter extends Voter { diff --git a/service_container/lazy_services.rst b/service_container/lazy_services.rst index 7c9ff74637b..fb0fe699b6e 100644 --- a/service_container/lazy_services.rst +++ b/service_container/lazy_services.rst @@ -30,15 +30,10 @@ until you interact with the proxy in some way. In PHP versions prior to 8.0 lazy services do not support parameters with default values for built-in PHP classes (e.g. ``PDO``). -Installation ------------- +.. versionadded:: 6.2 -In order to use the lazy service instantiation, you will need to install the -``symfony/proxy-manager-bridge`` package: - -.. code-block:: terminal - - $ composer require symfony/proxy-manager-bridge + Starting from Symfony 6.2, you don't have to install any package (e.g. + ``symfony/proxy-manager-bridge``) in order to use the lazy service instantiation. Configuration ------------- @@ -81,32 +76,16 @@ You can mark the service as ``lazy`` by manipulating its definition: $services->set(AppExtension::class)->lazy(); }; +Once you inject the service into another service, a lazy ghost object with the +same signature of the class representing the service should be injected. A lazy +`ghost object`_ is an object that is created empty and that is able to initialize +itself when being accessed for the first time). The same happens when calling +``Container::get()`` directly. -Once you inject the service into another service, a virtual `proxy`_ with the -same signature of the class representing the service should be injected. The -same happens when calling ``Container::get()`` directly. - -The actual class will be instantiated as soon as you try to interact with the -service (e.g. call one of its methods). - -To check if your proxy works you can check the interface of the received object:: +To check if your lazy service works you can check the interface of the received object:: dump(class_implements($service)); - // the output should include "ProxyManager\Proxy\LazyLoadingInterface" - -.. note:: - - If you don't install the `ProxyManager bridge`_ , the container will skip - over the ``lazy`` flag and directly instantiate the service as it would - normally do. - -Additional Resources --------------------- - -You can read more about how proxies are instantiated, generated and initialized -in the `documentation of ProxyManager`_. + // the output should include "Symfony\Component\VarExporter\LazyGhostObjectInterface" -.. _`ProxyManager bridge`: https://github.com/symfony/symfony/tree/master/src/Symfony/Bridge/ProxyManager -.. _`proxy`: https://en.wikipedia.org/wiki/Proxy_pattern -.. _`documentation of ProxyManager`: https://github.com/Ocramius/ProxyManager/blob/master/docs/lazy-loading-value-holder.md +.. _`ghost object`: https://en.wikipedia.org/wiki/Lazy_loading#Ghost .. _`final`: https://www.php.net/manual/en/language.oop5.final.php diff --git a/session/proxy_examples.rst b/session/proxy_examples.rst index 67d46adb27b..a9114216ea8 100644 --- a/session/proxy_examples.rst +++ b/session/proxy_examples.rst @@ -110,8 +110,8 @@ can intercept the session before it is written:: namespace App\Session; use App\Entity\User; + use Symfony\Bundle\SecurityBundle\Security\Security; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; - use Symfony\Component\Security\Core\Security; class ReadOnlySessionProxy extends SessionHandlerProxy { diff --git a/setup.rst b/setup.rst index 9db493bb34f..c76ecb31dfc 100644 --- a/setup.rst +++ b/setup.rst @@ -64,12 +64,12 @@ Symfony application using Composer: .. code-block:: terminal # run this if you are building a traditional web application - $ composer create-project symfony/skeleton:"6.1.*" my_project_directory + $ composer create-project symfony/skeleton:"6.2.*@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:"6.1.*" my_project_directory + $ composer create-project symfony/skeleton:"6.2.*@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 diff --git a/templates.rst b/templates.rst index e406445787b..9448756c3e7 100644 --- a/templates.rst +++ b/templates.rst @@ -470,6 +470,38 @@ If your controller does not extend from ``AbstractController``, you'll need to :ref:`fetch services in your controller ` and use the ``render()`` method of the ``twig`` service. +.. _templates-template-attribute: + +Another option is to use the ``#[Template()]`` attribute on the controller method +to define the template to render:: + + // src/Controller/ProductController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\HttpFoundation\Response; + + class ProductController extends AbstractController + { + #[Template('product/index.html.twig')] + public function index() + { + // ... + + // when using the #[Template()] attribute, you only need to return + // an array with the parameters to pass to the template (the attribute + // is the one which will create and return the Response object). + return [ + 'category' => '...', + 'promotions' => ['...', '...'], + ]; + } + } + +.. versionadded:: 6.2 + + The ``#[Template()]`` attribute was introduced in Symfony 6.2. + Rendering a Template in Services ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~