diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml index aea27d929db..fc13097eccc 100644 --- a/.doctor-rst.yaml +++ b/.doctor-rst.yaml @@ -40,18 +40,18 @@ rules: extend_abstract_controller: ~ # no_app_bundle: ~ - # 4.x + # master versionadded_directive_major_version: - major_version: 4 + major_version: 5 versionadded_directive_min_version: - min_version: '4.0' + min_version: '5.0' deprecated_directive_major_version: - major_version: 4 + major_version: 5 deprecated_directive_min_version: - min_version: '4.0' + min_version: '5.0' # do not report as violation whitelist: @@ -80,8 +80,12 @@ whitelist: - '.. versionadded:: 1.6' # Flex in setup/upgrade_minor.rst - '0 => 123' # assertion for var_dumper - components/var_dumper.rst - '1 => "foo"' # assertion for var_dumper - components/var_dumper.rst + - '123,' # assertion for var_dumper - components/var_dumper.rst + - '"foo",' # assertion for var_dumper - components/var_dumper.rst - '$var .= "Because of this `\xE9` octet (\\xE9),\n";' - "`Deploying Symfony 4 Apps on Heroku`_." - ".. _`Deploying Symfony 4 Apps on Heroku`: https://devcenter.heroku.com/articles/deploying-symfony4" + - "// 224, 165, 141, 224, 164, 164, 224, 165, 135])" - '.. versionadded:: 0.2' # MercureBundle + - 'provides a ``loginUser()`` method to simulate logging in in your functional' - '.. code-block:: twig' diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index bc7d6a94182..af709c9c60f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,7 +1,7 @@ + + + + + + + %env(TWILIO_DSN)% + + + + + + .. code-block:: php + + # config/packages/notifier.php + $container->loadFromExtension('framework', [ + 'notifier' => [ + 'texter_transports' => [ + 'twilio' => '%env(TWILIO_DSN)%', + ], + ], + ]); + +.. _notifier-chat-channel: +.. _notifier-chatter-dsn: + +Chat Channel +~~~~~~~~~~~~ + +The chat channel is used to send chat messages to users by using +:class:`Symfony\\Component\\Notifier\\Chatter` classes. Symfony provides +integration with these chat services: + +========== =============================== ============================================ +Service Package DSN +========== =============================== ============================================ +Slack ``symfony/slack-notifier`` ``slack://default/ID`` +Telegram ``symfony/telegram-notifier`` ``telegram://TOKEN@default?channel=CHAT_ID`` +Mattermost ``symfony/mattermost-notifier`` ``mattermost://TOKEN@ENDPOINT?channel=CHANNEL`` +RocketChat ``symfony/rocketchat-notifier`` ``rocketchat://TOKEN@ENDPOINT?channel=CHANNEL`` +========== =============================== ============================================ + +.. versionadded:: 5.1 + + The Mattermost and RocketChat integrations were introduced in Symfony + 5.1. The Slack DSN changed in Symfony 5.1 to use Slack Incoming + Webhooks instead of legacy tokens. + +Chatters are configured using the ``chatter_transports`` setting: + +.. code-block:: bash + + # .env + SLACK_DSN=slack://default/ID + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/notifier.yaml + framework: + notifier: + chatter_transports: + slack: '%env(SLACK_DSN)%' + + .. code-block:: xml + + + + + + + + + %env(SLACK_DSN)% + + + + + + .. code-block:: php + + # config/packages/notifier.php + $container->loadFromExtension('framework', [ + 'notifier' => [ + 'chatter_transports' => [ + 'slack' => '%env(SLACK_DSN)%', + ], + ], + ]); + +.. _notifier-email-channel: + +Email Channel +~~~~~~~~~~~~~ + +The email channel uses the :doc:`Symfony Mailer ` to send +notifications using the special +:class:`Symfony\\Bridge\\Twig\\Mime\\NotificationEmail`. It is +required to install the Twig bridge along with the Inky and CSS Inliner +Twig extensions: + +.. code-block:: terminal + + $ composer require symfony/twig-pack twig/cssinliner-extra twig/inky-extra + +After this, :ref:`configure the mailer `. You can +also set the default "from" email address that should be used to send the +notification emails: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/mailer.yaml + framework: + mailer: + dsn: '%env(MAILER_DSN)%' + envelope: + sender: 'notifications@example.com' + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + # config/packages/mailer.php + $container->loadFromExtension('framework', [ + 'mailer' => [ + 'dsn' => '%env(MAILER_DSN)%', + 'envelope' => [ + 'sender' => 'notifications@example.com', + ], + ], + ]); + +Configure to use Failover or Round-Robin Transports +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Besides configuring one or more separate transports, you can also use the +special ``||`` and ``&&`` characters to implement a failover or round-robin +transport: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/notifier.yaml + framework: + notifier: + chatter_transports: + # Send notifications to Slack and use Telegram if + # Slack errored + main: '%env(SLACK_DSN)% || %env(TELEGRAM_DSN)%' + + # Send notifications to the next scheduled transport calculated by round robin + roundrobin: '%env(SLACK_DSN)% && %env(TELEGRAM_DSN)%' + + .. code-block:: xml + + + + + + + + + + %env(SLACK_DSN)% || %env(TELEGRAM_DSN)% + + + + + + + + + .. code-block:: php + + # config/packages/notifier.php + $container->loadFromExtension('framework', [ + 'notifier' => [ + 'chatter_transports' => [ + // Send notifications to Slack and use Telegram if + // Slack errored + 'main' => '%env(SLACK_DSN)% || %env(TELEGRAM_DSN)%', + + // Send notifications to the next scheduled transport calculated by round robin + 'roundrobin' => '%env(SLACK_DSN)% && %env(TELEGRAM_DSN)%', + ], + ], + ]); + +Creating & Sending Notifications +-------------------------------- + +To send a notification, autowire the +:class:`Symfony\\Component\\Notifier\\NotifierInterface` (service ID +``notifier``). This class has a ``send()`` method that allows you to send a +:class:`Symfony\\Component\\Notifier\\Notification\\Notification` to a +:class:`Symfony\\Component\\Notifier\\Recipient\\Recipient`:: + + // src/Controller/InvoiceController.php + namespace App\Controller; + + use Symfony\Component\Notifier\Notification\Notification; + use Symfony\Component\Notifier\NotifierInterface; + use Symfony\Component\Notifier\Recipient\AdminRecipient; + + class InvoiceController extends AbstractController + { + /** + * @Route("/invoice/create") + */ + public function create(NotifierInterface $notifier) + { + // ... + + // Create a Notification that has to be sent + // using the "email" channel + $notification = (new Notification('New Invoice', ['email'])) + ->content('You got a new invoice for 15 EUR.'); + + // The receiver of the Notification + $recipient = new AdminRecipient( + $user->getEmail(), + $user->getPhonenumber() + ); + + // Send the notification to the recipient + $notifier->send($notification, $recipient); + + // ... + } + } + +The ``Notification`` is created by using two arguments: the subject and +channels. The channels specify which channel (or transport) should be used +to send the notification. For instance, ``['email', 'sms']`` will send +both an email and sms notification to the user. It is required to specify +the transport when using chatters (e.g. ``['email', 'chat/telegram']``). + +The default notification also has a ``content()`` and ``emoji()`` method to +set the notification content and icon. + +Symfony provides three types of recipients: + +:class:`Symfony\\Component\\Notifier\\Recipient\\NoRecipient` + This is the default and is useful when there is no need to have + information about the receiver. For example, the browser channel uses + the current requests's :ref:`session flashbag `; + +:class:`Symfony\\Component\\Notifier\\Recipient\\Recipient` + This contains only the email address of the user and can be used for + messages on the email and browser channel; + +:class:`Symfony\\Component\\Notifier\\Recipient\\AdminRecipient` + This can contain both email address and phonenumber of the user. This + recipient can be used for all channels (depending on whether they are + actually set). + +Configuring Channel Policies +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of specifying the target channels on creation, Symfony also allows +you to use notification importance levels. Update the configuration to +specify what channels should be used for specific levels (using +``channel_policy``): + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/notifier.yaml + framework: + notifier: + # ... + channel_policy: + # Use SMS, Slack and email for urgent notifications + urgent: ['sms', 'chat/slack', 'email'] + + # Use Slack for highly important notifications + high: ['chat/slack'] + + # Use browser for medium and low notifications + medium: ['browser'] + low: ['browser'] + + .. code-block:: xml + + + + + + + + + + + + sms + chat/slack + email + + + chat/slack + + + browser + browser + + + + + + .. code-block:: php + + # config/packages/notifier.php + $container->loadFromExtension('framework', [ + 'notifier' => [ + // ... + 'channel_policy' => [ + // Use SMS, Slack and email for urgent notifications + 'urgent' => ['sms', 'chat/slack', 'email'], + + // Use Slack for highly important notifications + 'high' => ['chat/slack'], + + // Use browser for medium and low notifications + 'medium' => ['browser'], + 'low' => ['browser'], + ], + ], + ]); + +Now, whenever the notification's importance is set to "high", it will be +sent using the Slack transport:: + + // ... + class InvoiceController extends AbstractController + { + /** + * @Route("/invoice/create") + */ + public function invoice(NotifierInterface $notifier) + { + // ... + + $notification = (new Notification('New Invoice')) + ->content('You got a new invoice for 15 EUR.') + ->importance(Notification::IMPORTANCE_HIGH); + + $notifier->send($notification, new Recipient('wouter@wouterj.nl')); + + // ... + } + } + +Customize Notifications +----------------------- + +You can extend the ``Notification`` or ``Recipient`` base classes to +customize their behavior. For instance, you can overwrite the +``getChannels()`` method to only return ``sms`` if the invoice price is +very high and the recipient has a phone number:: + + namespace App\Notifier; + + use Symfony\Component\Notifier\Notification\Notification; + use Symfony\Component\Notifier\Recipient\Recipient; + + class InvoiceNotification extends Notification + { + private $price; + + public function __construct(int $price) + { + $this->price = $price; + } + + public function getChannels(Recipient $recipient) + { + if ( + $this->price > 10000 + && $recipient instanceof AdminRecipient + && null !== $recipient->getPhone() + ) { + return ['sms']; + } + + return ['email']; + } + } + +Customize Notification Messages +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Each channel has its own notification interface that you can implement to +customize the notification message. For instance, if you want to modify the +message based on the chat service, implement +:class:`Symfony\\Component\\Notifier\\Notification\\ChatNotificationInterface` +and its ``asChatMessage()`` method:: + + // src/Notifier/InvoiceNotification.php + namespace App\Notifier; + + use Symfony\Component\Notifier\Message\ChatMessage; + use Symfony\Component\Notifier\Notification\ChatNotificationInterface; + use Symfony\Component\Notifier\Notification\Notification; + use Symfony\Component\Notifier\Recipient\Recipient; + + class InvoiceNotification extends Notification implements ChatNotificationInterface + { + private $price; + + public function __construct(int $price) + { + $this->price = $price; + } + + public function asChatMessage(Recipient $recipient, string $transport = null): ?ChatMessage + { + // Add a custom emoji if the message is sent to Slack + if ('slack' === $transport) { + return (new ChatMessage('You\'re invoiced '.$this->price.' EUR.')) + ->emoji('money'); + } + + // If you return null, the Notifier will create the ChatMessage + // based on this notification as it would without this method. + return null; + } + } + +The +:class:`Symfony\\Component\\Notifier\\Notification\\SmsNotificationInterface` +and +:class:`Symfony\\Component\\Notifier\\Notification\\EmailNotificationInterface` +also exists to modify messages send to those channels. + +Disabling Delivery +------------------ + +While developing (or testing), you may want to disable delivery of notifications +entirely. You can do this by forcing Notifier to use the ``NullTransport`` for +all configured texter and chatter transports only in the ``dev`` (and/or +``test``) environment: + +.. code-block:: yaml + + # config/packages/dev/notifier.yaml + framework: + notifier: + texter_transports: + twilio: 'null://null' + chatter_transports: + slack: 'null://null' + +.. TODO + - Using the message bus for asynchronous notification + - Describe notifier monolog handler + - Describe notification_on_failed_messages integration + +Learn more +---------- + +.. toctree:: + :maxdepth: 1 + :glob: + + notifier/* diff --git a/notifier/chatters.rst b/notifier/chatters.rst new file mode 100644 index 00000000000..17eac35885f --- /dev/null +++ b/notifier/chatters.rst @@ -0,0 +1,93 @@ +.. index:: + single: Notifier; Chatters + +How to send Chat Messages +========================= + +.. versionadded:: 5.0 + + The Notifier component was introduced in Symfony 5.0 as an + :doc:`experimental feature `. + +The :class:`Symfony\\Component\\Notifier\\ChatterInterface` class allows +you to send messages to chat services like Slack or Telegram:: + + // src/Controller/CheckoutController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Notifier\ChatterInterface; + use Symfony\Component\Notifier\Message\ChatMessage; + use Symfony\Component\Routing\Annotation\Route; + + class CheckoutController extends AbstractController + { + /** + * @Route("/checkout/thankyou") + */ + public function thankyou(ChatterInterface $chatter) + { + $message = (new ChatMessage('You got a new invoice for 15 EUR.')) + // if not set explicitly, the message is send to the + // default transport (the first one configured) + ->transport('slack'); + + $chatter->send($message); + + // ... + } + } + +.. seealso:: + + Read :ref:`the main Notifier guide ` to see how + to configure the different transports. + +Adding Interactions to a Slack Message +-------------------------------------- + +With a Slack message, you can use the +:class:`Symfony\\Component\\Notifier\\Bridge\\Slack\\SlackOptions` to add +some interactive options called `Block elements`_:: + + use Symfony\Component\Notifier\Bridge\Slack\Block\SlackActionsBlock; + use Symfony\Component\Notifier\Bridge\Slack\Block\SlackDividerBlock; + use Symfony\Component\Notifier\Bridge\Slack\Block\SlackImageBlock; + use Symfony\Component\Notifier\Bridge\Slack\Block\SlackSectionBlock; + use Symfony\Component\Notifier\Bridge\Slack\SlackOptions; + use Symfony\Component\Notifier\Message\ChatMessage; + + $chatMessage = new ChatMessage('Contribute To Symfony'); + + // Create Slack Actions Block and add some buttons + $contributeToSymfonyBlocks = (new SlackActionsBlock()) + ->button( + 'Improve Documentation', + 'https://symfony.com/doc/current/contributing/documentation/standards.html', + 'primary' + ) + ->button( + 'Report bugs', + 'https://symfony.com/doc/current/contributing/code/bugs.html', + 'danger' + ); + + $slackOptions = (new SlackOptions()) + ->block((new SlackSectionBlock()) + ->text('The Symfony Community') + ->accessory( + new SlackImageBlockElement( + 'https://example.com/symfony-logo.png', + 'Symfony' + ) + ) + ) + ->block(new SlackDividerBlock()) + ->block($contributeToSymfonyBlocks); + + // Add the custom options to the chat message and send the message + $chatMessage->options($slackOptions); + + $chatter->send($chatMessage); + +.. _`Block elements`: https://api.slack.com/reference/block-kit/block-elements diff --git a/notifier/texters.rst b/notifier/texters.rst new file mode 100644 index 00000000000..d4a0a91aa55 --- /dev/null +++ b/notifier/texters.rst @@ -0,0 +1,45 @@ +.. index:: + single: Notifier; Texters + +How to send SMS Messages +======================== + +.. versionadded:: 5.0 + + The Notifier component was introduced in Symfony 5.0 as an + :doc:`experimental feature `. + +The :class:`Symfony\\Component\\Notifier\\TexterInterface` class allows +you to send SMS messages:: + + // src/Controller/SecurityController.php + namespace App\Controller; + + use Symfony\Component\Notifier\Message\SmsMessage; + use Symfony\Component\Notifier\TexterInterface; + use Symfony\Component\Routing\Annotation\Route; + + class SecurityController + { + /** + * @Route("/login/success") + */ + public function loginSuccess(TexterInterface $texter) + { + $sms = new SmsMessage( + // the phone number to send the SMS message to + '+1411111111', + // the message + 'A new login was detected!' + ); + + $texter->send($sms); + + // ... + } + } + +.. seealso:: + + Read :ref:`the main Notifier guide ` to see how + to configure the different transports. diff --git a/page_creation.rst b/page_creation.rst index 4711685ae3d..43d06a5808c 100644 --- a/page_creation.rst +++ b/page_creation.rst @@ -52,7 +52,7 @@ random) number and prints it. To do that, create a "Controller" class and a class LuckyController { - public function number() + public function number(): Response { $number = random_int(0, 100); diff --git a/performance.rst b/performance.rst index 3fd0efc43c9..b919f6a3906 100644 --- a/performance.rst +++ b/performance.rst @@ -16,6 +16,7 @@ application to improve its performance: #. :ref:`Install APCu Polyfill if your server uses APC ` #. :ref:`Dump the service container into a single file ` +#. :ref:`Restrict the number of locales enabled in the application ` .. _performance-install-apcu-polyfill: @@ -32,11 +33,6 @@ features, such as the APCu Cache adapter. Dump the Service Container into a Single File ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 4.4 - - The ``container.dumper.inline_factories`` parameter was introduced in - Symfony 4.4. - Symfony compiles the :doc:`service container ` into multiple small files by default. Set this parameter to ``true`` to compile the entire container into a single file, which could improve performance when using @@ -72,6 +68,15 @@ container into a single file, which could improve performance when using // ... $container->setParameter('container.dumper.inline_factories', true); + +.. _performance-enabled-locales: + +Restrict the Number of Locales Enabled in the Application +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use the :ref:`framework.translator.enabled_locales ` +option to only generate the translation files actually used in your application. + Production Server Checklist --------------------------- @@ -100,10 +105,6 @@ used byte code cache is `APC`_. Use the OPcache class preloading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 4.4 - - The feature that generates the preloading file was introduced in Symfony 4.4. - Starting from PHP 7.4, OPcache can compile and load classes at start-up and make them available to all requests until the server is restarted, improving performance significantly. @@ -121,6 +122,10 @@ The preload file path is the same as the compiled service container but with the ; php.ini opcache.preload=/path/to/project/var/cache/prod/srcApp_KernelProdContainer.preload.php +Use the :ref:`container.preload ` and +:ref:`container.no_preload ` service tags to define +which classes should or should not be preloaded PHP. + .. _performance-configure-opcache: Configure OPcache for Maximum Performance diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst index 5705a50c6ee..9579ff7217b 100644 --- a/reference/configuration/doctrine.rst +++ b/reference/configuration/doctrine.rst @@ -222,33 +222,35 @@ Keep in mind that you can't use both syntaxes at the same time. Caching Drivers ~~~~~~~~~~~~~~~ -.. deprecated:: 4.4 - - All the Doctrine caching types are deprecated since Symfony 4.4 and won't - be available in Symfony 5.0 and higher. Replace them with either ``type: service`` - or ``type: pool`` and use any of the cache pools/services defined with - :doc:`Symfony Cache `. - -The built-in types of caching drivers are: ``array``, ``apc``, ``apcu``, -``memcache``, ``memcached``, ``redis``, ``wincache``, ``zenddata`` and ``xcache``. -There is a special type called ``service`` which lets you define the ID of your -own caching service. - -The following example shows an overview of the caching configurations: +Use any of the existing :doc:`Symfony Cache ` pools or define new pools +to cache each of Doctrine ORM elements (queries, results, etc.): .. code-block:: yaml + # config/packages/prod/doctrine.yaml + framework: + cache: + pools: + doctrine.result_cache_pool: + adapter: cache.app + doctrine.system_cache_pool: + adapter: cache.system + doctrine: orm: - auto_mapping: true - # each caching driver type defines its own config options - metadata_cache_driver: apc + # ... + metadata_cache_driver: + type: pool + pool: doctrine.system_cache_pool + query_cache_driver: + type: pool + pool: doctrine.system_cache_pool result_cache_driver: - type: memcache - host: localhost - port: 11211 - instance_class: Memcache - # the 'service' type requires to define the 'id' option too + type: pool + pool: doctrine.result_cache_pool + + # in addition to Symfony Cache pools, you can also use the + # 'type: service' option to use any service as the cache query_cache_driver: type: service id: App\ORM\MyCacheService diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst index 456fd5298a4..6c64626f07a 100644 --- a/reference/configuration/framework.rst +++ b/reference/configuration/framework.rst @@ -221,22 +221,13 @@ Configuration * `storage_id`_ * `use_cookies`_ -* `templating`_ - - * :ref:`cache ` - * `engines`_ - * :ref:`form ` - - * :ref:`resources ` - - * `loaders`_ - * `test`_ * `translator`_ * `cache_dir`_ * :ref:`default_path ` * :ref:`enabled ` + * :ref:`enabled_locales ` * `fallbacks`_ * `formatter`_ * `logging`_ @@ -260,7 +251,6 @@ Configuration * `endpoint`_ * `static_method`_ - * `strict_email`_ * `translation_domain`_ * `workflows`_ @@ -464,10 +454,6 @@ disallow_search_engine_index **type**: ``boolean`` **default**: ``true`` when the debug mode is enabled, ``false`` otherwise. -.. versionadded:: 4.3 - - The ``disallow_search_engine_index`` option was introduced in Symfony 4.3. - If ``true``, Symfony adds a ``X-Robots-Tag: noindex`` HTTP tag to all responses (unless your own app adds that header, in which case it's not modified). This `X-Robots-Tag HTTP header`_ tells search engines to not index your web site. @@ -604,10 +590,6 @@ error_controller **type**: ``string`` **default**: ``error_controller`` -.. versionadded:: 4.4 - - The ``error_controller`` option was introduced in Symfony 4.4. - This is the controller that is called when an exception is thrown anywhere in your application. The default controller (:class:`Symfony\\Component\\HttpKernel\\Controller\\ErrorController`) @@ -689,12 +671,6 @@ hinclude_default_template **type**: ``string`` **default**: ``null`` -.. versionadded:: 4.3 - - The ``framework.fragments.hinclude_default_template`` option was introduced - in Symfony 4.3. In previous Symfony versions it was defined under - ``framework.templating.hinclude_default_template``. - Sets the content shown during the loading of the fragment or when JavaScript is disabled. This can be either a template name or the content itself. @@ -786,10 +762,6 @@ auth_ntlm **type**: ``string`` -.. versionadded:: 4.4 - - The ``auth_ntlm`` option was introduced in Symfony 4.4. - The username and password used to create the ``Authorization`` HTTP header used in the `Microsoft NTLM authentication protocol`_. The value of this option must follow the format ``username:password``. This authentication mechanism requires @@ -840,10 +812,6 @@ If this option is a boolean value, the response is buffered when the value is returned value is ``true`` (the closure receives as argument an array with the response headers). -.. versionadded:: 4.4 - - The support of ``Closure`` in the ``buffer`` option was introduced in Symfony 4.4. - cafile ...... @@ -1004,10 +972,6 @@ 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. -.. versionadded:: 4.4 - - The ``max_duration`` option was introduced in Symfony 4.4. - verify_host ........... @@ -1209,6 +1173,11 @@ utf8 **type**: ``boolean`` **default**: ``false`` +.. deprecated:: 5.1 + + Not setting this option is deprecated since Symfony 5.1. Moreover, the + default value of this option will change to ``true`` in Symfony 6.0. + When this option is set to ``true``, the regular expressions used in the :ref:`requirements of route parameters ` will be run using the `utf-8 modifier`_. This will for example match any UTF-8 character @@ -1330,7 +1299,7 @@ to the cookie specification. cookie_samesite ............... -**type**: ``string`` or ``null`` **default**: ``null`` +**type**: ``string`` or ``null`` **default**: ``'lax'`` It controls the way cookies are sent when the HTTP request was not originated from the same domain the cookies are associated to. Setting this option is @@ -1361,10 +1330,10 @@ The possible values for this option are: cookie_secure ............. -**type**: ``boolean`` or ``string`` **default**: ``false`` +**type**: ``boolean`` or ``null`` **default**: ``null`` This determines whether cookies should only be sent over secure connections. In -addition to ``true`` and ``false``, there's a special ``'auto'`` value that +addition to ``true`` and ``false``, there's a special ``null`` value that means ``true`` for HTTPS requests and ``false`` for HTTP requests. cookie_httponly @@ -1909,10 +1878,11 @@ json_manifest_path **type**: ``string`` **default**: ``null`` -The file path to a ``manifest.json`` file containing an associative array of asset -names and their respective compiled names. A common cache-busting technique using -a "manifest" file works by writing out assets with a "hash" appended to their -file names (e.g. ``main.ae433f1cb.css``) during a front-end compilation routine. +The file path or absolute URL to a ``manifest.json`` file containing an +associative array of asset names and their respective compiled names. A common +cache-busting technique using a "manifest" file works by writing out assets with +a "hash" appended to their file names (e.g. ``main.ae433f1cb.css``) during a +front-end compilation routine. .. tip:: @@ -1933,6 +1903,8 @@ package: assets: # this manifest is applied to every asset (including packages) json_manifest_path: "%kernel.project_dir%/public/build/manifest.json" + # you can use absolute URLs too and Symfony will download them automatically + # json_manifest_path: 'https://cdn.example.com/manifest.json' packages: foo_package: # this package uses its own manifest (the default file is ignored) @@ -1954,6 +1926,8 @@ package: + + [ // this manifest is applied to every asset (including packages) 'json_manifest_path' => '%kernel.project_dir%/public/build/manifest.json', + // you can use absolute URLs too and Symfony will download them automatically + // 'json_manifest_path' => 'https://cdn.example.com/manifest.json', 'packages' => [ 'foo_package' => [ // this package uses its own manifest (the default file is ignored) @@ -1986,6 +1962,11 @@ package: ], ]); +.. versionadded:: 5.1 + + The option to use an absolute URL in ``json_manifest_path`` was introduced + in Symfony 5.1. + .. note:: This parameter cannot be set at the same time as ``version`` or ``version_strategy``. @@ -1997,53 +1978,58 @@ package: If you request an asset that is *not found* in the ``manifest.json`` file, the original - *unmodified* - asset path will be returned. -templating +.. note:: + + If an URL is set, the JSON manifest is downloaded on each request using the `http_client`_. + +translator ~~~~~~~~~~ -.. deprecated:: 4.3 +cache_dir +......... - The integration of the Templating component in FrameworkBundle has been - deprecated since version 4.3 and will be removed in 5.0. That's why all the - configuration options defined under ``framework.templating`` are deprecated too. +**type**: ``string`` | ``null`` **default**: ``%kernel.cache_dir%/translations/`` -.. _reference-templating-form: +Defines the directory where the translation cache is stored. Use ``null`` to +disable this cache. -form -.... +.. _reference-translator-enabled: -.. _reference-templating-form-resources: +enabled +....... -resources -""""""""" +**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation + +Whether or not to enable the ``translator`` service in the service container. + +.. _reference-translator-enabled-locales: -**type**: ``string[]`` **default**: ``['FrameworkBundle:Form']`` +enabled_locales +............... -.. deprecated:: 4.3 +**type**: ``array`` **default**: ``[]`` (empty array = enable all locales) - The integration of the Templating component in FrameworkBundle has been - deprecated since version 4.3 and will be removed in 5.0. Form theming with - PHP templates will no longer be supported and you'll need to use Twig instead. +.. versionadded:: 5.1 -A list of all resources for form theming in PHP. This setting is not required -if you're :ref:`using the Twig format for your themes `. + The ``enabled_locales`` option was introduced in Symfony 5.1. -Assume you have custom global form themes in ``templates/form_themes/``, you can -configure this like: +Symfony applications generate by default the translation files for validation +and security messages in all locales. If your application only uses some +locales, use this option to restrict the files generated by Symfony and improve +performance a bit: .. configuration-block:: .. code-block:: yaml - # config/packages/framework.yaml + # config/packages/translation.yaml framework: - templating: - form: - resources: - - 'form_themes' + translator: + enabled_locales: ['en', 'es'] .. code-block:: xml - + - - - form_themes - - + + en + es + .. code-block:: php - // config/packages/framework.php + // config/packages/translation.php $container->loadFromExtension('framework', [ - 'templating' => [ - 'form' => [ - 'resources' => [ - 'form_themes', - ], - ], + 'translator' => [ + 'enabled_locales' => ['en', 'es'], ], ]); -.. note:: - - The default form templates from ``FrameworkBundle:Form`` will always - be included in the form resources. - -.. seealso:: - - See :ref:`forms-theming-global` for more information. - -.. _reference-templating-cache: - -cache -..... - -**type**: ``string`` - -The path to the cache directory for templates. When this is not set, caching -is disabled. - -.. note:: - - When using Twig templating, the caching is already handled by the - TwigBundle and doesn't need to be enabled for the FrameworkBundle. - -engines -....... - -**type**: ``string[]`` / ``string`` **required** - -The Templating Engine to use. This can either be a string (when only one -engine is configured) or an array of engines. - -At least one engine is required. - -loaders -....... - -**type**: ``string[]`` - -An array (or a string when configuring just one loader) of service ids for -templating loaders. Templating loaders are used to find and load templates -from a resource (e.g. a filesystem or database). Templating loaders must -implement :class:`Symfony\\Component\\Templating\\Loader\\LoaderInterface`. - -translator -~~~~~~~~~~ - -cache_dir -......... - -**type**: ``string`` | ``null`` **default**: ``%kernel.cache_dir%/translations/`` - -.. versionadded:: 4.4 - - The ``cache_dir`` option was introduced in Symfony 4.4. - -Defines the directory where the translation cache is stored. Use ``null`` to -disable this cache. - -.. _reference-translator-enabled: - -enabled -....... - -**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation - -Whether or not to enable the ``translator`` service in the service container. +If some user makes requests with a locale not included in this option, the +application won't display any error because Symfony will display contents using +the fallback locale. .. _fallback: fallbacks ......... -**type**: ``string|array`` **default**: ``['en']`` +**type**: ``string|array`` **default**: value of `default_locale`_ This option is used when the translation key for the current locale wasn't found. @@ -2223,10 +2140,6 @@ throw_exception_on_invalid_property_path **type**: ``boolean`` **default**: ``true`` -.. versionadded:: 4.3 - - The ``throw_exception_on_invalid_property_path`` option was introduced in Symfony 4.3. - When enabled, the ``property_accessor`` service throws an exception when you try to access an invalid property path of an object. @@ -2301,10 +2214,6 @@ enabled **type**: ``boolean`` **default**: ``true`` -.. versionadded:: 4.3 - - The ``enabled`` option was introduced in Symfony 4.3. - If you set this option to ``false``, no HTTP requests will be made and the given password will be considered valid. This is useful when you don't want or can't make HTTP requests, such as in ``dev`` and ``test`` environments or in @@ -2315,10 +2224,6 @@ endpoint **type**: ``string`` **default**: ``null`` -.. versionadded:: 4.3 - - The ``endpoint`` option was introduced in Symfony 4.3. - By default, the :doc:`NotCompromisedPassword ` constraint uses the public API provided by `haveibeenpwned.com`_. This option allows to define a different, but compatible, API endpoint to make the password @@ -2335,20 +2240,6 @@ metadata of the class. You can define an array of strings with the names of several methods. In that case, all of them will be called in that order to load the metadata. -strict_email -............ - -**type**: ``Boolean`` **default**: ``false`` - -.. deprecated:: 4.1 - - The ``strict_email`` option was deprecated in Symfony 4.1. Use the new - ``email_validation_mode`` option instead. - -If this option is enabled, the `egulias/email-validator`_ library will be -used by the :doc:`/reference/constraints/Email` constraint validator. Otherwise, -the validator uses a simple regular expression to validate email addresses. - email_validation_mode ..................... @@ -2907,10 +2798,6 @@ A list of lock stores to be created by the framework extension. ], ]); -.. versionadded:: 4.2 - - The ``flock://`` store was introduced in Symfony 4.2. - .. _reference-lock-resources-name: name @@ -2930,7 +2817,7 @@ Name of the lock you want to create. lock.invoice.retry_till_save.store: class: Symfony\Component\Lock\Store\RetryTillSaveStore decorates: lock.invoice.store - arguments: ['@lock.invoice.retry_till_save.store.inner', 100, 50] + arguments: ['@.inner', 100, 50] workflows ~~~~~~~~~ diff --git a/reference/configuration/kernel.rst b/reference/configuration/kernel.rst index 5852927e7ad..5f52cd155e7 100644 --- a/reference/configuration/kernel.rst +++ b/reference/configuration/kernel.rst @@ -12,12 +12,17 @@ Configuration ------------- * `Charset`_ -* `Kernel Name`_ * `Project Directory`_ * `Cache Directory`_ * `Log Directory`_ * `Container Build Time`_ +In previous Symfony versions there was another configuration option to define +the "kernel name", which is only important when +:doc:`using applications with multiple kernels `. +If you need a unique ID for your kernels use the ``kernel.container_class`` +parameter or the ``Kernel::getContainerClass()`` method. + .. _configuration-kernel-charset: Charset @@ -44,29 +49,6 @@ charset:: } } -Kernel Name -~~~~~~~~~~~ - -**type**: ``string`` **default**: ``src`` (i.e. the directory name holding -the kernel class) - -.. deprecated:: 4.2 - - The ``kernel.name`` parameter and the ``Kernel::getName()`` method were - deprecated in Symfony 4.2. If you need a unique ID for your kernels use the - ``kernel.container_class`` parameter or the ``Kernel::getContainerClass()`` method. - -The name of the kernel isn't usually directly important - it's used in the -generation of cache files - and you probably will only change it when -:doc:`using applications with multiple kernels `. - -This value is exposed via the ``kernel.name`` configuration parameter and the -:method:`Symfony\\Component\\HttpKernel\\Kernel::getName` method. - -To change this setting, override the ``getName()`` method. Alternatively, move -your kernel into a different directory. For example, if you moved the kernel -into a ``foo/`` directory (instead of ``src/``), the kernel name will be ``foo``. - .. _configuration-kernel-project-directory: Project Directory @@ -121,11 +103,6 @@ Log Directory **type**: ``string`` **default**: ``$this->getProjectDir()/var/log`` -.. deprecated:: 4.2 - - The ``kernel.log_dir`` parameter was deprecated in Symfony 4.2, - use ``kernel.logs_dir`` instead. - This returns the absolute path of the log directory of your Symfony project. It's calculated automatically based on the current :ref:`environment `. diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst index c3f66e1c5f5..2392181cac5 100644 --- a/reference/configuration/security.rst +++ b/reference/configuration/security.rst @@ -71,10 +71,6 @@ When set to ``lazy``, Symfony loads the user (and starts 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). -.. versionadded:: 4.4 - - The ``lazy`` value of the ``anonymous`` option was introduced in Symfony 4.4. - erase_credentials ~~~~~~~~~~~~~~~~~ @@ -159,7 +155,6 @@ encoding algorithm. Also, each algorithm defines different config options: algorithm: 'sodium' memory_cost: 16384 # Amount in KiB. (16384 = 16 MiB) time_cost: 2 # Number of iterations - threads: 4 # Number of parallel threads # MessageDigestPasswordEncoder encoder using SHA512 hashing with default options AppBundle\Entity\User: 'sha512' @@ -172,7 +167,9 @@ encoding algorithm. Also, each algorithm defines different config options: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -197,14 +194,12 @@ encoding algorithm. Also, each algorithm defines different config options: + time_cost: number of iterations --> @@ -244,7 +239,6 @@ encoding algorithm. Also, each algorithm defines different config options: 'algorithm' => 'sodium', 'memory_cost' => 16384, // Amount in KiB. (16384 = 16 MiB) 'time_cost' => 2, // Number of iterations - 'threads' => 4, // Number of parallel threads ], // MessageDigestPasswordEncoder encoder using SHA512 hashing with default options @@ -254,17 +248,6 @@ encoding algorithm. Also, each algorithm defines different config options: ], ]); -.. deprecated:: 4.3 - - The ``threads`` configuration option was deprecated in Symfony 4.3. No - alternative is provided because starting from Symfony 5.0 this value will be - hardcoded to ``1`` (one thread). - -.. versionadded:: 4.3 - - The ``sodium`` algorithm was introduced in Symfony 4.3. In previous Symfony - versions it was called ``argon2i``. - .. tip:: You can also create your own password encoders as services and you can even @@ -277,11 +260,6 @@ encoding algorithm. Also, each algorithm defines different config options: Using the Sodium Password Encoder ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. versionadded:: 4.3 - - The ``SodiumPasswordEncoder`` was introduced in Symfony 4.3. In previous - Symfony versions it was called ``Argon2iPasswordEncoder``. - It uses the `Argon2 key derivation function`_ and it's the encoder recommended by Symfony. Argon2 support was introduced in PHP 7.2, but if you use an earlier PHP version, you can install the `libsodium`_ PHP extension. @@ -363,7 +341,9 @@ application: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -558,28 +538,18 @@ The ``invalidate_session`` option allows to redefine this behavior. Set this option to ``false`` in every firewall and the user will only be logged out from the current firewall and not the other ones. -logout_on_user_change -~~~~~~~~~~~~~~~~~~~~~ - -**type**: ``boolean`` **default**: ``true`` - -.. deprecated:: 4.1 - - The ``logout_on_user_change`` option was deprecated in Symfony 4.1. - -If ``false`` this option makes Symfony to not trigger a logout when the user has -changed. Doing that is deprecated, so this option should be set to ``true`` or -unset to avoid getting deprecation messages. - -The user is considered to have changed when the user class implements -:class:`Symfony\\Component\\Security\\Core\\User\\EquatableInterface` and the -``isEqualTo()`` method returns ``false``. Also, when any of the properties -required by the :class:`Symfony\\Component\\Security\\Core\\User\\UserInterface` -(like the username, password or salt) changes. +.. _reference-security-logout-success-handler: success_handler ~~~~~~~~~~~~~~~ +.. deprecated:: 5.1 + + This option is deprecated since Symfony 5.1. Register an + :doc:`event listener ` on the + :class:`Symfony\\Component\\Security\\Http\\Event\\LogoutEvent` + instead. + **type**: ``string`` **default**: ``'security.logout.success_handler'`` The service ID used for handling a successful logout. The service must implement @@ -705,7 +675,9 @@ multiple firewalls, the "context" could actually be shared: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst index 1d11db3e3da..e00d7f63958 100644 --- a/reference/configuration/twig.rst +++ b/reference/configuration/twig.rst @@ -42,7 +42,6 @@ Configuration * `debug`_ * `default_path`_ -* `exception_controller`_ * `form_themes`_ * `globals`_ * `number_format`_ @@ -196,29 +195,6 @@ The path to the directory where Symfony will look for the application Twig templates by default. If you store the templates in more than one directory, use the :ref:`paths ` option too. -.. _config-twig-exception-controller: - -exception_controller -~~~~~~~~~~~~~~~~~~~~ - -**type**: ``string`` **default**: ``twig.controller.exception:showAction`` - -.. deprecated:: 4.4 - - The ``exception_controller`` configuration option was deprecated in Symfony 4.4. - Set it to ``null`` and use the new ``error_controller`` option under ``framework`` - configuration instead. - -This is the controller that is activated after an exception is thrown anywhere -in your application. The default controller -(:class:`Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController`) -is what's responsible for rendering specific templates under different error -conditions (see :doc:`/controller/error_pages`). Modifying this -option is advanced. If you need to customize an error page you should use -the previous link. If you need to perform some behavior on an exception, -you should add an :doc:`event listener ` to the -:ref:`kernel.exception event `. - .. _config-twig-form-themes: form_themes diff --git a/reference/constraints.rst b/reference/constraints.rst index 317a836e396..1aeaca354e7 100644 --- a/reference/constraints.rst +++ b/reference/constraints.rst @@ -14,9 +14,11 @@ Validation Constraints Reference constraints/Type constraints/Email + constraints/ExpressionLanguageSyntax constraints/Length constraints/Url constraints/Regex + constraints/Hostname constraints/Ip constraints/Uuid constraints/Json @@ -62,6 +64,9 @@ Validation Constraints Reference constraints/Isbn constraints/Issn + constraints/AtLeastOneOf + constraints/Sequentially + constraints/Compound constraints/Callback constraints/Expression constraints/All diff --git a/reference/constraints/AtLeastOneOf.rst b/reference/constraints/AtLeastOneOf.rst new file mode 100644 index 00000000000..6a48c44a4fd --- /dev/null +++ b/reference/constraints/AtLeastOneOf.rst @@ -0,0 +1,196 @@ +AtLeastOneOf +============ + +This constraint checks that the value satisfies at least one of the given +constraints. The validation stops as soon as one constraint is satisfied. + +.. versionadded:: 5.1 + + The ``AtLeastOneOf`` constraint was introduced in Symfony 5.1. + +========== =================================================================== +Applies to :ref:`property or method ` +Options - `constraints`_ + - `includeInternalMessages`_ + - `message`_ + - `messageCollection`_ + - `groups`_ + - `payload`_ +Class :class:`Symfony\\Component\\Validator\\Constraints\\AtLeastOneOf` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\AtLeastOneOfValidator` +========== =================================================================== + +Basic Usage +----------- + +The following constraints ensure that: + +* the ``password`` of a ``Student`` either contains ``#`` or is at least ``10`` + characters long; +* the ``grades`` of a ``Student`` is an array which contains at least ``3`` + elements or that each element is greater than or equal to ``5``. + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Entity/Student.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Student + { + /** + * @Assert\AtLeastOneOf({ + * @Assert\Regex("/#/"), + * @Assert\Length(min=10) + * }) + */ + protected $password; + + /** + * @Assert\AtLeastOneOf({ + * @Assert\Count(min=3), + * @Assert\All( + * @Assert\GreaterThanOrEqual(5) + * ) + * }) + */ + protected $grades; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Entity\Student: + properties: + password: + - AtLeastOneOf: + - Regex: '/#/' + - Length: + min: 10 + grades: + - AtLeastOneOf: + - Count: + min: 3 + - All: + - GreaterThanOrEqual: 5 + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // src/Entity/Student.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class Student + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('password', new Assert\AtLeastOneOf([ + 'constraints' => [ + new Assert\Regex(['pattern' => '/#/']), + new Assert\Length(['min' => 10]), + ], + ])); + + $metadata->addPropertyConstraint('grades', new Assert\AtLeastOneOf([ + 'constraints' => [ + new Assert\Count(['min' => 3]), + new Assert\All([ + 'constraints' => [ + new Assert\GreaterThanOrEqual(['value' => 5]), + ], + ]), + ], + ])); + } + } + +Options +------- + +constraints +~~~~~~~~~~~ + +**type**: ``array`` [:ref:`default option `] + +This required option is the array of validation constraints from which at least one of +has to be satisfied in order for the validation to succeed. + +includeInternalMessages +~~~~~~~~~~~~~~~~~~~~~~~ + +**type**: ``bool`` **default**: ``true`` + +If set to ``true``, the message that is shown if the validation fails, +will include the list of messages for the internal constraints. See option +`message`_ for an example. + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should satisfy at least one of the following constraints:`` + +This is the intro of the message that will be shown if the validation fails. By default, +it will be followed by the list of messages for the internal constraints +(configurable by `includeInternalMessages`_ option) . For example, +if the above ``grades`` property fails to validate, the message will be +``This value should satisfy at least one of the following constraints: +[1] This collection should contain 3 elements or more. +[2] Each element of this collection should satisfy its own set of constraints.`` + +messageCollection +~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``Each element of this collection should satisfy its own set of constraints.`` + +This is the message that will be shown if the validation fails +and the internal constraint is either :doc:`/reference/constraints/All` +or :doc:`/reference/constraints/Collection`. See option `message`_ for an example. + +.. include:: /reference/constraints/_groups-option.rst.inc + +.. include:: /reference/constraints/_payload-option.rst.inc diff --git a/reference/constraints/Bic.rst b/reference/constraints/Bic.rst index 6496ae63d54..029a322e294 100644 --- a/reference/constraints/Bic.rst +++ b/reference/constraints/Bic.rst @@ -92,10 +92,6 @@ iban **type**: ``string`` **default**: ``null`` -.. versionadded:: 4.3 - - The ``iban`` option was introduced in Symfony 4.3. - An IBAN value to validate that its country code is the same as the BIC's one. ibanMessage @@ -103,10 +99,6 @@ ibanMessage **type**: ``string`` **default**: ``This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.`` -.. versionadded:: 4.3 - - The ``ibanMessage`` option was introduced in Symfony 4.3. - The default message supplied when the value does not pass the combined BIC/IBAN check. ibanPropertyPath @@ -114,10 +106,6 @@ ibanPropertyPath **type**: ``string`` **default**: ``null`` -.. versionadded:: 4.3 - - The ``ibanPropertyPath`` option was introduced in Symfony 4.3. - It defines the object property whose value stores the IBAN used to check the BIC with. For example, if you want to compare the ``$bic`` property of some object diff --git a/reference/constraints/CardScheme.rst b/reference/constraints/CardScheme.rst index 6362d9932ee..6adfe62d893 100644 --- a/reference/constraints/CardScheme.rst +++ b/reference/constraints/CardScheme.rst @@ -138,10 +138,6 @@ Valid values are: * ``UATP`` * ``VISA`` -.. versionadded:: 4.3 - - The ``UATP`` and ``MIR`` number schemes were introduced in Symfony 4.3. - For more information about the used schemes, see `Wikipedia: Issuer identification number (IIN)`_. diff --git a/reference/constraints/Choice.rst b/reference/constraints/Choice.rst index b1407c8add0..707bfd11bc5 100644 --- a/reference/constraints/Choice.rst +++ b/reference/constraints/Choice.rst @@ -322,10 +322,6 @@ Parameter Description ``{{ value }}`` The current (invalid) value ================= ============================================================ -.. versionadded:: 4.3 - - The ``{{ choices }}`` parameter was introduced in Symfony 4.3. - message ~~~~~~~ @@ -371,10 +367,6 @@ Parameter Description ``{{ value }}`` The current (invalid) value ================= ============================================================ -.. versionadded:: 4.3 - - The ``{{ choices }}`` parameter was introduced in Symfony 4.3. - multiple ~~~~~~~~ diff --git a/reference/constraints/Compound.rst b/reference/constraints/Compound.rst new file mode 100644 index 00000000000..8552058708c --- /dev/null +++ b/reference/constraints/Compound.rst @@ -0,0 +1,111 @@ +Compound +======== + +To the contrary to the other constraints, this constraint cannot be used on its own. +Instead, it allows you to create your own set of reusable constraints, representing +rules to use consistently across your application, by extending the constraint. + +.. versionadded:: 5.1 + + The ``Compound`` constraint was introduced in Symfony 5.1. + +========== =================================================================== +Applies to :ref:`class ` or :ref:`property or method ` +Options - `groups`_ + - `payload`_ +Class :class:`Symfony\\Component\\Validator\\Constraints\\Compound` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\CompoundValidator` +========== =================================================================== + +Basic Usage +----------- + +Suppose that you have different places where a user password must be validated, +you can create your own named set or requirements to be reused consistently everywhere:: + + // src/Validator/Constraints/PasswordRequirements.php + namespace App\Validator\Constraints; + + use Symfony\Component\Validator\Compound; + use Symfony\Component\Validator\Constraints as Assert; + + /** + * @Annotation + */ + class PasswordRequirements extends Compound + { + protected function getConstraints(array $options): array + { + return [ + new Assert\NotBlank(), + new Assert\Type('string'), + new Assert\Length(['min' => 12]), + new Assert\NotCompromisedPassword(), + ]; + } + } + +You can now use it anywhere you need it: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/User/RegisterUser.php + namespace App\User; + + use App\Validator\Constraints as AcmeAssert; + + class RegisterUser + { + /** + * @AcmeAssert\PasswordRequirements() + */ + public $password; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\User\RegisterUser: + properties: + password: + - App\Validator\Constraints\PasswordRequirements: ~ + + .. code-block:: xml + + + + + + + + + + + + + .. code-block:: php + + // src/User/RegisterUser.php + namespace App\User; + + use App\Validator\Constraints as AcmeAssert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class RegisterUser + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('password', new AcmeAssert\PasswordRequirements()); + } + } + +Options +------- + +.. include:: /reference/constraints/_groups-option.rst.inc + +.. include:: /reference/constraints/_payload-option.rst.inc diff --git a/reference/constraints/Count.rst b/reference/constraints/Count.rst index 2ff99b5adbb..4ce4691c6c9 100644 --- a/reference/constraints/Count.rst +++ b/reference/constraints/Count.rst @@ -6,7 +6,9 @@ Countable) element count is *between* some minimum and maximum value. ========== =================================================================== Applies to :ref:`property or method ` -Options - `exactMessage`_ +Options - `divisibleBy`_ + - `divisibleByMessage`_ + - `exactMessage`_ - `groups`_ - `max`_ - `maxMessage`_ @@ -101,6 +103,44 @@ you might add the following: Options ------- +divisibleBy +~~~~~~~~~~~ + +**type**: ``integer`` **default**: null + +.. versionadded:: 5.1 + + The ``divisibleBy`` option was introduced in Symfony 5.1. + +Validates that the number of elements of the given collection is divisible by +a certain number. + +.. seealso:: + + If you need to validate that other types of data different from collections + are divisible by a certain number, use the + :doc:`DivisibleBy ` constraint. + +divisibleByMessage +~~~~~~~~~~~~~~~~~~ + +**type**: ``string`` **default**: ``The number of elements in this collection should be a multiple of {{ compared_value }}.`` + +.. versionadded:: 5.1 + + The ``divisibleByMessage`` option was introduced in Symfony 5.1. + +The message that will be shown if the number of elements of the given collection +is not divisible by the number defined in the ``divisibleBy`` option. + +You can use the following parameters in this message: + +======================== =================================================== +Parameter Description +======================== =================================================== +``{{ compared_value }}`` The number configured in the ``divisibleBy`` option +======================== =================================================== + exactMessage ~~~~~~~~~~~~ diff --git a/reference/constraints/Country.rst b/reference/constraints/Country.rst index 4582a930cad..744de6dd0fb 100644 --- a/reference/constraints/Country.rst +++ b/reference/constraints/Country.rst @@ -5,7 +5,8 @@ Validates that a value is a valid `ISO 3166-1 alpha-2`_ country code. ========== =================================================================== Applies to :ref:`property or method ` -Options - `groups`_ +Options - `alpha3`_ + - `groups`_ - `message`_ - `payload`_ Class :class:`Symfony\\Component\\Validator\\Constraints\\Country` @@ -76,6 +77,19 @@ Basic Usage Options ------- +alpha3 +~~~~~~ + +.. versionadded:: 5.1 + + The ``alpha3`` option was introduced in Symfony 5.1. + +**type**: ``boolean`` **default**: ``false`` + +If this option is ``true``, the constraint checks that the value is a +`ISO 3166-1 alpha-3`_ three-letter code (e.g. France = ``FRA``) instead +of the default `ISO 3166-1 alpha-2`_ two-letter code (e.g. France = ``FR``). + .. include:: /reference/constraints/_groups-option.rst.inc ``message`` @@ -96,3 +110,5 @@ Parameter Description .. include:: /reference/constraints/_payload-option.rst.inc .. _`ISO 3166-1 alpha-2`: https://en.wikipedia.org/wiki/ISO_3166-1#Current_codes +.. _`ISO 3166-1 alpha-3`: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-3#Current_codes + diff --git a/reference/constraints/DivisibleBy.rst b/reference/constraints/DivisibleBy.rst index c93052a248a..4503959aa57 100644 --- a/reference/constraints/DivisibleBy.rst +++ b/reference/constraints/DivisibleBy.rst @@ -3,6 +3,12 @@ DivisibleBy Validates that a value is divisible by another value, defined in the options. +.. seealso:: + + If you need to validate that the number of elements in a collection is + divisible by a certain number, use the :doc:`Count ` + constraint with the ``divisibleBy`` option. + ========== =================================================================== Applies to :ref:`property or method ` Options - `groups`_ diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst index 5b149f0bf5f..ce8d428858a 100644 --- a/reference/constraints/Email.rst +++ b/reference/constraints/Email.rst @@ -6,9 +6,7 @@ cast to a string before being validated. ========== =================================================================== Applies to :ref:`property or method ` -Options - `checkHost`_ - - `checkMX`_ - - `groups`_ +Options - `groups`_ - `message`_ - `mode`_ - `normalizer`_ @@ -33,8 +31,7 @@ Basic Usage { /** * @Assert\Email( - * message = "The email '{{ value }}' is not a valid email.", - * checkMX = true + * message = "The email '{{ value }}' is not a valid email." * ) */ protected $email; @@ -48,7 +45,6 @@ Basic Usage email: - Email: message: The email "{{ value }}" is not a valid email. - checkMX: true .. code-block:: xml @@ -62,7 +58,6 @@ Basic Usage - @@ -82,7 +77,6 @@ Basic Usage { $metadata->addPropertyConstraint('email', new Assert\Email([ 'message' => 'The email "{{ value }}" is not a valid email.', - 'checkMX' => true, ])); } } @@ -92,36 +86,6 @@ Basic Usage Options ------- -checkHost -~~~~~~~~~ - -**type**: ``boolean`` **default**: ``false`` - -.. deprecated:: 4.2 - - This option was deprecated in Symfony 4.2. - -If true, then the :phpfunction:`checkdnsrr` PHP function will be used to -check the validity of the MX *or* the A *or* the AAAA record of the host -of the given email. - -checkMX -~~~~~~~ - -**type**: ``boolean`` **default**: ``false`` - -.. deprecated:: 4.2 - - This option was deprecated in Symfony 4.2. - -If true, then the :phpfunction:`checkdnsrr` PHP function will be used to -check the validity of the MX record of the host of the given email. - -.. caution:: - - This option is not reliable because it depends on the network conditions - and some valid servers refuse to respond to those requests. - .. include:: /reference/constraints/_groups-option.rst.inc message diff --git a/reference/constraints/ExpressionLanguageSyntax.rst b/reference/constraints/ExpressionLanguageSyntax.rst new file mode 100644 index 00000000000..2ca0355dfaf --- /dev/null +++ b/reference/constraints/ExpressionLanguageSyntax.rst @@ -0,0 +1,129 @@ +ExpressionLanguageSyntax +======================== + +This constraint checks that the value is valid as an `ExpressionLanguage`_ +expression. + +.. versionadded:: 5.1 + + The ``ExpressionLanguageSyntax`` constraint was introduced in Symfony 5.1. + +========== =================================================================== +Applies to :ref:`property or method ` +Options - `allowedVariables`_ + - `groups`_ + - `message`_ + - `payload`_ +Class :class:`Symfony\\Component\\Validator\\Constraints\\ExpressionLanguageSyntax` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\ExpressionLanguageSyntaxValidator` +========== =================================================================== + +Basic Usage +----------- + +The following constraints ensure that: + +* the ``promotion`` property stores a value which is valid as an + ExpressionLanguage expression; +* the ``shippingOptions`` property also ensures that the expression only uses + certain variables. + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Entity/Order.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class Order + { + /** + * @Assert\ExpressionLanguageSyntax() + */ + protected $promotion; + + /** + * @Assert\ExpressionLanguageSyntax( + * allowedVariables = ['user', 'shipping_centers'] + * ) + */ + protected $shippingOptions; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Entity\Order: + properties: + promotion: + - ExpressionLanguageSyntax: ~ + shippingOptions: + - ExpressionLanguageSyntax: + allowedVariables: ['user', 'shipping_centers'] + + .. code-block:: xml + + + + + + + + + + + + + + + + + + .. code-block:: php + + // src/Entity/Student.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class Order + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('promotion', new Assert\ExpressionLanguageSyntax()); + + $metadata->addPropertyConstraint('shippingOptions', new Assert\ExpressionLanguageSyntax([ + 'allowedVariables' => ['user', 'shipping_centers'], + ])); + } + } + +Options +------- + +allowedVariables +~~~~~~~~~~~~~~~~ + +**type**: ``array`` or ``null`` **default**: ``null`` + +If this option is defined, the expression can only use the variables whose names +are included in this option. Unset this option or set its value to ``null`` to +allow any variables. + +.. include:: /reference/constraints/_groups-option.rst.inc + +message +~~~~~~~ + +**type**: ``string`` **default**: ``This value should be a valid expression.`` + +This is the message displayed when the validation fails. + +.. include:: /reference/constraints/_payload-option.rst.inc + +.. _`ExpressionLanguage`: https://symfony.com/components/ExpressionLanguage diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst index 8c77fb008cb..f1a27ac8f20 100644 --- a/reference/constraints/File.rst +++ b/reference/constraints/File.rst @@ -252,10 +252,6 @@ You can find a list of existing mime types on the `IANA website`_. (i.e. the form type is not defined explicitly in the ``->add()`` method of the form builder) and when the field doesn't define its own ``accept`` value. - .. versionadded:: 4.4 - - This feature was introduced in Symfony 4.4. - mimeTypesMessage ~~~~~~~~~~~~~~~~ diff --git a/reference/constraints/Hostname.rst b/reference/constraints/Hostname.rst new file mode 100644 index 00000000000..4cbe606ccb4 --- /dev/null +++ b/reference/constraints/Hostname.rst @@ -0,0 +1,135 @@ +Hostname +======== + +This constraint ensures that the given value is a valid host name (internally it +uses the ``FILTER_VALIDATE_DOMAIN`` option of the :phpfunction:`filter_var` PHP +function). + +.. versionadded:: 5.1 + + The ``Hostname`` constraint was introduced in Symfony 5.1. + +========== =================================================================== +Applies to :ref:`property or method ` +Options - `groups`_ + - `message`_ + - `payload`_ + - `requireTld`_ +Class :class:`Symfony\\Component\\Validator\\Constraints\\Hostname` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\HostnameValidator` +========== =================================================================== + +Basic Usage +----------- + +To use the Hostname validator, apply it to a property on an object that +will contain a host name. + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Entity/ServerSettings.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + + class ServerSettings + { + /** + * @Assert\Hostname(message="The server name must be a valid hostname.") + */ + protected $name; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Entity\ServerSettings: + properties: + name: + - Hostname: + message: The server name must be a valid hostname. + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // src/Entity/ServerSettings.php + namespace App\Entity; + + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class ServerSettings + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('name', new Assert\Hostname([ + 'message' => 'The server name must be a valid hostname.', + ])); + } + } + +The following top-level domains (TLD) are reserved according to `RFC 2606`_ and +that's why hostnames containing them are not considered valid: ``.example``, +``.invalid``, ``.localhost``, and ``.test``. + +.. include:: /reference/constraints/_empty-values-are-valid.rst.inc + +Options +------- + +.. include:: /reference/constraints/_groups-option.rst.inc + +``message`` +~~~~~~~~~~~ + +**type**: ``string`` **default**: ``This value is not a valid hostname.`` + +The default message supplied when the value is not a valid hostname. + +You can use the following parameters in this message: + +=============== ============================================================== +Parameter Description +=============== ============================================================== +``{{ value }}`` The current (invalid) value +=============== ============================================================== + +.. include:: /reference/constraints/_payload-option.rst.inc + +``requireTld`` +~~~~~~~~~~~~~~ + +**type**: ``bool`` **default**: ``true`` + +By default, hostnames are considered valid only when they are fully qualified +and include their TLDs (top-level domain names). For instance, ``example.com`` +is valid but ``example`` is not. + +Set this option to ``false`` to not require any TLD in the hostnames. + +.. note:: + + This constraint does not validate that the given TLD value is included in + the `list of official top-level domains`_ (because that list is growing + continuously and it's hard to keep track of it). + +.. _`RFC 2606`: https://tools.ietf.org/html/rfc2606 +.. _`list of official top-level domains`: https://en.wikipedia.org/wiki/List_of_Internet_top-level_domains diff --git a/reference/constraints/Language.rst b/reference/constraints/Language.rst index 70d1e2e51cc..7d58491c416 100644 --- a/reference/constraints/Language.rst +++ b/reference/constraints/Language.rst @@ -6,7 +6,8 @@ Validates that a value is a valid language *Unicode language identifier* ========== =================================================================== Applies to :ref:`property or method ` -Options - `groups`_ +Options - `alpha3`_ + - `groups`_ - `message`_ - `payload`_ Class :class:`Symfony\\Component\\Validator\\Constraints\\Language` @@ -77,6 +78,19 @@ Basic Usage Options ------- +alpha3 +~~~~~~ + +.. versionadded:: 5.1 + + The ``alpha3`` option was introduced in Symfony 5.1. + +**type**: ``boolean`` **default**: ``false`` + +If this option is ``true``, the constraint checks that the value is a +`ISO 639-2`_ three-letter code (e.g. French = ``fra``) instead of the default +`ISO 639-1`_ two-letter code (e.g. French = ``fr``). + .. include:: /reference/constraints/_groups-option.rst.inc ``message`` @@ -95,3 +109,6 @@ Parameter Description =============== ============================================================== .. include:: /reference/constraints/_payload-option.rst.inc + +.. _`ISO 639-1`: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes +.. _`ISO 639-2`: https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes diff --git a/reference/constraints/Length.rst b/reference/constraints/Length.rst index 11ae53ae6b9..a74673a5354 100644 --- a/reference/constraints/Length.rst +++ b/reference/constraints/Length.rst @@ -117,16 +117,11 @@ Options allowEmptyString ~~~~~~~~~~~~~~~~ -**type**: ``boolean`` **default**: ``true`` +**type**: ``boolean`` **default**: ``false`` -.. versionadded:: 4.4 - - The ``allowEmptyString`` option was introduced in Symfony 4.4. - -When using the ``min`` option, it's mandatory to also define this option. If -set to ``true``, empty strings are considered valid (which is the same behavior -as previous Symfony versions). Set it to ``false`` to consider empty strings not -valid. +If set to ``true``, empty strings are considered valid (which is the same +behavior as previous Symfony versions). The default ``false`` value considers +empty strings not valid. .. caution:: diff --git a/reference/constraints/Locale.rst b/reference/constraints/Locale.rst index d7f16293b3a..dbdf0905df5 100644 --- a/reference/constraints/Locale.rst +++ b/reference/constraints/Locale.rst @@ -8,10 +8,13 @@ the two letter `ISO 639-1`_ *language* code (e.g. ``fr``), or the language code followed by an underscore (``_``) and the `ISO 3166-1 alpha-2`_ *country* code (e.g. ``fr_FR`` for French/France). +The given locale values are *canonicalized* before validating them to avoid +issues with wrong uppercase/lowercase values and to remove unneeded elements +(e.g. ``FR-fr.utf8`` will be validated as ``fr_FR``). + ========== =================================================================== Applies to :ref:`property or method ` -Options - `canonicalize`_ - - `groups`_ +Options - `groups`_ - `message`_ - `payload`_ Class :class:`Symfony\\Component\\Validator\\Constraints\\Locale` @@ -89,19 +92,6 @@ Basic Usage Options ------- -canonicalize -~~~~~~~~~~~~ - -**type**: ``boolean`` **default**: ``false`` - -.. deprecated:: 4.1 - - Using this option with value ``false`` was deprecated in Symfony 4.1 and it - will throw an exception in Symfony 5.0. Use ``true`` instead. - -If ``true``, the :phpmethod:`Locale::canonicalize` method will be applied before checking -the validity of the given locale (e.g. ``FR-fr.utf8`` is transformed into ``fr_FR``). - .. include:: /reference/constraints/_groups-option.rst.inc ``message`` diff --git a/reference/constraints/Negative.rst b/reference/constraints/Negative.rst index 4e3edee87f2..7468b4bfc4a 100644 --- a/reference/constraints/Negative.rst +++ b/reference/constraints/Negative.rst @@ -1,10 +1,6 @@ Negative ======== -.. versionadded:: 4.3 - - The ``Negative`` constraint was introduced in Symfony 4.3. - Validates that a value is a negative number. Zero is neither positive nor negative, so you must use :doc:`/reference/constraints/NegativeOrZero` if you want to allow zero as value. diff --git a/reference/constraints/NegativeOrZero.rst b/reference/constraints/NegativeOrZero.rst index 5a77a36ab67..f010acda0b1 100644 --- a/reference/constraints/NegativeOrZero.rst +++ b/reference/constraints/NegativeOrZero.rst @@ -1,10 +1,6 @@ NegativeOrZero ============== -.. versionadded:: 4.3 - - The ``NegativeOrZero`` constraint was introduced in Symfony 4.3. - Validates that a value is a negative number or equal to zero. If you don't want to allow zero as value, use :doc:`/reference/constraints/Negative` instead. diff --git a/reference/constraints/NotBlank.rst b/reference/constraints/NotBlank.rst index 19ac9542ec9..c3c16f21eae 100644 --- a/reference/constraints/NotBlank.rst +++ b/reference/constraints/NotBlank.rst @@ -90,10 +90,6 @@ allowNull If set to ``true``, ``null`` values are considered valid and won't trigger a constraint violation. -.. versionadded:: 4.3 - - The ``allowNull`` option was introduced in Symfony 4.3. - .. include:: /reference/constraints/_groups-option.rst.inc ``message`` diff --git a/reference/constraints/NotCompromisedPassword.rst b/reference/constraints/NotCompromisedPassword.rst index ffa9fe99d8d..6d2cb52f1aa 100644 --- a/reference/constraints/NotCompromisedPassword.rst +++ b/reference/constraints/NotCompromisedPassword.rst @@ -1,10 +1,6 @@ NotCompromisedPassword ====================== -.. versionadded:: 4.3 - - The ``NotCompromisedPassword`` constraint was introduced in Symfony 4.3. - Validates that the given password has not been compromised by checking that it is not included in any of the public data breaches tracked by `haveibeenpwned.com`_. diff --git a/reference/constraints/Positive.rst b/reference/constraints/Positive.rst index bfed77a763c..af76f205e53 100644 --- a/reference/constraints/Positive.rst +++ b/reference/constraints/Positive.rst @@ -1,10 +1,6 @@ Positive ======== -.. versionadded:: 4.3 - - The ``Positive`` constraint was introduced in Symfony 4.3. - Validates that a value is a positive number. Zero is neither positive nor negative, so you must use :doc:`/reference/constraints/PositiveOrZero` if you want to allow zero as value. diff --git a/reference/constraints/PositiveOrZero.rst b/reference/constraints/PositiveOrZero.rst index cc592f824c6..ea762e78f90 100644 --- a/reference/constraints/PositiveOrZero.rst +++ b/reference/constraints/PositiveOrZero.rst @@ -1,10 +1,6 @@ PositiveOrZero ============== -.. versionadded:: 4.3 - - The ``PositiveOrZero`` constraint was introduced in Symfony 4.3. - Validates that a value is a positive number or equal to zero. If you don't want to allow zero as value, use :doc:`/reference/constraints/Positive` instead. diff --git a/reference/constraints/Range.rst b/reference/constraints/Range.rst index 5743d8d04ef..6e7fc8d7f86 100644 --- a/reference/constraints/Range.rst +++ b/reference/constraints/Range.rst @@ -363,11 +363,7 @@ maxPropertyPath **type**: ``string`` -.. versionadded:: 4.4 - - The ``maxPropertyPath`` option was introduced in Symfony 4.4. - -It defines the object property whose value is used as `max`_ option. +It defines the object property whose value is used as ``max`` option. For example, if you want to compare the ``$submittedDate`` property of some object with regard to the ``$deadline`` property of the same object, use @@ -411,11 +407,7 @@ minPropertyPath **type**: ``string`` -.. versionadded:: 4.4 - - The ``minPropertyPath`` option was introduced in Symfony 4.4. - -It defines the object property whose value is used as `min`_ option. +It defines the object property whose value is used as ``min`` option. For example, if you want to compare the ``$endDate`` property of some object with regard to the ``$startDate`` property of the same object, use @@ -433,10 +425,6 @@ notInRangeMessage **type**: ``string`` **default**: ``This value should be between {{ min }} and {{ max }}.`` -.. versionadded:: 4.4 - - The ``notInRangeMessage`` option was introduced in Symfony 4.4. - The message that will be shown if the underlying value is less than the `min`_ option or greater than the `max`_ option. diff --git a/reference/constraints/Sequentially.rst b/reference/constraints/Sequentially.rst new file mode 100644 index 00000000000..a8e7c6be298 --- /dev/null +++ b/reference/constraints/Sequentially.rst @@ -0,0 +1,144 @@ +Sequentially +============ + +This constraint allows you to apply a set of rules that should be validated +step-by-step, allowing to interrupt the validation once the first violation is raised. + +As an alternative in situations ``Sequentially`` cannot solve, you may consider +using :doc:`GroupSequence` which allows more control. + +.. versionadded:: 5.1 + + The ``Sequentially`` constraint was introduced in Symfony 5.1. + +========== =================================================================== +Applies to :ref:`property or method ` +Options - `constraints`_ + - `groups`_ + - `payload`_ +Class :class:`Symfony\\Component\\Validator\\Constraints\\Sequentially` +Validator :class:`Symfony\\Component\\Validator\\Constraints\\SequentiallyValidator` +========== =================================================================== + +Basic Usage +----------- + +Suppose that you have a ``Place`` object with an ``$address`` property which +must match the following requirements: + +* it's a non-blank string +* of at least 10 chars long +* with a specific format +* and geolocalizable using an external service + +In such situations, you may encounter three issues: + +* the ``Length`` or ``Regex`` constraints may fail hard with a :class:`Symfony\\Component\\Validator\\Exception\\UnexpectedValueException` + exception if the actual value is not a string, as enforced by ``Type``. +* you may end with multiple error messages for the same property +* you may perform a useless and heavy external call to geolocalize the address, + while the format isn't valid. + +You can validate each of these constraints sequentially to solve these issues: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Localization/Place.php + namespace App\Localization; + + use App\Validator\Constraints as AcmeAssert; + use Symfony\Component\Validator\Constraints as Assert; + + class Place + { + /** + * @var string + * + * @Assert\Sequentially({ + * @Assert\NotNull(), + * @Assert\Type("string"), + * @Assert\Length(min=10), + * @Assert\Regex(Place::ADDRESS_REGEX), + * @AcmeAssert\Geolocalizable(), + * }) + */ + public $address; + } + + .. code-block:: yaml + + # config/validator/validation.yaml + App\Localization\Place: + properties: + address: + - Sequentially: + - NotNull: ~ + - Type: string + - Length: { min: 10 } + - Regex: !php/const App\Localization\Place::ADDRESS_REGEX + - App\Validator\Constraints\Geolocalizable: ~ + + .. code-block:: xml + + + + + + + + + + string + + + + + + + + + + + + + .. code-block:: php + + // src/Localization/Place.php + namespace App\Localization; + + use App\Validator\Constraints as AcmeAssert; + use Symfony\Component\Validator\Constraints as Assert; + use Symfony\Component\Validator\Mapping\ClassMetadata; + + class Place + { + public static function loadValidatorMetadata(ClassMetadata $metadata) + { + $metadata->addPropertyConstraint('address', new Assert\Sequentially([ + new Assert\NotNull(), + new Assert\Type("string"), + new Assert\Length(['min' => 10]), + new Assert\Regex(self::ADDRESS_REGEX), + new AcmeAssert\Geolocalizable(), + ])); + } + } + +Options +------- + +``constraints`` +~~~~~~~~~~~~~~~ + +**type**: ``array`` [:ref:`default option `] + +This required option is the array of validation constraints that you want +to apply sequentially. + +.. include:: /reference/constraints/_groups-option.rst.inc + +.. include:: /reference/constraints/_payload-option.rst.inc diff --git a/reference/constraints/Timezone.rst b/reference/constraints/Timezone.rst index c5f27e1cbfb..045c258bda4 100644 --- a/reference/constraints/Timezone.rst +++ b/reference/constraints/Timezone.rst @@ -1,10 +1,6 @@ Timezone ======== -.. versionadded:: 4.3 - - The ``Timezone`` constraint was introduced in Symfony 4.3. - Validates that a value is a valid timezone identifier (e.g. ``Europe/Paris``). ========== ====================================================================== diff --git a/reference/constraints/Type.rst b/reference/constraints/Type.rst index 8aa0edd1ba2..209520c41c7 100644 --- a/reference/constraints/Type.rst +++ b/reference/constraints/Type.rst @@ -143,11 +143,6 @@ This will check if ``id`` is an instance of ``Ramsey\Uuid\UuidInterface``, } } -.. versionadded:: 4.4 - - The feature to define multiple types in the ``type`` option was introduced - in Symfony 4.4. - Options ------- @@ -178,11 +173,6 @@ type **type**: ``string`` or ``array`` [:ref:`default option `] -.. versionadded:: 4.4 - - The feature to define multiple types in the ``type`` option was introduced - in Symfony 4.4. - This required option defines the type or collection of types allowed for the given value. Each type is either the FQCN (fully qualified class name) of some PHP class/interface or a valid PHP datatype (checked by PHP's ``is_()`` functions): diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst index 4c9885d0147..fdc58880797 100644 --- a/reference/constraints/Url.rst +++ b/reference/constraints/Url.rst @@ -5,9 +5,7 @@ Validates that a value is a valid URL string. ========== =================================================================== Applies to :ref:`property or method ` -Options - `checkDNS`_ - - `dnsMessage`_ - - `groups`_ +Options - `groups`_ - `message`_ - `normalizer`_ - `payload`_ @@ -76,170 +74,15 @@ Basic Usage } } +This constraint doesn't check that the host of the given URL really exists, +because the information of the DNS records is not reliable. Use the +:phpfunction:`checkdnsrr` PHP function if you still want to check that. + .. include:: /reference/constraints/_empty-values-are-valid.rst.inc Options ------- -checkDNS -~~~~~~~~ - -**type**: ``boolean`` **default**: ``false`` - -.. deprecated:: 4.1 - - This option was deprecated in Symfony 4.1 and will be removed in Symfony 5.0, - because checking the DNS records is not reliable enough to validate the - existence of the host. Use the :phpfunction:`checkdnsrr` PHP function if you - still want to use this kind of validation. - -By default, this constraint just validates the syntax of the given URL. If you -also need to check whether the associated host exists, set the ``checkDNS`` -option to the value of any of the ``CHECK_DNS_TYPE_*`` constants in the -:class:`Symfony\\Component\\Validator\\Constraints\\Url` class: - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Entity/Author.php - namespace App\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Author - { - /** - * @Assert\Url( - * checkDNS = "ANY" - * ) - */ - protected $bioUrl; - } - - .. code-block:: yaml - - # config/validator/validation.yaml - App\Entity\Author: - properties: - bioUrl: - - Url: { checkDNS: 'ANY' } - - .. 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) - { - $metadata->addPropertyConstraint('bioUrl', new Assert\Url([ - 'checkDNS' => Assert\Url::CHECK_DNS_TYPE_ANY, - ])); - } - } - -This option uses the :phpfunction:`checkdnsrr` PHP function to check the validity -of the DNS record corresponding to the host associated with the given URL. - -dnsMessage -~~~~~~~~~~ - -**type**: ``string`` **default**: ``The host could not be resolved.`` - -.. deprecated:: 4.1 - - This option was deprecated in Symfony 4.1 and will be removed in Symfony 5.0, - because checking the DNS records is not reliable enough to validate the - existence of the host. Use the :phpfunction:`checkdnsrr` PHP function if you - still want to use this kind of validation. - -This message is shown when the ``checkDNS`` option is set to ``true`` and the -DNS check failed. - -.. configuration-block:: - - .. code-block:: php-annotations - - // src/Entity/Author.php - namespace App\Entity; - - use Symfony\Component\Validator\Constraints as Assert; - - class Author - { - /** - * @Assert\Url( - * dnsMessage = "The host '{{ value }}' could not be resolved." - * ) - */ - protected $bioUrl; - } - - .. code-block:: yaml - - # config/validator/validation.yaml - App\Entity\Author: - properties: - bioUrl: - - Url: { dnsMessage: 'The host "{{ value }}" could not be resolved.' } - - .. 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) - { - $metadata->addPropertyConstraint('bioUrl', new Assert\Url([ - 'dnsMessage' => 'The host "{{ value }}" could not be resolved.', - ])); - } - } - .. include:: /reference/constraints/_groups-option.rst.inc message diff --git a/reference/constraints/_comparison-propertypath-option.rst.inc b/reference/constraints/_comparison-propertypath-option.rst.inc index 0491d7dea1f..35f0da4d189 100644 --- a/reference/constraints/_comparison-propertypath-option.rst.inc +++ b/reference/constraints/_comparison-propertypath-option.rst.inc @@ -15,7 +15,3 @@ with regard to the ``$startDate`` property of the same object, use ``{{ compared_value_path }}`` placeholder. Although it's not intended to include it in the error messages displayed to end users, it's useful when using APIs for doing any mapping logic on client-side. - - .. versionadded:: 4.4 - - The ``{{ compared_value_path }}`` placeholder was introduced in Symfony 4.4. diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc index 337798c51da..2d16e7e34ee 100644 --- a/reference/constraints/map.rst.inc +++ b/reference/constraints/map.rst.inc @@ -16,9 +16,11 @@ String Constraints ~~~~~~~~~~~~~~~~~~ * :doc:`Email ` +* :doc:`ExpressionLanguageSyntax ` * :doc:`Length ` * :doc:`Url ` * :doc:`Regex ` +* :doc:`Hostname ` * :doc:`Ip ` * :doc:`Json` * :doc:`Uuid` @@ -83,6 +85,9 @@ Financial and other Number Constraints Other Constraints ~~~~~~~~~~~~~~~~~ +* :doc:`AtLeastOneOf ` +* :doc:`Sequentially ` +* :doc:`Compound ` * :doc:`Callback ` * :doc:`Expression ` * :doc:`All ` diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 7bb0aca674e..f7f43dca376 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -14,6 +14,8 @@ Tag Name Usage `auto_alias`_ Define aliases based on the value of container parameters `console.command`_ Add a command `container.hot_path`_ Add to list of always needed services +`container.no_preload`_ Remove a class from the list of classes preloaded by PHP +`container.preload`_ Add some class to the list of classes preloaded by PHP `controller.argument_value_resolver`_ Register a value resolver for controller arguments such as ``Request`` `data_collector`_ Create a class that collects custom data for the profiler `doctrine.event_listener`_ Add a Doctrine event listener @@ -38,7 +40,6 @@ Tag Name Usage `serializer.encoder`_ Register a new encoder in the ``serializer`` service `serializer.normalizer`_ Register a new normalizer in the ``serializer`` service `swiftmailer.default.plugin`_ Register a custom SwiftMailer Plugin -`templating.helper`_ Make your service available in PHP templates `translation.loader`_ Register a custom service that loads translations `translation.extractor`_ Register a custom service that extracts translation messages from a file `translation.dumper`_ Register a custom service that dumps translation messages @@ -181,10 +182,12 @@ wrapping their names with ``%`` characters). sense most of the times to prevent accessing those services directly instead of using the generic service alias. -.. note:: +.. versionadded:: 5.1 - You need to manually add the ``Symfony\Component\DependencyInjection\Compiler\AutoAliasServicePass`` - compiler pass to the container for this feature to work. + In Symfony versions prior to 5.1, you needed to manually add the + ``Symfony\Component\DependencyInjection\Compiler\AutoAliasServicePass`` + compiler pass to the container for this feature to work. This compiler pass + is now added automatically. console.command --------------- @@ -211,6 +214,113 @@ for services and their class hierarchy. The result is as significant performance Use this tag with great caution, you have to be sure that the tagged service is always used. +.. _dic-tags-container-nopreload: + +container.no_preload +-------------------- + +**Purpose**: Remove a class from the list of classes preloaded by PHP + +.. versionadded:: 5.1 + + The ``container.no_preload`` tag was introduced in Symfony 5.1. + +Add this tag to a service and its class won't be preloaded when using +`PHP class preloading`_: + +.. configuration-block:: + + .. code-block:: yaml + + services: + App\SomeNamespace\SomeService: + tags: ['container.no_preload'] + + .. code-block:: xml + + + + + + + + + + + + .. code-block:: php + + use App\SomeNamespace\SomeService; + + $container + ->register(SomeService::class) + ->addTag('container.no_preload') + ; + +If you add some service tagged with ``container.no_preload`` as an argument of +another service, the ``container.no_preload`` tag is applied automatically to +that service too. + +.. _dic-tags-container-preload: + +container.preload +----------------- + +**Purpose**: Add some class to the list of classes preloaded by PHP + +.. versionadded:: 5.1 + + The ``container.preload`` tag was introduced in Symfony 5.1. + +When using `PHP class preloading`_, this tag allows you to define which PHP +classes should be preloaded. This can improve performance by making some of the +classes used by your service always available for all requests (until the server +is restarted): + +.. configuration-block:: + + .. code-block:: yaml + + services: + App\SomeNamespace\SomeService: + tags: + - { name: 'container.preload', class: 'App\SomeClass' } + - { name: 'container.preload', class: 'App\Some\OtherClass' } + # ... + + .. code-block:: xml + + + + + + + + + + + + + + .. code-block:: php + + use App\Some\OtherClass; + use App\SomeClass; + use App\SomeNamespace\SomeService; + + $container + ->register(SomeService::class) + ->addTag('container.preload', ['class' => SomeClass::class) + ->addTag('container.preload', ['class' => OtherClass::class) + // ... + ; + controller.argument_value_resolver ---------------------------------- @@ -362,6 +472,7 @@ the :class:`Symfony\\Component\\HttpKernel\\CacheWarmer\\CacheWarmerInterface` i // src/Cache/MyCustomWarmer.php namespace App\Cache; + use App\Foo\Bar; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; class MyCustomWarmer implements CacheWarmerInterface @@ -369,6 +480,17 @@ the :class:`Symfony\\Component\\HttpKernel\\CacheWarmer\\CacheWarmerInterface` i public function warmUp($cacheDirectory) { // ... do some sort of operations to "warm" your cache + + $filesAndClassesToPreload = []; + $filesAndClassesToPreload[] = Bar::class; + + foreach (scandir($someCacheDir) as $file) { + if (!is_dir($file = $someCacheDir.'/'.$file)) { + $filesAndClassesToPreload[] = $file; + } + } + + return $filesAndClassesToPreload; } public function isOptional() @@ -377,6 +499,16 @@ the :class:`Symfony\\Component\\HttpKernel\\CacheWarmer\\CacheWarmerInterface` i } } +The ``warmUp()`` method must return an array with the files and classes to +preload. Files must be absolute paths and classes must be fully-qualified class +names. The only restriction is that files must be stored in the cache directory. +If you don't need to preload anything, return an empty array + +.. deprecated:: 5.1 + + Not returning an array from the ``warmUp()`` method with the files to + preload is deprecated since Symfony 5.1. + The ``isOptional()`` method should return true if it's possible to use the application without calling this cache warmer. In Symfony, optional warmers are always executed by default (you can change this by using the @@ -508,10 +640,6 @@ This tag is used to register your own :ref:`MIME type guessers ` don't fit your needs. -.. versionadded:: 4.3 - - The ``mime.mime_type_guesser`` tag was introduced in Symfony 4.3. - .. _dic_tags-monolog: monolog.logger @@ -834,53 +962,6 @@ For more information on plugins, see `SwiftMailer's Plugin Documentation`_. Several SwiftMailer plugins are core to Symfony and can be activated via different configuration. For details, see :doc:`/reference/configuration/swiftmailer`. -templating.helper ------------------ - -**Purpose**: Make your service available in PHP templates - -.. deprecated:: 4.3 - - The ``templating.helper`` tag is deprecated since version 4.3 and will be - removed in 5.0; use Twig instead. - -To enable a custom template helper, add it as a regular service in one -of your configuration, tag it with ``templating.helper`` and define an -``alias`` attribute (the helper will be accessible via this alias in the -templates): - -.. configuration-block:: - - .. code-block:: yaml - - services: - App\Templating\AppHelper: - tags: - - { name: templating.helper, alias: alias_name } - - .. code-block:: xml - - - - - - - - - - - - .. code-block:: php - - use App\Templating\AppHelper; - - $container->register(AppHelper::class) - ->addTag('templating.helper', ['alias' => 'alias_name']) - ; - .. _dic-tags-translation-loader: translation.loader @@ -1264,3 +1345,4 @@ Bridge. .. _`Twig's documentation`: https://twig.symfony.com/doc/2.x/advanced.html#creating-an-extension .. _`SwiftMailer's Plugin Documentation`: https://swiftmailer.symfony.com/docs/plugins.html .. _`Twig Loader`: https://twig.symfony.com/doc/2.x/api.html#loaders +.. _`PHP class preloading`: https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.preload diff --git a/reference/forms/types/button.rst b/reference/forms/types/button.rst index 20ae46ca6d4..655d515215b 100644 --- a/reference/forms/types/button.rst +++ b/reference/forms/types/button.rst @@ -13,6 +13,7 @@ A simple, non-responsive button. | options | - `attr_translation_parameters`_ | | | - `disabled`_ | | | - `label`_ | +| | - `label_html`_ | | | - `label_translation_parameters`_ | | | - `row_attr`_ | | | - `translation_domain`_ | @@ -53,6 +54,8 @@ as a key. This can be useful when you need to set a custom class for the button: .. include:: /reference/forms/types/options/button_label.rst.inc +.. include:: /reference/forms/types/options/label_html.rst.inc + .. include:: /reference/forms/types/options/button_translation_domain.rst.inc label_translation_parameters @@ -60,10 +63,6 @@ label_translation_parameters **type**: ``array`` **default**: ``[]`` -.. versionadded:: 4.3 - - The ``label_translation_parameters`` option was introduced in Symfony 4.3. - The content of the `label`_ option is translated before displaying it, so it can contain :ref:`translation placeholders `. This option defines the values used to replace those placeholders. diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst index d90173eb2e6..fb1df969b9d 100644 --- a/reference/forms/types/choice.rst +++ b/reference/forms/types/choice.rst @@ -14,6 +14,7 @@ To use this field, you must specify *either* ``choices`` or ``choice_loader`` op +-------------+------------------------------------------------------------------------------+ | Options | - `choices`_ | | | - `choice_attr`_ | +| | - `choice_filter`_ | | | - `choice_label`_ | | | - `choice_loader`_ | | | - `choice_name`_ | @@ -181,6 +182,11 @@ To get fancier, use the `group_by`_ option instead. Field Options ------------- +.. versionadded:: 5.1 + + The :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class was + introduced in Symfony 5.1, to help configuring choices options. + choices ~~~~~~~ @@ -207,35 +213,15 @@ correct types will be assigned to the model. .. include:: /reference/forms/types/options/choice_attr.rst.inc +.. include:: /reference/forms/types/options/choice_filter.rst.inc + .. _reference-form-choice-label: .. include:: /reference/forms/types/options/choice_label.rst.inc -choice_loader -~~~~~~~~~~~~~ - -**type**: :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\ChoiceLoaderInterface` - -The ``choice_loader`` can be used to only partially load the choices in cases where -a fully-loaded list is not necessary. This is only needed in advanced cases and -would replace the ``choices`` option. - -You can use an instance of :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\CallbackChoiceLoader` -if you want to take advantage of lazy loading:: - - use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader; - use Symfony\Component\Form\Extension\Core\Type\ChoiceType; - // ... - - $builder->add('constants', ChoiceType::class, [ - 'choice_loader' => new CallbackChoiceLoader(function() { - return StaticClass::getConstants(); - }), - ]); +.. _reference-form-choice-loader: -This will cause the call of ``StaticClass::getConstants()`` to not happen if the -request is redirected and if there is no pre set or submitted data. Otherwise -the choice options would need to be resolved thus triggering the callback. +.. include:: /reference/forms/types/options/choice_loader.rst.inc .. include:: /reference/forms/types/options/choice_name.rst.inc diff --git a/reference/forms/types/color.rst b/reference/forms/types/color.rst index f6e4ec5ba58..a290b31e673 100644 --- a/reference/forms/types/color.rst +++ b/reference/forms/types/color.rst @@ -17,6 +17,8 @@ element. +-------------+---------------------------------------------------------------------+ | Rendered as | ``input`` ``color`` field (a text box) | +-------------+---------------------------------------------------------------------+ +| Options | - `html5`_ | ++-------------+---------------------------------------------------------------------+ | Inherited | - `attr`_ | | options | - `data`_ | | | - `disabled`_ | @@ -41,6 +43,22 @@ element. .. include:: /reference/forms/types/options/_debug_form.rst.inc +Field Options +------------- + +html5 +~~~~~ + +**type**: ``bool`` **default**: ``false`` + +.. versionadded:: 5.1 + + This option was introduced in Symfony 5.1. + +When this option is set to ``true``, the form type checks that its value matches +the `HTML5 color format`_ (``/^#[0-9a-f]{6}$/i``). If it doesn't match it, +you'll see the following error message: *"This value is not a valid HTML5 color"*. + Inherited Options ----------------- @@ -83,3 +101,5 @@ The default value is ``''`` (the empty string). .. include:: /reference/forms/types/options/row_attr.rst.inc .. include:: /reference/forms/types/options/trim.rst.inc + +.. _`HTML5 color format`: https://www.w3.org/TR/html52/sec-forms.html#color-state-typecolor diff --git a/reference/forms/types/country.rst b/reference/forms/types/country.rst index dee6827a0ca..bbda2b3ec46 100644 --- a/reference/forms/types/country.rst +++ b/reference/forms/types/country.rst @@ -68,10 +68,6 @@ alpha3 **type**: ``boolean`` **default**: ``false`` -.. versionadded:: 4.4 - - The ``alpha3`` option was introduced in Symfony 4.4. - If this option is ``true``, the choice values use the `ISO 3166-1 alpha-3`_ three-letter codes (e.g. New Zealand = ``NZL``) instead of the default `ISO 3166-1 alpha-2`_ two-letter codes (e.g. New Zealand = ``NZ``). diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst index c69f7fd05f0..ea96cf3d937 100644 --- a/reference/forms/types/date.rst +++ b/reference/forms/types/date.rst @@ -173,11 +173,6 @@ values for the year, month and day fields:: .. include:: /reference/forms/types/options/date_format.rst.inc -.. deprecated:: 4.3 - - Using the ``format`` option when the ``html5`` option is enabled is deprecated - since Symfony 4.3. - .. include:: /reference/forms/types/options/html5.rst.inc .. _form-reference-date-input: diff --git a/reference/forms/types/datetime.rst b/reference/forms/types/datetime.rst index 6adc3e733eb..72af8847075 100644 --- a/reference/forms/types/datetime.rst +++ b/reference/forms/types/datetime.rst @@ -76,11 +76,6 @@ Defines the ``format`` option that will be passed down to the date field. See the :ref:`DateType's format option ` for more details. -.. deprecated:: 4.3 - - Using the ``date_format`` option when the form is rendered as an HTML 5 - datetime input is deprecated since Symfony 4.3. - date_label ~~~~~~~~~~ @@ -100,11 +95,6 @@ date_widget .. include:: /reference/forms/types/options/date_widget_description.rst.inc -.. deprecated:: 4.3 - - Using the ``date_widget`` option when the ``widget`` option is set to - ``single_text`` is deprecated since Symfony 4.3. - .. include:: /reference/forms/types/options/days.rst.inc placeholder @@ -146,11 +136,6 @@ used by the HTML5 ``datetime-local`` field. Keeping the default value will cause the field to be rendered as an ``input`` field with ``type="datetime-local"``. For more information on valid formats, see `Date/Time Format Syntax`_. -.. deprecated:: 4.3 - - Using the ``format`` option when the ``html5`` option is enabled is deprecated - since Symfony 4.3. - .. include:: /reference/forms/types/options/hours.rst.inc .. include:: /reference/forms/types/options/html5.rst.inc @@ -210,11 +195,6 @@ time_widget Defines the ``widget`` option for the :doc:`TimeType `. -.. deprecated:: 4.3 - - Using the ``time_widget`` option when the ``widget`` option is set to - ``single_text`` is deprecated since Symfony 4.3. - .. include:: /reference/forms/types/options/view_timezone.rst.inc widget diff --git a/reference/forms/types/form.rst b/reference/forms/types/form.rst index 4099436430b..8a0c219f410 100644 --- a/reference/forms/types/form.rst +++ b/reference/forms/types/form.rst @@ -42,6 +42,7 @@ on all types for which ``FormType`` is the parent. | | - `block_prefix`_ | | | - `disabled`_ | | | - `label`_ | +| | - `label_html`_ | | | - `row_attr`_ | | | - `translation_domain`_ | | | - `label_translation_parameters`_ | @@ -172,6 +173,8 @@ of the form type tree (i.e. it cannot be used as a form type on its own). .. include:: /reference/forms/types/options/label.rst.inc +.. include:: /reference/forms/types/options/label_html.rst.inc + .. include:: /reference/forms/types/options/row_attr.rst.inc .. include:: /reference/forms/types/options/translation_domain.rst.inc diff --git a/reference/forms/types/integer.rst b/reference/forms/types/integer.rst index d228f4f8145..fa5660158bc 100644 --- a/reference/forms/types/integer.rst +++ b/reference/forms/types/integer.rst @@ -20,7 +20,7 @@ integers. By default, all non-integer values (e.g. 6.78) will round down | | - `rounding_mode`_ | +-------------+-----------------------------------------------------------------------+ | Overridden | - `compound`_ | -| options | - `scale`_ | +| options | | +-------------+-----------------------------------------------------------------------+ | Inherited | - `attr`_ | | options | - `data`_ | @@ -86,21 +86,6 @@ Overridden Options .. include:: /reference/forms/types/options/compound_type.rst.inc -``scale`` -~~~~~~~~~ - -**type**: ``integer`` **default**: ``0`` - -.. deprecated:: 4.2 - - The ``scale`` option is deprecated since Symfony 4.2 and will be removed - in 5.0. - -This specifies how many decimals will be allowed until the field rounds the -submitted value (via ``rounding_mode``). This option inherits from -:doc:`number ` type and is overridden to ``0`` for -``IntegerType``. - Inherited Options ----------------- diff --git a/reference/forms/types/language.rst b/reference/forms/types/language.rst index bba93983ca4..bf2c99789f8 100644 --- a/reference/forms/types/language.rst +++ b/reference/forms/types/language.rst @@ -24,6 +24,7 @@ manually, but then you should just use the ``ChoiceType`` directly. | Rendered as | can be various tags (see :ref:`forms-reference-choice-tags`) | +-------------+------------------------------------------------------------------------+ | Options | - `alpha3`_ | +| | - `choice_self_translation`_ | | | - `choice_translation_locale`_ | +-------------+------------------------------------------------------------------------+ | Overridden | - `choices`_ | @@ -70,14 +71,29 @@ alpha3 **type**: ``boolean`` **default**: ``false`` -.. versionadded:: 4.4 - - The ``alpha3`` option was introduced in Symfony 4.4. - If this option is ``true``, the choice values use the `ISO 639-2 alpha-3`_ three-letter codes (e.g. French = ``fra``) instead of the default `ISO 639-1 alpha-2`_ two-letter codes (e.g. French = ``fr``). +choice_self_translation +~~~~~~~~~~~~~~~~~~~~~~~ + +**type**: ``boolean`` **default**: ``false`` + +.. versionadded:: 5.1 + + The ``choice_self_translation`` option was introduced in Symfony 5.1. + +By default, language names are translated into the current locale of the +application. For example, when browsing the application in English, you'll get +an array like ``[..., 'cs' => 'Czech', ..., 'es' => 'Spanish', ..., 'zh' => 'Chinese']`` +and when browsing it in French, you'll get the following array: +``[..., 'cs' => 'tchèque', ..., 'es' => 'espagnol', ..., 'zh' => 'chinois']``. + +If this option is ``true``, each language is translated into its own language, +regardless of the current application locale: +``[..., 'cs' => 'čeština', ..., 'es' => 'español', ..., 'zh' => '中文']``. + .. include:: /reference/forms/types/options/choice_translation_locale.rst.inc Overridden Options diff --git a/reference/forms/types/number.rst b/reference/forms/types/number.rst index 6e9215c276a..599d0efa4cd 100644 --- a/reference/forms/types/number.rst +++ b/reference/forms/types/number.rst @@ -55,10 +55,6 @@ html5 **type**: ``boolean`` **default**: ``false`` -.. versionadded:: 4.3 - - The ``html5`` option was introduced in Symfony 4.3. - If set to ``true``, the HTML input will be rendered as a native HTML5 ``type="number"`` form. @@ -67,10 +63,6 @@ input **type**: ``string`` **default**: ``number`` -.. versionadded:: 4.3 - - The ``input`` option was introduced in Symfony 4.3. - The format of the input data - i.e. the format that the number is stored on your underlying object. Valid values are ``number`` and ``string``. Setting this option to ``string`` can be useful if the underlying data is a string diff --git a/reference/forms/types/options/attr_translation_parameters.rst.inc b/reference/forms/types/options/attr_translation_parameters.rst.inc index 98326ba4abe..71187cd75c5 100644 --- a/reference/forms/types/options/attr_translation_parameters.rst.inc +++ b/reference/forms/types/options/attr_translation_parameters.rst.inc @@ -3,10 +3,6 @@ attr_translation_parameters **type**: ``array`` **default**: ``[]`` -.. versionadded:: 4.3 - - The ``attr_translation_parameters`` option was introduced in Symfony 4.3. - The content of the ``title`` and ``placeholder`` values defined in the `attr`_ option is translated before displaying it, so it can contain :ref:`translation placeholders `. This diff --git a/reference/forms/types/options/block_prefix.rst.inc b/reference/forms/types/options/block_prefix.rst.inc index f02feb0ce70..db012bc3c42 100644 --- a/reference/forms/types/options/block_prefix.rst.inc +++ b/reference/forms/types/options/block_prefix.rst.inc @@ -4,10 +4,6 @@ block_prefix **type**: ``string`` or ``null`` **default**: ``null`` (see :ref:`Knowing which block to customize `) -.. versionadded:: 4.3 - - The ``block_prefix`` option was introduced in Symfony 4.3. - Allows you to add a custom block prefix and override the block name used to render the form type. Useful for example if you have multiple instances of the same form and you need to personalize the rendering diff --git a/reference/forms/types/options/choice_attr.rst.inc b/reference/forms/types/options/choice_attr.rst.inc index ac149f3999d..1c9f5138d66 100644 --- a/reference/forms/types/options/choice_attr.rst.inc +++ b/reference/forms/types/options/choice_attr.rst.inc @@ -24,3 +24,20 @@ If an array, the keys of the ``choices`` array must be used as keys:: return ['class' => 'attending_'.strtolower($key)]; }, ]); + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use App\Entity\Category; + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'choice_label' => ChoiceList::attr($this, function (?Category $category) { + return $category ? ['data-uuid' => $category->getUuid()] : []; + }), + ]); + + See the :ref:`"choice_loader" option documentation `. diff --git a/reference/forms/types/options/choice_filter.rst.inc b/reference/forms/types/options/choice_filter.rst.inc new file mode 100644 index 00000000000..d7563dc8a1c --- /dev/null +++ b/reference/forms/types/options/choice_filter.rst.inc @@ -0,0 +1,82 @@ +``choice_filter`` +~~~~~~~~~~~~~~~~~ + +**type**: ``callable``, ``string`` or :class:`Symfony\\Component\\PropertyAccess\\PropertyPath` **default**: ``null`` + +.. versionadded:: 5.1 + + The ``choice_filter`` option has been introduced in Symfony 5.1. + +When using predefined choice types from Symfony core or vendor libraries (i.e. +:doc:`CountryType `) this option lets you +define a callable that takes each choice as the only argument and must return +``true`` to keep it or ``false`` to discard it:: + + // src/Form/Type/AddressType.php + namespace App\Form\Type; + + use Symfony\Component\Form\AbstractType; + use Symfony\Component\Form\Extension\Core\Type\CountryType; + use Symfony\Component\Form\FormBuilderInterface; + use Symfony\Component\OptionsResolver\OptionsResolver; + + class AddressType extends AbstractType + { + public function configureOptions(OptionsResolver $resolver) + { + $resolver + ->setDefaults([ + // enable this type to accept a limited set of countries + 'allowed_countries' => null, + ]) + ; + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + $allowedCountries = $options['allowed_countries']; + + $builder + // ... + ->add('country', CountryType::class, [ + // if the AddressType "allowed_countries" option is passed, + // use it to create a filter + 'choice_filter' => $allowedCountries ? function ($countryCode) use ($allowedCountries) { + return in_array($countryCode, $allowedCountries, true); + } : null, + + ]) + ; + } + +The option can be a callable or a property path when choices are objects:: + + // ... + $builder + ->add('category', ChoiceType::class, [ + // ... + 'choice_filter' => 'isSelectable', + ]) + ; + +.. tip:: + + Considering this ``AddressType`` could be an entry of a ``CollectionType`` + you should use the :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` + class helper to enable caching:: + + // src/Form/Type/AddressType.php + // ... + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + 'choice_filter' => $allowedCountries ? ChoiceList::filter( + // pass the type as first argument + $this, + function ($countryCode) use ($allowedCountries) { + return in_array($countryCode, $allowedCountries, true); + }, + // pass the option that makes the filter "vary" to compute a unique hash + $allowedCountries + ) : null, + // ... diff --git a/reference/forms/types/options/choice_label.rst.inc b/reference/forms/types/options/choice_label.rst.inc index 53cd469b916..6cfac9323ae 100644 --- a/reference/forms/types/options/choice_label.rst.inc +++ b/reference/forms/types/options/choice_label.rst.inc @@ -53,3 +53,17 @@ If your choice values are objects, then ``choice_label`` can also be a If set to ``false``, all the tag labels will be discarded for radio or checkbox inputs. You can also return ``false`` from the callable to discard certain labels. + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'choice_label' => ChoiceList::label($this, 'displayName'), + ]); + + See the :ref:`"choice_loader" option documentation `. diff --git a/reference/forms/types/options/choice_loader.rst.inc b/reference/forms/types/options/choice_loader.rst.inc new file mode 100644 index 00000000000..c44601ed3eb --- /dev/null +++ b/reference/forms/types/options/choice_loader.rst.inc @@ -0,0 +1,75 @@ +choice_loader +~~~~~~~~~~~~~ + +**type**: :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\ChoiceLoaderInterface` + +The ``choice_loader`` option can be used instead of the ``choices`` option. It +allows to create a list lazily or partially when fetching only the choices for a +set of submitted values (i.e. querying a search engine like ``ElasticSearch`` +can be a heavy process). + +You can use an instance of :class:`Symfony\\Component\\Form\\ChoiceList\\Loader\\CallbackChoiceLoader` +if you want to take advantage of lazy loading:: + + use App\StaticClass; + use Symfony\Component\Form\ChoiceList\Loader\CallbackChoiceLoader; + use Symfony\Component\Form\Extension\Core\Type\ChoiceType; + // ... + + $builder->add('loaded_choices', ChoiceType::class, [ + 'choice_loader' => new CallbackChoiceLoader(function() { + return StaticClass::getConstants(); + }), + ]); + +This will cause the call of ``StaticClass::getConstants()`` to not happen if the +request is redirected and if there is no pre set or submitted data. Otherwise +the choice options would need to be resolved thus triggering the callback. + +When you're defining a custom choice type that may be reused in many fields +(like entries of a collection) or reused in multiple forms at once, you +should use the :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` +static methods to wrap the loader and make the choice list cacheable for +better performance:: + + use App\Form\ChoiceList\CustomChoiceLoader; + use App\StaticClass; + use Symfony\Component\Form\AbstractType; + use Symfony\Component\Form\ChoiceList\ChoiceList; + use Symfony\Component\Form\Extension\Core\Type\ChoiceType; + use Symfony\Component\OptionsResolver\Options; + use Symfony\Component\OptionsResolver\OptionsResolver; + + class ConstantsType extends AbstractType + { + public static function getExtendedTypes(): iterable + { + return [ChoiceType::class]; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + // the example below will create a CallbackChoiceLoader from the callable + 'choice_loader' => ChoiceList::lazy($this, function() { + return StaticClass::getConstants(); + }), + + // you can pass your own loader as well, depending on other options + 'some_key' => null, + 'choice_loader' => function (Options $options) { + return ChoiceList::loader( + // pass the instance of the type or type extension which is + // currently configuring the choice list as first argument + $this, + // pass the other option to the loader + new CustomChoiceLoader($options['some_key']), + // ensure the type stores a loader per key + // by using the special third argument "$vary" + // an array containing anything that "changes" the loader + [$options['some_key']] + ); + }, + ]); + } + } diff --git a/reference/forms/types/options/choice_name.rst.inc b/reference/forms/types/options/choice_name.rst.inc index a01341b5418..4ec8abb6ffe 100644 --- a/reference/forms/types/options/choice_name.rst.inc +++ b/reference/forms/types/options/choice_name.rst.inc @@ -5,12 +5,26 @@ Controls the internal field name of the choice. You normally don't care about this, but in some advanced cases, you might. For example, this "name" becomes the index -of the choice views in the template and is used as part o the field name +of the choice views in the template and is used as part of the field name attribute. This can be a callable or a property path. See `choice_label`_ for similar usage. By default, the choice key or an incrementing integer may be used (starting at ``0``). +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'choice_name' => ChoiceList::fieldName($this, 'name'), + ]); + + See the :ref:`"choice_loader" option documentation `. + .. caution:: The configured value must be a valid form name. Make sure to only return diff --git a/reference/forms/types/options/choice_value.rst.inc b/reference/forms/types/options/choice_value.rst.inc index a37a36cf299..13bc324cd2a 100644 --- a/reference/forms/types/options/choice_value.rst.inc +++ b/reference/forms/types/options/choice_value.rst.inc @@ -18,3 +18,17 @@ for each choice or ``null`` in a placeholder is used, which you need to handle:: 'choice_value' => function (?MyOptionEntity $entity) { return $entity ? $entity->getId() : ''; }, + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'choice_value' => ChoiceList::value($this, 'uuid'), + ]); + + See the :ref:`"choice_loader" option documentation `. diff --git a/reference/forms/types/options/date_input_format_description.rst.inc b/reference/forms/types/options/date_input_format_description.rst.inc index 4cd9b353e31..e411cd12d70 100644 --- a/reference/forms/types/options/date_input_format_description.rst.inc +++ b/reference/forms/types/options/date_input_format_description.rst.inc @@ -1,7 +1,3 @@ -.. versionadded:: 4.3 - - The ``input_format`` option was introduced in Symfony 4.3. - If the ``input`` option is set to ``string``, this option specifies the format of the date. This must be a valid `PHP date format`_. diff --git a/reference/forms/types/options/extra_fields_message.rst.inc b/reference/forms/types/options/extra_fields_message.rst.inc index ca54c91ec54..5c969f7afce 100644 --- a/reference/forms/types/options/extra_fields_message.rst.inc +++ b/reference/forms/types/options/extra_fields_message.rst.inc @@ -1,9 +1,17 @@ ``extra_fields_message`` ~~~~~~~~~~~~~~~~~~~~~~~~ +.. versionadded:: 5.1 + + Pluralization support was introduced in Symfony 5.1. + **type**: ``string`` **default**: ``This form should not contain extra fields.`` This is the validation error message that's used if the submitted form data contains one or more fields that are not part of the form definition. The placeholder ``{{ extra_fields }}`` can be used to display a comma separated list of the submitted extra field names. + +This message can be pluralized, see +:ref:`formatting pluralized messages ` for +details. diff --git a/reference/forms/types/options/group_by.rst.inc b/reference/forms/types/options/group_by.rst.inc index b649793e9ff..ca747683662 100644 --- a/reference/forms/types/options/group_by.rst.inc +++ b/reference/forms/types/options/group_by.rst.inc @@ -40,3 +40,17 @@ a "Later" ````: If you return ``null``, the option won't be grouped. You can also pass a string "property path" that will be called to get the group. See the `choice_label`_ for details about using a property path. + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'group_by' => ChoiceList::groupBy($this, 'category'), + ]); + + See the :ref:`"choice_loader" option documentation `. diff --git a/reference/forms/types/options/help_translation_parameters.rst.inc b/reference/forms/types/options/help_translation_parameters.rst.inc index 4294fb2b185..2b69e237941 100644 --- a/reference/forms/types/options/help_translation_parameters.rst.inc +++ b/reference/forms/types/options/help_translation_parameters.rst.inc @@ -3,10 +3,6 @@ help_translation_parameters **type**: ``array`` **default**: ``[]`` -.. versionadded:: 4.3 - - The ``help_translation_parameters`` option was introduced in Symfony 4.3. - The content of the `help`_ option is translated before displaying it, so it can contain :ref:`translation placeholders `. This option defines the values used to replace those placeholders. diff --git a/reference/forms/types/options/label_html.rst.inc b/reference/forms/types/options/label_html.rst.inc new file mode 100644 index 00000000000..06568ed08f4 --- /dev/null +++ b/reference/forms/types/options/label_html.rst.inc @@ -0,0 +1,12 @@ +``label_html`` +~~~~~~~~~~~~~~ + +**type**: ``bool`` **default**: ``false`` + +.. versionadded:: 5.1 + + The ``label_html`` option was introduced in Symfony 5.1. + +By default, the contents of the ``label`` option are escaped before rendering +them in the template. Set this option to ``true`` to not escape them, which is +useful when the label contains HTML elements. diff --git a/reference/forms/types/options/label_translation_parameters.rst.inc b/reference/forms/types/options/label_translation_parameters.rst.inc index 443c6706f14..815b780553e 100644 --- a/reference/forms/types/options/label_translation_parameters.rst.inc +++ b/reference/forms/types/options/label_translation_parameters.rst.inc @@ -3,10 +3,6 @@ label_translation_parameters **type**: ``array`` **default**: ``[]`` -.. versionadded:: 4.3 - - The ``label_translation_parameters`` option was introduced in Symfony 4.3. - The content of the `label`_ option is translated before displaying it, so it can contain :ref:`translation placeholders `. This option defines the values used to replace those placeholders. diff --git a/reference/forms/types/options/preferred_choices.rst.inc b/reference/forms/types/options/preferred_choices.rst.inc index 11eb2b7f8b4..bffb021f864 100644 --- a/reference/forms/types/options/preferred_choices.rst.inc +++ b/reference/forms/types/options/preferred_choices.rst.inc @@ -20,12 +20,6 @@ form of languages, you can list the most popular on top, like Bork and Pirate:: 'preferred_choices' => ['muppets', 'arr'], ]); -.. versionadded:: 4.4 - - Starting from Symfony 4.4, the preferred choices are displayed both at the - top of the list and at their original locations on the list. In prior - Symfony versions, they were only displayed at the top of the list. - This options can also be a callback function to give you more flexibility. This might be especially useful if your values are objects:: @@ -69,3 +63,17 @@ when rendering the field: widget($form['publishAt'], [ 'separator' => '=====', ]) ?> + +.. tip:: + + When defining a custom type, you should use the + :class:`Symfony\\Component\\Form\\ChoiceList\\ChoiceList` class helper:: + + use Symfony\Component\Form\ChoiceList\ChoiceList; + + // ... + $builder->add('choices', ChoiceType::class, [ + 'preferred_choices' => ChoiceList::preferred($this, 'taggedAsFavorite'), + ]); + + See the :ref:`"choice_loader" option documentation `. diff --git a/reference/forms/types/options/rounding_mode.rst.inc b/reference/forms/types/options/rounding_mode.rst.inc index 066a1da0a22..525f5d99cdf 100644 --- a/reference/forms/types/options/rounding_mode.rst.inc +++ b/reference/forms/types/options/rounding_mode.rst.inc @@ -1,32 +1,38 @@ rounding_mode ~~~~~~~~~~~~~ -**type**: ``integer`` **default**: ``NumberToLocalizedStringTransformer::ROUND_HALF_UP`` +**type**: ``integer`` **default**: ``\NumberFormatter::ROUND_HALFUP`` -If a submitted number needs to be rounded (based on the `scale`_ -option), you have several configurable options for that rounding. Each -option is a constant on the :class:`Symfony\\Component\\Form\\Extension\\Core\\DataTransformer\\NumberToLocalizedStringTransformer`: +If a submitted number needs to be rounded (based on the `scale`_ option), you +have several configurable options for that rounding. Each option is a constant +on the :phpclass:`NumberFormatter` class: -* ``NumberToLocalizedStringTransformer::ROUND_DOWN`` Round towards zero. It +* ``\NumberFormatter::ROUND_DOWN`` Round towards zero. It rounds ``1.4`` to ``1`` and ``-1.4`` to ``-1``. -* ``NumberToLocalizedStringTransformer::ROUND_FLOOR`` Round towards negative +* ``\NumberFormatter::ROUND_FLOOR`` Round towards negative infinity. It rounds ``1.4`` to ``1`` and ``-1.4`` to ``-2``. -* ``NumberToLocalizedStringTransformer::ROUND_UP`` Round away from zero. It +* ``\NumberFormatter::ROUND_UP`` Round away from zero. It rounds ``1.4`` to ``2`` and ``-1.4`` to ``-2``. -* ``NumberToLocalizedStringTransformer::ROUND_CEILING`` Round towards positive +* ``\NumberFormatter::ROUND_CEILING`` Round towards positive infinity. It rounds ``1.4`` to ``2`` and ``-1.4`` to ``-1``. -* ``NumberToLocalizedStringTransformer::ROUND_HALF_DOWN`` Round towards the +* ``\NumberFormatter::ROUND_HALFDOWN`` Round towards the "nearest neighbor". If both neighbors are equidistant, round down. It rounds ``2.5`` and ``1.6`` to ``2``, ``1.5`` and ``1.4`` to ``1``. -* ``NumberToLocalizedStringTransformer::ROUND_HALF_EVEN`` Round towards the +* ``\NumberFormatter::ROUND_HALFEVEN`` Round towards the "nearest neighbor". If both neighbors are equidistant, round towards the even neighbor. It rounds ``2.5``, ``1.6`` and ``1.5`` to ``2`` and ``1.4`` to ``1``. -* ``NumberToLocalizedStringTransformer::ROUND_HALF_UP`` Round towards the +* ``\NumberFormatter::ROUND_HALFUP`` Round towards the "nearest neighbor". If both neighbors are equidistant, round up. It rounds ``2.5`` to ``3``, ``1.6`` and ``1.5`` to ``2`` and ``1.4`` to ``1``. + +.. deprecated:: 5.1 + + In Symfony versions prior to 5.1, these constants were also defined as aliases + in the :class:`Symfony\\Component\\Form\\Extension\\Core\\DataTransformer\\NumberToLocalizedStringTransformer` + class, but they are now deprecated in favor of the :phpclass:`NumberFormatter` constants. diff --git a/reference/forms/types/options/row_attr.rst.inc b/reference/forms/types/options/row_attr.rst.inc index 4cb9775b5e5..e8cbaa6b564 100644 --- a/reference/forms/types/options/row_attr.rst.inc +++ b/reference/forms/types/options/row_attr.rst.inc @@ -14,7 +14,3 @@ to render the :ref:`form type row `:: Use the ``attr`` option if you want to add these attributes to the the :ref:`form type widget ` element. - -.. versionadded:: 4.3 - - The ``row_attr`` option was introduced in Symfony 4.3. diff --git a/reference/forms/types/percent.rst b/reference/forms/types/percent.rst index b6a516fffc0..ca6c1c2e456 100644 --- a/reference/forms/types/percent.rst +++ b/reference/forms/types/percent.rst @@ -15,7 +15,8 @@ the input. +-------------+-----------------------------------------------------------------------+ | Rendered as | ``input`` ``text`` field | +-------------+-----------------------------------------------------------------------+ -| Options | - `scale`_ | +| Options | - `rounding_mode`_ | +| | - `scale`_ | | | - `symbol`_ | | | - `type`_ | +-------------+-----------------------------------------------------------------------+ @@ -50,23 +51,27 @@ the input. Field Options ------------- +.. include:: /reference/forms/types/options/rounding_mode.rst.inc + +.. versionadded:: 5.1 + + The ``rounding_mode`` option was introduced in Symfony 5.1. + scale ~~~~~ **type**: ``integer`` **default**: ``0`` -By default, the input numbers are rounded. To allow for more decimal places, -use this option. +This specifies how many decimals will be allowed until the field rounds +the submitted value (via ``rounding_mode``). For example, if ``scale`` is set +to ``2``, a submitted value of ``20.123`` will be rounded to, for example, +``20.12`` (depending on your `rounding_mode`_). symbol ~~~~~~ **type**: ``boolean`` or ``string`` **default**: ``%`` -.. versionadded:: 4.3 - - The ``symbol`` option was introduced in Symfony 4.3. - By default, fields are rendered with a percentage sign ``%`` after the input. Setting the value to ``false`` will not display the percentage sign. Setting the value to a ``string`` (e.g. ``‱``), will show that string instead of the default diff --git a/reference/forms/types/submit.rst b/reference/forms/types/submit.rst index 7c6c5d69b1c..8976dc30a02 100644 --- a/reference/forms/types/submit.rst +++ b/reference/forms/types/submit.rst @@ -45,10 +45,6 @@ validate **type**: ``boolean`` **default**: ``true`` -.. versionadded:: 4.4 - - The ``validate`` option was introduced in Symfony 4.4. - Set this option to ``false`` to disable the client-side validation of the form performed by the browser. diff --git a/reference/forms/types/time.rst b/reference/forms/types/time.rst index f893c4d1bf0..aca9905f985 100644 --- a/reference/forms/types/time.rst +++ b/reference/forms/types/time.rst @@ -140,10 +140,6 @@ input_format **type**: ``string`` **default**: ``H:i:s`` -.. versionadded:: 4.3 - - The ``input_format`` option was introduced in Symfony 4.3. - If the ``input`` option is set to ``string``, this option specifies the format of the time. This must be a valid `PHP time format`_. @@ -161,10 +157,6 @@ reference_date **type**: ``DateTimeInterface`` **default**: ``null`` -.. versionadded:: 4.4 - - The ``reference_date`` option was introduced in Symfony 4.4. - Configuring a reference date is required when the `model_timezone`_ and `view_timezone`_ are different. Timezone conversions will be calculated based on this date. @@ -173,6 +165,9 @@ based on this date. .. include:: /reference/forms/types/options/view_timezone.rst.inc +When no `reference_date`_ is set the ``view_timezone`` defaults to the +configured `model_timezone`_. + .. caution:: When using different values for `model_timezone`_ and ``view_timezone``, diff --git a/reference/forms/types/timezone.rst b/reference/forms/types/timezone.rst index fb2efb4ce21..a493f406378 100644 --- a/reference/forms/types/timezone.rst +++ b/reference/forms/types/timezone.rst @@ -19,7 +19,6 @@ manually, but then you should just use the ``ChoiceType`` directly. +-------------+------------------------------------------------------------------------+ | Options | - `input`_ | | | - `intl`_ | -| | - `regions`_ | +-------------+------------------------------------------------------------------------+ | Overridden | - `choices`_ | | options | | @@ -73,19 +72,11 @@ on your underlying object. Valid values are: * ``intltimezone`` (an ``\IntlTimeZone`` object) * ``string`` (e.g. ``America/New_York``) -.. versionadded:: 4.3 - - The ``intltimezone`` input type was introduced in Symfony 4.3. - intl ~~~~ **type**: ``boolean`` **default**: ``false`` -.. versionadded:: 4.3 - - This option was introduced in Symfony 4.3. - If this option is set to ``true``, the timezone selector will display the timezones from the `ICU Project`_ via the :doc:`Intl component ` instead of the regular PHP timezones. @@ -99,17 +90,6 @@ with the ``choice_translation_locale`` option. The :doc:`Timezone constraint ` can validate both timezone sets and adapts to the selected set automatically. -``regions`` -~~~~~~~~~~~ - -**type**: ``int`` **default**: ``\DateTimeZone::ALL`` - -.. deprecated:: 4.2 - - This option was deprecated in Symfony 4.2. - -The available regions in the timezone choice list. For example: ``DateTimeZone::AMERICA | DateTimeZone::EUROPE`` - Overridden Options ------------------ diff --git a/reference/forms/types/week.rst b/reference/forms/types/week.rst index 754139c9cd6..6967df09bb7 100644 --- a/reference/forms/types/week.rst +++ b/reference/forms/types/week.rst @@ -4,10 +4,6 @@ WeekType Field ============== -.. versionadded:: 4.4 - - The ``WeekType`` type was introduced in Symfony 4.4. - This field type allows the user to modify data that represents a specific `ISO 8601`_ week number (e.g. ``1984-W05``). diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst index 84e52b0cd35..4a97ab73308 100644 --- a/reference/twig_reference.rst +++ b/reference/twig_reference.rst @@ -144,19 +144,13 @@ is_granted {{ is_granted(role, object = null, field = null) }} ``role`` - **type**: ``string``, ``string[]`` + **type**: ``string`` ``object`` *(optional)* **type**: ``object`` ``field`` *(optional)* **type**: ``string`` -Returns ``true`` if the current user has the given role. If several roles are -passed in an array, ``true`` is returned if the user has at least one of -them. - -.. deprecated:: 4.4 - - The feature to pass an array of roles to ``is_granted()`` was deprecated in Symfony 4.4. +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`. @@ -329,33 +323,6 @@ trans Translates the text into the current language. More information in :ref:`Translation Filters `. -transchoice -~~~~~~~~~~~ - -.. deprecated:: 4.2 - - The ``transchoice`` filter is deprecated since Symfony 4.2 and will be - removed in 5.0. Use the :doc:`ICU MessageFormat ` with - the ``trans`` filter instead. - -.. code-block:: twig - - {{ message|transchoice(count, arguments = [], domain = null, locale = null) }} - -``message`` - **type**: ``string`` -``count`` - **type**: ``integer`` -``arguments`` *(optional)* - **type**: ``array`` **default**: ``[]`` -``domain`` *(optional)* - **type**: ``string`` **default**: ``null`` -``locale`` *(optional)* - **type**: ``string`` **default**: ``null`` - -Translates the text with pluralization support. More information in -:ref:`Translation Filters `. - yaml_encode ~~~~~~~~~~~ @@ -564,31 +531,6 @@ trans Renders the translation of the content. More information in :ref:`translation-tags`. -transchoice -~~~~~~~~~~~ - -.. deprecated:: 4.2 - - The ``transchoice`` tag is deprecated since Symfony 4.2 and will be - removed in 5.0. Use the :doc:`ICU MessageFormat ` with - the ``trans`` tag instead. - -.. code-block:: twig - - {% transchoice count with vars from domain into locale %}{% endtranschoice %} - -``count`` - **type**: ``integer`` -``vars`` *(optional)* - **type**: ``array`` **default**: ``[]`` -``domain`` *(optional)* - **type**: ``string`` **default**: ``null`` -``locale`` *(optional)* - **type**: ``string`` **default**: ``null`` - -Renders the translation of the content with pluralization support, more -information in :ref:`translation-tags`. - trans_default_domain ~~~~~~~~~~~~~~~~~~~~ diff --git a/routing.rst b/routing.rst index aa59369865e..b2e252d87b5 100644 --- a/routing.rst +++ b/routing.rst @@ -733,11 +733,6 @@ If you want to always include some default value in the generated URL (for example to force the generation of ``/blog/1`` instead of ``/blog`` in the previous example) add the ``!`` character before the parameter name: ``/blog/{!page}`` -.. versionadded:: 4.3 - - The feature to force the inclusion of default values in generated URLs was - introduced in Symfony 4.3. - As it happens with requirements, default values can also be inlined in each parameter using the syntax ``{parameter_name?default_value}``. This feature is compatible with inlined requirements, so you can inline both in a single @@ -803,6 +798,54 @@ parameter: To give a ``null`` default value to any parameter, add nothing after the ``?`` character (e.g. ``/blog/{page?}``). +Priority Parameter +~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 5.1 + + The ``priority`` parameter was introduced in Symfony 5.1 + +When defining a greedy pattern that matches many routes, this may be at the +beginning of your routing collection and prevents any route defined after to be +matched. +A ``priority`` optional parameter is available in order to let you choose the +order of your routes, and it is only available when using annotations. + +.. code-block:: php-annotations + + // src/Controller/BlogController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Routing\Annotation\Route; + + class BlogController extends AbstractController + { + /** + * This route has a greedy pattern and is defined first. + * + * @Route("/blog/{slug}", name="blog_show") + */ + public function show(string $slug) + { + // ... + } + + /** + * This route could not be matched without defining a higher priority than 0. + * + * @Route("/blog/list", name="blog_list", priority=2) + */ + public function list() + { + // ... + } + } + +The priority parameter expects an integer value. Routes with higher priority +are sorted before routes with lower priority. The default value when it is not +defined is ``0``. + Parameter Conversion ~~~~~~~~~~~~~~~~~~~~ @@ -959,10 +1002,6 @@ and in route imports. Symfony defines some special attributes with the same name ; }; -.. versionadded:: 4.3 - - The special attributes were introduced in Symfony 4.3. - Extra Parameters ~~~~~~~~~~~~~~~~ @@ -1234,11 +1273,6 @@ the common configuration using options when importing the routes. ; }; -.. versionadded:: 4.4 - - The option to exclude some files or subdirectories when loading annotations - was introduced in Symfony 4.4. - In this example, the route of the ``index()`` action will be called ``blog_index`` and its URL will be ``/blog/``. The route of the ``show()`` action will be called ``blog_show`` and its URL will be ``/blog/{_locale}/posts/{slug}``. Both routes @@ -1418,13 +1452,6 @@ Use the ``RedirectController`` to redirect to other routes and URLs: Symfony also provides some utilities to :ref:`redirect inside controllers ` -.. versionadded:: 4.4 - - In Symfony versions prior to 4.4, you needed to define the specific - ``RedirectController`` method to use (either ``redirectAction`` or - ``urlRedirectAction``). Starting from Symfony 4.4 this is no longer needed - because Symfony detects if the redirection is to a route or an URL. - .. _routing-trailing-slash-redirection: Redirecting URLs with Trailing Slashes @@ -1655,10 +1682,6 @@ these routes. Localized Routes (i18n) ----------------------- -.. versionadded:: 4.1 - - The i18n routing was introduced in Symfony 4.1. - If your application is translated into multiple languages, each route can define a different URL per each :doc:`translation locale `. This avoids the need for duplicating routes, which also reduces the potential bugs: @@ -1782,6 +1805,84 @@ with a locale. This can be done by defining a different prefix for each locale ; }; +.. _stateless-routing: + +Stateless Routes +---------------- + +.. versionadded:: 5.1 + + The ``stateless`` option was introduced in Symfony 5.1. + +Sometimes, when an HTTP response should be cached, it is important to ensure +that can happen. However, whenever session is started during a request, Symfony +turns the response into a private non-cacheable response. + +For details, see :doc:`/http_cache`. + +Routes can configure a ``stateless`` boolean option in order to declare that the +session shouldn't be used when matching a request: + +.. configuration-block:: + + .. code-block:: php-annotations + + // src/Controller/MainController.php + namespace App\Controller; + + use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + use Symfony\Component\Routing\Annotation\Route; + + class MainController extends AbstractController + { + /** + * @Route("/", name="homepage", stateless=true) + */ + public function homepage() + { + // ... + } + } + + .. code-block:: yaml + + # config/routes.yaml + homepage: + controller: App\Controller\MainController::homepage + path: / + stateless: true + + .. code-block:: xml + + + + + + + + .. code-block:: php + + // config/routes.php + use App\Controller\MainController; + use Symfony\Bundle\FrameworkBundle\Routing\Loader\Configurator\RoutingConfigurator; + + return function (RoutingConfigurator $routes) { + $routes->add('homepage', '/') + ->controller([MainController::class, 'homepage']) + ->stateless() + ; + }; + +Now, if the session is used, the application will report it based on your +``kernel.debug`` parameter: +* ``enabled``: will throw an :class:`Symfony\\Component\\HttpKernel\\Exception\\UnexpectedSessionUsageException` exception +* ``disabled``: will log a warning + +It well help you understanding and hopefully fixing unexpected behavior in your application. + .. _routing-generating-urls: Generating URLs @@ -1930,49 +2031,57 @@ Generating URLs in Commands Generating URLs in commands works the same as :ref:`generating URLs in services `. The -only difference is that commands are not executed in the HTTP context, so they -don't have access to HTTP requests. In practice, this means that if you generate -absolute URLs, you'll get ``http://localhost/`` as the host name instead of your -real host name. +only difference is that commands are not executed in the HTTP context. Therefore, +if you generate absolute URLs, you'll get ``http://localhost/`` as the host name +instead of your real host name. -The solution is to configure the "request context" used by commands when they -generate URLs. This context can be configured globally for all commands: +The solution is to configure the ``default_uri`` option to define the +"request context" used by commands when they generate URLs: .. configuration-block:: .. code-block:: yaml - # config/services.yaml - parameters: - router.request_context.host: 'example.org' - router.request_context.base_url: 'my/path' - asset.request_context.base_path: '%router.request_context.base_url%' + # config/packages/routing.yaml + framework: + router: + # ... + default_uri: 'https://example.org/my/path/' .. code-block:: xml - - + + - - - example.org - my/path - %router.request_context.base_url% - + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/symfony + https://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> + + + + + .. code-block:: php - // config/services.php - $container->setParameter('router.request_context.host', 'example.org'); - $container->setParameter('router.request_context.base_url', 'my/path'); - $container->setParameter('asset.request_context.base_path', $container->getParameter('router.request_context.base_url')); + // config/packages/routing.php + $container->loadFromExtension('framework', [ + 'router' => [ + // ... + 'default_uri' => "https://example.org/my/path/", + ], + ]); + +.. versionadded:: 5.1 -This information can be configured per command too:: + The ``default_uri`` option was introduced in Symfony 5.1. + +Now you'll get the expected results when generating URLs in your commands:: // src/Command/SomeCommand.php namespace App\Command; @@ -1997,11 +2106,6 @@ This information can be configured per command too:: protected function execute(InputInterface $input, OutputInterface $output) { - // these values override any global configuration - $context = $this->router->getContext(); - $context->setHost('example.com'); - $context->setBaseUrl('my/path'); - // generate a URL with no route arguments $signUpPage = $this->router->generate('sign_up'); @@ -2022,6 +2126,12 @@ This information can be configured per command too:: } } +.. note:: + + By default, the URLs generated for web assets use the same ``default_uri`` + value, but you can change it with the ``asset.request_context.base_path`` + and ``asset.request_context.secure`` container parameters. + Checking if a Route Exists ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/routing/custom_route_loader.rst b/routing/custom_route_loader.rst index b893bf0114e..6b8d2370cfe 100644 --- a/routing/custom_route_loader.rst +++ b/routing/custom_route_loader.rst @@ -206,10 +206,6 @@ implement the :class:`Symfony\\Bundle\\FrameworkBundle\\Routing\\RouteLoaderInte interface to be tagged automatically. If you're **not using autoconfigure**, tag it manually with ``routing.route_loader``. -.. deprecated:: 4.4 - - Not tagging or implementing your route loader was deprecated in Symfony 4.4. - .. note:: The routes defined using service route loaders will be automatically @@ -220,11 +216,6 @@ tag it manually with ``routing.route_loader``. If your service is invokable, you don't need to precise the method to use. -.. versionadded:: 4.3 - - The support of the ``__invoke()`` method to create invokable service route - loaders was introduced in Symfony 4.3. - Creating a custom Loader ------------------------ @@ -252,7 +243,7 @@ you do. The resource name itself is not actually used in the example:: { private $isLoaded = false; - public function load($resource, $type = null) + public function load($resource, string $type = null) { if (true === $this->isLoaded) { throw new \RuntimeException('Do not add the "extra" loader twice'); @@ -279,7 +270,7 @@ you do. The resource name itself is not actually used in the example:: return $routes; } - public function supports($resource, $type = null) + public function supports($resource, string $type = null) { return 'extra' === $type; } @@ -418,7 +409,7 @@ configuration file - you can call the class AdvancedLoader extends Loader { - public function load($resource, $type = null) + public function load($resource, string $type = null) { $routes = new RouteCollection(); @@ -432,7 +423,7 @@ configuration file - you can call the return $routes; } - public function supports($resource, $type = null) + public function supports($resource, string $type = null) { return 'advanced_extra' === $type; } diff --git a/security.rst b/security.rst index 60fb74ed40c..358ce477b8c 100644 --- a/security.rst +++ b/security.rst @@ -37,6 +37,49 @@ install the security feature before using it: $ composer require symfony/security-bundle + +.. tip:: + + A :doc:`new experimental Security ` + was introduced in Symfony 5.1, which will eventually replace security in + Symfony 6.0. This system is almost fully backwards compatible with the + current Symfony security, add this line to your security configuration to start + using it: + + .. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + enable_authenticator_manager: true + # ... + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/packages/security.php + $container->loadFromExtension('security', [ + 'enable_authenticator_manager' => true, + // ... + ]); + .. _initial-security-yml-setup-authentication: .. _initial-security-yaml-setup-authentication: .. _create-user-class: @@ -137,7 +180,9 @@ command will pre-configure this for you: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -172,6 +217,8 @@ Now that Symfony knows *how* you want to encode the passwords, you can use the ``UserPasswordEncoderInterface`` service to do this before saving your users to the database. +.. _user-data-fixture: + For example, by using :ref:`DoctrineFixturesBundle `, you can create dummy database users: @@ -227,6 +274,11 @@ You can manually encode a password by running: 3a) Authentication & Firewalls ------------------------------ +.. versionadded:: 5.1 + + The ``lazy: true`` option was introduced in Symfony 5.1. Prior to version 5.1, + it was enabled using ``anonymous: lazy`` + The security system is configured in ``config/packages/security.yaml``. The *most* important section is ``firewalls``: @@ -241,7 +293,8 @@ important section is ``firewalls``: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: - anonymous: lazy + anonymous: true + lazy: true .. code-block:: xml @@ -251,15 +304,18 @@ important section is ``firewalls``: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> - - + @@ -274,15 +330,12 @@ important section is ``firewalls``: 'security' => false, ], 'main' => [ - 'anonymous' => 'lazy', + 'anonymous' => true, + 'lazy' => true, ], ], ]); -.. versionadded:: 4.4 - - The ``lazy`` anonymous mode has been introduced in Symfony 4.4. - A "firewall" is your authentication system: the configuration below it defines *how* your users will be able to authenticate (e.g. login form, API token, etc). @@ -361,8 +414,8 @@ authentication process. There are many different ways to build an authenticator; here are a few common use-cases: * :doc:`/security/form_login_setup` -* :doc:`/security/guard_authentication` – see this for the most detailed description of - authenticators and how they work +* :doc:`/security/guard_authentication` – see this for the most detailed + description of authenticators and how they work .. _`security-authorization`: .. _denying-access-roles-and-other-authorization: @@ -476,7 +529,9 @@ start with ``/admin``, you can: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -552,7 +607,9 @@ the list and stops when it finds the first match: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -694,7 +751,7 @@ You can use ``IS_AUTHENTICATED_FULLY`` anywhere roles are used: like ``access_control`` or in Twig. ``IS_AUTHENTICATED_FULLY`` isn't a role, but it kind of acts like one, and every -user that has logged in will have this. Actually, there are 3 special attributes +user that has logged in will have this. Actually, there are some special attributes like this: * ``IS_AUTHENTICATED_REMEMBERED``: *All* logged in users have this, even @@ -710,6 +767,21 @@ like this: this - this is useful when *whitelisting* URLs to guarantee access - some details are in :doc:`/security/access_control`. +* ``IS_ANONYMOUS``: *Only* anonymous users are matched by this attribute. + +* ``IS_REMEMBERED``: *Only* users authenticated using the + :doc:`remember me functionality `, (i.e. a + remember-me cookie). + +* ``IS_IMPERSONATOR``: When the current user is + :doc:`impersonating ` another user in this + session, this attribute will match. + +.. versionadded:: 5.1 + + The ``IS_ANONYMOUS``, ``IS_REMEMBERED`` and ``IS_IMPERSONATOR`` + attributes were introduced in Symfony 5.1. + .. _retrieving-the-user-object: 5a) Fetching the User Object @@ -806,7 +878,9 @@ To enable logging out, activate the ``logout`` config parameter under your fire xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -889,11 +963,93 @@ Next, you'll need to create a route for this URL (but not a controller): And that's it! By sending a user to the ``app_logout`` route (i.e. to ``/logout``) Symfony will un-authenticate the current user and redirect them. +Customizing Logout +~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 5.1 + + The ``LogoutEvent`` was introduced in Symfony 5.1. Prior to this + version, you had to use a + :ref:`logout success handler ` + to customize the logout. + +In some cases you need to execute extra logic upon logout (e.g. invalidate +some tokens) or want to customize what happens after a logout. During +logout, a :class:`Symfony\\Component\\Security\\Http\\Event\\LogoutEvent` +is dispatched. Register an :doc:`event listener or subscriber ` +to execute custom logic. The following information is available in the +event class: + +``getToken()`` + Returns the security token of the session that is about to be logged + out. +``getRequest()`` + Returns the current request. +``getResponse()`` + Returns a response, if it is already set by a custom listener. Use + ``setResponse()`` to configure a custom logout response. + + .. tip:: - Need more control of what happens after logout? Add a ``success_handler`` key - under ``logout`` and point it to a service id of a class that implements - :class:`Symfony\\Component\\Security\\Http\\Logout\\LogoutSuccessHandlerInterface`. + Every Security firewall has its own event dispatcher + (``security.event_dispatcher.FIREWALLNAME``). The logout event is + dispatched on both the global and firewall dispatcher. You can register + on the firewall dispatcher if you want your listener to only be + executed for a specific firewall. For instance, if you have an ``api`` + and ``main`` firewall, use this configuration to register only on the + logout event in the ``main`` firewall: + + .. configuration-block:: + + .. code-block:: yaml + + # config/services.yaml + services: + # ... + + App\EventListener\CustomLogoutSubscriber: + tags: + - name: kernel.event_subscriber + dispatcher: security.event_dispatcher.main + + .. code-block:: xml + + + + + + + + + + + + + + + .. code-block:: php + + // config/services.php + namespace Symfony\Component\DependencyInjection\Loader\Configurator; + + use App\EventListener\CustomLogoutListener; + use App\EventListener\CustomLogoutSubscriber; + use Symfony\Component\Security\Http\Event\LogoutEvent; + + return function(ContainerConfigurator $configurator) { + $services = $configurator->services(); + + $services->set(CustomLogoutSubscriber::class) + ->tag('kernel.event_subscriber', [ + 'dispatcher' => 'security.event_dispatcher.main', + ]); + }; .. _security-role-hierarchy: @@ -923,7 +1079,9 @@ rules by creating a role hierarchy: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -1014,6 +1172,7 @@ Authentication (Identifying/Logging in the User) .. toctree:: :maxdepth: 1 + security/experimental_authenticators security/form_login_setup security/reset_password security/json_login_setup diff --git a/security/access_control.rst b/security/access_control.rst index 51734876cdc..d7a96345b8e 100644 --- a/security/access_control.rst +++ b/security/access_control.rst @@ -44,8 +44,6 @@ Take the following ``access_control`` entries as an example: - { path: '^/admin', roles: ROLE_USER_PORT, ip: 127.0.0.1, port: 8080 } - { path: '^/admin', roles: ROLE_USER_HOST, host: symfony\.com$ } - { path: '^/admin', roles: ROLE_USER_METHOD, methods: [POST, PUT] } - # when defining multiple roles, users must have at least one of them (it's like an OR condition) - - { path: '^/admin', roles: [ROLE_MANAGER, ROLE_ADMIN] } .. code-block:: xml @@ -55,7 +53,9 @@ Take the following ``access_control`` entries as an example: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -63,8 +63,6 @@ Take the following ``access_control`` entries as an example: - - @@ -94,12 +92,7 @@ Take the following ``access_control`` entries as an example: 'path' => '^/admin', 'roles' => 'ROLE_USER_METHOD', 'methods' => 'POST, PUT', - ], - [ - 'path' => '^/admin', - // when defining multiple roles, users must have at least one of them (it's like an OR condition) - 'roles' => ['ROLE_MANAGER', 'ROLE_ADMIN'], - ], + ] ], ]); @@ -130,11 +123,6 @@ if ``ip``, ``port``, ``host`` or ``method`` are not specified for an entry, that | ``/admin/user`` | 168.0.0.1 | 80 | example.com | POST | rule #4 (``ROLE_USER_METHOD``) | The ``ip`` and ``host`` don't match the first two entries, | | | | | | | | but the third - ``ROLE_USER_METHOD`` - matches and is used. | +-----------------+-------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ -| ``/admin/user`` | 168.0.0.1 | 80 | example.com | GET | rule #4 (``ROLE_MANAGER``) | The ``ip``, ``host`` and ``method`` prevent the first | -| | | | | | | three entries from matching. But since the URI matches the | -| | | | | | | ``path`` pattern, then the ``ROLE_MANAGER`` (or the | -| | | | | | | ``ROLE_ADMIN``) is used. | -+-----------------+-------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ | ``/foo`` | 127.0.0.1 | 80 | symfony.com | POST | matches no entries | This doesn't match any ``access_control`` rules, since its | | | | | | | | URI doesn't match any of the ``path`` values. | +-----------------+-------------+-------------+-------------+------------+--------------------------------+-------------------------------------------------------------+ @@ -227,7 +215,9 @@ pattern so that it is only accessible by requests from the local server itself: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -314,7 +304,9 @@ key: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -390,7 +382,9 @@ access those URLs via a specific port. This could be useful for example for xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -441,7 +435,9 @@ the user will be redirected to ``https``: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/auth_providers.rst b/security/auth_providers.rst index f5ec3723c7b..349f16a219a 100644 --- a/security/auth_providers.rst +++ b/security/auth_providers.rst @@ -21,8 +21,6 @@ use-case matches one of these exactly, they're a great option: * :doc:`json_login ` * :ref:`X.509 Client Certificate Authentication (x509) ` * :ref:`REMOTE_USER Based Authentication (remote_user) ` -* ``simple_form`` -* ``simple_pre_auth`` .. _security-http_basic: @@ -57,7 +55,9 @@ To support HTTP Basic authentication, add the ``http_basic`` key to your firewal xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -123,7 +123,9 @@ Enable the x509 authentication for a particular firewall in the security configu xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -199,7 +201,12 @@ corresponding firewall in your security configuration: + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:srv="http://symfony.com/schema/dic/services" + xsi:schemaLocation="http://symfony.com/schema/dic/services + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/csrf.rst b/security/csrf.rst index b6186705c1c..a346340bc24 100644 --- a/security/csrf.rst +++ b/security/csrf.rst @@ -113,10 +113,6 @@ You can also customize the rendering of the CSRF form field creating a custom the field (e.g. define ``{% block csrf_token_widget %} ... {% endblock %}`` to customize the entire form field contents). -.. versionadded:: 4.3 - - The ``csrf_token`` form field prefix was introduced in Symfony 4.3. - CSRF Protection in Login Forms ------------------------------ diff --git a/security/custom_authentication_provider.rst b/security/custom_authentication_provider.rst index d0db9c91168..3199128b26a 100644 --- a/security/custom_authentication_provider.rst +++ b/security/custom_authentication_provider.rst @@ -307,7 +307,7 @@ create a class which implements class WsseFactory implements SecurityFactoryInterface { - public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint) { $providerId = 'security.authentication.provider.wsse.'.$id; $container @@ -433,13 +433,14 @@ to service ids that may not exist yet: ``App\Security\Authentication\Provider\Ws $services = $configurator->services(); $services->set(WsseProvider::class) - ->arg('$cachePool', ref('cache.app')) + ->arg('$cachePool', service('cache.app')) ; $services->set(WsseListener::class) ->args([ - ref('security.token_storage'), - ref('security.authentication.manager'), + // In versions earlier to Symfony 5.1 the service() function was called ref() + service('security.token_storage'), + service('security.authentication.manager'), ]) ; }; @@ -488,7 +489,9 @@ You are finished! You can now define parts of your app as under WSSE protection. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -560,7 +563,7 @@ in order to put it to use:: class WsseFactory implements SecurityFactoryInterface { - public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint) + public function create(ContainerBuilder $container, string $id, array $config, string $userProvider, ?string $defaultEntryPoint) { $providerId = 'security.authentication.provider.wsse.'.$id; $container @@ -604,7 +607,9 @@ set to any desirable value per firewall. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/experimental_authenticators.rst b/security/experimental_authenticators.rst new file mode 100644 index 00000000000..a8c5ee3cc55 --- /dev/null +++ b/security/experimental_authenticators.rst @@ -0,0 +1,499 @@ +Using the new Authenticator-based Security +========================================== + +.. versionadded:: 5.1 + + Authenticator-based security was introduced as an + :doc:`experimental feature ` in + Symfony 5.1. + +In Symfony 5.1, a new authentication system was introduced. This system +changes the internals of Symfony Security, to make it more extensible +and more understandable. + +Enabling the System +------------------- + +The authenticator-based system can be enabled using the +``enable_authenticator_manager`` setting: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + enable_authenticator_manager: true + # ... + + .. code-block:: xml + + + + + + + + + + + .. code-block:: php + + // config/packages/security.php + $container->loadFromExtension('security', [ + 'enable_authenticator_manager' => true, + // ... + ]); + +The new system is backwards compatible with the current authentication +system, with some exceptions that will be explained in this article: + +* :ref:`Anonymous users no longer exist ` +* :ref:`Configuring the authentication entry point is required when more than one authenticator is used ` +* :ref:`The authentication providers are refactored into Authenticators ` + +.. _authenticators-removed-anonymous: + +Adding Support for Unsecured Access (i.e. Anonymous Users) +---------------------------------------------------------- + +In Symfony, visitors that haven't yet logged in to your website were called +:ref:`anonymous users `. The new system no longer +has anonymous authentication. Instead, these sessions are now treated as +unauthenticated (i.e. there is no security token). When using +``isGranted()``, the result will always be ``false`` (i.e. denied) as this +session is handled as a user without any privileges. + +In the ``access_control`` configuration, you can use the new +``PUBLIC_ACCESS`` security attribute to whitelist some routes for +unauthenticated access (e.g. the login page): + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + enable_authenticator_manager: true + + # ... + access_control: + # allow unauthenticated users to access the login form + - { path: ^/admin/login, roles: PUBLIC_ACCESS } + + # but require authentication for all other admin routes + - { path: ^/admin, roles: ROLE_ADMIN } + + .. code-block:: xml + + + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/packages/security.php + use Symfony\Component\Security\Http\Firewall\AccessListener; + + $container->loadFromExtension('security', [ + 'enable_authenticator_manager' => true, + + // ... + 'access_control' => [ + // allow unauthenticated users to access the login form + ['path' => '^/admin/login', 'roles' => AccessListener::PUBLIC_ACCESS], + + // but require authentication for all other admin routes + ['path' => '^/admin', 'roles' => 'ROLE_ADMIN'], + ], + ]); + +.. _authenticators-required-entry-point: + +Configuring the Authentication Entry Point +------------------------------------------ + +Sometimes, one firewall has multiple ways to authenticate (e.g. both a form +login and an API token authentication). In these cases, it is now required +to configure the *authentication entry point*. The entry point is used to +generate a response when the user is not yet authenticated but tries to access +a page that requires authentication. This can be used for instance to redirect +the user to the login page. + +You can configure this using the ``entry_point`` setting: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + enable_authenticator_manager: true + + # ... + firewalls: + main: + # allow authentication using a form or HTTP basic + form_login: ~ + http_basic: ~ + + # configure the form authentication as the entry point for unauthenticated users + entry_point: form_login + + .. code-block:: xml + + + + + + + + + + + + + + + + + .. code-block:: php + + // config/packages/security.php + use Symfony\Component\Security\Http\Firewall\AccessListener; + + $container->loadFromExtension('security', [ + 'enable_authenticator_manager' => true, + + // ... + 'firewalls' => [ + 'main' => [ + // allow authentication using a form or HTTP basic + 'form_login' => null, + 'http_basic' => null, + + // configure the form authentication as the entry point for unauthenticated users + 'entry_point' => 'form_login' + ], + ], + ]); + +.. note:: + + You can also create your own authentication entry point by creating a + class that implements + :class:`Symfony\\Component\\Security\\Http\\EntryPoint\\AuthenticationEntryPointInterface`. + You can then set ``entry_point`` to the service id (e.g. + ``entry_point: App\Security\CustomEntryPoint``) + +.. _authenticators-removed-authentication-providers: + +Creating a Custom Authenticator +------------------------------- + +Security traditionally could be extended by writing +:doc:`custom authentication providers `. +The authenticator-based system dropped support for these providers and +introduced a new authenticator interface as a base for custom +authentication methods. + +.. tip:: + + :doc:`Guard authenticators ` are still + supported in the authenticator-based system. It is however recommended + to also update these when you're refactoring your application to the + new system. The new authenticator interface has many similarities with the + guard authenticator interface, making the rewrite easier. + +Authenticators should implement the +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\AuthenticatorInterface`. +You can also extend +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\AbstractAuthenticator`, +which has a default implementation for the ``createAuthenticatedToken()`` +method that fits most use-cases:: + + // src/Security/ApiKeyAuthenticator.php + namespace App\Security; + + use App\Entity\User; + use Doctrine\ORM\EntityManagerInterface; + use Symfony\Component\HttpFoundation\JsonResponse; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; + use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\Exception\AuthenticationException; + use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException; + use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; + use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator; + use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; + use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; + + class ApiKeyAuthenticator extends AbstractAuthenticator + { + private $entityManager; + + public function __construct(EntityManagerInterface $entityManager) + { + $this->entityManager = $entityManager; + } + + /** + * Called on every request to decide if this authenticator should be + * used for the request. Returning `false` will cause this authenticator + * to be skipped. + */ + public function supports(Request $request): ?bool + { + return $request->headers->has('X-AUTH-TOKEN'); + } + + public function authenticate(Request $request): PassportInterface + { + $apiToken = $request->headers->get('X-AUTH-TOKEN'); + if (null === $apiToken) { + // The token header was empty, authentication fails with HTTP Status + // Code 401 "Unauthorized" + throw new CustomUserMessageAuthenticationException('No API token provided'); + } + + $user = $this->entityManager->getRepository(User::class) + ->findOneBy(['apiToken' => $apiToken]) + ; + if (null === $user) { + throw new UsernameNotFoundException(); + } + + return new SelfValidatingPassport($user); + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response + { + // on success, let the request continue + return null; + } + + public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response + { + $data = [ + // you may want to customize or obfuscate the message first + 'message' => strtr($exception->getMessageKey(), $exception->getMessageData()) + + // or to translate this message + // $this->translator->trans($exception->getMessageKey(), $exception->getMessageData()) + ]; + + return new JsonResponse($data, Response::HTTP_UNAUTHORIZED); + } + } + +The authenticator can be enabled using the ``custom_authenticators`` setting: + +.. configuration-block:: + + .. code-block:: yaml + + # config/packages/security.yaml + security: + enable_authenticator_manager: true + + # ... + firewalls: + main: + custom_authenticators: + - App\Security\ApiKeyAuthenticator + + # don't forget to also configure the entry_point if the + # authenticator implements AuthenticatorEntryPointInterface + # entry_point: App\Security\CustomFormLoginAuthenticator + + .. code-block:: xml + + + + + + + + + + + + App\Security\ApiKeyAuthenticator + + + + + .. code-block:: php + + // config/packages/security.php + use App\Security\ApiKeyAuthenticator; + use Symfony\Component\Security\Http\Firewall\AccessListener; + + $container->loadFromExtension('security', [ + 'enable_authenticator_manager' => true, + + // ... + 'firewalls' => [ + 'main' => [ + 'custom_authenticators' => [ + ApiKeyAuthenticator::class, + ], + + // don't forget to also configure the entry_point if the + // authenticator implements AuthenticatorEntryPointInterface + // 'entry_point' => [App\Security\CustomFormLoginAuthenticator::class], + ], + ], + ]); + +The ``authenticate()`` method is the most important method of the +authenticator. Its job is to extract credentials (e.g. username & +password, or API tokens) from the ``Request`` object and transform these +into a security +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Passport`. + +.. tip:: + + If you want to customize the login form, you can also extend from the + :class:`Symfony\\Component\\Security\\Http\\Authenticator\\AbstractLoginFormAuthenticator` + class instead. + +Security Passports +~~~~~~~~~~~~~~~~~~ + +A passport is an object that contains the user that will be authenticated as +well as other pieces of information, like whether a password should be checked +or if "remember me" functionality should be enabled. + +The default +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Passport`. +requires a user object and credentials. The following credential classes +are supported by default: + + +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Credentials\\PasswordCredentials` + This requires a plaintext ``$password``, which is validated using the + :ref:`password encoder configured for the user `. + +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Credentials\\CustomCredentials` + Allows a custom closure to check credentials:: + + // ... + return new Passport($user, new CustomCredentials( + // If this function returns anything else than `true`, the credentials + // are marked as invalid. + // The $credentials parameter is equal to the next argument of this class + function ($credentials, UserInterface $user) { + return $user->getApiToken() === $credentials; + }, + + // The custom credentials + $apiToken + )); + +.. note:: + + If you don't need any credentials to be checked (e.g. a JWT token), you + can use the + :class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\SelfValidatingPassport`. + This class only requires a user and optionally `Passport Badges`_. + +Passport Badges +~~~~~~~~~~~~~~~ + +The ``Passport`` also optionally allows you to add *security badges*. +Badges attach more data to the passport (to extend security). By default, +the following badges are supported: + +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Badge\\RememberMeBadge` + When this badge is added to the passport, the authenticator indicates + remember me is supported. Whether remember me is actually used depends + on special ``remember_me`` configuration. Read + :doc:`/security/remember_me` for more information. + +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Badge\\PasswordUpgradeBadge` + This is used to automatically upgrade the password to a new hash upon + successful login. This badge requires the plaintext password and a + password upgrader (e.g. the user repository). See :doc:`/security/password_migration`. + +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Badge\\CsrfTokenBadge` + Automatically validates CSRF tokens for this authenticator during + authentication. The constructor requires a token ID (unique per form) + and CSRF token (unique per request). See :doc:`/security/csrf`. + +:class:`Symfony\\Component\\Security\\Http\\Authenticator\\Passport\\Badge\\PreAuthenticatedUserBadge` + Indicates that this user was pre-authenticated (i.e. before Symfony was + initiated). This skips the + :doc:`pre-authentication user checker `. + +For instance, if you want to add CSRF and password migration to your custom +authenticator, you would initialize the passport like this:: + + // ... + use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator; + use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; + use Symfony\Component\Security\Http\Authenticator\Passport\Badge\PasswordUpgradeBadge; + use Symfony\Component\Security\Http\Authenticator\Passport\Passport; + use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface; + + class LoginAuthenticator extends AbstractAuthenticator + { + public function authenticate(Request $request): PassportInterface + { + $password = $request->request->get('password'); + $username = $request->request->get('username'); + $csrfToken = $request->request->get('csrf_token'); + + // ... get the $user from the $username and validate no + // parameter is empty + + return new Passport($user, new PasswordCredentials($password), [ + // $this->userRepository must implement PasswordUpgraderInterface + new PasswordUpgradeBadge($password, $this->userRepository), + new CsrfTokenBadge('login', $csrfToken); + ]); + } + } diff --git a/security/expressions.rst b/security/expressions.rst index 2013d6656d7..fefee9bac17 100644 --- a/security/expressions.rst +++ b/security/expressions.rst @@ -18,7 +18,7 @@ accepts an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` object:: public function index() { $this->denyAccessUnlessGranted(new Expression( - '"ROLE_ADMIN" in roles or (not is_anonymous() and user.isSuperAdmin())' + '"ROLE_ADMIN" in role_names or (not is_anonymous() and user.isSuperAdmin())' )); // ... diff --git a/security/firewall_restriction.rst b/security/firewall_restriction.rst index 0ce0c2e3ff5..ee0950083bc 100644 --- a/security/firewall_restriction.rst +++ b/security/firewall_restriction.rst @@ -49,7 +49,9 @@ if the request path matches the configured ``pattern``. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -106,7 +108,9 @@ only initialize if the host from the request matches against the configuration. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -164,7 +168,9 @@ the provided HTTP methods. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -220,7 +226,9 @@ If the above options don't fit your needs you can configure any service implemen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/force_https.rst b/security/force_https.rst index 797446ce4b9..9492e0fece0 100644 --- a/security/force_https.rst +++ b/security/force_https.rst @@ -36,7 +36,9 @@ access control: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/form_login.rst b/security/form_login.rst index cd53d277c88..4dcd039e347 100644 --- a/security/form_login.rst +++ b/security/form_login.rst @@ -41,7 +41,9 @@ First, enable ``form_login`` under your firewall: xmlns:srv="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -279,7 +281,9 @@ security component: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -361,7 +365,9 @@ After this, you have protected your login form against CSRF attacks. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -435,7 +441,9 @@ a relative/absolute URL or a Symfony route name: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -492,7 +500,9 @@ previously requested URL and always redirect to the default page: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -580,7 +590,9 @@ parameter is included in the request, you may use the value of the xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -646,7 +658,9 @@ option to define a new target via a relative/absolute URL or a Symfony route nam xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -721,7 +735,9 @@ redirects can be customized using the ``target_path_parameter`` and xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/form_login_setup.rst b/security/form_login_setup.rst index 7e82ef333a4..c5db7ba1309 100644 --- a/security/form_login_setup.rst +++ b/security/form_login_setup.rst @@ -114,7 +114,9 @@ Edit the ``security.yaml`` file in order to declare the ``/logout`` path: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -318,7 +320,9 @@ a traditional HTML form that submits to ``/login``: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/guard_authentication.rst b/security/guard_authentication.rst index b229fe733af..d88bea0de20 100644 --- a/security/guard_authentication.rst +++ b/security/guard_authentication.rst @@ -14,6 +14,11 @@ Guard authentication can be used to: and many more. In this example, we'll build an API token authentication system, so we can learn more about Guard in detail. +.. tip:: + + A :doc:`new experimental authenticator-based system ` + was introduced in Symfony 5.1, which will eventually replace Guards in Symfony 6.0. + Step 1) Prepare your User Class ------------------------------- @@ -204,7 +209,9 @@ Finally, configure your ``firewalls`` key in ``security.yaml`` to use this authe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -298,7 +305,7 @@ Each authenticator needs the following methods: (or throw an :ref:`AuthenticationException `), authentication will fail. -**onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)** +**onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey)** This is called after successful authentication and your job is to either return a :class:`Symfony\\Component\\HttpFoundation\\Response` object that will be sent to the client or ``null`` to continue the request diff --git a/security/impersonating_user.rst b/security/impersonating_user.rst index 634052722a5..8ab25dab9a6 100644 --- a/security/impersonating_user.rst +++ b/security/impersonating_user.rst @@ -38,7 +38,9 @@ listener: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -85,22 +87,23 @@ role to the users that need it. Knowing When Impersonation Is Active ------------------------------------ -When a user is being impersonated, Symfony grants them a special role called -``ROLE_PREVIOUS_ADMIN`` (in addition to the roles the user may have). Use this -special role, for instance, to show a link to exit impersonation in a template: +You can use the special attribute ``IS_IMPERSONATOR`` to check if the +impersonation is active in this session. Use this special role, for +instance, to show a link to exit impersonation in a template: .. code-block:: html+twig - {% if is_granted('ROLE_PREVIOUS_ADMIN') %} + {% if is_granted('IS_IMPERSONATOR') %} Exit impersonation {% endif %} -Finding the Original User -------------------------- +.. versionadded:: 5.1 -.. versionadded:: 4.3 + The ``IS_IMPERSONATOR`` was introduced in Symfony 5.1. Use + ``ROLE_PREVIOUS_ADMIN`` prior to Symfony 5.1. - The ``SwitchUserToken`` class was introduced in Symfony 4.3. +Finding the Original User +------------------------- In some cases, you may need to get the object that represents the impersonator user rather than the impersonated user. When a user is impersonated the token @@ -164,7 +167,9 @@ also adjust the query parameter name via the ``parameter`` setting: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -197,7 +202,7 @@ Limiting User Switching If you need more control over user switching, you can use a security voter. First, configure ``switch_user`` to check for some new, custom attribute. This can be -anything, but *cannot* start with ``ROLE_`` (to enforce that only your voter will +anything, but *cannot* start with ``ROLE_`` (to enforce that only your voter will be called): .. configuration-block:: @@ -221,7 +226,9 @@ be called): xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/json_login_setup.rst b/security/json_login_setup.rst index 060821704e2..44ffa5d02e9 100644 --- a/security/json_login_setup.rst +++ b/security/json_login_setup.rst @@ -29,7 +29,9 @@ First, enable the JSON login under your firewall: xmlns:srv="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -177,7 +179,9 @@ The security configuration should be: xmlns:srv="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/ldap.rst b/security/ldap.rst index 5bff1ae9925..0610f867a35 100644 --- a/security/ldap.rst +++ b/security/ldap.rst @@ -34,12 +34,6 @@ This means that the following scenarios will work: * Loading user information from an LDAP server, while using another authentication strategy (token-based pre-authentication, for example). -.. deprecated:: 4.4 - - The class ``Symfony\Component\Security\Core\User\LdapUserProvider`` - has been deprecated in Symfony 4.4. Use the class - ``Symfony\Component\Ldap\Security\LdapUserProvider`` instead. - Installation ------------ @@ -165,7 +159,9 @@ use the ``ldap`` user provider. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -279,10 +275,6 @@ extra_fields **type**: ``array`` **default**: ``null`` -.. versionadded:: 4.4 - - The ``extra_fields`` option was introduced in Symfony 4.4. - Defines the custom fields to pull from the LDAP server. If any field does not exist, an ``\InvalidArgumentException`` will be thrown. @@ -389,7 +381,9 @@ Configuration example for form login xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -442,7 +436,9 @@ Configuration example for HTTP Basic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -502,7 +498,9 @@ Configuration example for form login and query_string xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -536,11 +534,6 @@ Configuration example for form login and query_string ] ]); -.. deprecated:: 4.4 - - Using the ``query_string`` config option without defining ``search_dn`` and - ``search_password`` is deprecated since Symfony 4.4. - .. _`LDAP PHP extension`: https://www.php.net/manual/en/intro.ldap.php .. _`RFC4515`: http://www.faqs.org/rfcs/rfc4515.html .. _`LDAP injection`: http://projects.webappsec.org/w/page/13246947/LDAP%20Injection diff --git a/security/multiple_guard_authenticators.rst b/security/multiple_guard_authenticators.rst index f9145e1d897..d3ef59a2494 100644 --- a/security/multiple_guard_authenticators.rst +++ b/security/multiple_guard_authenticators.rst @@ -42,7 +42,9 @@ This is how your security configuration can look in action: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -120,7 +122,9 @@ the solution is to split the configuration into two separate firewalls: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/named_encoders.rst b/security/named_encoders.rst index aad4d740fb1..cacf302d5d9 100644 --- a/security/named_encoders.rst +++ b/security/named_encoders.rst @@ -27,7 +27,9 @@ to apply to all instances of a specific class: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd" + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd" > @@ -81,7 +83,9 @@ be done with named encoders: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd" + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd" > @@ -158,7 +162,9 @@ you must register a service for it in order to use it as a named encoder: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd" + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd" > diff --git a/security/password_migration.rst b/security/password_migration.rst index 9553d46de5a..c30acf70a9e 100644 --- a/security/password_migration.rst +++ b/security/password_migration.rst @@ -4,10 +4,6 @@ How to Migrate a Password Hash ============================== -.. versionadded:: 4.4 - - Password migration was introduced in Symfony 4.4. - In order to protect passwords, it is recommended to store them using the latest hash algorithms. This means that if a better hash algorithm is supported on your system, the user's password should be *rehashed* using the newer algorithm and @@ -54,6 +50,8 @@ on the new encoder to point to the old, legacy encoder(s): xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://symfony.com/schema/dic/security" xsi:schemaLocation="http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd + http://symfony.com/schema/dic/security https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/remember_me.rst b/security/remember_me.rst index 94a1f7a3587..446f821a6ef 100644 --- a/security/remember_me.rst +++ b/security/remember_me.rst @@ -38,7 +38,9 @@ the session lasts using a cookie with the ``remember_me`` firewall option: xmlns:srv="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -132,6 +134,14 @@ The ``remember_me`` firewall defines the following configuration options: Defines the service id of a token provider to use. If you want to store tokens in the database, see :ref:`remember-me-token-in-database`. +``service`` (default value: ``null``) + Defines the ID of the service used to handle the Remember Me feature. It's + useful if you need to overwrite the current behavior entirely. + + .. versionadded:: 5.1 + + The ``service`` option was introduced in Symfony 5.1. + Forcing the User to Opt-Out of the Remember Me Feature ------------------------------------------------------ @@ -168,7 +178,8 @@ visiting the site. In some cases, however, you may want to force the user to actually re-authenticate before accessing certain resources. For example, you might not allow "remember me" -users to change their password. You can do this by leveraging a few special "roles":: +users to change their password. You can do this by leveraging a few special +"attributes":: // src/Controller/AccountController.php // ... @@ -192,6 +203,15 @@ users to change their password. You can do this by leveraging a few special "rol // ... } +.. tip:: + + There is also a ``IS_REMEMBERED`` attribute that grants *only* when the + user is authenticated via the remember me mechanism. + +.. versionadded:: 5.1 + + The ``IS_REMEMBERED`` attribute was introduced in Symfony 5.1. + .. _remember-me-token-in-database: Storing Remember Me Tokens in the Database @@ -303,7 +323,9 @@ service you just created: xmlns:srv="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/user_checkers.rst b/security/user_checkers.rst index 83e1dc0554b..9ded2a00449 100644 --- a/security/user_checkers.rst +++ b/security/user_checkers.rst @@ -15,15 +15,17 @@ User checkers are classes that must implement the :class:`Symfony\\Component\\Security\\Core\\User\\UserCheckerInterface`. This interface defines two methods called ``checkPreAuth()`` and ``checkPostAuth()`` to perform checks before and after user authentication. If one or more conditions -are not met, an exception should be thrown which extends the -:class:`Symfony\\Component\\Security\\Core\\Exception\\AccountStatusException` -or :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationException`:: +are not met, throw an exception which extends the +:class:`Symfony\\Component\\Security\\Core\\Exception\\AccountStatusException` class. +Consider using :class:`Symfony\\Component\\Security\\Core\\Exception\\CustomUserMessageAccountStatusException`, +which extends ``AccountStatusException`` and allows to customize the error message +displayed to the user:: namespace App\Security; - use App\Exception\AccountDeletedException; use App\Security\User as AppUser; use Symfony\Component\Security\Core\Exception\AccountExpiredException; + use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; @@ -35,9 +37,9 @@ or :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationExceptio return; } - // user is deleted, show a generic Account Not Found message. if ($user->isDeleted()) { - throw new AccountDeletedException(); + // the message passed to this exception is meant to be displayed to the user + throw new CustomUserMessageAccountStatusException('Your user account no longer exists.'); } } @@ -54,6 +56,10 @@ or :class:`Symfony\\Component\\Security\\Core\\Exception\\AuthenticationExceptio } } +.. versionadded:: 5.1 + + The ``CustomUserMessageAccountStatusException`` class was introduced in Symfony 5.1. + Enabling the Custom User Checker -------------------------------- @@ -86,7 +92,9 @@ is the service id of your user checker: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> diff --git a/security/user_provider.rst b/security/user_provider.rst index 04a587b2529..b64f689ac48 100644 --- a/security/user_provider.rst +++ b/security/user_provider.rst @@ -173,7 +173,9 @@ To finish this, remove the ``property`` key from the user provider in xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd"> + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd"> @@ -238,7 +240,9 @@ users will encode their passwords: xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:srv="http://symfony.com/schema/dic/services" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd" + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd" > diff --git a/security/voters.rst b/security/voters.rst index 7368fa027ba..7b68e9468be 100644 --- a/security/voters.rst +++ b/security/voters.rst @@ -26,7 +26,7 @@ uses the authorization checker), or by Ultimately, Symfony takes the responses from all voters and makes the final decision (to allow or deny access to the resource) according to the strategy defined -in the application, which can be: affirmative, consensus or unanimous. +in the application, which can be: affirmative, consensus, unanimous or priority. For more information take a look at :ref:`the section about access decision managers `. @@ -44,8 +44,8 @@ which makes creating a voter even easier:: abstract class Voter implements VoterInterface { - abstract protected function supports($attribute, $subject); - abstract protected function voteOnAttribute($attribute, $subject, TokenInterface $token); + abstract protected function supports(string $attribute, $subject); + abstract protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token); } .. _how-to-use-the-voter-in-a-controller: @@ -118,7 +118,7 @@ would look like this:: const VIEW = 'view'; const EDIT = 'edit'; - protected function supports($attribute, $subject) + protected function supports(string $attribute, $subject) { // if the attribute isn't one we support, return false if (!in_array($attribute, [self::VIEW, self::EDIT])) { @@ -133,7 +133,7 @@ would look like this:: return true; } - protected function voteOnAttribute($attribute, $subject, TokenInterface $token) + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token) { $user = $token->getUser(); @@ -178,7 +178,7 @@ That's it! The voter is done! Next, :ref:`configure it security = $security; } - protected function voteOnAttribute($attribute, $subject, TokenInterface $token) + protected function voteOnAttribute(string $attribute, $subject, TokenInterface $token) { // ... @@ -271,7 +271,15 @@ There are three strategies available: ``unanimous`` This only grants access if there is no voter denying access. If all voters abstained from voting, the decision is based on the ``allow_if_all_abstain`` - config option (which defaults to ``false``). + config option (which defaults to ``false``); + +``priority`` + This grants or denies access by the first voter that does not abstain, + based on their service priority; + + .. versionadded:: 5.1 + + The ``priority`` version strategy was introduced in Symfony 5.1. In the above scenario, both voters should grant access in order to grant access to the user to read the post. In this case, the default strategy is no longer @@ -296,7 +304,9 @@ security configuration: xmlns:srv="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services - https://symfony.com/schema/dic/services/services-1.0.xsd" + https://symfony.com/schema/dic/services/services-1.0.xsd + http://symfony.com/schema/dic/security + https://symfony.com/schema/dic/security/security-1.0.xsd" > diff --git a/serializer.rst b/serializer.rst index 7209bd4cb8b..b8c1e5ff18c 100644 --- a/serializer.rst +++ b/serializer.rst @@ -74,18 +74,6 @@ As well as the following normalizers: * :class:`Symfony\\Component\\Serializer\\Normalizer\\ConstraintViolationListNormalizer` for objects implementing the :class:`Symfony\\Component\\Validator\\ConstraintViolationListInterface` interface * :class:`Symfony\\Component\\Serializer\\Normalizer\\ProblemNormalizer` for :class:`Symfony\\Component\\ErrorHandler\\Exception\\FlattenException` objects -.. versionadded:: 4.1 - - The ``ConstraintViolationListNormalizer`` was introduced in Symfony 4.1. - -.. versionadded:: 4.3 - - The ``DateTimeZoneNormalizer`` was introduced in Symfony 4.3. - -.. versionadded:: 4.4 - - The ``ProblemNormalizer`` was introduced in Symfony 4.4. - Custom normalizers and/or encoders can also be loaded by tagging them as :ref:`serializer.normalizer ` and :ref:`serializer.encoder `. It's also diff --git a/serializer/custom_normalizer.rst b/serializer/custom_normalizer.rst index 9f2e50fdcf1..9893db60488 100644 --- a/serializer/custom_normalizer.rst +++ b/serializer/custom_normalizer.rst @@ -35,7 +35,7 @@ to customize the normalized data. To do that, leverage the ``ObjectNormalizer``: $this->normalizer = $normalizer; } - public function normalize($topic, $format = null, array $context = []) + public function normalize($topic, string $format = null, array $context = []) { $data = $this->normalizer->normalize($topic, $format, $context); @@ -47,7 +47,7 @@ to customize the normalized data. To do that, leverage the ``ObjectNormalizer``: return $data; } - public function supportsNormalization($data, $format = null, array $context = []) + public function supportsNormalization($data, string $format = null, array $context = []) { return $data instanceof Topic; } diff --git a/service_container.rst b/service_container.rst index 7c4ae87b117..b08b8680237 100644 --- a/service_container.rst +++ b/service_container.rst @@ -159,7 +159,7 @@ 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,Entity,Migrations,Tests,Kernel.php}' + exclude: '../src/{DependencyInjection,Entity,Tests,Kernel.php}' # ... @@ -178,7 +178,7 @@ each time you ask for it. - + @@ -201,7 +201,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,Migrations,Tests,Kernel.php}'); + ->exclude('../src/{DependencyInjection,Entity,Tests,Kernel.php}'); }; .. tip:: @@ -408,7 +408,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume # same as before App\: resource: '../src/*' - exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' + exclude: '../src/{DependencyInjection,Entity,Tests,Kernel.php}' # explicitly configure the service App\Updates\SiteUpdateManager: @@ -431,7 +431,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume @@ -453,7 +453,7 @@ pass here. No problem! In your configuration, you can explicitly set this argume // same as before $services->load('App\\', '../src/*') - ->exclude('../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'); + ->exclude('../src/{DependencyInjection,Entity,Tests,Kernel.php}'); $services->set(SiteUpdateManager::class) ->arg('$adminEmail', 'manager@example.com') @@ -526,7 +526,8 @@ parameter and in PHP config use the ``Reference`` class: $services = $configurator->services(); $services->set(MessageGenerator::class) - ->args([ref('logger')]) + // In versions earlier to Symfony 5.1 the service() function was called ref() + ->args([service('logger')]) ; }; @@ -633,7 +634,7 @@ But, you can control this and pass in a different logger: // explicitly configure the service $services->set(SiteUpdateManager::class) - ->arg('$logger', ref('monolog.logger.request')) + ->arg('$logger', service('monolog.logger.request')) ; }; @@ -738,25 +739,21 @@ You can also use the ``bind`` keyword to bind specific arguments by name or type // pass this service to any $requestLogger argument for any // service that's defined in this file - ->bind('$requestLogger', ref('monolog.logger.request')) + ->bind('$requestLogger', service('monolog.logger.request')) // pass this service for any LoggerInterface type-hint for any // service that's defined in this file - ->bind(LoggerInterface::class, ref('monolog.logger.request')) + ->bind(LoggerInterface::class, service('monolog.logger.request')) // optionally you can define both the name and type of the argument to match ->bind('string $adminEmail', 'manager@example.com') - ->bind(LoggerInterface::class.' $requestLogger', ref('monolog.logger.request')) + ->bind(LoggerInterface::class.' $requestLogger', service('monolog.logger.request')) ->bind('iterable $rules', tagged_iterator('app.foo.rule')) ; // ... }; -.. versionadded:: 4.4 - - The feature to bind tagged services was introduced in Symfony 4.4. - By putting the ``bind`` key under ``_defaults``, you can specify the value of *any* argument for *any* service defined in this file! You can bind arguments by name (e.g. ``$adminEmail``), by type (e.g. ``Psr\Log\LoggerInterface``) or both @@ -801,10 +798,6 @@ constructor arguments without any configuration. Linting Service Definitions --------------------------- -.. versionadded:: 4.4 - - The ``lint:container`` command was introduced in Symfony 4.4. - The ``lint:container`` command checks that the arguments injected into services match their type declarations. It's useful to run it before deploying your application to production (e.g. in your continuous integration server): @@ -825,47 +818,16 @@ loss, enable the compiler pass in your application. Public Versus Private Services ------------------------------ -From Symfony 4.0, every service defined is private by default. - -What does this mean? When a service **is** public, you can access it directly -from the container object, which can also be injected thanks to autowiring. -This is mostly useful when you want to fetch services lazily:: - - namespace App\Generator; +Every service defined is private by default. When a service is private, you +cannot access it directly from the container using ``$container->get()``. As a +best practice, you should only create *private* services and you should fetch +services using dependency injection instead of using ``$container->get()``. - use Psr\Container\ContainerInterface; +If you need to fetch services lazily, instead of using public services you +should consider using a :ref:`service locator `. - class MessageGenerator - { - private $container; - - public function __construct(ContainerInterface $container) - { - $this->container = $container; - } - - public function generate(string $message, string $template = null, array $context = []): string - { - if ($template && $this->container->has('twig')) { - // there IS a public "twig" service in the container - $twig = $this->container->get('twig'); - - return $twig->render($template, $context + ['message' => $message]); - } - - // if no template is passed, the "twig" service will not be loaded - - // ... - } - -As a best practice, you should only create *private* services. This allows for -safe container optimizations, e.g. removing unused services. You should not use -``$container->get()`` to fetch public services, as it will make it harder to -make those services private later. Instead consider -:ref:`injecting services ` or using -:doc:`Service Subscribers or Locators `. - -But, if you *do* need to make a service public, override the ``public`` setting: +But, if you *do* need to make a service public, override the ``public`` +setting: .. configuration-block:: @@ -912,10 +874,10 @@ But, if you *do* need to make a service public, override the ``public`` setting: ; }; -.. note:: +.. deprecated:: 5.1 - Instead of injecting the container you should consider using a - :ref:`service locator ` instead. + As of Symfony 5.1, it is no longer possible to autowire the service + container by type-hinting ``Psr\Container\ContainerInterface``. .. _service-psr4-loader: @@ -937,7 +899,7 @@ key. For example, the default Symfony configuration contains this: # this creates a service per class whose id is the fully-qualified class name App\: resource: '../src/*' - exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}' + exclude: '../src/{DependencyInjection,Entity,Tests,Kernel.php}' .. code-block:: xml @@ -951,7 +913,7 @@ key. For example, the default Symfony configuration contains this: - + @@ -966,7 +928,7 @@ key. For example, the default Symfony configuration contains this: // 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,Migrations,Tests,Kernel.php}'); + ->exclude('../src/{DependencyInjection,Entity,Tests,Kernel.php}'); }; .. tip:: @@ -1152,16 +1114,16 @@ admin email. In this case, each needs to have a unique service id: ->autowire(false) // manually wire all arguments ->args([ - ref(MessageGenerator::class), - ref('mailer'), + service(MessageGenerator::class), + service('mailer'), 'superadmin@example.com', ]); $services->set('site_update_manager.normal_users', SiteUpdateManager::class) ->autowire(false) ->args([ - ref(MessageGenerator::class), - ref('mailer'), + service(MessageGenerator::class), + service('mailer'), 'contact@example.com', ]); diff --git a/service_container/alias_private.rst b/service_container/alias_private.rst index e9088ad4cfc..97255503c12 100644 --- a/service_container/alias_private.rst +++ b/service_container/alias_private.rst @@ -160,6 +160,12 @@ This means that when using the container directly, you can access the Deprecating Service Aliases ~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. versionadded:: 5.1 + + The ``package`` and ``version`` options were introduced in Symfony 5.1. + Prior to 5.1, you had to use ``deprecated: true`` or + ``deprecated: 'Custom message'``. + If you decide to deprecate the use of a service alias (because it is outdated or you decided not to maintain it anymore), you can deprecate its definition: @@ -170,11 +176,17 @@ or you decided not to maintain it anymore), you can deprecate its definition: app.mailer: alias: '@App\Mail\PhpMailer' - # this will display a generic deprecation message... - deprecated: true + # this outputs the following generic deprecation message: + # Since acme/package 1.2: The "app.mailer" service alias is deprecated. You should stop using it, as it will be removed in the future + deprecated: + package: 'acme/package' + version: '1.2' - # ...but you can also define a custom deprecation message - deprecated: 'The "%alias_id%" alias is deprecated. Do not use it anymore.' + # you can also define a custom deprecation message (%alias_id% placeholder is available) + deprecated: + package: 'acme/package' + version: '1.2' + message: 'The "%alias_id%" alias is deprecated. Do not use it anymore.' .. code-block:: xml @@ -185,11 +197,14 @@ or you decided not to maintain it anymore), you can deprecate its definition: - - - - - The "%alias_id%" service alias is deprecated. Don't use it anymore. + + + + + + The "%alias_id%" service alias is deprecated. Don't use it anymore. + @@ -199,12 +214,14 @@ or you decided not to maintain it anymore), you can deprecate its definition: $container ->setAlias('app.mailer', 'App\Mail\PhpMailer') - // this will display a generic deprecation message... - ->setDeprecated(true) + // this outputs the following generic deprecation message: + // Since acme/package 1.2: The "app.mailer" service alias is deprecated. You should stop using it, as it will be removed in the future + ->setDeprecated('acme/package', '1.2') - // ...but you can also define a custom deprecation message + // you can also define a custom deprecation message (%alias_id% placeholder is available) ->setDeprecated( - true, + 'acme/package', + '1.2', 'The "%alias_id%" service alias is deprecated. Don\'t use it anymore.' ) ; @@ -216,10 +233,6 @@ The message is actually a message template, which replaces occurrences of the ``%alias_id%`` placeholder by the service alias id. You **must** have at least one occurrence of the ``%alias_id%`` placeholder in your template. -.. versionadded:: 4.3 - - The ``deprecated`` option for service aliases was introduced in Symfony 4.3. - Anonymous Services ------------------ @@ -271,7 +284,8 @@ The following example shows how to inject an anonymous service into another serv $services = $configurator->services(); $services->set(Foo::class) - ->args([inline(AnonymousBar::class)]) + // In versions earlier to Symfony 5.1 the inline_service() function was called inline() + ->args([inline_service(AnonymousBar::class)]) }; .. note:: @@ -279,7 +293,7 @@ The following example shows how to inject an anonymous service into another serv Anonymous services do *NOT* inherit the definitions provided from the defaults defined in the configuration. So you'll need to explicitly mark service as autowired or autoconfigured when doing an anonymous service - e.g.: ``inline(Foo::class)->autowire()->autoconfigure()``. + e.g.: ``inline_service(Foo::class)->autowire()->autoconfigure()``. Using an anonymous service as a factory looks like this: @@ -322,7 +336,7 @@ Using an anonymous service as a factory looks like this: $services = $configurator->services(); $services->set(Foo::class) - ->factory([inline(AnonymousBar::class), 'constructFoo']) + ->factory([inline_service(AnonymousBar::class), 'constructFoo']) }; Deprecating Services diff --git a/service_container/autowiring.rst b/service_container/autowiring.rst index bf17f0db369..f3fd75d8b54 100644 --- a/service_container/autowiring.rst +++ b/service_container/autowiring.rst @@ -262,11 +262,6 @@ class is type-hinted. adds an alias: ``Psr\Log\LoggerInterface`` that points to the ``logger`` service. This is why arguments type-hinted with ``Psr\Log\LoggerInterface`` can be autowired. -.. versionadded:: 4.2 - - Since Monolog Bundle 3.5 each channel bind into container by type-hinted alias. - More info in the part about :ref:`how to autowire monolog channels `. - .. _autowiring-interface-alias: Working with Interfaces @@ -512,7 +507,7 @@ the injection:: // If you wanted to choose the non-default service and do not // want to use a named autowiring alias, wire it manually: - // ->arg('$transformer', ref(UppercaseTransformer::class)) + // ->arg('$transformer', service(UppercaseTransformer::class)) // ... }; @@ -523,10 +518,6 @@ If the argument is named ``$shoutyTransformer``, But, you can also manually wire any *other* service by specifying the argument under the arguments key. -.. versionadded:: 4.2 - - Named autowiring aliases have been introduced in Symfony 4.2. - Fixing Non-Autowireable Arguments --------------------------------- diff --git a/service_container/calls.rst b/service_container/calls.rst index 46f25456400..b929c998abc 100644 --- a/service_container/calls.rst +++ b/service_container/calls.rst @@ -72,14 +72,10 @@ To configure the container to call the ``setLogger`` method, use the ``calls`` k // ... $services->set(MessageGenerator::class) - ->call('setLogger', [ref('logger')]); + // In versions earlier to Symfony 5.1 the service() function was called ref() + ->call('setLogger', [service('logger')]); }; - -.. versionadded:: 4.3 - - The ``immutable-setter`` injection was introduced in Symfony 4.3. - To provide immutable services, some classes implement immutable setters. Such setters return a new instance of the configured class instead of mutating the object they were called on:: diff --git a/service_container/configurators.rst b/service_container/configurators.rst index 01029d96181..bf196b59575 100644 --- a/service_container/configurators.rst +++ b/service_container/configurators.rst @@ -179,19 +179,16 @@ all the classes are already loaded as services. All you need to do is specify th $services->load('App\\', '../src/*'); // override the services to set the configurator + // In versions earlier to Symfony 5.1 the service() function was called ref() $services->set(NewsletterManager::class) - ->configurator(ref(EmailConfigurator::class), 'configure'); + ->configurator(service(EmailConfigurator::class), 'configure'); $services->set(GreetingCardManager::class) - ->configurator(ref(EmailConfigurator::class), 'configure'); + ->configurator(service(EmailConfigurator::class), 'configure'); }; .. _configurators-invokable: -.. versionadded:: 4.3 - - Invokable configurators for services were introduced in Symfony 4.3. - Services can be configured via invokable configurators (replacing the ``configure()`` method with ``__invoke()``) by omitting the method name, just as routes can reference :ref:`invokable controllers `. @@ -254,10 +251,10 @@ routes can reference :ref:`invokable controllers `. // override the services to set the configurator $services->set(NewsletterManager::class) - ->configurator(ref(EmailConfigurator::class)); + ->configurator(service(EmailConfigurator::class)); $services->set(GreetingCardManager::class) - ->configurator(ref(EmailConfigurator::class)); + ->configurator(service(EmailConfigurator::class)); }; That's it! When requesting the ``App\Mail\NewsletterManager`` or diff --git a/service_container/factories.rst b/service_container/factories.rst index 3da70636b22..e42026db5c7 100644 --- a/service_container/factories.rst +++ b/service_container/factories.rst @@ -158,7 +158,8 @@ Configuration of the service container then looks like this: // second, use the factory service as the first argument of the 'factory' // method and the factory method as the second argument $services->set(NewsletterManager::class) - ->factory([ref(NewsletterManagerFactory::class), 'createNewsletterManager']); + // In versions earlier to Symfony 5.1 the service() function was called ref() + ->factory([service(NewsletterManagerFactory::class), 'createNewsletterManager']); }; .. _factories-invokable: @@ -181,10 +182,6 @@ factory service can be used as a callback:: } } -.. versionadded:: 4.3 - - Invokable factories for services were introduced in Symfony 4.3. - Services can be created and configured via invokable factories by omitting the method name, just as routes can reference :ref:`invokable controllers `. @@ -232,7 +229,7 @@ method name, just as routes can reference $services = $configurator->services(); $services->set(NewsletterManager::class) - ->factory(ref(NewsletterManagerFactory::class)); + ->factory(service(NewsletterManagerFactory::class)); }; .. _factories-passing-arguments-factory-method: @@ -292,8 +289,8 @@ previous examples takes the ``templating`` service as an argument: $services = $configurator->services(); $services->set(NewsletterManager::class) - ->factory([ref(NewsletterManagerFactory::class), 'createNewsletterManager']) - ->args([ref('templating')]) + ->factory([service(NewsletterManagerFactory::class), 'createNewsletterManager']) + ->args([service('templating')]) ; }; diff --git a/service_container/injection_types.rst b/service_container/injection_types.rst index 77098c0b9fb..145ede76475 100644 --- a/service_container/injection_types.rst +++ b/service_container/injection_types.rst @@ -77,10 +77,10 @@ service container configuration: $services = $configurator->services(); $services->set(NewsletterManager::class) - ->args(ref('mailer')); + // In versions earlier to Symfony 5.1 the service() function was called ref() + ->args(service('mailer')); }; - .. tip:: Type hinting the injected object means that you can be sure that a suitable @@ -108,10 +108,6 @@ then extending it and overriding the constructor becomes problematic. Immutable-setter Injection -------------------------- -.. versionadded:: 4.3 - - The ``immutable-setter`` injection was introduced in Symfony 4.3. - Another possible injection is to use a method which returns a separate instance by cloning the original service, this approach allows you to make a service immutable:: @@ -277,7 +273,7 @@ that accepts the dependency:: $services = $configurator->services(); $services->set(NewsletterManager::class) - ->call('setMailer', [ref('mailer')]); + ->call('setMailer', [service('mailer')]); }; This time the advantages are: @@ -357,7 +353,7 @@ Another possibility is setting public fields of the class directly:: $services = $configurator->services(); $services->set('app.newsletter_manager', NewsletterManager::class) - ->property('mailer', ref('mailer')); + ->property('mailer', service('mailer')); }; There are mainly only disadvantages to using property injection, it is similar diff --git a/service_container/optional_dependencies.rst b/service_container/optional_dependencies.rst index 3593934c5c3..5071e563932 100644 --- a/service_container/optional_dependencies.rst +++ b/service_container/optional_dependencies.rst @@ -42,10 +42,10 @@ if the service does not exist: $services = $configurator->services(); $services->set(NewsletterManager::class) - ->args([ref('logger')->nullOnInvalid()]); + // In versions earlier to Symfony 5.1 the service() function was called ref() + ->args([service('logger')->nullOnInvalid()]); }; - .. note:: The "null" strategy is not currently supported by the YAML driver. @@ -99,7 +99,7 @@ call if the service exists and remove the method call if it does not: $services = $configurator->services(); $services->set(NewsletterManager::class) - ->call('setLogger', [ref('logger')->ignoreOnInvalid()]) + ->call('setLogger', [service('logger')->ignoreOnInvalid()]) ; }; diff --git a/service_container/parent_services.rst b/service_container/parent_services.rst index ee04e377238..09a152b64b5 100644 --- a/service_container/parent_services.rst +++ b/service_container/parent_services.rst @@ -128,8 +128,9 @@ duplicated service definitions: $services->set(BaseDoctrineRepository::class) ->abstract() - ->args([ref('doctrine.orm.entity_manager')]) - ->call('setLogger', [ref('logger')]) + ->args([service('doctrine.orm.entity_manager')]) + // In versions earlier to Symfony 5.1 the service() function was called ref() + ->call('setLogger', [service('logger')]) ; $services->set(DoctrineUserRepository::class) @@ -150,12 +151,6 @@ be called when ``App\Repository\DoctrineUserRepository`` is instantiated. All attributes on the parent service are shared with the child **except** for ``shared``, ``abstract`` and ``tags``. These are *not* inherited from the parent. -.. note:: - - If you have a ``_defaults`` section in your file, all child services are required - to explicitly override those values to avoid ambiguity. You will see a clear - error message about this. - .. tip:: In the examples shown, the classes sharing the same configuration also @@ -253,13 +248,13 @@ the child class: // appends the '@app.username_checker' argument to the parent // argument list - ->args([ref('app.username_checker')]) + ->args([service('app.username_checker')]) ; $services->set(DoctrinePostRepository::class) ->parent(BaseDoctrineRepository::class) # overrides the first argument - ->arg(0, ref('doctrine.custom_entity_manager')) + ->arg(0, service('doctrine.custom_entity_manager')) ; }; diff --git a/service_container/service_decoration.rst b/service_container/service_decoration.rst index 56a1ec65789..4b8b400cdf4 100644 --- a/service_container/service_decoration.rst +++ b/service_container/service_decoration.rst @@ -58,7 +58,7 @@ Most of the time, that's exactly what you want to do. But sometimes, you might want to decorate the old one instead (i.e. apply the `Decorator pattern`_). In this case, the old service should be kept around to be able to reference it in the new one. This configuration replaces ``App\Mailer`` with a new one, -but keeps a reference of the old one as ``App\DecoratingMailer.inner``: +but keeps a reference of the old one as ``.inner``: .. configuration-block:: @@ -70,7 +70,7 @@ but keeps a reference of the old one as ``App\DecoratingMailer.inner``: App\DecoratingMailer: # overrides the App\Mailer service - # but that service is still available as App\DecoratingMailer.inner + # but that service is still available as ".inner" decorates: App\Mailer .. code-block:: xml @@ -84,6 +84,8 @@ but keeps a reference of the old one as ``App\DecoratingMailer.inner``: + @@ -106,7 +108,7 @@ but keeps a reference of the old one as ``App\DecoratingMailer.inner``: $services->set(DecoratingMailer::class) // overrides the App\Mailer service - // but that service is still available as App\DecoratingMailer.inner + // but that service is still available as ".inner" ->decorate(Mailer::class); }; @@ -119,7 +121,7 @@ decorating service has one argument type-hinted with the decorated service class If you are not using autowiring or the decorating service has more than one constructor argument type-hinted with the decorated service class, you must inject the decorated service explicitly (the ID of the decorated service is -automatically changed to ``decorating_service_id + '.inner'``): +automatically changed to ``'.inner'``): .. configuration-block:: @@ -132,7 +134,7 @@ automatically changed to ``decorating_service_id + '.inner'``): App\DecoratingMailer: decorates: App\Mailer # pass the old service as an argument - arguments: ['@App\DecoratingMailer.inner'] + arguments: ['@.inner'] .. code-block:: xml @@ -148,7 +150,7 @@ automatically changed to ``decorating_service_id + '.inner'``): - + @@ -170,9 +172,14 @@ automatically changed to ``decorating_service_id + '.inner'``): $services->set(DecoratingMailer::class) ->decorate(Mailer::class) // pass the old service as an argument - ->args([ref(DecoratingMailer::class.'.inner')]); + // In versions earlier to Symfony 5.1 the service() function was called ref() + ->args([service('.inner')]); }; +.. versionadded:: 5.1 + + The special ``.inner`` value was introduced in Symfony 5.1. In previous + versions you needed to use: ``decorating_service_id + '.inner'``. .. tip:: @@ -236,7 +243,7 @@ automatically changed to ``decorating_service_id + '.inner'``): $services->set(DecoratingMailer::class) ->decorate(Mailer::class, DecoratingMailer::class.'.wooz') - ->args([ref(DecoratingMailer::class.'.wooz')]); + ->args([service(DecoratingMailer::class.'.wooz')]); }; Decoration Priority @@ -256,12 +263,12 @@ the ``decoration_priority`` option. Its value is an integer that defaults to Bar: decorates: Foo decoration_priority: 5 - arguments: ['@Bar.inner'] + arguments: ['@.inner'] Baz: decorates: Foo decoration_priority: 1 - arguments: ['@Baz.inner'] + arguments: ['@.inner'] .. code-block:: xml @@ -276,11 +283,11 @@ the ``decoration_priority`` option. Its value is an integer that defaults to - + - + @@ -297,11 +304,11 @@ the ``decoration_priority`` option. Its value is an integer that defaults to $services->set(Bar::class) ->decorate(Foo::class, null, 5) - ->args([ref(Bar::class.'.inner')]); + ->args([service('.inner')]); $services->set(Baz::class) ->decorate(Foo::class, null, 1) - ->args([ref(Baz::class.'.inner')]); + ->args([service('.inner')]); }; @@ -312,11 +319,6 @@ The generated code will be the following:: Control the Behavior When the Decorated Service Does Not Exist -------------------------------------------------------------- -.. versionadded:: 4.4 - - The ``decoration_on_invalid`` option has been introduced in Symfony 4.4. - In previous versions, a ``ServiceNotFoundException`` was always thrown. - When you decorate a service that doesn't exist, the ``decoration_on_invalid`` option allows you to choose the behavior to adopt. @@ -336,7 +338,7 @@ Three different behaviors are available: Bar: decorates: Foo decoration_on_invalid: ignore - arguments: ['@Bar.inner'] + arguments: ['@.inner'] .. code-block:: xml @@ -351,7 +353,7 @@ Three different behaviors are available: - + @@ -370,7 +372,7 @@ Three different behaviors are available: $services->set(Bar::class) ->decorate(Foo::class, null, 0, ContainerInterface::IGNORE_ON_INVALID_REFERENCE) - ->args([ref(Bar::class.'.inner')]) + ->args([service('.inner')]) ; }; diff --git a/service_container/service_subscribers_locators.rst b/service_container/service_subscribers_locators.rst index b4074f53a7f..a7714a99bb3 100644 --- a/service_container/service_subscribers_locators.rst +++ b/service_container/service_subscribers_locators.rst @@ -65,7 +65,7 @@ through a **Service Locator**, a separate lazy-loaded container. Defining a Service Subscriber ----------------------------- -First, turn ``CommandBus`` into an implementation of :class:`Symfony\\Component\\DependencyInjection\\ServiceSubscriberInterface`. +First, turn ``CommandBus`` into an implementation of :class:`Symfony\\Contracts\\Service\\ServiceSubscriberInterface`. Use its ``getSubscribedServices()`` method to include as many services as needed in the service subscriber and change the type hint of the container to a PSR-11 ``ContainerInterface``:: @@ -311,9 +311,10 @@ service definition to pass a collection of services to the service locator: $services = $configurator->services(); $services->set('app.command_handler_locator', ServiceLocator::class) + // In versions earlier to Symfony 5.1 the service() function was called ref() ->args([[ - 'App\FooCommand' => ref('app.command_handler.foo'), - 'App\BarCommand' => ref('app.command_handler.bar'), + 'App\FooCommand' => service('app.command_handler.foo'), + 'App\BarCommand' => service('app.command_handler.bar'), ]]) // if you are not using the default service autoconfiguration, // add the following tag to the service definition: @@ -323,22 +324,11 @@ service definition to pass a collection of services to the service locator: // if the element has no key, the ID of the original service is used $services->set('app.another_command_handler_locator', ServiceLocator::class) ->args([[ - ref('app.command_handler.baz'), + service('app.command_handler.baz'), ]]) ; }; -.. versionadded:: 4.1 - - The service locator autoconfiguration was introduced in Symfony 4.1. In - previous Symfony versions you always needed to add the - ``container.service_locator`` tag explicitly. - -.. versionadded:: 4.2 - - The ability to add services without specifying their id was introduced in - Symfony 4.2. - .. note:: The services defined in the service locator argument must include keys, @@ -383,7 +373,7 @@ Now you can use the service locator by injecting it in any other service: $services = $configurator->services(); $services->set(CommandBus::class) - ->args([ref('app.command_handler_locator')]); + ->args([service('app.command_handler_locator')]); }; In :doc:`compiler passes ` it's recommended diff --git a/session.rst b/session.rst index 8d465516a58..47e8cc3d269 100644 --- a/session.rst +++ b/session.rst @@ -181,7 +181,7 @@ the default ``AttributeBag`` by the ``NamespacedAttributeBag``: session: public: true class: Symfony\Component\HttpFoundation\Session\Session - arguments: ['@session.storage', '@session.namespacedattributebag', '@session.flash_bag'] + arguments: ['@session.storage', '@session.namespacedattributebag'] session.namespacedattributebag: class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag diff --git a/setup.rst b/setup.rst index 011e2302366..2039b15bac2 100644 --- a/setup.rst +++ b/setup.rst @@ -17,7 +17,7 @@ Technical Requirements Before creating your first Symfony application you must: -* Install PHP 7.1 or higher and these PHP extensions (which are installed and +* Install PHP 7.2.5 or higher and these PHP extensions (which are installed and enabled by default in most PHP 7 installations): `Ctype`_, `iconv`_, `JSON`_, `PCRE`_, `Session`_, `SimpleXML`_, and `Tokenizer`_; * `Install Composer`_, which is used to install PHP packages. @@ -50,10 +50,10 @@ application: .. code-block:: terminal # run this if you are building a traditional web application - $ symfony new my_project_name --version=4.4 --full + $ symfony new my_project_name --full # run this if you are building a microservice, console application or API - $ symfony new my_project_name --version=4.4 + $ symfony new my_project_name The only difference between these two commands is the number of packages installed by default. The ``--full`` option installs all the packages that you @@ -65,10 +65,10 @@ Symfony application using Composer: .. code-block:: terminal # run this if you are building a traditional web application - $ composer create-project symfony/website-skeleton:"^4.4" my_project_name + $ composer create-project symfony/website-skeleton my_project_name # run this if you are building a microservice, console application or API - $ composer create-project symfony/skeleton:"^4.4" my_project_name + $ composer create-project symfony/skeleton my_project_name No matter which command you run to create the Symfony application. All of them will create a new ``my_project_name/`` directory, download some dependencies diff --git a/setup/built_in_web_server.rst b/setup/built_in_web_server.rst deleted file mode 100644 index 506e7d60f9c..00000000000 --- a/setup/built_in_web_server.rst +++ /dev/null @@ -1,127 +0,0 @@ -.. index:: - single: Web Server; Built-in Web Server - -How to Use PHP's built-in Web Server -==================================== - -.. deprecated:: 4.4 - - This article explains how to use the WebServerBundle to run Symfony - applications on your local computer. However, that bundle is deprecated - since Symfony 4.4 and will be removed in Symfony 5.0. - - Instead of using WebServerBundle, the preferred way to run your Symfony - applications locally is to use the :doc:`Symfony Local Web Server `. - -The PHP CLI SAPI comes with a `built-in web server`_. It can be used to run your -PHP applications locally during development, for testing or for application -demonstrations. This way, you don't have to bother configuring a full-featured -web server such as :doc:`Apache or nginx `. - -.. caution:: - - The built-in web server is meant to be run in a controlled environment. - It is not designed to be used on public networks. - -Symfony provides a web server built on top of this PHP server to simplify your -local setup. This server is distributed as a bundle, so you must first install -and enable the server bundle. - -Installing the Web Server Bundle --------------------------------- - -Move into your project directory and run this command: - -.. code-block:: terminal - - $ cd your-project/ - $ composer require --dev symfony/web-server-bundle - -Starting the Web Server ------------------------ - -To run a Symfony application using PHP's built-in web server, execute the -``server:start`` command: - -.. code-block:: terminal - - $ php bin/console server:start - -This starts the web server at ``localhost:8000`` in the background that serves -your Symfony application. - -By default, the web server listens on port 8000 on the loopback device. You -can change the socket passing an IP address and a port as a command-line argument: - -.. code-block:: terminal - - # passing a specific IP and port - $ php bin/console server:start 192.168.0.1:8080 - - # passing '*' as the IP means to use 0.0.0.0 (i.e. any local IP address) - $ php bin/console server:start *:8080 - -.. note:: - - You can use the ``server:status`` command to check if a web server is - listening: - - .. code-block:: terminal - - $ php bin/console server:status - -.. tip:: - - Some systems do not support the ``server:start`` command, in these cases - you can execute the ``server:run`` command. This command behaves slightly - different. Instead of starting the server in the background, it will block - the current terminal until you terminate it (this is usually done by - pressing Ctrl and C). - -.. sidebar:: Using the built-in Web Server from inside a Virtual Machine - - If you want to use the built-in web server from inside a virtual machine - and then load the site from a browser on your host machine, you'll need - to listen on the ``0.0.0.0:8000`` address (i.e. on all IP addresses that - are assigned to the virtual machine): - - .. code-block:: terminal - - $ php bin/console server:start 0.0.0.0:8000 - - .. caution:: - - You should **NEVER** listen to all interfaces on a computer that is - directly accessible from the Internet. The built-in web server is - not designed to be used on public networks. - -Command Options -~~~~~~~~~~~~~~~ - -The built-in web server expects a "router" script (read about the "router" -script on `php.net`_) as an argument. Symfony already passes such a router -script when the command is executed in the ``prod`` or ``dev`` environment. -Use the ``--router`` option to use your own router script: - -.. code-block:: terminal - - $ php bin/console server:start --router=config/my_router.php - -If your application's document root differs from the standard directory layout, -you have to pass the correct location using the ``--docroot`` option: - -.. code-block:: terminal - - $ php bin/console server:start --docroot=public_html - -Stopping the Server -------------------- - -When you finish your work, you can stop the web server with the following command: - -.. code-block:: terminal - - $ php bin/console server:stop - -.. _`built-in web server`: https://www.php.net/manual/en/features.commandline.webserver.php -.. _`php.net`: https://www.php.net/manual/en/features.commandline.webserver.php#example-415 diff --git a/setup/file_permissions.rst b/setup/file_permissions.rst index 4c65ac8325e..b18a02d4bab 100644 --- a/setup/file_permissions.rst +++ b/setup/file_permissions.rst @@ -2,7 +2,7 @@ Setting up or Fixing File Permissions ===================================== In Symfony 3.x, you needed to do some extra work to make sure that your cache directory -was writable. But that is no longer true! In Symfony 4, everything works automatically: +was writable. But that is no longer true! Since Symfony 4, everything works automatically: * In the ``dev`` environment, ``umask()`` is used in ``bin/console`` and ``public/index.php`` so that any created files are writable by everyone. diff --git a/setup/flex.rst b/setup/flex.rst index df1bf1eaa14..dd26b2aa9fb 100644 --- a/setup/flex.rst +++ b/setup/flex.rst @@ -193,9 +193,9 @@ If you customize these paths, some files copied from a recipe still may contain references to the original path. In other words: you may need to update some things manually after a recipe is installed. -.. _`default services.yaml file`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/3.3/config/services.yaml +.. _`default services.yaml file`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/4.2/config/services.yaml .. _`shown in this example`: https://github.com/symfony/skeleton/blob/8e33fe617629f283a12bbe0a6578bd6e6af417af/composer.json#L24-L33 .. _`shown in this example of the skeleton-project`: https://github.com/symfony/skeleton/blob/8e33fe617629f283a12bbe0a6578bd6e6af417af/composer.json#L44-L46 -.. _`copying Symfony's index.php source`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/3.4/public/index.php -.. _`copying Symfony's bin/console source`: https://github.com/symfony/recipes/blob/master/symfony/console/3.4/bin/console +.. _`copying Symfony's index.php source`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/5.1/public/index.php +.. _`copying Symfony's bin/console source`: https://github.com/symfony/recipes/blob/master/symfony/console/5.1/bin/console .. _`Symfony Requirements Checker`: https://github.com/symfony/requirements-checker diff --git a/setup/upgrade_patch.rst b/setup/upgrade_patch.rst index de532e02d36..632f6602550 100644 --- a/setup/upgrade_patch.rst +++ b/setup/upgrade_patch.rst @@ -1,7 +1,7 @@ .. index:: single: Upgrading; Patch Version -Upgrading a Patch Version (e.g. 4.1.0 to 4.1.1) +Upgrading a Patch Version (e.g. 5.0.0 to 5.0.1) =============================================== When a new patch version is released (only the last number changed), it is a diff --git a/templates.rst b/templates.rst index fb30f9a7c03..540e8e493fb 100644 --- a/templates.rst +++ b/templates.rst @@ -492,6 +492,11 @@ provided by Symfony: # whether or not caching should apply for client caches only private: true + # optionally you can define some arguments passed to the template + context: + site_name: 'ACME' + theme: 'dark' + .. code-block:: xml @@ -509,8 +514,15 @@ provided by Symfony: 86400 86400 - + + true + + + + ACME + dark + @@ -533,10 +545,20 @@ provided by Symfony: // whether or not caching should apply for client caches only 'private' => true, + + // optionally you can define some arguments passed to the template + 'context' => [ + 'site_name' => 'ACME', + 'theme' => 'dark', + ] ]) ; }; +.. versionadded:: 5.1 + + The ``context`` option was introduced in Symfony 5.1. + Checking if a Template Exists ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -585,12 +607,6 @@ errors. It's useful to run it before deploying your application to production # you can also show the deprecated features used in your templates $ php bin/console lint:twig --show-deprecations templates/email/ -.. versionadded:: 4.4 - - The feature that checks all the application templates when not passing any - arguments to ``lint:twig`` and the ``--show-deprecations`` option were - introduced in Symfony 4.4. - Inspecting Twig Information ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1038,12 +1054,6 @@ When rendering a template, Symfony looks for it first in the ``twig.paths`` directories that don't define a namespace and then falls back to the default template directory (usually, ``templates/``). -.. deprecated:: 4.2 - - Symfony looks for templates in the ``src/Resources/views/`` too before - falling back to the default directory. But that behavior is deprecated since - Symfony 4.2 and will be removed in Symfony 5.0. - Using the above configuration, if your application renders for example the ``layout.html.twig`` template, Symfony will first look for ``email/default/templates/layout.html.twig`` and ``backend/templates/layout.html.twig``. diff --git a/templating/PHP.rst b/templating/PHP.rst index f26815dc558..9928984f8d8 100644 --- a/templating/PHP.rst +++ b/templating/PHP.rst @@ -4,571 +4,7 @@ How to Use PHP instead of Twig for Templates ============================================ -.. deprecated:: 4.3 - - PHP templates have been deprecated in Symfony 4.3 and they will no longer be - supported in Symfony 5.0. Use :ref:`Twig templates ` instead. - -Symfony defaults to Twig for its template engine, but you can still use -plain PHP code if you want. Both templating engines are supported equally in -Symfony. Symfony adds some nice features on top of PHP to make writing -templates with PHP more powerful. - -.. tip:: - - If you choose *not* use Twig and you disable it, you'll need to implement - your own exception handler via the ``kernel.exception`` event. - -Rendering PHP Templates ------------------------ - -If you want to use the PHP templating engine, first install the templating component: - -.. code-block:: terminal - - $ composer require symfony/templating - -.. deprecated:: 4.3 - - The integration of the Templating component in FrameworkBundle has been - deprecated since version 4.3 and will be removed in 5.0. - -Next, enable the PHP engine: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - # ... - templating: - engines: ['twig', 'php'] - - .. code-block:: xml - - - - - - - - - - - - - - - .. code-block:: php - - // config/packages/framework.php - $container->loadFromExtension('framework', [ - // ... - 'templating' => [ - 'engines' => ['twig', 'php'], - ], - ]); - -You can now render a PHP template instead of a Twig one by using the ``.php`` -extension in the template name instead of ``.twig``. The controller below -renders the ``index.html.php`` template:: - - // src/Controller/HelloController.php - - // ... - public function index($name) - { - // template is stored in src/Resources/views/hello/index.html.php - return $this->render('hello/index.html.php', [ - 'name' => $name - ]); - } - .. caution:: - Enabling the ``php`` and ``twig`` template engines simultaneously is - allowed, but it will produce an undesirable side effect in your application: - the ``@`` notation for Twig namespaces will no longer be supported for the - ``render()`` method:: - - public function index() - { - // ... - - // namespaced templates will no longer work in controllers - $this->render('@SomeNamespace/hello/index.html.twig'); - - // you must use the traditional template notation - $this->render('hello/index.html.twig'); - } - - .. code-block:: twig - - {# inside a Twig template, namespaced templates work as expected #} - {{ include('@SomeNamespace/hello/index.html.twig') }} - - {# traditional template notation will also work #} - {{ include('hello/index.html.twig') }} - -.. index:: - single: Templating; Layout - single: Layout - -Decorating Templates --------------------- - -More often than not, templates in a project share common elements, like the -well-known header and footer. In Symfony, this problem is thought about -differently: a template can be decorated by another one. - -The ``index.html.php`` template is decorated by ``layout.html.php``, thanks to -the ``extend()`` call: - -.. code-block:: html+php - - - extend('layout.html.php') ?> - - Hello ! - -Now, have a look at the ``layout.html.php`` file: - -.. code-block:: html+php - - - extend('base.html.php') ?> - -

Hello Application

- - output('_content') ?> - -The layout is itself decorated by another one (``base.html.php``). Symfony -supports multiple decoration levels: a layout can itself be decorated by -another one: - -.. code-block:: html+php - - - - - - - <?php $view['slots']->output('title', 'Hello Application') ?> - - - output('_content') ?> - - - -For both layouts, the ``$view['slots']->output('_content')`` expression is -replaced by the content of the child template, ``index.html.php`` and -``layout.html.php`` respectively (more on slots in the next section). - -As you can see, Symfony provides methods on a mysterious ``$view`` object. In -a template, the ``$view`` variable is always available and refers to a special -object that provides a bunch of methods that makes the template engine tick. - -.. index:: - single: Templating; Slot - single: Slot - -Working with Slots ------------------- - -A slot is a snippet of code, defined in a template, and reusable in any layout -decorating the template. In the ``index.html.php`` template, define a -``title`` slot: - -.. code-block:: html+php - - - extend('layout.html.php') ?> - - set('title', 'Hello World Application') ?> - - Hello ! - -The base layout already has the code to output the title in the header: - -.. code-block:: html+php - - - - - <?php $view['slots']->output('title', 'Hello Application') ?> - - -The ``output()`` method inserts the content of a slot and optionally takes a -default value if the slot is not defined. And ``_content`` is just a special -slot that contains the rendered child template. - -For large slots, there is also an extended syntax: - -.. code-block:: html+php - - start('title') ?> - Some large amount of HTML - stop() ?> - -.. index:: - single: Templating; Include - -Including other Templates -------------------------- - -The best way to share a snippet of template code is to define a template that -can then be included into other templates. - -Create a ``hello.html.php`` template: - -.. code-block:: html+php - - - Hello ! - -And change the ``index.html.php`` template to include it: - -.. code-block:: html+php - - - extend('layout.html.php') ?> - - render('hello/hello.html.php', ['name' => $name]) ?> - -The ``render()`` method evaluates and returns the content of another template -(this is the exact same method as the one used in the controller). - -.. index:: - single: Templating; Embedding pages - -Embedding other Controllers ---------------------------- - -And what if you want to embed the result of another controller in a template? -That's very useful when working with Ajax, or when the embedded template needs -some variable not available in the main template. - -If you create a ``fancy`` action, and want to include it into the -``index.html.php`` template, use the following code: - -.. code-block:: html+php - - - render( - new \Symfony\Component\HttpKernel\Controller\ControllerReference( - 'App\Controller\HelloController::fancy', - [ - 'name' => $name, - 'color' => 'green', - ] - ) - ) ?> - -But where is the ``$view['actions']`` array element defined? Like -``$view['slots']``, it's called a template helper, and the next section tells -you more about those. - -.. index:: - single: Templating; Helpers - -Using Template Helpers ----------------------- - -The Symfony templating system can be extended via helpers. Helpers are -PHP objects that provide features useful in a template context. ``actions`` and -``slots`` are two of the built-in Symfony helpers. - -Creating Links between Pages -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Speaking of web applications, creating links between pages is a must. Instead -of hardcoding URLs in templates, the ``router`` helper knows how to generate -URLs based on the routing configuration. That way, all your URLs can be -updated by changing the configuration: - -.. code-block:: html+php - - - Greet Thomas! - - -The ``path()`` method takes the route name and an array of parameters as -arguments. The route name is the main key under which routes are referenced -and the parameters are the values of the placeholders defined in the route -pattern: - -.. code-block:: yaml - - # config/routes.yaml - hello: - path: /hello/{name} - controller: App\Controller\HelloController::index - -Using Assets: Images, JavaScripts and Stylesheets -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -What would the Internet be without images, JavaScripts, and stylesheets? -Symfony provides the ``assets`` tag to deal with them: - -.. code-block:: html+php - - - - - -The ``assets`` helper's main purpose is to make your application more -portable. Thanks to this helper, you can move the application root directory -anywhere under your web root directory without changing anything in your -template's code. - -Profiling Templates -~~~~~~~~~~~~~~~~~~~ - -By using the ``stopwatch`` helper, you are able to time parts of your template -and display it on the timeline of the WebProfilerBundle:: - - start('foo') ?> - ... things that get timed - stop('foo') ?> - -.. tip:: - - If you use the same name more than once in your template, the times are - grouped on the same line in the timeline. - -Output Escaping ---------------- - -When using PHP templates, escape variables whenever they are displayed to the -user:: - - escape($var) ?> - -By default, the ``escape()`` method assumes that the variable is outputted -within an HTML context. The second argument lets you change the context. For -instance, to output something in a JavaScript script, use the ``js`` context:: - - escape($var, 'js') ?> - -Form Theming in PHP -------------------- - -When using PHP as a templating engine, the only method to customize a fragment -is to create a new template file - this is similar to the second method used by -Twig. - -The template file must be named after the fragment. You must create a ``integer_widget.html.php`` -file in order to customize the ``integer_widget`` fragment. - -.. code-block:: html+php - - -
- block( - $form, - 'form_widget_simple', - ['type' => isset($type) ? $type : "number"] - ) ?> -
- -Now that you've created the customized form template, you need to tell Symfony -to use it. Inside the template where you're actually rendering your form, -tell Symfony to use the theme via the ``setTheme()`` helper method:: - - setTheme($form, [':form']) ?> - - widget($form['age']) ?> - -When the ``form.age`` widget is rendered, Symfony will use the customized -``integer_widget.html.php`` template and the ``input`` tag will be wrapped in -the ``div`` element. - -If you want to apply a theme to a specific child form, pass it to the ``setTheme()`` -method:: - - setTheme($form['child'], ':form') ?> - -.. note:: - - The ``:form`` syntax is based on the functional names for templates: - ``Bundle:Directory``. As the form directory lives in the - ``templates/`` directory, the ``Bundle`` part is empty, resulting - in ``:form``. - -Making Application-wide Customizations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you'd like a certain form customization to be global to your application, -you can accomplish this by making the form customizations in an external -template and then importing it inside your application configuration. - -By using the following configuration, any customized form fragments inside the -``templates/form`` folder will be used globally when a -form is rendered. - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - templating: - form: - resources: - - 'App:Form' - # ... - - .. code-block:: xml - - - - - - - - - App:Form - - - - - - - .. code-block:: php - - // config/packages/framework.php - // PHP - $container->loadFromExtension('framework', [ - 'templating' => [ - 'form' => [ - 'resources' => [ - 'App:Form', - ], - ], - ], - - // ... - ]); - -By default, the PHP engine uses a *div* layout when rendering forms. Some people, -however, may prefer to render forms in a *table* layout. Use the ``FrameworkBundle:FormTable`` -resource to use such a layout: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/framework.yaml - framework: - templating: - form: - resources: - - 'FrameworkBundle:FormTable' - - .. code-block:: xml - - - - - - - - - FrameworkBundle:FormTable - - - - - - - .. code-block:: php - - // config/packages/framework.php - $container->loadFromExtension('framework', [ - 'templating' => [ - 'form' => [ - 'resources' => [ - 'FrameworkBundle:FormTable', - ], - ], - ], - - // ... - ]); - -If you only want to make the change in one template, add the following line to -your template file rather than adding the template as a resource: - -.. code-block:: html+php - - setTheme($form, ['FrameworkBundle:FormTable']) ?> - -Note that the ``$form`` variable in the above code is the form view variable -that you passed to your template. - -Adding a "Required" Asterisk to Field Labels -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you want to denote all of your required fields with a required asterisk -(``*``), you can do this by customizing the ``form_label`` fragment. - -When using PHP as a templating engine you have to copy the content from the -original template: - -.. code-block:: html+php - - - - - - - humanize($name); } ?> - - - - - * - - -Adding "help" Messages -~~~~~~~~~~~~~~~~~~~~~~ - -You can also customize your form widgets to have an optional "help" message. - -When using PHP as a templating engine you have to copy the content from the -original template: - -.. code-block:: html+php - - - - - value="escape($value) ?>" - block($form, 'widget_attributes') ?> - /> - - - - escape($help) ?> - + Starting from Symfony 5.0, PHP templates are no longer supported. Use + :doc:`Twig ` instead to create your templates. diff --git a/templating/hinclude.rst b/templating/hinclude.rst index def7939ab17..b192ad08b8a 100644 --- a/templating/hinclude.rst +++ b/templating/hinclude.rst @@ -67,12 +67,6 @@ default content rendering some template: ], ]); -.. versionadded:: 4.3 - - The ``framework.fragments.hinclude_default_template`` option was introduced - in Symfony 4.3. In previous Symfony versions it was called - ``framework.templating.hinclude_default_template``. - You can define default templates per ``render()`` function (which will override any global default template that is defined): diff --git a/testing.rst b/testing.rst index ce863b7e319..1a705a5698a 100644 --- a/testing.rst +++ b/testing.rst @@ -220,10 +220,6 @@ the page and check for its existence, attributes, text, etc. The ``assertSelectorTextContains`` method is not a native PHPUnit assertion and is available thanks to the ``WebTestCase`` class. -.. versionadded:: 4.3 - - The ``WebTestCase`` assertions were introduced in Symfony 4.3 - The crawler can also be used to interact with the page. Click on a link by first selecting it with the crawler using either an XPath expression or a CSS selector, then use the client to click on it:: @@ -266,7 +262,7 @@ Or test against the response content directly if you just want to assert that the content contains some text or in case that the response is not an XML/HTML document:: - $this->assertContains( + $this->assertStringContainsString( 'Hello World', $client->getResponse()->getContent() ); @@ -309,7 +305,7 @@ document:: )); // asserts that the response content contains a string - $this->assertContains('foo', $client->getResponse()->getContent()); + $this->assertStringContainsString('foo', $client->getResponse()->getContent()); // ...or matches a regex $this->assertRegExp('/foo(bar)?/', $client->getResponse()->getContent()); @@ -335,12 +331,6 @@ document:: // ...or check that the response is a redirect to any URL $this->assertResponseRedirects(); -.. versionadded:: 4.3 - - The ``assertResponseHeaderSame()``, ``assertResponseIsSuccessful()``, - ``assertResponseStatusCodeSame()``, ``assertResponseRedirects()`` and other - related methods were introduced in Symfony 4.3. - .. _testing-data-providers: Testing against Different Sets of Data @@ -403,13 +393,13 @@ returns a ``Crawler`` instance. The full signature of the ``request()`` method is:: request( - $method, - $uri, + string $method, + string $uri, array $parameters = [], array $files = [], array $server = [], - $content = null, - $changeHistory = true + string $content = null, + bool $changeHistory = true ) The ``server`` array is the raw values that you'd expect to normally @@ -617,6 +607,64 @@ it via that alias: If the information you need to check is available from the profiler, use it instead. +.. _testing_logging_in_users: + +Logging in Users (Authentication) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 5.1 + + The ``loginUser()`` method was introduced in Symfony 5.1. + +When you want to add functional tests for protected pages, you have to +first "login" as a user. Reproducing the actual steps - such as +submitting a login form - make a test very slow. For this reason, Symfony +provides a ``loginUser()`` method to simulate logging in in your functional +tests. + +Instead of login in with real users, it's recommended to create a user only for +tests. You can do that with Doctrine :ref:`data fixtures `, +to load the testing users only in the test database. + +After loading users in your database, use your user repository to fetch +this user and use +:method:`$client->loginUser() ` +to simulate a login request:: + + // tests/Controller/ProfileControllerTest.php + namespace App\Tests\Controller; + + use App\Repository\UserRepository; + use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; + + class ProfileControllerTest extends WebTestCase + { + // ... + + public function testVisitingWhileLoggedIn() + { + $client = static::createClient(); + $userRepository = static::$container->get(UserRepository::class); + + // retrieve the test user + $testUser = $userRepository->findOneByEmail('john.doe@example.com'); + + // simulate $testUser being logged in + $client->loginUser($testUser); + + // test e.g. the profile page + $client->request('GET', '/profile'); + $this->assertResponseIsSuccessful(); + $this->assertSelectorTextContains('h1', 'Hello John!'); + } + } + +You can pass any +:class:`Symfony\\Component\\Security\\Core\\User\\UserInterface` instance to +``loginUser()``. This method creates a special +:class:`Symfony\\Bundle\\FrameworkBundle\\Test\\TestBrowserToken` object and +stores in the session of the test client. + Accessing the Profiler Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -739,6 +787,8 @@ Extracting Information The Crawler can extract information from the nodes:: + use Symfony\Component\DomCrawler\Crawler; + // returns the attribute value for the first node $crawler->attr('class'); @@ -759,14 +809,10 @@ The Crawler can extract information from the nodes:: $info = $crawler->extract(['_text', 'href']); // executes a lambda for each node and return an array of results - $data = $crawler->each(function ($node, $i) { + $data = $crawler->each(function (Crawler $node, $i) { return $node->attr('href'); }); -.. versionadded:: 4.4 - - The option to trim white spaces in ``text()`` was introduced in Symfony 4.4. - Links ~~~~~ @@ -872,10 +918,6 @@ their type:: :method:`Symfony\\Component\\DomCrawler\\Form::getName` method to get the form name. - .. versionadded:: 4.4 - - The ``getName()`` method was introduced in Symfony 4.4. - .. tip:: If you purposefully want to select "invalid" select/radio values, see diff --git a/testing/bootstrap.rst b/testing/bootstrap.rst index cf1b63621a2..bdd7448a519 100644 --- a/testing/bootstrap.rst +++ b/testing/bootstrap.rst @@ -6,21 +6,21 @@ running those tests. For example, if you're running a functional test and have introduced a new translation resource, then you will need to clear your cache before running those tests. -To do this, first add a file that executes your bootstrap work:: +Symfony already created the following ``tests/bootstrap.php`` file when installing +the package to work with tests. If you don't have this file, create it:: // tests/bootstrap.php - if (isset($_ENV['BOOTSTRAP_CLEAR_CACHE_ENV'])) { - // executes the "php bin/console cache:clear" command - passthru(sprintf( - 'APP_ENV=%s php "%s/../bin/console" cache:clear --no-warmup', - $_ENV['BOOTSTRAP_CLEAR_CACHE_ENV'], - __DIR__ - )); - } + use Symfony\Component\Dotenv\Dotenv; + + require dirname(__DIR__).'/vendor/autoload.php'; - require __DIR__.'/../config/bootstrap.php'; + if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) { + require dirname(__DIR__).'/config/bootstrap.php'; + } elseif (method_exists(Dotenv::class, 'bootEnv')) { + (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); + } -Then, configure ``phpunit.xml.dist`` to execute this ``bootstrap.php`` file +Then, check that your ``phpunit.xml.dist`` file runs this ``bootstrap.php`` file before running the tests: .. code-block:: xml diff --git a/testing/database.rst b/testing/database.rst index 96d0dc7d3d1..bae4ea85286 100644 --- a/testing/database.rst +++ b/testing/database.rst @@ -221,7 +221,7 @@ so, get the entity manager via the service container as follows:: */ private $entityManager; - protected function setUp() + protected function setUp(): void { $kernel = self::bootKernel(); @@ -240,7 +240,7 @@ so, get the entity manager via the service container as follows:: $this->assertSame(14.50, $product->getPrice()); } - protected function tearDown() + protected function tearDown(): void { parent::tearDown(); diff --git a/testing/functional_tests_assertions.rst b/testing/functional_tests_assertions.rst index edf1a6bb679..1f3b13d9763 100644 --- a/testing/functional_tests_assertions.rst +++ b/testing/functional_tests_assertions.rst @@ -4,11 +4,6 @@ Functional Test specific Assertions =================================== -.. versionadded:: 4.3 - - The shortcut methods for assertions using ``WebTestCase`` were introduced - in Symfony 4.3. - When doing functional tests, sometimes you need to make complex assertions in order to check whether the ``Request``, the ``Response`` or the ``Crawler`` contain the expected information to make your test succeed. @@ -23,23 +18,17 @@ This is the same example using the assertions provided by Symfony:: $this->assertResponseRedirects('https://example.com', 301); -.. note:: - - These assertions only work if a request has been made with the ``Client`` - in a test case extending the ``WebTestCase`` class. - Assertions Reference --------------------- -.. versionadded:: 4.4 - - Starting from Symfony 4.4, when using `symfony/panther`_ for end-to-end - testing, you can use all the following assertions except the ones related to - the :doc:`Crawler `. - Response ~~~~~~~~ +.. note:: + + The following assertions only work if a request has been made with the + ``Client`` in a test case extending the ``WebTestCase`` class. + - ``assertResponseIsSuccessful()`` - ``assertResponseStatusCodeSame()`` - ``assertResponseRedirects()`` @@ -54,12 +43,22 @@ Response Request ~~~~~~~ +.. note:: + + The following assertions only work if a request has been made with the + ``Client`` in a test case extending the ``WebTestCase`` class. + - ``assertRequestAttributeValueSame()`` - ``assertRouteSame()`` Browser ~~~~~~~ +.. note:: + + The following assertions only work if a request has been made with the + ``Client`` in a test case extending the ``WebTestCase`` class. + - ``assertBrowserHasCookie()`` - ``assertBrowserNotHasCookie()`` - ``assertBrowserCookieValueSame()`` @@ -67,6 +66,12 @@ Browser Crawler ~~~~~~~ +.. note:: + + The following assertions only work if a request has been made with the + ``Client`` in a test case extending the ``WebTestCase`` class. In addition, + they are not available when using `symfony/panther`_ for end-to-end testing. + - ``assertSelectorExists()`` - ``assertSelectorNotExists()`` - ``assertSelectorTextContains()`` (note: it only checks the first selector occurrence) @@ -80,6 +85,11 @@ Crawler Mailer ~~~~~~ +.. versionadded:: 5.1 + + Starting from Symfony 5.1, the following assertions no longer require to make + a request with the ``Client`` in a test case extending the ``WebTestCase`` class. + - ``assertEmailCount()`` - ``assertQueuedEmailCount()`` - ``assertEmailIsQueued()`` @@ -95,8 +105,4 @@ Mailer - ``assertEmailHeaderNotSame()`` - ``assertEmailAddressContains()`` -.. versionadded:: 4.4 - - The mailer assert methods were introduced in Symfony 4.4. - .. _`symfony/panther`: https://github.com/symfony/panther diff --git a/testing/http_authentication.rst b/testing/http_authentication.rst index 158cb2f2cae..a55ae639e0b 100644 --- a/testing/http_authentication.rst +++ b/testing/http_authentication.rst @@ -4,130 +4,14 @@ How to Simulate HTTP Authentication in a Functional Test ======================================================== -Authenticating requests in functional tests can slow down the entire test suite. -This could become an issue especially when the tests reproduce the same steps -that users follow to authenticate, such as submitting a login form or using -OAuth authentication services. +.. caution:: -This article explains the two most popular techniques to avoid these issues and -create fast tests when using authentication. + Starting from Symfony 5.1, a ``loginUser()`` method was introduced to + ease testing secured applications. See :ref:`testing_logging_in_users` + for more information about this. -Using a Faster Authentication Mechanism Only for Tests ------------------------------------------------------- + If you are still using an older version of Symfony, view + `previous versions of this article`_ for information on how to simulate + HTTP authentication. -When your application is using a ``form_login`` authentication, you can make -your tests faster by allowing them to use HTTP authentication. This way your -tests authenticate with the simple and fast HTTP Basic method whilst your real -users still log in via the normal login form. - -The trick is to use the ``http_basic`` authentication in your application -firewall, but only in the configuration file used by tests: - -.. configuration-block:: - - .. code-block:: yaml - - # config/packages/test/security.yaml - security: - firewalls: - # replace 'main' by the name of your own firewall - main: - http_basic: ~ - - .. code-block:: xml - - - - - - - - - - .. code-block:: php - - // config/packages/test/security.php - $container->loadFromExtension('security', [ - 'firewalls' => [ - // replace 'main' by the name of your own firewall - 'main' => [ - 'http_basic' => [], - ], - ], - ]); - -Tests can now authenticate via HTTP passing the username and password as server -variables using the second argument of ``createClient()``:: - - $client = static::createClient([], [ - 'PHP_AUTH_USER' => 'username', - 'PHP_AUTH_PW' => 'pa$$word', - ]); - -The username and password can also be passed on a per request basis:: - - $client->request('DELETE', '/post/12', [], [], [ - 'PHP_AUTH_USER' => 'username', - 'PHP_AUTH_PW' => 'pa$$word', - ]); - -Creating the Authentication Token ---------------------------------- - -If your application uses a more advanced authentication mechanism, you can't -use the previous trick, but it's still possible to make tests faster. The trick -now is to bypass the authentication process, create the *authentication token* -yourself and store it in the session. - -This technique requires some knowledge of the Security component internals, -but the following example shows a complete example that you can adapt to your -needs:: - - // tests/Controller/DefaultControllerTest.php - namespace App\Tests\Controller; - - use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; - use Symfony\Component\BrowserKit\Cookie; - use Symfony\Component\HttpFoundation\Response; - use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; - - class DefaultControllerTest extends WebTestCase - { - private $client = null; - - public function setUp() - { - $this->client = static::createClient(); - } - - public function testSecuredHello() - { - $this->logIn(); - $crawler = $this->client->request('GET', '/admin'); - - $this->assertSame(Response::HTTP_OK, $this->client->getResponse()->getStatusCode()); - $this->assertSame('Admin Dashboard', $crawler->filter('h1')->text()); - } - - private function logIn() - { - $session = self::$container->get('session'); - - // somehow fetch the user (e.g. using the user repository) - $user = ...; - - $firewallName = 'secure_area'; - // if you don't define multiple connected firewalls, the context defaults to the firewall name - // See https://symfony.com/doc/current/reference/configuration/security.html#firewall-context - $firewallContext = 'secured_area'; - - // you may need to use a different token class depending on your application. - // for example, when using Guard authentication you must instantiate PostAuthenticationGuardToken - $token = new UsernamePasswordToken($user, null, $firewallName, $user->getRoles()); - $session->set('_security_'.$firewallContext, serialize($token)); - $session->save(); - - $cookie = new Cookie($session->getName(), $session->getId()); - $this->client->getCookieJar()->set($cookie); - } - } +.. _previous versions of this article: https://symfony.com/doc/5.0/testing/http_authentication.html diff --git a/translation.rst b/translation.rst index 998bcfd773f..1c0caf7aff3 100644 --- a/translation.rst +++ b/translation.rst @@ -292,12 +292,6 @@ To manage these situations, Symfony follows the `ICU MessageFormat`_ syntax by using PHP's :phpclass:`MessageFormatter` class. Read more about this in :doc:`/translation/message_format`. -.. versionadded:: 4.2 - - Support for ICU MessageFormat was introduced in Symfony 4.2. Prior to this, - pluralization was managed by the - :method:`Symfony\\Component\\Translation\\Translator::transChoice` method. - .. _translation-in-templates: Translations in Templates @@ -354,11 +348,6 @@ The ``translation:update`` command looks for missing translations in: * Any PHP file/class that injects or :doc:`autowires ` the ``translator`` service and makes calls to the ``trans()`` function. -.. versionadded:: 4.3 - - The extraction of missing translation strings from PHP files was introduced - in Symfony 4.3. - .. _translation-resource-locations: Translation Resource/File Names and Locations @@ -366,18 +355,11 @@ Translation Resource/File Names and Locations Symfony looks for message files (i.e. translations) in the following default locations: -#. the ``translations/`` directory (at the root of the project); -#. the ``src/Resources//translations/`` directory; -#. the ``Resources/translations/`` directory inside of any bundle. - -.. deprecated:: 4.2 - - Using the ``src/Resources//translations/`` directory to store - translations was deprecated in Symfony 4.2. Use instead the directory - defined in the ``default_path`` option (which is ``translations/`` by default). +* the ``translations/`` directory (at the root of the project); +* the ``Resources/translations/`` directory inside of any bundle. The locations are listed here with the highest priority first. That is, you can -override the translation messages of a bundle in any of the top two directories. +override the translation messages of a bundle in the first directory. 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 @@ -476,13 +458,6 @@ if you're generating translations with specialized programs or teams. :class:`Symfony\\Component\\Translation\\Loader\\LoaderInterface` interface. See the :ref:`dic-tags-translation-loader` tag for more information. -.. versionadded:: 4.3 - - Starting from Symfony 4.3, when you create a new translation file (or - install a bundle that includes translation files), you don't have to clear - the cache with the command ``php bin/console cache:clear`` as you had to do - in previous Symfony versions. - Handling the User's Locale -------------------------- @@ -549,12 +524,6 @@ checks translation resources for several locales: // ... ]); -.. deprecated:: 4.4 - - In Symfony versions before 4.4, the ``fallbacks`` option was initialized to - ``en`` (English) when not configured explicitly. Starting from Symfony 4.4, - this option is initialized to the same value as the ``default_locale`` option. - .. note:: When Symfony can't find a translation in the given locale, it will diff --git a/translation/debug.rst b/translation/debug.rst index afe1c778e77..74e52783245 100644 --- a/translation/debug.rst +++ b/translation/debug.rst @@ -180,3 +180,33 @@ unused or only the missing messages, by using the ``--only-unused`` or $ php bin/console debug:translation en --only-unused $ php bin/console debug:translation en --only-missing + +Debug Command Exit Codes +------------------------ + +The exit code of the ``debug:translation`` command changes depending on the +status of the translations. Use the following public constants to check it:: + + use Symfony\Bundle\FrameworkBundle\Command\TranslationDebugCommand; + + // generic failure (e.g. there are no translations) + TranslationDebugCommand::EXIT_CODE_GENERAL_ERROR; + + // there are missing translations + TranslationDebugCommand::EXIT_CODE_MISSING; + + // there are unused translations + TranslationDebugCommand::EXIT_CODE_UNUSED; + + // some translations are using the fallback translation + TranslationDebugCommand::EXIT_CODE_FALLBACK; + +These constants are defined as "bit masks", so you can combine them as follows:: + + if (TranslationDebugCommand::EXIT_CODE_MISSING | TranslationDebugCommand::EXIT_CODE_UNUSED) { + // ... there are missing and/or unused translations + } + +.. versionadded:: 5.1 + + The exit codes were introduced in Symfony 5.1 diff --git a/translation/lint.rst b/translation/lint.rst index 29cec3c5008..d9129a79108 100644 --- a/translation/lint.rst +++ b/translation/lint.rst @@ -32,3 +32,16 @@ The linter results can be exported to JSON using the ``--format`` option: $ php bin/console lint:yaml translations/ --format=json $ php bin/console lint:xliff translations/ --format=json + +.. tip:: + + The Yaml component provides a stand-alone ``yaml-lint`` binary allowing + you to lint YAML files without having to create a console application: + + .. code-block:: terminal + + $ php vendor/bin/yaml-lint translations/ + + .. versionadded:: 5.1 + + The ``yaml-lint`` binary was introduced in Symfony 5.1. diff --git a/translation/message_format.rst b/translation/message_format.rst index 5d1a5b63eec..2b07f534ca0 100644 --- a/translation/message_format.rst +++ b/translation/message_format.rst @@ -4,10 +4,6 @@ How to Translate Messages using the ICU MessageFormat ===================================================== -.. versionadded:: 4.2 - - Support for ICU MessageFormat was introduced in Symfony 4.2. - Messages (i.e. strings) in applications are almost never completely static. They contain variables or other complex logic like pluralization. In order to handle this, the Translator component supports the `ICU MessageFormat`_ syntax. @@ -173,6 +169,9 @@ you to use literal text in the select statements: readable for translators and, as you can see in the ``other`` case, other parts of the sentence might be influenced by the variables. + +.. _component-translation-pluralization: + Pluralization ------------- diff --git a/translation/templates.rst b/translation/templates.rst index 903f1934d92..b820bfb0fba 100644 --- a/translation/templates.rst +++ b/translation/templates.rst @@ -9,27 +9,13 @@ Twig Templates Using Twig Tags ~~~~~~~~~~~~~~~ -Symfony provides specialized Twig tags (``trans`` and ``transchoice``) to -help with message translation of *static blocks of text*: +Symfony provides a specialized Twig tag ``trans`` to help with message +translation of *static blocks of text*: .. code-block:: twig {% trans %}Hello %name%{% endtrans %} - {% transchoice count %} - {0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples - {% endtranschoice %} - -The ``transchoice`` tag automatically gets the ``%count%`` variable from -the current context and passes it to the translator. This mechanism only -works when you use a placeholder following the ``%var%`` pattern. - -.. deprecated:: 4.2 - - The ``transchoice`` tag is deprecated since Symfony 4.2 and will be - removed in 5.0. Use the :doc:`ICU MessageFormat ` with - the ``trans`` tag instead. - .. caution:: The ``%var%`` notation of placeholders is required when translating in @@ -48,34 +34,19 @@ You can also specify the message domain and pass some additional variables: {% trans with {'%name%': 'Fabien'} from 'app' into 'fr' %}Hello %name%{% endtrans %} - {% transchoice count with {'%name%': 'Fabien'} from 'app' %} - {0} %name%, there are no apples|{1} %name%, there is one apple|]1,Inf[ %name%, there are %count% apples - {% endtranschoice %} - .. _translation-filters: Using Twig Filters ~~~~~~~~~~~~~~~~~~ -The ``trans`` and ``transchoice`` filters can be used to translate *variable -texts* and complex expressions: +The ``trans`` filter can be used to translate *variable texts* and complex expressions: .. code-block:: twig {{ message|trans }} - {{ message|transchoice(5) }} - {{ message|trans({'%name%': 'Fabien'}, 'app') }} - {{ message|transchoice(5, {'%name%': 'Fabien'}, 'app') }} - -.. deprecated:: 4.2 - - The ``transchoice`` filter is deprecated since Symfony 4.2 and will be - removed in 5.0. Use the :doc:`ICU MessageFormat ` with - the ``trans`` filter instead. - .. tip:: Using the translation tags or filters have the same effect, but with @@ -116,8 +87,3 @@ The translator service is accessible in PHP templates through the trans('Symfony is great') ?> - transChoice( - '{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples', - 10, - ['%count%' => 10] - ) ?> diff --git a/validation/custom_constraint.rst b/validation/custom_constraint.rst index ad13504ecd3..68210299dc9 100644 --- a/validation/custom_constraint.rst +++ b/validation/custom_constraint.rst @@ -93,11 +93,6 @@ The validator class is also simple, and only has one required method ``validate( } } -.. versionadded:: 4.4 - - The feature to allow passing an object as the ``buildViolation()`` argument - was introduced in Symfony 4.4. - Inside ``validate``, you don't need to return a value. Instead, you add violations to the validator's ``context`` property and a value will be considered valid if it causes no violations. The ``buildViolation()`` method takes the error @@ -186,6 +181,16 @@ then your validator is already registered as a service and :doc:`tagged ` like any other service. +Create a Reusable Set of Constraints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In case you need to apply some common set of constraints in different places +consistently across your application, you can extend the :doc:`Compound constraint`. + +.. versionadded:: 5.1 + + The ``Compound`` constraint was introduced in Symfony 5.1. + Class Constraint Validator ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/validation/sequence_provider.rst b/validation/sequence_provider.rst index 98a9389212f..ad59e529839 100644 --- a/validation/sequence_provider.rst +++ b/validation/sequence_provider.rst @@ -355,3 +355,14 @@ provides a sequence of groups to be validated: // ... } } + +How to Sequentially Apply Constraints on a Single Property +---------------------------------------------------------- + +Sometimes, you may want to apply constraints sequentially on a single +property. The :doc:`Sequentially constraint` +can solve this for you in a more straightforward way than using a ``GroupSequence``. + +.. versionadded:: 5.1 + + The ``Sequentially`` constraint was introduced in Symfony 5.1. diff --git a/workflow.rst b/workflow.rst index f1498464717..3694500e06d 100644 --- a/workflow.rst +++ b/workflow.rst @@ -351,16 +351,19 @@ order: * ``workflow.[workflow name].announce`` * ``workflow.[workflow name].announce.[transition name]`` + You can avoid triggering those events by using the context:: + + $workflow->apply($subject, $transitionName, [Workflow::DISABLE_ANNOUNCE_EVENT => true]); + + .. versionadded:: 5.1 + + The ``Workflow::DISABLE_ANNOUNCE_EVENT`` constant was introduced in Symfony 5.1. + .. note:: The leaving and entering events are triggered even for transitions that stay in same place. -.. versionadded:: 4.3 - - Following events are also dispatched when the subject enters the workflow - for the first time: ``workflow.entered`` and ``workflow.[worflow name].entered``. - Here is an example of how to enable logging for every time a "blog_publishing" workflow leaves a place:: @@ -433,8 +436,7 @@ missing a title:: $title = $post->title; if (empty($title)) { - // Block the transition "to_review" if the post has no title - $event->setBlocked(true); + $event->setBlocked(true, 'This blog post cannot be marked as reviewed because it has no title.'); } } @@ -446,6 +448,10 @@ missing a title:: } } +.. versionadded:: 5.1 + + The optional second argument of ``setBlocked()`` was introduced in Symfony 5.1. + Event Methods ~~~~~~~~~~~~~ @@ -642,10 +648,6 @@ place:: } } -.. versionadded:: 4.1 - - The transition blockers were introduced in Symfony 4.1. - Usage in Twig ------------- @@ -699,10 +701,6 @@ The following example shows these functions in action: Storing Metadata ---------------- -.. versionadded:: 4.1 - - The feature to store metadata in workflows was introduced in Symfony 4.1. - In case you need it, you can store arbitrary metadata in workflows, their places, and their transitions using the ``metadata`` option. This metadata can be as simple as the title of the workflow or as complex as your own application