+
Then retrieve it from your JS file:
.. code-block:: javascript
@@ -290,6 +296,9 @@ Then retrieve it from your JS file:
const eventSource = new EventSource(url);
// ...
+ // with Stimulus
+ this.eventSource = new EventSource(this.mercureUrlValue);
+
Mercure also allows subscribing to several topics,
and to use URI Templates or the special value ``*`` (matched by all topics)
as patterns:
@@ -704,6 +713,9 @@ enable it::
:alt: The Mercure panel of the Symfony Profiler, showing information like time, memory, topics and data of each message sent by Mercure.
:class: with-browser
+The Mercure hub itself provides a debug tool that can be enabled and it's
+available on ``/.well-known/mercure/ui/``
+
Async dispatching
-----------------
diff --git a/messenger.rst b/messenger.rst
index 97829327d03..61763453764 100644
--- a/messenger.rst
+++ b/messenger.rst
@@ -203,8 +203,23 @@ Routing Messages to a Transport
Now that you have a transport configured, instead of handling a message immediately,
you can configure them to be sent to a transport:
+.. _messenger-message-attribute:
+
.. configuration-block::
+ .. code-block:: php-attributes
+
+ // src/Message/SmsNotification.php
+ namespace App\Message;
+
+ use Symfony\Component\Messenger\Attribute\AsMessage;
+
+ #[AsMessage('async')]
+ class SmsNotification
+ {
+ // ...
+ }
+
.. code-block:: yaml
# config/packages/messenger.yaml
@@ -251,15 +266,26 @@ you can configure them to be sent to a transport:
;
};
+.. versionadded:: 7.2
+
+ The ``#[AsMessage]`` attribute was introduced in Symfony 7.2.
+
Thanks to this, the ``App\Message\SmsNotification`` will be sent to the ``async``
transport and its handler(s) will *not* be called immediately. Any messages not
matched under ``routing`` will still be handled immediately, i.e. synchronously.
.. note::
- You may use a partial PHP namespace like ``'App\Message\*'`` to match all
- the messages within the matching namespace. The only requirement is that the
- ``'*'`` wildcard has to be placed at the end of the namespace.
+ If you configure routing with both YAML/XML/PHP configuration files and
+ PHP attributes, the configuration always takes precedence over the class
+ attribute. This behavior allows you to override routing on a per-environment basis.
+
+.. note::
+
+ When configuring the routing in separate YAML/XML/PHP files, you can use a partial
+ PHP namespace like ``'App\Message\*'`` to match all the messages within the
+ matching namespace. The only requirement is that the ``'*'`` wildcard has to
+ be placed at the end of the namespace.
You may use ``'*'`` as the message class. This will act as a default routing
rule for any message not matched under ``routing``. This is useful to ensure
@@ -275,6 +301,27 @@ to multiple transports:
.. configuration-block::
+ .. code-block:: php-attributes
+
+ // src/Message/SmsNotification.php
+ namespace App\Message;
+
+ use Symfony\Component\Messenger\Attribute\AsMessage;
+
+ #[AsMessage(['async', 'audit'])]
+ class SmsNotification
+ {
+ // ...
+ }
+
+ // if you prefer, you can also apply multiple attributes to the message class
+ #[AsMessage('async')]
+ #[AsMessage('audit')]
+ class SmsNotification
+ {
+ // ...
+ }
+
.. code-block:: yaml
# config/packages/messenger.yaml
@@ -496,6 +543,24 @@ command with the ``--all`` option:
The ``--all`` option was introduced in Symfony 7.1.
+Messages that take a long time to process may be redelivered prematurely because
+some transports assume that an unacknowledged message is lost. To prevent this
+issue, use the ``--keepalive`` command option to specify an interval (in seconds;
+default value = ``5``) at which the message is marked as "in progress". This prevents
+the message from being redelivered until the worker completes processing it:
+
+.. code-block:: terminal
+
+ $ php bin/console messenger:consume --keepalive
+
+.. note::
+
+ This option is only available for the following transports: Beanstalkd, AmazonSQS, Doctrine and Redis.
+
+.. versionadded:: 7.2
+
+ The ``--keepalive`` option was introduced in Symfony 7.2.
+
.. tip::
In a development environment and if you're using the Symfony CLI tool,
@@ -688,6 +753,14 @@ of some or all transports:
# shows stats only for some transports
$ php bin/console messenger:stats my_transport_name other_transport_name
+ # you can also output the stats in JSON format
+ $ php bin/console messenger:stats --format=json
+ $ php bin/console messenger:stats my_transport_name other_transport_name --format=json
+
+.. versionadded:: 7.2
+
+ The ``format`` option was introduced in Symfony 7.2.
+
.. note::
In order for this command to work, the configured transport's receiver must implement
@@ -776,7 +849,56 @@ message before terminating.
However, you might prefer to use different POSIX signals for graceful shutdown.
You can override default ones by setting the ``framework.messenger.stop_worker_on_signals``
-configuration option.
+configuration option:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/messenger.yaml
+ framework:
+ messenger:
+ stop_worker_on_signals:
+ - SIGTERM
+ - SIGINT
+ - SIGUSR1
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+ SIGTERM
+ SIGINT
+ SIGUSR1
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/messenger.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->messenger()
+ ->stopWorkerOnSignals(['SIGTERM', 'SIGINT', 'SIGUSR1']);
+ };
+
+.. versionadded:: 7.3
+
+ Support for signals plain names in configuration was introduced in Symfony 7.3.
+ Previously, you had to use the numeric values of signals as defined by the
+ ``pcntl`` extension's `predefined constants`_.
In some cases the ``SIGTERM`` signal is sent by Supervisor itself (e.g. stopping
a Docker container having Supervisor as its entrypoint). In these cases you
@@ -808,6 +930,8 @@ directory. For example, you can create a new ``messenger-worker.service`` file.
[Service]
ExecStart=php /path/to/your/app/bin/console messenger:consume async --time-limit=3600
+ # for Redis, set a custom consumer name for each instance
+ Environment="MESSENGER_CONSUMER_NAME=symfony-%n-%i"
Restart=always
RestartSec=30
@@ -1079,6 +1203,15 @@ and must be retried. If you throw
:class:`Symfony\\Component\\Messenger\\Exception\\RecoverableMessageHandlingException`,
the message will always be retried infinitely and ``max_retries`` setting will be ignored.
+You can define a custom retry delay (e.g., to use the value from the ``Retry-After``
+header in an HTTP response) by setting the ``retryDelay`` argument in the
+constructor of the ``RecoverableMessageHandlingException``.
+
+.. versionadded:: 7.2
+
+ The ``retryDelay`` argument and the ``getRetryDelay()`` method were introduced
+ in Symfony 7.2.
+
.. _messenger-failure-transport:
Saving & Retrying Failed Messages
@@ -1155,8 +1288,8 @@ to retry them:
# see the 10 first messages
$ php bin/console messenger:failed:show --max=10
- # see only MyClass messages
- $ php bin/console messenger:failed:show --class-filter='MyClass'
+ # see only App\Message\MyMessage messages
+ $ php bin/console messenger:failed:show --class-filter='App\Message\MyMessage'
# see the number of messages by message class
$ php bin/console messenger:failed:show --stats
@@ -1164,7 +1297,7 @@ to retry them:
# see details about a specific failure
$ php bin/console messenger:failed:show 20 -vv
- # view and retry messages one-by-one
+ # for each message, this command asks whether to retry, skip, or delete
$ php bin/console messenger:failed:retry -vv
# retry specific messages
@@ -1179,10 +1312,23 @@ to retry them:
# remove all messages in the failure transport
$ php bin/console messenger:failed:remove --all
+ # remove only App\Message\MyMessage messages
+ $ php bin/console messenger:failed:remove --class-filter='App\Message\MyMessage'
+
If the message fails again, it will be re-sent back to the failure transport
due to the normal :ref:`retry rules
`. Once the max
retry has been hit, the message will be discarded permanently.
+.. versionadded:: 7.2
+
+ The option to skip a message in the ``messenger:failed:retry`` command was
+ introduced in Symfony 7.2
+
+.. versionadded:: 7.3
+
+ The option to filter by a message class in the ``messenger:failed:remove`` command was
+ introduced in Symfony 7.3
+
Multiple Failed Transports
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1358,7 +1504,7 @@ RabbitMQ. Install it by running:
$ composer require symfony/amqp-messenger
-The AMQP transport DSN may looks like this:
+The AMQP transport DSN may look like this:
.. code-block:: env
@@ -1499,11 +1645,15 @@ The transport has a number of options:
Exchange flags
``exchange[name]``
- Name of the exchange
+ Name of the exchange. Use an empty string to use the default exchange.
``exchange[type]`` (default: ``fanout``)
Type of exchange
+.. versionadded:: 7.3
+
+ Empty string support for ``exchange[name]`` was introduced in Symfony 7.3.
+
You can also configure AMQP-specific settings on your message by adding
:class:`Symfony\\Component\\Messenger\\Bridge\\Amqp\\Transport\\AmqpStamp` to
your Envelope::
@@ -1545,7 +1695,7 @@ Install it by running:
$ composer require symfony/doctrine-messenger
-The Doctrine transport DSN may looks like this:
+The Doctrine transport DSN may look like this:
.. code-block:: env
@@ -1583,7 +1733,7 @@ The transport has a number of options:
.. note::
- Set ``redeliver_timeout`` to a greater value than your slowest message
+ Set ``redeliver_timeout`` to a greater value than your longest message
duration. Otherwise, some messages will start a second time while the
first one is still being handled.
@@ -1607,6 +1757,13 @@ in the table.
The length of time to wait for a response when calling
``PDO::pgsqlGetNotify``, in milliseconds.
+The Doctrine transport supports the ``--keepalive`` option by periodically updating
+the ``delivered_at`` timestamp to prevent the message from being redelivered.
+
+.. versionadded:: 7.3
+
+ Keepalive support was introduced in Symfony 7.3.
+
Beanstalkd Transport
~~~~~~~~~~~~~~~~~~~~
@@ -1629,8 +1786,13 @@ The Beanstalkd transport DSN may looks like this:
The transport has a number of options:
-``tube_name`` (default: ``default``)
- Name of the queue
+``bury_on_reject`` (default: ``false``)
+ When set to ``true``, rejected messages are placed into a "buried" state
+ in Beanstalkd instead of being deleted.
+
+ .. versionadded:: 7.3
+
+ The ``bury_on_reject`` option was introduced in Symfony 7.3.
``timeout`` (default: ``0``)
Message reservation timeout - in seconds. 0 will cause the server to
@@ -1640,6 +1802,33 @@ The transport has a number of options:
The message time to run before it is put back in the ready queue - in
seconds.
+``tube_name`` (default: ``default``)
+ Name of the queue
+
+The Beanstalkd transport supports the ``--keepalive`` option by using Beanstalkd's
+``touch`` command to periodically reset the job's ``ttr``.
+
+.. versionadded:: 7.2
+
+ Keepalive support was introduced in Symfony 7.2.
+
+The Beanstalkd transport lets you set the priority of the messages being dispatched.
+Use the :class:`Symfony\\Component\\Messenger\\Bridge\\Beanstalkd\\Transport\\BeanstalkdPriorityStamp`
+and pass a number to specify the priority (default = ``1024``; lower numbers mean higher priority)::
+
+ use App\Message\SomeMessage;
+ use Symfony\Component\Messenger\Stamp\BeanstalkdPriorityStamp;
+
+ $this->bus->dispatch(new SomeMessage('some data'), [
+ // 0 = highest priority
+ // 2**32 - 1 = lowest priority
+ new BeanstalkdPriorityStamp(0),
+ ]);
+
+.. versionadded:: 7.3
+
+ ``BeanstalkdPriorityStamp`` support was introduced in Symfony 7.3.
+
.. _messenger-redis-transport:
Redis Transport
@@ -1680,7 +1869,20 @@ under the transport in ``messenger.yaml``:
The Redis consumer group name
``consumer`` (default: ``consumer``)
- Consumer name used in Redis
+ Consumer name used in Redis. Allows setting an explicit consumer name identifier.
+ Recommended in environments with multiple workers to prevent duplicate message
+ processing. Typically set via an environment variable:
+
+ .. code-block:: yaml
+
+ # config/packages/messenger.yaml
+ framework:
+ messenger:
+ transports:
+ redis:
+ dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
+ options:
+ consumer: '%env(MESSENGER_CONSUMER_NAME)%'
``auto_setup`` (default: ``true``)
Whether to create the Redis group automatically
@@ -1773,6 +1975,13 @@ under the transport in ``messenger.yaml``:
in your case) to avoid memory leaks. Otherwise, all messages will remain
forever in Redis.
+The Redis transport supports the ``--keepalive`` option by using Redis's ``XCLAIM``
+command to periodically reset the message's idle time to zero.
+
+.. versionadded:: 7.3
+
+ Keepalive support was introduced in Symfony 7.3.
+
In Memory Transport
~~~~~~~~~~~~~~~~~~~
@@ -1915,6 +2124,12 @@ The transport has a number of options:
``queue_name`` (default: ``messages``)
Name of the queue
+``queue_attributes``
+ Attributes of a queue as per `SQS CreateQueue API`_. Array of strings indexed by keys of ``AsyncAws\Sqs\Enum\QueueAttributeName``.
+
+``queue_tags``
+ Cost allocation tags of a queue as per `SQS CreateQueue API`_. Array of strings indexed by strings.
+
``region`` (default: ``eu-west-1``)
Name of the AWS region
@@ -1930,6 +2145,10 @@ The transport has a number of options:
``wait_time`` (default: ``20``)
`Long polling`_ duration in seconds
+.. versionadded:: 7.3
+
+ The ``queue_attributes`` and ``queue_tags`` options were introduced in Symfony 7.3.
+
.. note::
The ``wait_time`` parameter defines the maximum duration Amazon SQS should
@@ -1962,6 +2181,13 @@ The transport has a number of options:
FIFO queues don't support setting a delay per message, a value of ``delay: 0``
is required in the retry strategy settings.
+The SQS transport supports the ``--keepalive`` option by using the ``ChangeMessageVisibility``
+action to periodically update the ``VisibilityTimeout`` of the message.
+
+.. versionadded:: 7.2
+
+ Keepalive support was introduced in Symfony 7.2.
+
Serializing Messages
~~~~~~~~~~~~~~~~~~~~
@@ -2045,6 +2271,22 @@ on a case-by-case basis via the :class:`Symfony\\Component\\Messenger\\Stamp\\Se
provides that control. See `SymfonyCasts' message serializer tutorial`_ for
details.
+Closing Connections
+~~~~~~~~~~~~~~~~~~~
+
+When using a transport that requires a connection, you can close it by calling the
+:method:`Symfony\\Component\\Messenger\\Transport\\CloseableTransportInterface::close`
+method to free up resources in long-running processes.
+
+This interface is implemented by the following transports: AmazonSqs, Amqp, and Redis.
+If you need to close a Doctrine connection, you can do so
+:ref:`using middleware `.
+
+.. versionadded:: 7.3
+
+ The ``CloseableTransportInterface`` and its ``close()`` method were introduced
+ in Symfony 7.3.
+
Running Commands And External Processes
---------------------------------------
@@ -2100,8 +2342,9 @@ will take care of creating a new process with the parameters you passed::
class CleanUpService
{
- public function __construct(private readonly MessageBusInterface $bus)
- {
+ public function __construct(
+ private readonly MessageBusInterface $bus,
+ ) {
}
public function cleanUp(): void
@@ -2112,6 +2355,34 @@ will take care of creating a new process with the parameters you passed::
}
}
+If you want to use shell features such as redirections or pipes, use the static
+:method:`Symfony\\Component\\Process\\Messenger\\RunProcessMessage::fromShellCommandline` factory method::
+
+ use Symfony\Component\Messenger\MessageBusInterface;
+ use Symfony\Component\Process\Messenger\RunProcessMessage;
+
+ class CleanUpService
+ {
+ public function __construct(
+ private readonly MessageBusInterface $bus,
+ ) {
+ }
+
+ public function cleanUp(): void
+ {
+ $this->bus->dispatch(RunProcessMessage::fromShellCommandline('echo "Hello World" > var/log/hello.txt'));
+
+ // ...
+ }
+ }
+
+For more information, read the documentation about
+:ref:`using features from the OS shell `.
+
+.. versionadded:: 7.3
+
+ The ``RunProcessMessage::fromShellCommandline()`` method was introduced in Symfony 7.3.
+
Once handled, the handler will return a
:class:`Symfony\\Component\\Process\\Messenger\\RunProcessContext` which
contains many useful information such as the exit code or the output of the
@@ -2258,6 +2529,15 @@ wherever you need a query bus behavior instead of the ``MessageBusInterface``::
}
}
+You can also add new stamps when handling a message; they will be appended
+to the existing ones::
+
+ $this->handle(new SomeMessage($data), [new SomeStamp(), new AnotherStamp()]);
+
+.. versionadded:: 7.3
+
+ The ``$stamps`` parameter of the ``handle()`` method was introduced in Symfony 7.3.
+
Customizing Handlers
--------------------
@@ -2352,7 +2632,8 @@ Possible options to configure with tags are:
Name of the method that will process the message.
``priority``
- Priority of the handler when multiple handlers can process the same message.
+ Defines the order in which the handler is executed when multiple handlers
+ can process the same message; those with higher priority run first.
.. _handler-subscriber-options:
@@ -2478,7 +2759,7 @@ using the ``DispatchAfterCurrentBusMiddleware`` and adding a
{
public function __construct(
private MailerInterface $mailer,
- EntityManagerInterface $em,
+ private EntityManagerInterface $em,
) {
}
@@ -2830,6 +3111,11 @@ and a different instance will be created per bus.
$bus->middleware()->id('App\Middleware\AnotherMiddleware');
};
+.. tip::
+
+ If you have installed the MakerBundle, you can use the ``make:messenger-middleware``
+ command to bootstrap the creation of your own messenger middleware.
+
.. _middleware-doctrine:
Middleware for Doctrine
@@ -3478,3 +3764,5 @@ Learn more
.. _`high connection churn`: https://www.rabbitmq.com/connections.html#high-connection-churn
.. _`article about CQRS`: https://martinfowler.com/bliki/CQRS.html
.. _`SSL context options`: https://php.net/context.ssl
+.. _`predefined constants`: https://www.php.net/pcntl.constants
+.. _`SQS CreateQueue API`: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_CreateQueue.html
diff --git a/notifier.rst b/notifier.rst
index b74f4eb02e0..49a1c2d533b 100644
--- a/notifier.rst
+++ b/notifier.rst
@@ -33,7 +33,14 @@ The notifier component supports the following channels:
services like Slack and Telegram;
* :ref:`Email channel ` integrates the :doc:`Symfony Mailer `;
* Browser channel uses :ref:`flash messages `.
-* :ref:`Push channel ` sends notifications to phones and browsers via push notifications.
+* :ref:`Push channel ` sends notifications to phones and
+ browsers via push notifications.
+* :ref:`Desktop channel ` displays desktop notifications
+ on the same host machine.
+
+.. versionadded:: 7.2
+
+ The ``Desktop`` channel was introduced in Symfony 7.2.
.. _notifier-sms-channel:
@@ -61,6 +68,7 @@ Service
`AllMySms`_ **Install**: ``composer require symfony/all-my-sms-notifier`` \
**DSN**: ``allmysms://LOGIN:APIKEY@default?from=FROM`` \
**Webhook support**: No
+ **Extra properties in SentMessage**: ``nbSms``, ``balance``, ``cost``
`AmazonSns`_ **Install**: ``composer require symfony/amazon-sns-notifier`` \
**DSN**: ``sns://ACCESS_KEY:SECRET_KEY@default?region=REGION`` \
**Webhook support**: No
@@ -69,7 +77,7 @@ Service
**Webhook support**: No
`Brevo`_ **Install**: ``composer require symfony/brevo-notifier`` \
**DSN**: ``brevo://API_KEY@default?sender=SENDER`` \
- **Webhook support**: No
+ **Webhook support**: Yes
`Clickatell`_ **Install**: ``composer require symfony/clickatell-notifier`` \
**DSN**: ``clickatell://ACCESS_TOKEN@default?from=FROM`` \
**Webhook support**: No
@@ -132,9 +140,13 @@ Service
`OvhCloud`_ **Install**: ``composer require symfony/ovh-cloud-notifier`` \
**DSN**: ``ovhcloud://APPLICATION_KEY:APPLICATION_SECRET@default?consumer_key=CONSUMER_KEY&service_name=SERVICE_NAME`` \
**Webhook support**: No
+ **Extra properties in SentMessage**:: ``totalCreditsRemoved``
`Plivo`_ **Install**: ``composer require symfony/plivo-notifier`` \
**DSN**: ``plivo://AUTH_ID:AUTH_TOKEN@default?from=FROM`` \
**Webhook support**: No
+`Primotexto`_ **Install**: ``composer require symfony/primotexto-notifier`` \
+ **DSN**: ``primotexto://API_KEY@default?from=FROM`` \
+ **Webhook support**: No
`Redlink`_ **Install**: ``composer require symfony/redlink-notifier`` \
**DSN**: ``redlink://API_KEY:APP_KEY@default?from=SENDER_NAME&version=API_VERSION`` \
**Webhook support**: No
@@ -156,6 +168,9 @@ Service
`Sinch`_ **Install**: ``composer require symfony/sinch-notifier`` \
**DSN**: ``sinch://ACCOUNT_ID:AUTH_TOKEN@default?from=FROM`` \
**Webhook support**: No
+`Sipgate`_ **Install**: ``composer require symfony/sipgate-notifier`` \
+ **DSN**: ``sipgate://TOKEN_ID:TOKEN@default?senderId=SENDER_ID`` \
+ **Webhook support**: No
`SmsSluzba`_ **Install**: ``composer require symfony/sms-sluzba-notifier`` \
**DSN**: ``sms-sluzba://USERNAME:PASSWORD@default`` \
**Webhook support**: No
@@ -164,7 +179,7 @@ Service
**Webhook support**: No
`Smsbox`_ **Install**: ``composer require symfony/smsbox-notifier`` \
**DSN**: ``smsbox://APIKEY@default?mode=MODE&strategy=STRATEGY&sender=SENDER`` \
- **Webhook support**: No
+ **Webhook support**: Yes
`SmsBiuras`_ **Install**: ``composer require symfony/sms-biuras-notifier`` \
**DSN**: ``smsbiuras://UID:API_KEY@default?from=FROM&test_mode=0`` \
**Webhook support**: No
@@ -180,6 +195,9 @@ Service
`SpotHit`_ **Install**: ``composer require symfony/spot-hit-notifier`` \
**DSN**: ``spothit://TOKEN@default?from=FROM`` \
**Webhook support**: No
+`Sweego`_ **Install**: ``composer require symfony/sweego-notifier`` \
+ **DSN**: ``sweego://API_KEY@default?region=REGION&campaign_type=CAMPAIGN_TYPE`` \
+ **Webhook support**: Yes
`Telnyx`_ **Install**: ``composer require symfony/telnyx-notifier`` \
**DSN**: ``telnyx://API_KEY@default?from=FROM&messaging_profile_id=MESSAGING_PROFILE_ID`` \
**Webhook support**: No
@@ -216,6 +234,16 @@ Service
The ``Smsbox``, ``SmsSluzba``, ``SMSense``, ``LOX24`` and ``Unifonic``
integrations were introduced in Symfony 7.1.
+.. versionadded:: 7.2
+
+ The ``Primotexto``, ``Sipgate`` and ``Sweego`` integrations were introduced in Symfony 7.2.
+
+.. versionadded:: 7.3
+
+ Webhook support for the ``Brevo`` integration was introduced in Symfony 7.3.
+ The extra properties in ``SentMessage`` for ``AllMySms`` and ``OvhCloud``
+ providers were introduced in Symfony 7.3 too.
+
.. deprecated:: 7.1
The `Sms77`_ integration is deprecated since
@@ -330,35 +358,71 @@ 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
-======================================= ==================================== =============================================================================
-`AmazonSns`_ ``symfony/amazon-sns-notifier`` ``sns://ACCESS_KEY:SECRET_KEY@default?region=REGION``
-`Bluesky`_ ``symfony/bluesky-notifier`` ``bluesky://USERNAME:PASSWORD@default``
-`Chatwork`_ ``symfony/chatwork-notifier`` ``chatwork://API_TOKEN@default?room_id=ID``
-`Discord`_ ``symfony/discord-notifier`` ``discord://TOKEN@default?webhook_id=ID``
-`FakeChat`_ ``symfony/fake-chat-notifier`` ``fakechat+email://default?to=TO&from=FROM`` or ``fakechat+logger://default``
-`Firebase`_ ``symfony/firebase-notifier`` ``firebase://USERNAME:PASSWORD@default``
-`Gitter`_ ``symfony/gitter-notifier`` ``gitter://TOKEN@default?room_id=ROOM_ID``
-`GoogleChat`_ ``symfony/google-chat-notifier`` ``googlechat://ACCESS_KEY:ACCESS_TOKEN@default/SPACE?thread_key=THREAD_KEY``
-`LINE Notify`_ ``symfony/line-notify-notifier`` ``linenotify://TOKEN@default``
-`LinkedIn`_ ``symfony/linked-in-notifier`` ``linkedin://TOKEN:USER_ID@default``
-`Mastodon`_ ``symfony/mastodon-notifier`` ``mastodon://ACCESS_TOKEN@HOST``
-`Mattermost`_ ``symfony/mattermost-notifier`` ``mattermost://ACCESS_TOKEN@HOST/PATH?channel=CHANNEL``
-`Mercure`_ ``symfony/mercure-notifier`` ``mercure://HUB_ID?topic=TOPIC``
-`MicrosoftTeams`_ ``symfony/microsoft-teams-notifier`` ``microsoftteams://default/PATH``
-`RocketChat`_ ``symfony/rocket-chat-notifier`` ``rocketchat://TOKEN@ENDPOINT?channel=CHANNEL``
-`Slack`_ ``symfony/slack-notifier`` ``slack://TOKEN@default?channel=CHANNEL``
-`Telegram`_ ``symfony/telegram-notifier`` ``telegram://TOKEN@default?channel=CHAT_ID``
-`Twitter`_ ``symfony/twitter-notifier`` ``twitter://API_KEY:API_SECRET:ACCESS_TOKEN:ACCESS_SECRET@default``
-`Zendesk`_ ``symfony/zendesk-notifier`` ``zendesk://EMAIL:TOKEN@SUBDOMAIN``
-`Zulip`_ ``symfony/zulip-notifier`` ``zulip://EMAIL:TOKEN@HOST?channel=CHANNEL``
-====================================== ==================================== =============================================================================
+====================================== =====================================================================================
+Service
+====================================== =====================================================================================
+`AmazonSns`_ **Install**: ``composer require symfony/amazon-sns-notifier`` \
+ **DSN**: ``sns://ACCESS_KEY:SECRET_KEY@default?region=REGION``
+`Bluesky`_ **Install**: ``composer require symfony/bluesky-notifier`` \
+ **DSN**: ``bluesky://USERNAME:PASSWORD@default``
+ **Extra properties in SentMessage**: ``cid``
+`Chatwork`_ **Install**: ``composer require symfony/chatwork-notifier`` \
+ **DSN**: ``chatwork://API_TOKEN@default?room_id=ID``
+`Discord`_ **Install**: ``composer require symfony/discord-notifier`` \
+ **DSN**: ``discord://TOKEN@default?webhook_id=ID``
+`FakeChat`_ **Install**: ``composer require symfony/fake-chat-notifier`` \
+ **DSN**: ``fakechat+email://default?to=TO&from=FROM`` or ``fakechat+logger://default``
+`Firebase`_ **Install**: ``composer require symfony/firebase-notifier`` \
+ **DSN**: ``firebase://USERNAME:PASSWORD@default``
+`GoogleChat`_ **Install**: ``composer require symfony/google-chat-notifier`` \
+ **DSN**: ``googlechat://ACCESS_KEY:ACCESS_TOKEN@default/SPACE?thread_key=THREAD_KEY``
+`LINE Bot`_ **Install**: ``composer require symfony/line-bot-notifier`` \
+ **DSN**: ``linebot://TOKEN@default?receiver=RECEIVER``
+`LINE Notify`_ **Install**: ``composer require symfony/line-notify-notifier`` \
+ **DSN**: ``linenotify://TOKEN@default``
+`LinkedIn`_ **Install**: ``composer require symfony/linked-in-notifier`` \
+ **DSN**: ``linkedin://TOKEN:USER_ID@default``
+`Mastodon`_ **Install**: ``composer require symfony/mastodon-notifier`` \
+ **DSN**: ``mastodon://ACCESS_TOKEN@HOST``
+`Matrix`_ **Install**: ``composer require symfony/matrix-notifier`` \
+ **DSN**: ``matrix://HOST:PORT/?accessToken=ACCESSTOKEN&ssl=SSL``
+`Mattermost`_ **Install**: ``composer require symfony/mattermost-notifier`` \
+ **DSN**: ``mattermost://ACCESS_TOKEN@HOST/PATH?channel=CHANNEL``
+`Mercure`_ **Install**: ``composer require symfony/mercure-notifier`` \
+ **DSN**: ``mercure://HUB_ID?topic=TOPIC``
+`MicrosoftTeams`_ **Install**: ``composer require symfony/microsoft-teams-notifier`` \
+ **DSN**: ``microsoftteams://default/PATH``
+`RocketChat`_ **Install**: ``composer require symfony/rocket-chat-notifier`` \
+ **DSN**: ``rocketchat://TOKEN@ENDPOINT?channel=CHANNEL``
+`Slack`_ **Install**: ``composer require symfony/slack-notifier`` \
+ **DSN**: ``slack://TOKEN@default?channel=CHANNEL``
+`Telegram`_ **Install**: ``composer require symfony/telegram-notifier`` \
+ **DSN**: ``telegram://TOKEN@default?channel=CHAT_ID``
+`Twitter`_ **Install**: ``composer require symfony/twitter-notifier`` \
+ **DSN**: ``twitter://API_KEY:API_SECRET:ACCESS_TOKEN:ACCESS_SECRET@default``
+`Zendesk`_ **Install**: ``composer require symfony/zendesk-notifier`` \
+ **DSN**: ``zendesk://EMAIL:TOKEN@SUBDOMAIN``
+`Zulip`_ **Install**: ``composer require symfony/zulip-notifier`` \
+ **DSN**: ``zulip://EMAIL:TOKEN@HOST?channel=CHANNEL``
+====================================== =====================================================================================
.. versionadded:: 7.1
The ``Bluesky`` integration was introduced in Symfony 7.1.
+.. versionadded:: 7.2
+
+ The ``LINE Bot`` integration was introduced in Symfony 7.2.
+
+.. deprecated:: 7.2
+
+ The ``Gitter`` integration was removed in Symfony 7.2 because that service
+ no longer provides an API.
+
+.. versionadded:: 7.3
+
+ The ``Matrix`` integration was introduced in Symfony 7.3.
+
.. warning::
By default, if you have the :doc:`Messenger component ` installed,
@@ -540,18 +604,26 @@ The push channel is used to send notifications to users by using
:class:`Symfony\\Component\\Notifier\\Texter` classes. Symfony provides
integration with these push services:
-=============== ==================================== ==============================================================================
-Service Package DSN
-=============== ==================================== ==============================================================================
-`Engagespot`_ ``symfony/engagespot-notifier`` ``engagespot://API_KEY@default?campaign_name=CAMPAIGN_NAME``
-`Expo`_ ``symfony/expo-notifier`` ``expo://Token@default``
-`Novu`_ ``symfony/novu-notifier`` ``novu://API_KEY@default``
-`Ntfy`_ ``symfony/ntfy-notifier`` ``ntfy://default/TOPIC``
-`OneSignal`_ ``symfony/one-signal-notifier`` ``onesignal://APP_ID:API_KEY@default?defaultRecipientId=DEFAULT_RECIPIENT_ID``
-`PagerDuty`_ ``symfony/pager-duty-notifier`` ``pagerduty://TOKEN@SUBDOMAIN``
-`Pushover`_ ``symfony/pushover-notifier`` ``pushover://USER_KEY:APP_TOKEN@default``
-`Pushy`_ ``symfony/pushy-notifier`` ``pushy://API_KEY@default``
-=============== ==================================== ==============================================================================
+=============== =======================================================================================
+Service
+=============== =======================================================================================
+`Engagespot`_ **Install**: ``composer require symfony/engagespot-notifier`` \
+ **DSN**: ``engagespot://API_KEY@default?campaign_name=CAMPAIGN_NAME``
+`Expo`_ **Install**: ``composer require symfony/expo-notifier`` \
+ **DSN**: ``expo://TOKEN@default``
+`Novu`_ **Install**: ``composer require symfony/novu-notifier`` \
+ **DSN**: ``novu://API_KEY@default``
+`Ntfy`_ **Install**: ``composer require symfony/ntfy-notifier`` \
+ **DSN**: ``ntfy://default/TOPIC``
+`OneSignal`_ **Install**: ``composer require symfony/one-signal-notifier`` \
+ **DSN**: ``onesignal://APP_ID:API_KEY@default?defaultRecipientId=DEFAULT_RECIPIENT_ID``
+`PagerDuty`_ **Install**: ``composer require symfony/pager-duty-notifier`` \
+ **DSN**: ``pagerduty://TOKEN@SUBDOMAIN``
+`Pushover`_ **Install**: ``composer require symfony/pushover-notifier`` \
+ **DSN**: ``pushover://USER_KEY:APP_TOKEN@default``
+`Pushy`_ **Install**: ``composer require symfony/pushy-notifier`` \
+ **DSN**: ``pushy://API_KEY@default``
+=============== =======================================================================================
To enable a texter, add the correct DSN in your ``.env`` file and
configure the ``texter_transports``:
@@ -607,6 +679,124 @@ configure the ``texter_transports``:
;
};
+.. _notifier-desktop-channel:
+
+Desktop Channel
+~~~~~~~~~~~~~~~
+
+The desktop channel is used to display local desktop notifications on the same
+host machine using :class:`Symfony\\Component\\Notifier\\Texter` classes. Currently,
+Symfony is integrated with the following providers:
+
+=============== ================================================ ==============================================================================
+Provider Install DSN
+=============== ================================================ ==============================================================================
+`JoliNotif`_ ``composer require symfony/joli-notif-notifier`` ``jolinotif://default``
+=============== ================================================ ==============================================================================
+
+.. versionadded:: 7.2
+
+ The JoliNotif bridge was introduced in Symfony 7.2.
+
+If you are using :ref:`Symfony Flex `, installing that package will
+also create the necessary environment variable and configuration. Otherwise, you'll
+need to add the following manually:
+
+1) Add the correct DSN in your ``.env`` file:
+
+.. code-block:: bash
+
+ # .env
+ JOLINOTIF=jolinotif://default
+
+2) Update the Notifier configuration to add a new texter transport:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/notifier.yaml
+ framework:
+ notifier:
+ texter_transports:
+ jolinotif: '%env(JOLINOTIF)%'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+ %env(JOLINOTIF)%
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/notifier.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->notifier()
+ ->texterTransport('jolinotif', env('JOLINOTIF'))
+ ;
+ };
+
+Now you can send notifications to your desktop as follows::
+
+ // src/Notifier/SomeService.php
+ use Symfony\Component\Notifier\Message\DesktopMessage;
+ use Symfony\Component\Notifier\TexterInterface;
+ // ...
+
+ class SomeService
+ {
+ public function __construct(
+ private TexterInterface $texter,
+ ) {
+ }
+
+ public function notifyNewSubscriber(User $user): void
+ {
+ $message = new DesktopMessage(
+ 'New subscription! 🎉',
+ sprintf('%s is a new subscriber', $user->getFullName())
+ );
+
+ $this->texter->send($message);
+ }
+ }
+
+These notifications can be customized further, and depending on your operating system,
+they may support features like custom sounds, icons, and more::
+
+ use Symfony\Component\Notifier\Bridge\JoliNotif\JoliNotifOptions;
+ // ...
+
+ $options = (new JoliNotifOptions())
+ ->setIconPath('/path/to/icons/error.png')
+ ->setExtraOption('sound', 'sosumi')
+ ->setExtraOption('url', 'https://example.com');
+
+ $message = new DesktopMessage('Production is down', <<send($message);
+
Configure to use Failover or Round-Robin Transports
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -910,9 +1100,10 @@ and its ``asChatMessage()`` method::
The
:class:`Symfony\\Component\\Notifier\\Notification\\SmsNotificationInterface`,
-:class:`Symfony\\Component\\Notifier\\Notification\\EmailNotificationInterface`
-and
+:class:`Symfony\\Component\\Notifier\\Notification\\EmailNotificationInterface`,
:class:`Symfony\\Component\\Notifier\\Notification\\PushNotificationInterface`
+and
+:class:`Symfony\\Component\\Notifier\\Notification\\DesktopNotificationInterface`
also exists to modify messages sent to those channels.
Customize Browser Notifications (Flash Messages)
@@ -1093,19 +1284,21 @@ is dispatched. Listeners receive a
.. _`Firebase`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Firebase/README.md
.. _`FreeMobile`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/FreeMobile/README.md
.. _`GatewayApi`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/GatewayApi/README.md
-.. _`Gitter`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Gitter/README.md
-.. _`GoIP`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/GoIp/README.md
+.. _`GoIP`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/GoIP/README.md
.. _`GoogleChat`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/GoogleChat/README.md
.. _`Infobip`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Infobip/README.md
.. _`Iqsms`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Iqsms/README.md
.. _`iSendPro`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Isendpro/README.md
+.. _`JoliNotif`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/JoliNotif/README.md
.. _`KazInfoTeh`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/KazInfoTeh/README.md
+.. _`LINE Bot`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LineBot/README.md
.. _`LINE Notify`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LineNotify/README.md
.. _`LightSms`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LightSms/README.md
.. _`LinkedIn`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/LinkedIn/README.md
.. _`LOX24`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Lox24/README.md
.. _`Mailjet`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Mailjet/README.md
.. _`Mastodon`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Mastodon/README.md
+.. _`Matrix`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Matrix/README.md
.. _`Mattermost`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Mattermost/README.md
.. _`Mercure`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Mercure/README.md
.. _`MessageBird`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/MessageBird/README.md
@@ -1121,6 +1314,7 @@ is dispatched. Listeners receive a
.. _`OvhCloud`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/OvhCloud/README.md
.. _`PagerDuty`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/PagerDuty/README.md
.. _`Plivo`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Plivo/README.md
+.. _`Primotexto`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Primotexto/README.md
.. _`Pushover`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Pushover/README.md
.. _`Pushy`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Pushy/README.md
.. _`Redlink`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Redlink/README.md
@@ -1133,6 +1327,7 @@ is dispatched. Listeners receive a
.. _`Seven.io`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sevenio/README.md
.. _`SimpleTextin`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SimpleTextin/README.md
.. _`Sinch`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sinch/README.md
+.. _`Sipgate`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sipgate/README.md
.. _`Slack`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Slack/README.md
.. _`Sms77`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sms77/README.md
.. _`SmsBiuras`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SmsBiuras/README.md
@@ -1142,6 +1337,7 @@ is dispatched. Listeners receive a
.. _`SMSense`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SMSense/README.md
.. _`SmsSluzba`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SmsSluzba/README.md
.. _`SpotHit`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/SpotHit/README.md
+.. _`Sweego`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Sweego/README.md
.. _`Telegram`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Telegram/README.md
.. _`Telnyx`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/Telnyx/README.md
.. _`TurboSms`: https://github.com/symfony/symfony/blob/{version}/src/Symfony/Component/Notifier/Bridge/TurboSms/README.md
diff --git a/object_mapper.rst b/object_mapper.rst
new file mode 100644
index 00000000000..625466ffefc
--- /dev/null
+++ b/object_mapper.rst
@@ -0,0 +1,738 @@
+Object Mapper
+=============
+
+.. versionadded:: 7.3
+
+ The ObjectMapper component was introduced in Symfony 7.3 as an
+ :doc:`experimental feature `.
+
+This component transforms one object into another, simplifying tasks such as
+converting DTOs (Data Transfer Objects) into entities or vice versa. It can also
+be helpful when decoupling API input/output from internal models, particularly
+when working with legacy code or implementing hexagonal architectures.
+
+Installation
+------------
+
+Run this command to install the component before using it:
+
+.. code-block:: terminal
+
+ $ composer require symfony/object-mapper
+
+Usage
+-----
+
+The object mapper service will be :doc:`autowired `
+automatically in controllers or services when type-hinting for
+:class:`Symfony\\Component\\ObjectMapper\\ObjectMapperInterface`::
+
+ // src/Controller/UserController.php
+ namespace App\Controller;
+
+ use App\Dto\UserInput;
+ use App\Entity\User;
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\ObjectMapper\ObjectMapperInterface;
+
+ class UserController extends AbstractController
+ {
+ public function updateUser(UserInput $userInput, ObjectMapperInterface $objectMapper): Response
+ {
+ $user = new User();
+ // Map properties from UserInput to User
+ $objectMapper->map($userInput, $user);
+
+ // ... persist $user and return response
+ return new Response('User updated!');
+ }
+ }
+
+Basic Mapping
+-------------
+
+The core functionality is provided by the ``map()`` method. It accepts a
+source object and maps its properties to a target. The target can either be
+a class name (to create a new instance) or an existing object (to update it).
+
+Mapping to a New Object
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Provide the target class name as the second argument::
+
+ use App\Dto\ProductInput;
+ use App\Entity\Product;
+ use Symfony\Component\ObjectMapper\ObjectMapper;
+
+ $productInput = new ProductInput();
+ $productInput->name = 'Wireless Mouse';
+ $productInput->sku = 'WM-1024';
+
+ $mapper = new ObjectMapper();
+ // creates a new Product instance and maps properties from $productInput
+ $product = $mapper->map($productInput, Product::class);
+
+ // $product is now an instance of Product
+ // with $product->name = 'Wireless Mouse' and $product->sku = 'WM-1024'
+
+Mapping to an Existing Object
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Provide an existing object instance as the second argument to update it::
+
+ use App\Dto\ProductUpdateInput;
+ use App\Entity\Product;
+ use Symfony\Component\ObjectMapper\ObjectMapper;
+
+ $product = $productRepository->find(1);
+
+ $updateInput = new ProductUpdateInput();
+ $updateInput->price = 99.99;
+
+ $mapper = new ObjectMapper();
+ // updates the existing $product instance
+ $mapper->map($updateInput, $product);
+
+ // $product->price is now 99.99
+
+Mapping from ``stdClass``
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The source object can also be an instance of ``stdClass``. This can be
+useful when working with decoded JSON data or loosely typed input::
+
+ use App\Entity\Product;
+ use Symfony\Component\ObjectMapper\ObjectMapper;
+
+ $productData = new \stdClass();
+ $productData->name = 'Keyboard';
+ $productData->sku = 'KB-001';
+
+ $mapper = new ObjectMapper();
+ $product = $mapper->map($productData, Product::class);
+
+ // $product is an instance of Product with properties mapped from $productData
+
+Configuring Mapping with Attributes
+-----------------------------------
+
+ObjectMapper uses PHP attributes to configure how properties are mapped.
+The primary attribute is :class:`Symfony\\Component\\ObjectMapper\\Attribute\\Map`.
+
+Defining the Default Target Class
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Apply ``#[Map]`` to the source class to define its default mapping target::
+
+ // src/Dto/ProductInput.php
+ namespace App\Dto;
+
+ use App\Entity\Product;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ #[Map(target: Product::class)]
+ class ProductInput
+ {
+ public string $name = '';
+ public string $sku = '';
+ }
+
+ // now you can call map() without the second argument if ProductInput is the source:
+ $mapper = new ObjectMapper();
+ $product = $mapper->map($productInput); // Maps to Product automatically
+
+Configuring Property Mapping
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can apply the ``#[Map]`` attribute to properties to customize their mapping behavior:
+
+* ``target``: Specifies the name of the property in the target object;
+* ``source``: Specifies the name of the property in the source object (useful
+ when mapping is defined on the target, see below);
+* ``if``: Defines a condition for mapping the property;
+* ``transform``: Applies a transformation to the value before mapping.
+
+This is how it looks in practice::
+
+ // src/Dto/OrderInput.php
+ namespace App\Dto;
+
+ use App\Entity\Order;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ #[Map(target: Order::class)]
+ class OrderInput
+ {
+ // map 'customerEmail' from source to 'email' in target
+ #[Map(target: 'email')]
+ public string $customerEmail = '';
+
+ // do not map this property at all
+ #[Map(if: false)]
+ public string $internalNotes = '';
+
+ // only map 'discountCode' if it's a non-empty string
+ // (uses PHP's strlen() function as a condition)
+ #[Map(if: 'strlen')]
+ public ?string $discountCode = null;
+ }
+
+By default, if a property exists in the source but not in the target, it is
+ignored. If a property exists in both and no ``#[Map]`` is defined, the mapper
+assumes a direct mapping when names match.
+
+Conditional Mapping with Services
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For complex conditions, you can use a dedicated service implementing
+:class:`Symfony\\Component\\ObjectMapper\\ConditionCallableInterface`::
+
+ // src/ObjectMapper/IsShippableCondition.php
+ namespace App\ObjectMapper;
+
+ use App\Dto\OrderInput;
+ use App\Entity\Order; // Target type hint
+ use Symfony\Component\ObjectMapper\ConditionCallableInterface;
+
+ /**
+ * @implements ConditionCallableInterface
+ */
+ final class IsShippableCondition implements ConditionCallableInterface
+ {
+ public function __invoke(mixed $value, object $source, ?object $target): bool
+ {
+ // example: Only map shipping address if order total is above 50
+ return $source->total > 50;
+ }
+ }
+
+Then, pass the service name (its class name by default) to the ``if`` parameter::
+
+ // src/Dto/OrderInput.php
+ namespace App\Dto;
+
+ use App\Entity\Order;
+ use App\ObjectMapper\IsShippableCondition;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ #[Map(target: Order::class)]
+ class OrderInput
+ {
+ public float $total = 0.0;
+
+ #[Map(if: IsShippableCondition::class)]
+ public ?string $shippingAddress = null;
+ }
+
+For this to work, ``IsShippableCondition`` must be registered as a service.
+
+.. _object_mapper-conditional-property-target:
+
+Conditional Property Mapping based on Target
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When a source class maps to multiple targets, you may want to include or exclude
+certain properties depending on which target is being used. Use the
+:class:`Symfony\\Component\\ObjectMapper\\Condition\\TargetClass` condition within
+the ``if`` parameter of a property's ``#[Map]`` attribute to achieve this.
+
+This pattern is useful for building multiple representations (e.g., public vs. admin)
+from a given source object, and can be used as an alternative to
+:ref:`serialization groups `::
+
+ // src/Entity/User.php
+ namespace App\Entity;
+
+ use App\Dto\AdminUserProfile;
+ use App\Dto\PublicUserProfile;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+ use Symfony\Component\ObjectMapper\Condition\TargetClass;
+
+ // this User entity can be mapped to two different DTOs
+ #[Map(target: PublicUserProfile::class)]
+ #[Map(target: AdminUserProfile::class)]
+ class User
+ {
+ // map 'lastLoginIp' to 'ipAddress' ONLY when the target is AdminUserProfile
+ #[Map(target: 'ipAddress', if: new TargetClass(AdminUserProfile::class))]
+ public ?string $lastLoginIp = '192.168.1.100';
+
+ // map 'registrationDate' to 'memberSince' for both targets
+ #[Map(target: 'memberSince')]
+ public \DateTimeImmutable $registrationDate;
+
+ public function __construct() {
+ $this->registrationDate = new \DateTimeImmutable();
+ }
+ }
+
+ // src/Dto/PublicUserProfile.php
+ namespace App\Dto;
+ class PublicUserProfile
+ {
+ public \DateTimeImmutable $memberSince;
+ // no $ipAddress property here
+ }
+
+ // src/Dto/AdminUserProfile.php
+ namespace App\Dto;
+ class AdminUserProfile
+ {
+ public \DateTimeImmutable $memberSince;
+ public ?string $ipAddress = null; // mapped from lastLoginIp
+ }
+
+ // usage:
+ $user = new User();
+ $mapper = new ObjectMapper();
+
+ $publicProfile = $mapper->map($user, PublicUserProfile::class);
+ // no IP address available
+
+ $adminProfile = $mapper->map($user, AdminUserProfile::class);
+ // $adminProfile->ipAddress = '192.168.1.100'
+
+Transforming Values
+-------------------
+
+Use the ``transform`` option within ``#[Map]`` to change a value before it is
+assigned to the target. This can be a callable (e.g., a built-in PHP function,
+static method, or anonymous function) or a service implementing
+:class:`Symfony\\Component\\ObjectMapper\\TransformCallableInterface`.
+
+Using Callables
+~~~~~~~~~~~~~~~
+
+Consider the following static utility method::
+
+ // src/Util/PriceFormatter.php
+ namespace App\Util;
+
+ class PriceFormatter
+ {
+ public static function format(float $value, object $source): string
+ {
+ return number_format($value, 2, '.', '');
+ }
+ }
+
+You can use that method to format a property when mapping it::
+
+ // src/Dto/ProductInput.php
+ namespace App\Dto;
+
+ use App\Entity\Product;
+ use App\Util\PriceFormatter;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ #[Map(target: Product::class)]
+ class ProductInput
+ {
+ // use a static method from another class for formatting
+ #[Map(target: 'displayPrice', transform: [PriceFormatter::class, 'format'])]
+ public float $price = 0.0;
+
+ // can also use built-in PHP functions
+ #[Map(transform: 'intval')]
+ public string $stockLevel = '100';
+ }
+
+Using Transformer Services
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Similar to conditions, complex transformations can be encapsulated in services
+implementing :class:`Symfony\\Component\\ObjectMapper\\TransformCallableInterface`::
+
+ // src/ObjectMapper/FullNameTransformer.php
+ namespace App\ObjectMapper;
+
+ use App\Dto\UserInput;
+ use App\Entity\User;
+ use Symfony\Component\ObjectMapper\TransformCallableInterface;
+
+ /**
+ * @implements TransformCallableInterface
+ */
+ final class FullNameTransformer implements TransformCallableInterface
+ {
+ public function __invoke(mixed $value, object $source, ?object $target): mixed
+ {
+ return trim($source->firstName . ' ' . $source->lastName);
+ }
+ }
+
+Then, use this service to format the mapped property::
+
+ // src/Dto/UserInput.php
+ namespace App\Dto;
+
+ use App\Entity\User;
+ use App\ObjectMapper\FullNameTransformer;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ #[Map(target: User::class)]
+ class UserInput
+ {
+ // this property's value will be generated by the transformer
+ #[Map(target: 'fullName', transform: FullNameTransformer::class)]
+ public string $firstName = '';
+
+ public string $lastName = '';
+ }
+
+Class-Level Transformation
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can define a transformation at the class level using the ``transform``
+parameter on the ``#[Map]`` attribute. This callable runs *after* the target
+object is created (if the target is a class name, ``newInstanceWithoutConstructor``
+is used), but *before* any properties are mapped. It must return a correctly
+initialized instance of the target class (replacing the one created by the mapper
+if needed)::
+
+ // src/Dto/LegacyUserData.php
+ namespace App\Dto;
+
+ use App\Entity\User;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ // use a static factory method on the target User class for instantiation
+ #[Map(target: User::class, transform: [User::class, 'createFromLegacy'])]
+ class LegacyUserData
+ {
+ public int $userId = 0;
+ public string $name = '';
+ }
+
+And the related target object must define the ``createFromLegacy()`` method::
+
+ // src/Entity/User.php
+ namespace App\Entity;
+ class User
+ {
+ public string $name = '';
+ private int $legacyId = 0;
+
+ // uses a private constructor to avoid direct instantiation
+ private function __construct() {}
+
+ public static function createFromLegacy(mixed $value, object $source): self
+ {
+ // $value is the initially created (empty) User object
+ // $source is the LegacyUserData object
+ $user = new self();
+ $user->legacyId = $source->userId;
+
+ // property mapping will happen *after* this method returns $user
+ return $user;
+ }
+ }
+
+Mapping Multiple Targets
+------------------------
+
+A source class can be configured to map to multiple different target classes.
+Apply the ``#[Map]`` attribute multiple times at the class level, typically
+using the ``if`` condition to determine which target is appropriate based on the
+source object's state or other logic::
+
+ // src/Dto/EventInput.php
+ namespace App\Dto;
+
+ use App\Entity\OnlineEvent;
+ use App\Entity\PhysicalEvent;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ #[Map(target: OnlineEvent::class, if: [self::class, 'isOnline'])]
+ #[Map(target: PhysicalEvent::class, if: [self::class, 'isPhysical'])]
+ class EventInput
+ {
+ public string $type = 'online'; // e.g., 'online' or 'physical'
+ public string $title = '';
+
+ /**
+ * In class-level conditions, $value is null.
+ */
+ public static function isOnline(?mixed $value, object $source): bool
+ {
+ return 'online' === $source->type;
+ }
+
+ public static function isPhysical(?mixed $value, object $source): bool
+ {
+ return 'physical' === $source->type;
+ }
+ }
+
+ // consider that the src/Entity/OnlineEvent.php and PhysicalEvent.php
+ // files exist and define the needed classes
+
+ // usage:
+ $eventInput = new EventInput();
+ $eventInput->type = 'physical';
+ $mapper = new ObjectMapper();
+ $event = $mapper->map($eventInput); // automatically maps to PhysicalEvent
+
+Mapping Based on Target Properties (Source Mapping)
+---------------------------------------------------
+
+Sometimes, it's more convenient to define how a target object should retrieve
+its values from a source, especially when working with external data formats.
+This is done using the ``source`` parameter in the ``#[Map]`` attribute on the
+target class's properties.
+
+Note that if both the ``source`` and the ``target`` classes define the ``#[Map]``
+attribute, the ``source`` takes precedence.
+
+Consider the following class that stores the data obtained from an external API
+that uses snake_case property names::
+
+ // src/Api/Payload.php
+ namespace App\Api;
+
+ class Payload
+ {
+ public string $product_name = '';
+ public float $price_amount = 0.0;
+ }
+
+In your application, classes use camelCase for property names, so you can map
+them as follows::
+
+ // src/Entity/Product.php
+ namespace App\Entity;
+
+ use App\Api\Payload;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ // define that Product can be mapped from Payload
+ #[Map(source: Payload::class)]
+ class Product
+ {
+ // define where 'name' should get its value from in the Payload source
+ #[Map(source: 'product_name')]
+ public string $name = '';
+
+ // define where 'price' should get its value from
+ #[Map(source: 'price_amount')]
+ public float $price = 0.0;
+ }
+
+Using it in practice::
+
+ $payload = new Payload();
+ $payload->product_name = 'Super Widget';
+ $payload->price_amount = 123.45;
+
+ $mapper = new ObjectMapper();
+ // map from the payload to the Product class
+ $product = $mapper->map($payload, Product::class);
+
+ // $product->name = 'Super Widget'
+ // $product->price = 123.45
+
+When using source-based mapping, the ``ObjectMapper`` will automatically use the
+target's ``#[Map(source: ...)]`` attributes if no mapping is defined on the
+source class.
+
+Handling Recursion
+------------------
+
+The ObjectMapper automatically detects and handles recursive relationships between
+objects (e.g., a ``User`` has a ``manager`` which is another ``User``, who might
+manage the first user). When it encounters previously mapped objects in the graph,
+it reuses the corresponding target instances to prevent infinite loops::
+
+ // src/Entity/User.php
+ namespace App\Entity;
+
+ use App\Dto\UserDto;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ #[Map(target: UserDto::class)]
+ class User
+ {
+ public string $name = '';
+ public ?User $manager = null;
+ }
+
+The target DTO object defines the ``User`` class as its source and the
+ObjectMapper component detects the cyclic reference::
+
+ // src/Dto/UserDto.php
+ namespace App\Dto;
+
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+
+ #[Map(source: \App\Entity\User::class)] // can also define mapping here
+ class UserDto
+ {
+ public string $name = '';
+ public ?UserDto $manager = null;
+ }
+
+Using it in practice::
+
+ $manager = new User();
+ $manager->name = 'Alice';
+ $employee = new User();
+ $employee->name = 'Bob';
+ $employee->manager = $manager;
+ // manager's manager is the employee:
+ $manager->manager = $employee;
+
+ $mapper = new ObjectMapper();
+ $employeeDto = $mapper->map($employee, UserDto::class);
+
+ // recursion is handled correctly:
+ // $employeeDto->name === 'Bob'
+ // $employeeDto->manager->name === 'Alice'
+ // $employeeDto->manager->manager === $employeeDto
+
+.. _objectmapper-custom-mapping-logic:
+
+Custom Mapping Logic
+--------------------
+
+For very complex mapping scenarios or if you prefer separating mapping rules from
+your DTOs/Entities, you can implement a custom mapping strategy using the
+:class:`Symfony\\Component\\ObjectMapper\\Metadata\\ObjectMapperMetadataFactoryInterface`.
+This allows defining mapping rules within dedicated mapper services, similar
+to the approach used by libraries like MapStruct in the Java ecosystem.
+
+First, create your custom metadata factory. The following example reads mapping
+rules defined via ``#[Map]`` attributes on a dedicated mapper service class,
+specifically on its ``map`` method for property mappings and on the class itself
+for the source-to-target relationship::
+
+ namespace App\ObjectMapper\Metadata;
+
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+ use Symfony\Component\ObjectMapper\Metadata\Mapping;
+ use Symfony\Component\ObjectMapper\Metadata\ObjectMapperMetadataFactoryInterface;
+ use Symfony\Component\ObjectMapper\ObjectMapperInterface;
+
+ /**
+ * A Metadata factory that implements basics similar to MapStruct.
+ * Reads mapping configuration from attributes on a dedicated mapper service.
+ */
+ final class MapStructMapperMetadataFactory implements ObjectMapperMetadataFactoryInterface
+ {
+ /**
+ * @param class-string $mapperClass The FQCN of the mapper service class
+ */
+ public function __construct(private readonly string $mapperClass)
+ {
+ if (!is_a($this->mapperClass, ObjectMapperInterface::class, true)) {
+ throw new \RuntimeException(sprintf('Mapper class "%s" must implement "%s".', $this->mapperClass, ObjectMapperInterface::class));
+ }
+ }
+
+ public function create(object $object, ?string $property = null, array $context = []): array
+ {
+ try {
+ $refl = new \ReflectionClass($this->mapperClass);
+ } catch (\ReflectionException $e) {
+ throw new \RuntimeException("Failed to reflect mapper class: " . $e->getMessage(), 0, $e);
+ }
+
+ $mapConfigs = [];
+ $sourceIdentifier = $property ?? $object::class;
+
+ // read attributes from the map method (for property mapping) or the class (for class mapping)
+ $attributesSource = $property ? $refl->getMethod('map') : $refl;
+ foreach ($attributesSource->getAttributes(Map::class, \ReflectionAttribute::IS_INSTANCEOF) as $attribute) {
+ $map = $attribute->newInstance();
+
+ // check if the attribute's source matches the current property or source class
+ if ($map->source === $sourceIdentifier) {
+ $mapConfigs[] = new Mapping($map->target, $map->source, $map->if, $map->transform);
+ }
+ }
+
+ // if it's a property lookup and no specific mapping was found, map to the same property
+ if ($property && empty($mapConfigs)) {
+ $mapConfigs[] = new Mapping(target: $property, source: $property);
+ }
+
+ return $mapConfigs;
+ }
+ }
+
+Next, define your mapper service class. This class implements ``ObjectMapperInterface``
+but typically delegates the actual mapping back to a standard ``ObjectMapper``
+instance configured with the custom metadata factory. Mapping rules are defined
+using ``#[Map]`` attributes on this class and its ``map`` method::
+
+ namespace App\ObjectMapper;
+
+ use App\Dto\LegacyUser;
+ use App\Dto\UserDto;
+ use App\ObjectMapper\Metadata\MapStructMapperMetadataFactory;
+ use Symfony\Component\ObjectMapper\Attribute\Map;
+ use Symfony\Component\ObjectMapper\ObjectMapper;
+ use Symfony\Component\ObjectMapper\ObjectMapperInterface;
+
+ // define the source-to-target mapping at the class level
+ #[Map(source: LegacyUser::class, target: UserDto::class)]
+ class LegacyUserMapper implements ObjectMapperInterface
+ {
+ private readonly ObjectMapperInterface $objectMapper;
+
+ // inject the standard ObjectMapper or necessary dependencies
+ public function __construct(?ObjectMapperInterface $objectMapper = null)
+ {
+ // create an ObjectMapper instance configured with *this* mapper's rules
+ $metadataFactory = new MapStructMapperMetadataFactory(self::class);
+ $this->objectMapper = $objectMapper ?? new ObjectMapper($metadataFactory);
+ }
+
+ // define property-specific mapping rules on the map method
+ #[Map(source: 'fullName', target: 'name')] // Map LegacyUser::fullName to UserDto::name
+ #[Map(source: 'creationTimestamp', target: 'registeredAt', transform: [\DateTimeImmutable::class, 'createFromFormat'])]
+ #[Map(source: 'status', if: false)] // Ignore the 'status' property from LegacyUser
+ public function map(object $source, object|string|null $target = null): object
+ {
+ // delegate the actual mapping to the configured ObjectMapper
+ return $this->objectMapper->map($source, $target);
+ }
+ }
+
+Finally, use your custom mapper service::
+
+ use App\Dto\LegacyUser;
+ use App\ObjectMapper\LegacyUserMapper;
+
+ $legacyUser = new LegacyUser();
+ $legacyUser->fullName = 'Jane Doe';
+ $legacyUser->status = 'active'; // this will be ignored
+
+ // instantiate your custom mapper service
+ $mapperService = new LegacyUserMapper();
+
+ // use the map method of your service
+ $userDto = $mapperService->map($legacyUser); // Target (UserDto) is inferred from #[Map] on LegacyUserMapper
+
+This approach keeps mapping logic centralized within dedicated services, which can
+be beneficial for complex applications or when adhering to specific architectural patterns.
+
+Advanced Configuration
+----------------------
+
+The ``ObjectMapper`` constructor accepts optional arguments for advanced usage:
+
+* ``ObjectMapperMetadataFactoryInterface $metadataFactory``: Allows custom metadata
+ factories, such as the one shown in :ref:`the MapStruct-like example `.
+ The default is :class:`Symfony\\Component\\ObjectMapper\\Metadata\\ReflectionObjectMapperMetadataFactory`,
+ which uses ``#[Map]`` attributes from source and target classes.
+* ``?PropertyAccessorInterface $propertyAccessor``: Lets you customize how
+ properties are read and written to the target object, useful for accessing
+ private properties or using getters/setters.
+* ``?ContainerInterface $transformCallableLocator``: A PSR-11 container (service locator)
+ that resolves service IDs referenced by the ``transform`` option in ``#[Map]``.
+* ``?ContainerInterface $conditionCallableLocator``: A PSR-11 container for resolving
+ service IDs used in ``if`` conditions within ``#[Map]``.
+
+These dependencies are automatically configured when you use the
+``ObjectMapperInterface`` service provided by Symfony.
diff --git a/page_creation.rst b/page_creation.rst
index f8b2fdaf251..e58e99f317c 100644
--- a/page_creation.rst
+++ b/page_creation.rst
@@ -36,7 +36,7 @@ Creating a Page: Route and Controller
Suppose you want to create a page - ``/lucky/number`` - that generates a lucky (well,
random) number and prints it. To do that, create a "Controller" class and a
-"controller" method inside of it::
+"number" method inside of it::
`,
+That's it! If you are using :ref:`the Symfony web server `,
try it out by going to: http://localhost:8000/lucky/number
.. tip::
diff --git a/performance.rst b/performance.rst
index cd9dacddb1a..828333f338b 100644
--- a/performance.rst
+++ b/performance.rst
@@ -362,6 +362,13 @@ method does, which stops an event and then restarts it immediately::
// Lap information is stored as "periods" within the event:
// $event->getPeriods();
+ // Gets the last event period:
+ // $event->getLastPeriod();
+
+.. versionadded:: 7.2
+
+ The ``getLastPeriod()`` method was introduced in Symfony 7.2.
+
Profiling Sections
..................
@@ -382,10 +389,16 @@ All events that don't belong to any named section are added to the special secti
called ``__root__``. This way you can get all stopwatch events, even if you don't
know their names, as follows::
- foreach($this->stopwatch->getSectionEvents('__root__') as $event) {
+ use Symfony\Component\Stopwatch\Stopwatch;
+
+ foreach($this->stopwatch->getSectionEvents(Stopwatch::ROOT) as $event) {
echo (string) $event;
}
+.. versionadded:: 7.2
+
+ The ``Stopwatch::ROOT`` constant as a shortcut for ``__root__`` was introduced in Symfony 7.2.
+
Learn more
----------
diff --git a/profiler.rst b/profiler.rst
index 57d412472ba..7fc97c8ee33 100644
--- a/profiler.rst
+++ b/profiler.rst
@@ -217,9 +217,48 @@ user by dynamically rewriting the current page rather than loading entire new
pages from a server.
By default, the debug toolbar displays the information of the initial page load
-and doesn't refresh after each AJAX request. However, you can set the
-``Symfony-Debug-Toolbar-Replace`` header to a value of ``'1'`` in the response to
-the AJAX request to force the refresh of the toolbar::
+and doesn't refresh after each AJAX request. However, you can configure the
+toolbar to be refreshed after each AJAX request by enabling ``ajax_replace`` in the
+``web_profiler`` configuration:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/web_profiler.yaml
+ web_profiler:
+ toolbar:
+ ajax_replace: true
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/web_profiler.php
+ use Symfony\Config\WebProfilerConfig;
+
+ return static function (WebProfilerConfig $profiler): void {
+ $profiler->toolbar()
+ ->ajaxReplace(true);
+ };
+
+If you need a more sophisticated solution, you can set the
+``Symfony-Debug-Toolbar-Replace`` header to a value of ``'1'`` in the response
+yourself::
$response->headers->set('Symfony-Debug-Toolbar-Replace', '1');
@@ -228,31 +267,21 @@ production. To do that, create an :doc:`event subscriber `
and listen to the :ref:`kernel.response `
event::
+ use Symfony\Component\DependencyInjection\Attribute\When;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelInterface;
// ...
+ #[When(env: 'dev')]
class MySubscriber implements EventSubscriberInterface
{
- public function __construct(
- private KernelInterface $kernel,
- ) {
- }
-
// ...
public function onKernelResponse(ResponseEvent $event): void
{
- if (!$this->kernel->isDebug()) {
- return;
- }
-
- $request = $event->getRequest();
- if (!$request->isXmlHttpRequest()) {
- return;
- }
+ // Your custom logic here
$response = $event->getResponse();
$response->headers->set('Symfony-Debug-Toolbar-Replace', '1');
diff --git a/quick_tour/the_architecture.rst b/quick_tour/the_architecture.rst
index a323461885d..3b66570b3d3 100644
--- a/quick_tour/the_architecture.rst
+++ b/quick_tour/the_architecture.rst
@@ -159,29 +159,22 @@ Twig Extension & Autoconfiguration
Thanks to Symfony's service handling, you can *extend* Symfony in many ways, like
by creating an event subscriber or a security voter for complex authorization
rules. Let's add a new filter to Twig called ``greet``. How? Create a class
-that extends ``AbstractExtension``::
+with your logic::
// src/Twig/GreetExtension.php
namespace App\Twig;
use App\GreetingGenerator;
- use Twig\Extension\AbstractExtension;
- use Twig\TwigFilter;
+ use Twig\Attribute\AsTwigFilter;
- class GreetExtension extends AbstractExtension
+ class GreetExtension
{
public function __construct(
private GreetingGenerator $greetingGenerator,
) {
}
- public function getFilters(): array
- {
- return [
- new TwigFilter('greet', [$this, 'greetUser']),
- ];
- }
-
+ #[AsTwigFilter('greet')]
public function greetUser(string $name): string
{
$greeting = $this->greetingGenerator->getRandomGreeting();
@@ -198,7 +191,7 @@ After creating just *one* file, you can use this immediately:
{# Will print something like "Hey Symfony!" #}
{{ name|greet }}
-How does this work? Symfony notices that your class extends ``AbstractExtension``
+How does this work? Symfony notices that your class uses the ``#[AsTwigFilter]`` attribute
and so *automatically* registers it as a Twig extension. This is called autoconfiguration,
and it works for *many* many things. Create a class and then extend a base class
(or implement an interface). Symfony takes care of the rest.
diff --git a/quick_tour/the_big_picture.rst b/quick_tour/the_big_picture.rst
index b069cb4f716..ba7cc78e28b 100644
--- a/quick_tour/the_big_picture.rst
+++ b/quick_tour/the_big_picture.rst
@@ -42,8 +42,8 @@ Symfony application:
Can we already load the project in a browser? Yes! You can set up
:doc:`Nginx or Apache ` and configure their
document root to be the ``public/`` directory. But, for development, it's better
-to :doc:`install the Symfony local web server ` and run
-it as follows:
+to install the :doc:`Symfony CLI ` tool and run its
+:ref:`local web server ` as follows:
.. code-block:: terminal
diff --git a/rate_limiter.rst b/rate_limiter.rst
index 6c158ee52d0..3a517c37bd4 100644
--- a/rate_limiter.rst
+++ b/rate_limiter.rst
@@ -230,6 +230,12 @@ prevents that number from being higher than 5,000).
Rate Limiting in Action
-----------------------
+.. versionadded:: 7.3
+
+ :class:`Symfony\\Component\\RateLimiter\\RateLimiterFactoryInterface` was
+ added and should now be used for autowiring instead of
+ :class:`Symfony\\Component\\RateLimiter\\RateLimiterFactory`.
+
After having installed and configured the rate limiter, inject it in any service
or controller and call the ``consume()`` method to try to consume a given number
of tokens. For example, this controller uses the previous rate limiter to control
@@ -242,13 +248,13 @@ the number of requests to the API::
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
- use Symfony\Component\RateLimiter\RateLimiterFactory;
+ use Symfony\Component\RateLimiter\RateLimiterFactoryInterface;
class ApiController extends AbstractController
{
// if you're using service autowiring, the variable name must be:
// "rate limiter name" (in camelCase) + "Limiter" suffix
- public function index(Request $request, RateLimiterFactory $anonymousApiLimiter): Response
+ public function index(Request $request, RateLimiterFactoryInterface $anonymousApiLimiter): Response
{
// create a limiter based on a unique identifier of the client
// (e.g. the client's IP address, a username/email, an API key, etc.)
@@ -291,11 +297,11 @@ using the ``reserve()`` method::
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
- use Symfony\Component\RateLimiter\RateLimiterFactory;
+ use Symfony\Component\RateLimiter\RateLimiterFactoryInterface;
class ApiController extends AbstractController
{
- public function registerUser(Request $request, RateLimiterFactory $authenticatedApiLimiter): Response
+ public function registerUser(Request $request, RateLimiterFactoryInterface $authenticatedApiLimiter): Response
{
$apiKey = $request->headers->get('apikey');
$limiter = $authenticatedApiLimiter->create($apiKey);
@@ -350,11 +356,11 @@ the :class:`Symfony\\Component\\RateLimiter\\Reservation` object returned by the
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
- use Symfony\Component\RateLimiter\RateLimiterFactory;
+ use Symfony\Component\RateLimiter\RateLimiterFactoryInterface;
class ApiController extends AbstractController
{
- public function index(Request $request, RateLimiterFactory $anonymousApiLimiter): Response
+ public function index(Request $request, RateLimiterFactoryInterface $anonymousApiLimiter): Response
{
$limiter = $anonymousApiLimiter->create($request->getClientIp());
$limit = $limiter->consume();
@@ -461,9 +467,10 @@ simultaneous requests (e.g. three servers of a company hitting your API at the
same time). Rate limiters use :doc:`locks ` to protect their operations
against these race conditions.
-By default, Symfony uses the global lock configured by ``framework.lock``, but
-you can use a specific :ref:`named lock ` via the
-``lock_factory`` option (or none at all):
+By default, if the :doc:`lock ` component is installed, Symfony uses the
+global lock configured by ``framework.lock``, but you can use a specific
+:ref:`named lock ` via the ``lock_factory`` option (or none
+at all):
.. configuration-block::
@@ -534,6 +541,129 @@ you can use a specific :ref:`named lock ` via the
;
};
+.. versionadded:: 7.3
+
+ Before Symfony 7.3, configuring a rate limiter and using the default configured
+ lock factory (``lock.factory``) failed if the Symfony Lock component was not
+ installed in the application.
+
+Compound Rate Limiter
+---------------------
+
+.. versionadded:: 7.3
+
+ Support for configuring compound rate limiters was introduced in Symfony 7.3.
+
+You can configure multiple rate limiters to work together:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/rate_limiter.yaml
+ framework:
+ rate_limiter:
+ two_per_minute:
+ policy: 'fixed_window'
+ limit: 2
+ interval: '1 minute'
+ five_per_hour:
+ policy: 'fixed_window'
+ limit: 5
+ interval: '1 hour'
+ contact_form:
+ policy: 'compound'
+ limiters: [two_per_minute, five_per_hour]
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+ two_per_minute
+ five_per_hour
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/rate_limiter.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->rateLimiter()
+ ->limiter('two_per_minute')
+ ->policy('fixed_window')
+ ->limit(2)
+ ->interval('1 minute')
+ ;
+
+ $framework->rateLimiter()
+ ->limiter('two_per_minute')
+ ->policy('fixed_window')
+ ->limit(5)
+ ->interval('1 hour')
+ ;
+
+ $framework->rateLimiter()
+ ->limiter('contact_form')
+ ->policy('compound')
+ ->limiters(['two_per_minute', 'five_per_hour'])
+ ;
+ };
+
+Then, inject and use as normal::
+
+ // src/Controller/ContactController.php
+ namespace App\Controller;
+
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+ use Symfony\Component\HttpFoundation\Request;
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\RateLimiter\RateLimiterFactory;
+
+ class ContactController extends AbstractController
+ {
+ public function registerUser(Request $request, RateLimiterFactoryInterface $contactFormLimiter): Response
+ {
+ $limiter = $contactFormLimiter->create($request->getClientIp());
+
+ if (false === $limiter->consume(1)->isAccepted()) {
+ // either of the two limiters has been reached
+ }
+
+ // ...
+ }
+
+ // ...
+ }
+
.. _`DoS attacks`: https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html
.. _`Apache mod_ratelimit`: https://httpd.apache.org/docs/current/mod/mod_ratelimit.html
.. _`NGINX rate limiting`: https://www.nginx.com/blog/rate-limiting-nginx/
diff --git a/reference/attributes.rst b/reference/attributes.rst
index 7975a8aaee4..968c7df1568 100644
--- a/reference/attributes.rst
+++ b/reference/attributes.rst
@@ -14,7 +14,7 @@ Doctrine Bridge
Command
~~~~~~~
-* :ref:`AsCommand `
+* :ref:`AsCommand `
Contracts
~~~~~~~~~
@@ -43,6 +43,7 @@ Dependency Injection
* :ref:`TaggedLocator `
* :ref:`Target `
* :ref:`When `
+* :ref:`WhenNot `
.. deprecated:: 7.1
@@ -78,6 +79,7 @@ HttpKernel
Messenger
~~~~~~~~~
+* :ref:`AsMessage `
* :ref:`AsMessageHandler `
RemoteEvent
@@ -121,6 +123,9 @@ Twig
~~~~
* :ref:`Template `
+* :ref:`AsTwigFilter `
+* :ref:`AsTwigFunction `
+* ``AsTwigTest``
Symfony UX
~~~~~~~~~~
diff --git a/reference/configuration/debug.rst b/reference/configuration/debug.rst
index 9b8dc2a6f0c..6ca05b49bd7 100644
--- a/reference/configuration/debug.rst
+++ b/reference/configuration/debug.rst
@@ -8,14 +8,14 @@ key in your application configuration.
.. code-block:: terminal
# displays the default config values defined by Symfony
- $ php bin/console config:dump-reference framework
+ $ php bin/console config:dump-reference debug
# displays the actual config values used by your application
- $ php bin/console debug:config framework
+ $ php bin/console debug:config debug
# displays the config values used by your application and replaces the
# environment variables with their actual values
- $ php bin/console debug:config --resolve-env framework
+ $ php bin/console debug:config --resolve-env debug
.. note::
@@ -23,35 +23,6 @@ key in your application configuration.
namespace and the related XSD schema is available at:
``https://symfony.com/schema/dic/debug/debug-1.0.xsd``
-Configuration
--------------
-
-max_items
-~~~~~~~~~
-
-**type**: ``integer`` **default**: ``2500``
-
-This is the maximum number of items to dump. Setting this option to ``-1``
-disables the limit.
-
-min_depth
-~~~~~~~~~
-
-**type**: ``integer`` **default**: ``1``
-
-Configures the minimum tree depth until which all items are guaranteed to
-be cloned. After this depth is reached, only ``max_items`` items will be
-cloned. The default value is ``1``, which is consistent with older Symfony
-versions.
-
-max_string_length
-~~~~~~~~~~~~~~~~~
-
-**type**: ``integer`` **default**: ``-1``
-
-This option configures the maximum string length before truncating the
-string. The default value (``-1``) means that strings are never truncated.
-
.. _configuration-debug-dump_destination:
dump_destination
@@ -101,3 +72,29 @@ Typically, you would set this to ``php://stderr``:
Configure it to ``"tcp://%env(VAR_DUMPER_SERVER)%"`` in order to use the :ref:`ServerDumper feature `.
+
+max_items
+~~~~~~~~~
+
+**type**: ``integer`` **default**: ``2500``
+
+This is the maximum number of items to dump. Setting this option to ``-1``
+disables the limit.
+
+max_string_length
+~~~~~~~~~~~~~~~~~
+
+**type**: ``integer`` **default**: ``-1``
+
+This option configures the maximum string length before truncating the
+string. The default value (``-1``) means that strings are never truncated.
+
+min_depth
+~~~~~~~~~
+
+**type**: ``integer`` **default**: ``1``
+
+Configures the minimum tree depth until which all items are guaranteed to
+be cloned. After this depth is reached, only ``max_items`` items will be
+cloned. The default value is ``1``, which is consistent with older Symfony
+versions.
diff --git a/reference/configuration/doctrine.rst b/reference/configuration/doctrine.rst
index 272ad6b6804..2aa716ae9fc 100644
--- a/reference/configuration/doctrine.rst
+++ b/reference/configuration/doctrine.rst
@@ -100,6 +100,36 @@ The following block shows all possible configuration keys:
+ .. code-block:: php
+
+ use Symfony\Config\DoctrineConfig;
+
+ return static function (DoctrineConfig $doctrine): void {
+ $dbal = $doctrine->dbal();
+
+ $dbal = $dbal
+ ->connection('default')
+ ->dbname('database')
+ ->host('localhost')
+ ->port(1234)
+ ->user('user')
+ ->password('secret')
+ ->driver('pdo_mysql')
+ ->url('mysql://db_user:db_password@127.0.0.1:3306/db_name') // if the url option is specified, it will override the above config
+ ->driverClass(App\DBAL\MyDatabaseDriver::class) // the DBAL driverClass option
+ ->option('foo', 'bar') // the DBAL driverOptions option
+ ->path('%kernel.project_dir%/var/data/data.sqlite')
+ ->memory(true)
+ ->unixSocket('/tmp/mysql.sock')
+ ->wrapperClass(App\DBAL\MyConnectionWrapper::class) // the DBAL wrapperClass option
+ ->charset('utf8mb4')
+ ->logging('%kernel.debug%')
+ ->platformService(App\DBAL\MyDatabasePlatformService::class)
+ ->serverVersion('8.0.37')
+ ->mappingType('enum', 'string')
+ ->type('custom', App\DBAL\MyCustomType::class);
+ };
+
.. note::
The ``server_version`` option was added in Doctrine DBAL 2.5, which
@@ -125,24 +155,49 @@ The following block shows all possible configuration keys:
If you want to configure multiple connections in YAML, put them under the
``connections`` key and give them a unique name:
-.. code-block:: yaml
+.. configuration-block::
- doctrine:
- dbal:
- default_connection: default
- connections:
- default:
- dbname: Symfony
- user: root
- password: null
- host: localhost
- server_version: '8.0.37'
- customer:
- dbname: customer
- user: root
- password: null
- host: localhost
- server_version: '8.2.0'
+ .. code-block:: yaml
+
+ doctrine:
+ dbal:
+ default_connection: default
+ connections:
+ default:
+ dbname: Symfony
+ user: root
+ password: null
+ host: localhost
+ server_version: '8.0.37'
+ customer:
+ dbname: customer
+ user: root
+ password: null
+ host: localhost
+ server_version: '8.2.0'
+
+ .. code-block:: php
+
+ use Symfony\Config\DoctrineConfig;
+
+ return static function (DoctrineConfig $doctrine): void {
+ $dbal = $doctrine->dbal();
+ $dbal->defaultConnection('default');
+
+ $dbal->connection('default')
+ ->dbname('Symfony')
+ ->user('root')
+ ->password('null')
+ ->host('localhost')
+ ->serverVersion('8.0.37');
+
+ $dbal->connection('customer')
+ ->dbname('customer')
+ ->user('root')
+ ->password('null')
+ ->host('localhost')
+ ->serverVersion('8.2.0');
+ };
The ``database_connection`` service always refers to the *default* connection,
which is the first one defined or the one configured via the
@@ -172,20 +227,45 @@ Doctrine ORM Configuration
This following configuration example shows all the configuration defaults
that the ORM resolves to:
-.. code-block:: yaml
+.. configuration-block::
- doctrine:
- orm:
- auto_mapping: true
- # the standard distribution overrides this to be true in debug, false otherwise
- auto_generate_proxy_classes: false
- proxy_namespace: Proxies
- proxy_dir: '%kernel.cache_dir%/doctrine/orm/Proxies'
- default_entity_manager: default
- metadata_cache_driver: array
- query_cache_driver: array
- result_cache_driver: array
- naming_strategy: doctrine.orm.naming_strategy.default
+ .. code-block:: yaml
+
+ doctrine:
+ orm:
+ auto_mapping: false
+ # the standard distribution overrides this to be true in debug, false otherwise
+ auto_generate_proxy_classes: false
+ proxy_namespace: Proxies
+ proxy_dir: '%kernel.cache_dir%/doctrine/orm/Proxies'
+ default_entity_manager: default
+ metadata_cache_driver: array
+ query_cache_driver: array
+ result_cache_driver: array
+ naming_strategy: doctrine.orm.naming_strategy.default
+
+ .. code-block:: php
+
+ use Symfony\Config\DoctrineConfig;
+
+ return static function (DoctrineConfig $doctrine): void {
+ $orm = $doctrine->orm();
+
+ $orm
+ ->entityManager('default')
+ ->connection('default')
+ ->autoMapping(true)
+ ->metadataCacheDriver()->type('array')
+ ->queryCacheDriver()->type('array')
+ ->resultCacheDriver()->type('array')
+ ->namingStrategy('doctrine.orm.naming_strategy.default');
+
+ $orm
+ ->autoGenerateProxyClasses(false)
+ ->proxyNamespace('Proxies')
+ ->proxyDir('%kernel.cache_dir%/doctrine/orm/Proxies')
+ ->defaultEntityManager('default');
+ };
There are lots of other configuration options that you can use to overwrite
certain classes, but those are for very advanced use-cases only.
@@ -230,35 +310,70 @@ Caching Drivers
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
+.. configuration-block::
- # config/packages/prod/doctrine.yaml
- framework:
- cache:
- pools:
- doctrine.result_cache_pool:
- adapter: cache.app
- doctrine.system_cache_pool:
- adapter: cache.system
+ .. code-block:: yaml
- doctrine:
- orm:
- # ...
- metadata_cache_driver:
- type: pool
- pool: doctrine.system_cache_pool
- query_cache_driver:
- type: pool
- pool: doctrine.system_cache_pool
- result_cache_driver:
- type: pool
- pool: doctrine.result_cache_pool
+ # config/packages/prod/doctrine.yaml
+ framework:
+ cache:
+ pools:
+ doctrine.result_cache_pool:
+ adapter: cache.app
+ doctrine.system_cache_pool:
+ adapter: cache.system
- # 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
+ doctrine:
+ orm:
+ # ...
+ metadata_cache_driver:
+ type: pool
+ pool: doctrine.system_cache_pool
+ query_cache_driver:
+ type: pool
+ pool: doctrine.system_cache_pool
+ result_cache_driver:
+ 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 a cache pool
+ query_cache_driver:
+ type: service
+ id: App\ORM\MyCacheService
+
+ .. code-block:: php
+
+ use Symfony\Config\DoctrineConfig;
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework, DoctrineConfig $doctrine): void {
+ $framework
+ ->cache()
+ ->pool('doctrine.result_cache_pool')
+ ->adapters('cache.app')
+ ->pool('doctrine.system_cache_pool')
+ ->adapters('cache.sytsem');
+
+ $doctrine->orm()
+ // ...
+ ->entityManager('default')
+ ->metadataCacheDriver()
+ ->type('pool')
+ ->pool('doctrine.system_cache_pool')
+ ->queryCacheDriver()
+ ->type('pool')
+ ->pool('doctrine.system_cache_pool')
+ ->resultCacheDriver()
+ ->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 a cache pool
+ ->queryCacheDriver()
+ ->type('service')
+ ->id(App\ORM\MyCacheService::class);
+ };
Mapping Configuration
~~~~~~~~~~~~~~~~~~~~~
@@ -507,9 +622,9 @@ set up the connection using environment variables for the certificate paths:
server-version="8.0.31"
driver="pdo_mysql">
- %env(MYSQL_SSL_KEY)%
- %env(MYSQL_SSL_CERT)%
- %env(MYSQL_SSL_CA)%
+ %env(MYSQL_SSL_KEY)%
+ %env(MYSQL_SSL_CERT)%
+ %env(MYSQL_SSL_CA)%
diff --git a/reference/configuration/framework.rst b/reference/configuration/framework.rst
index d4ff35a6381..0ba76bb7ea0 100644
--- a/reference/configuration/framework.rst
+++ b/reference/configuration/framework.rst
@@ -19,234 +19,801 @@ configured under the ``framework`` key in your application configuration.
namespace and the related XSD schema is available at:
``https://symfony.com/schema/dic/symfony/symfony-1.0.xsd``
-Configuration
--------------
+annotations
+~~~~~~~~~~~
+
+.. _reference-annotations-cache:
+
+cache
+.....
+
+**type**: ``string`` **default**: ``php_array``
+
+This option can be one of the following values:
+
+php_array
+ Use a PHP array to cache annotations in memory
+file
+ Use the filesystem to cache annotations
+none
+ Disable the caching of annotations
+
+debug
+.....
+
+**type**: ``boolean`` **default**: ``%kernel.debug%``
+
+Whether to enable debug mode for caching. If enabled, the cache will
+automatically update when the original file is changed (both with code and
+annotation changes). For performance reasons, it is recommended to disable
+debug mode in production, which will happen automatically if you use the
+default value.
+
+file_cache_dir
+..............
+
+**type**: ``string`` **default**: ``%kernel.cache_dir%/annotations``
+
+The directory to store cache files for annotations, in case
+``annotations.cache`` is set to ``'file'``.
+
+.. _reference-assets:
+
+assets
+~~~~~~
+
+.. _reference-assets-base-path:
+
+base_path
+.........
+
+**type**: ``string``
+
+This option allows you to define a base path to be used for assets:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ # ...
+ assets:
+ base_path: '/images'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ // ...
+ $framework->assets()
+ ->basePath('/images');
+ };
+
+.. _reference-templating-base-urls:
+.. _reference-assets-base-urls:
+
+base_urls
+.........
+
+**type**: ``array``
+
+This option allows you to define base URLs to be used for assets.
+If multiple base URLs are provided, Symfony will select one from the
+collection each time it generates an asset's path:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ # ...
+ assets:
+ base_urls:
+ - 'http://cdn.example.com/'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ // ...
+ $framework->assets()
+ ->baseUrls(['http://cdn.example.com/']);
+ };
+
+.. _reference-assets-json-manifest-path:
+.. _reference-templating-json-manifest-path:
+
+json_manifest_path
+..................
+
+**type**: ``string`` **default**: ``null``
+
+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::
+
+ Symfony's :ref:`Webpack Encore ` supports
+ :ref:`outputting hashed assets `. Moreover, this
+ can be incorporated into many other workflows, including Webpack and
+ Gulp using `webpack-manifest-plugin`_ and `gulp-rev`_, respectively.
+
+This option can be set globally for all assets and individually for each asset
+package:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ 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)
+ json_manifest_path: "%kernel.project_dir%/public/build/a_different_manifest.json"
+ # Throws an exception when an asset is not found in the manifest
+ strict_mode: %kernel.debug%
+ bar_package:
+ # this package uses the global manifest (the default file is used)
+ base_path: '/images'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ // ...
+ $framework->assets()
+ // this manifest is applied to every asset (including packages)
+ ->jsonManifestPath('%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',
+ $framework->assets()->package('foo_package')
+ // this package uses its own manifest (the default file is ignored)
+ ->jsonManifestPath('%kernel.project_dir%/public/build/a_different_manifest.json')
+ // Throws an exception when an asset is not found in the manifest
+ ->setStrictMode('%kernel.debug%');
+
+ $framework->assets()->package('bar_package')
+ // this package uses the global manifest (the default file is used)
+ ->basePath('/images');
+ };
+
+.. note::
+
+ This parameter cannot be set at the same time as ``version`` or ``version_strategy``.
+ Additionally, this option cannot be nullified at the package scope if a global manifest
+ file is specified.
+
+.. tip::
+
+ If you request an asset that is *not found* in the ``manifest.json`` file, the original -
+ *unmodified* - asset path will be returned.
+ You can set ``strict_mode`` to ``true`` to get an exception when an asset is *not found*.
+
+.. note::
+
+ If a URL is set, the JSON manifest is downloaded on each request using the `http_client`_.
+
+.. _reference-framework-assets-packages:
+
+packages
+........
+
+You can group assets into packages, to specify different base URLs for them:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ # ...
+ assets:
+ packages:
+ avatars:
+ base_urls: 'http://static_cdn.example.com/avatars'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ // ...
+ $framework->assets()
+ ->package('avatars')
+ ->baseUrls(['http://static_cdn.example.com/avatars']);
+ };
+
+Now you can use the ``avatars`` package in your templates:
+
+.. code-block:: html+twig
+
+
+
+Each package can configure the following options:
+
+* :ref:`base_path `
+* :ref:`base_urls `
+* :ref:`version_strategy `
+* :ref:`version `
+* :ref:`version_format `
+* :ref:`json_manifest_path `
+* :ref:`strict_mode `
+
+.. _reference-assets-strict-mode:
+
+strict_mode
+...........
+
+**type**: ``boolean`` **default**: ``false``
+
+When enabled, the strict mode asserts that all requested assets are in the
+manifest file. This option is useful to detect typos or missing assets, the
+recommended value is ``%kernel.debug%``.
+
+.. _reference-framework-assets-version:
+.. _ref-framework-assets-version:
+
+version
+.......
+
+**type**: ``string``
+
+This option is used to *bust* the cache on assets by globally adding a query
+parameter to all rendered asset paths (e.g. ``/images/logo.png?v2``). This
+applies only to assets rendered via the Twig ``asset()`` function (or PHP
+equivalent).
+
+For example, suppose you have the following:
+
+.. code-block:: html+twig
+
+
+
+By default, this will render a path to your image such as ``/images/logo.png``.
+Now, activate the ``version`` option:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ # ...
+ assets:
+ version: 'v2'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ // ...
+ $framework->assets()
+ ->version('v2');
+ };
+
+Now, the same asset will be rendered as ``/images/logo.png?v2`` If you use
+this feature, you **must** manually increment the ``version`` value
+before each deployment so that the query parameters change.
+
+You can also control how the query string works via the `version_format`_
+option.
+
+.. note::
+
+ This parameter cannot be set at the same time as ``version_strategy`` or ``json_manifest_path``.
+
+.. tip::
+
+ As with all settings, you can use a parameter as value for the
+ ``version``. This makes it easier to increment the cache on each
+ deployment.
+
+.. _reference-templating-version-format:
+.. _reference-assets-version-format:
+
+version_format
+..............
-.. _configuration-framework-secret:
+**type**: ``string`` **default**: ``%%s?%%s``
-secret
-~~~~~~
+This specifies a :phpfunction:`sprintf` pattern that will be used with the
+`version`_ option to construct an asset's path. By default, the pattern
+adds the asset's version as a query string. For example, if
+``version_format`` is set to ``%%s?version=%%s`` and ``version``
+is set to ``5``, the asset's path would be ``/images/logo.png?version=5``.
-**type**: ``string`` **required**
+.. note::
-This is a string that should be unique to your application and it's commonly
-used to add more entropy to security related operations. Its value should
-be a series of characters, numbers and symbols chosen randomly and the
-recommended length is around 32 characters.
+ All percentage signs (``%``) in the format string must be doubled to
+ escape the character. Without escaping, values might inadvertently be
+ interpreted as :ref:`service-container-parameters`.
-In practice, Symfony uses this value for encrypting the cookies used
-in the :doc:`remember me functionality ` and for
-creating signed URIs when using :ref:`ESI (Edge Side Includes) `.
-That's why you should treat this value as if it were a sensitive credential and
-**never make it public**.
+.. tip::
-This option becomes the service container parameter named ``kernel.secret``,
-which you can use whenever the application needs an immutable random string
-to add more entropy.
+ Some CDN's do not support cache-busting via query strings, so injecting
+ the version into the actual file path is necessary. Thankfully,
+ ``version_format`` is not limited to producing versioned query
+ strings.
-As with any other security-related parameter, it is a good practice to change
-this value from time to time. However, keep in mind that changing this value
-will invalidate all signed URIs and Remember Me cookies. That's why, after
-changing this value, you should regenerate the application cache and log
-out all the application users.
+ The pattern receives the asset's original path and version as its first
+ and second parameters, respectively. Since the asset's path is one
+ parameter, you cannot modify it in-place (e.g. ``/images/logo-v5.png``);
+ however, you can prefix the asset's path using a pattern of
+ ``version-%%2$s/%%1$s``, which would result in the path
+ ``version-5/images/logo.png``.
-handle_all_throwables
-~~~~~~~~~~~~~~~~~~~~~
+ URL rewrite rules could then be used to disregard the version prefix
+ before serving the asset. Alternatively, you could copy assets to the
+ appropriate version path as part of your deployment process and forgot
+ any URL rewriting. The latter option is useful if you would like older
+ asset versions to remain accessible at their original URL.
-**type**: ``boolean`` **default**: ``true``
+.. _reference-assets-version-strategy:
+.. _reference-templating-version-strategy:
-When set to ``true``, the Symfony kernel will catch all ``\Throwable`` exceptions
-thrown by the application and will turn them into HTTP responses.
+version_strategy
+................
-.. _configuration-framework-http_cache:
+**type**: ``string`` **default**: ``null``
-http_cache
-~~~~~~~~~~
+The service id of the :doc:`asset version strategy `
+applied to the assets. This option can be set globally for all assets and
+individually for each asset package:
-enabled
-.......
+.. configuration-block::
-**type**: ``boolean`` **default**: ``false``
+ .. code-block:: yaml
-debug
+ # config/packages/framework.yaml
+ framework:
+ assets:
+ # this strategy is applied to every asset (including packages)
+ version_strategy: 'app.asset.my_versioning_strategy'
+ packages:
+ foo_package:
+ # this package removes any versioning (its assets won't be versioned)
+ version: ~
+ bar_package:
+ # this package uses its own strategy (the default strategy is ignored)
+ version_strategy: 'app.asset.another_version_strategy'
+ baz_package:
+ # this package inherits the default strategy
+ base_path: '/images'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ // ...
+ $framework->assets()
+ ->versionStrategy('app.asset.my_versioning_strategy');
+
+ $framework->assets()->package('foo_package')
+ // this package removes any versioning (its assets won't be versioned)
+ ->version(null);
+
+ $framework->assets()->package('bar_package')
+ // this package uses its own strategy (the default strategy is ignored)
+ ->versionStrategy('app.asset.another_version_strategy');
+
+ $framework->assets()->package('baz_package')
+ // this package inherits the default strategy
+ ->basePath('/images');
+ };
+
+.. note::
+
+ This parameter cannot be set at the same time as ``version`` or ``json_manifest_path``.
+
+.. _reference-cache:
+
+cache
+~~~~~
+
+.. _reference-cache-app:
+
+app
+...
+
+**type**: ``string`` **default**: ``cache.adapter.filesystem``
+
+The cache adapter used by the ``cache.app`` service. The FrameworkBundle
+ships with multiple adapters: ``cache.adapter.apcu``, ``cache.adapter.system``,
+``cache.adapter.filesystem``, ``cache.adapter.psr6``, ``cache.adapter.redis``,
+``cache.adapter.memcached``, ``cache.adapter.pdo`` and
+``cache.adapter.doctrine_dbal``.
+
+There's also a special adapter called ``cache.adapter.array`` which stores
+contents in memory using a PHP array and it's used to disable caching (mostly on
+the ``dev`` environment).
+
+.. tip::
+
+ It might be tough to understand at the beginning, so to avoid confusion
+ remember that all pools perform the same actions but on different medium
+ given the adapter they are based on. Internally, a pool wraps the definition
+ of an adapter.
+
+default_doctrine_provider
+.........................
+
+**type**: ``string``
+
+The service name to use as your default Doctrine provider. The provider is
+available as the ``cache.default_doctrine_provider`` service.
+
+default_memcached_provider
+..........................
+
+**type**: ``string`` **default**: ``memcached://localhost``
+
+The DSN to use by the Memcached provider. The provider is available as the ``cache.default_memcached_provider``
+service.
+
+default_pdo_provider
+....................
+
+**type**: ``string`` **default**: ``doctrine.dbal.default_connection``
+
+The service id of the database connection, which should be either a PDO or a
+Doctrine DBAL instance. The provider is available as the ``cache.default_pdo_provider``
+service.
+
+default_psr6_provider
+.....................
+
+**type**: ``string``
+
+The service name to use as your default PSR-6 provider. It is available as
+the ``cache.default_psr6_provider`` service.
+
+default_redis_provider
+......................
+
+**type**: ``string`` **default**: ``redis://localhost``
+
+The DSN to use by the Redis provider. The provider is available as the ``cache.default_redis_provider``
+service.
+
+directory
+.........
+
+**type**: ``string`` **default**: ``%kernel.cache_dir%/pools``
+
+The path to the cache directory used by services inheriting from the
+``cache.adapter.filesystem`` adapter (including ``cache.app``).
+
+pools
.....
-**type**: ``boolean`` **default**: ``%kernel.debug%``
+**type**: ``array``
-If true, exceptions are thrown when things go wrong. Otherwise, the cache will
-try to carry on and deliver a meaningful response.
+A list of cache pools to be created by the framework extension.
-trace_level
-...........
+.. seealso::
+
+ For more information about how pools work, see :ref:`cache pools `.
+
+To configure a Redis cache pool with a default lifetime of 1 hour, do the following:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ cache:
+ pools:
+ cache.mycache:
+ adapter: cache.adapter.redis
+ default_lifetime: 3600
+
+ .. code-block:: xml
+
+
+
+
-**type**: ``string`` **possible values**: ``'none'``, ``'short'`` or ``'full'``
+
+
+
+
+
+
+
-For 'short', a concise trace of the main request will be added as an HTTP header.
-'full' will add traces for all requests (including ESI subrequests).
-(default: 'full' if in debug; 'none' otherwise)
+ .. code-block:: php
-trace_header
-............
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
-**type**: ``string``
+ return static function (FrameworkConfig $framework): void {
+ $framework->cache()
+ ->pool('cache.mycache')
+ ->adapters(['cache.adapter.redis'])
+ ->defaultLifetime(3600);
+ };
-Header name to use for traces. (default: X-Symfony-Cache)
+adapter
+"""""""
-default_ttl
-...........
+**type**: ``string`` **default**: ``cache.app``
-**type**: ``integer``
+The service name of the adapter to use. You can specify one of the default
+services that follow the pattern ``cache.adapter.[type]``. Alternatively you
+can specify another cache pool as base, which will make this pool inherit the
+settings from the base pool as defaults.
-The number of seconds that a cache entry should be considered fresh when no
-explicit freshness information is provided in a response. Explicit
-Cache-Control or Expires headers override this value. (default: 0)
+.. note::
-private_headers
-...............
+ Your service needs to implement the ``Psr\Cache\CacheItemPoolInterface`` interface.
-**type**: ``array``
+clearer
+"""""""
-Set of request headers that trigger "private" cache-control behavior on responses
-that don't explicitly state whether the response is public or private via a
-Cache-Control directive. (default: Authorization and Cookie)
+**type**: ``string``
-skip_response_headers
-.....................
+The cache clearer used to clear your PSR-6 cache.
-**type**: ``array`` **default**: ``Set-Cookie``
+.. seealso::
-Set of response headers that will never be cached even when the response is cacheable
-and public.
+ For more information, see :class:`Symfony\\Component\\HttpKernel\\CacheClearer\\Psr6CacheClearer`.
-allow_reload
-............
+default_lifetime
+""""""""""""""""
-**type**: ``string``
+**type**: ``integer`` | ``string``
-Specifies whether the client can force a cache reload by including a
-Cache-Control "no-cache" directive in the request. Set it to ``true``
-for compliance with RFC 2616. (default: false)
+Default lifetime of your cache items. Give an integer value to set the default
+lifetime in seconds. A string value could be ISO 8601 time interval, like ``"PT5M"``
+or a PHP date expression that is accepted by ``strtotime()``, like ``"5 minutes"``.
-allow_revalidate
-................
+If no value is provided, the cache adapter will fallback to the default value on
+the actual cache storage.
-**type**: ``string``
+.. _reference-cache-pools-name:
-Specifies whether the client can force a cache revalidate by including a
-Cache-Control "max-age=0" directive in the request. Set it to ``true``
-for compliance with RFC 2616. (default: false)
+name
+""""
-stale_while_revalidate
-......................
+**type**: ``prototype``
-**type**: ``integer``
+Name of the pool you want to create.
-Specifies the default number of seconds (the granularity is the second as the
-Response TTL precision is a second) during which the cache can immediately return
-a stale response while it revalidates it in the background (default: 2).
-This setting is overridden by the stale-while-revalidate HTTP Cache-Control
-extension (see RFC 5861).
+.. note::
-stale_if_error
-..............
+ Your pool name must differ from ``cache.app`` or ``cache.system``.
-**type**: ``integer``
+provider
+""""""""
-Specifies the default number of seconds (the granularity is the second) during
-which the cache can serve a stale response when an error is encountered
-(default: 60). This setting is overridden by the stale-if-error HTTP
-Cache-Control extension (see RFC 5861).
+**type**: ``string``
- .. _configuration-framework-http_method_override:
+Overwrite the default service name or DSN respectively, if you do not want to
+use what is configured as ``default_X_provider`` under ``cache``. See the
+description of the default provider setting above for information on how to
+specify your specific provider.
-http_method_override
-~~~~~~~~~~~~~~~~~~~~
+public
+""""""
**type**: ``boolean`` **default**: ``false``
-This determines whether the ``_method`` request parameter is used as the
-intended HTTP method on POST requests. If enabled, the
-:method:`Request::enableHttpMethodParameterOverride `
-method gets called automatically. It becomes the service container parameter
-named ``kernel.http_method_override``.
-
-.. seealso::
+Whether your service should be public or not.
- :ref:`Changing the Action and HTTP Method ` of
- Symfony forms.
+tags
+""""
-.. warning::
+**type**: ``boolean`` | ``string`` **default**: ``null``
- If you're using the :ref:`HttpCache Reverse Proxy `
- with this option, the kernel will ignore the ``_method`` parameter,
- which could lead to errors.
+Whether your service should be able to handle tags or not.
+Can also be the service id of another cache pool where tags will be stored.
- To fix this, invoke the ``enableHttpMethodParameterOverride()`` method
- before creating the ``Request`` object::
+.. _reference-cache-prefix-seed:
- // public/index.php
+prefix_seed
+...........
- // ...
- $kernel = new CacheKernel($kernel);
+**type**: ``string`` **default**: ``_%kernel.project_dir%.%kernel.container_class%``
- Request::enableHttpMethodParameterOverride(); // <-- add this line
- $request = Request::createFromGlobals();
- // ...
+This value is used as part of the "namespace" generated for the
+cache item keys. A common practice is to use the unique name of the application
+(e.g. ``symfony.com``) because that prevents naming collisions when deploying
+multiple applications into the same path (on different servers) that share the
+same cache backend.
-trust_x_sendfile_type_header
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+It's also useful when using `blue/green deployment`_ strategies and more
+generally, when you need to abstract out the actual deployment directory (for
+example, when warming caches offline).
-**type**: ``boolean`` **default**: ``false``
+.. note::
-``X-Sendfile`` is a special HTTP header that tells web servers to replace the
-response contents by the file that is defined in that header. This improves
-performance because files are no longer served by your application but directly
-by the web server.
+ The ``prefix_seed`` option is used at compile time. This means
+ that any change made to this value after container's compilation
+ will have no effect.
-This configuration option determines whether to trust ``x-sendfile`` header for
-BinaryFileResponse. If enabled, Symfony calls the
-:method:`BinaryFileResponse::trustXSendfileTypeHeader `
-method automatically. It becomes the service container parameter named
-``kernel.trust_x_sendfile_type_header``.
+.. _reference-cache-system:
-.. _reference-framework-trusted-headers:
+system
+......
-trusted_headers
-~~~~~~~~~~~~~~~
+**type**: ``string`` **default**: ``cache.adapter.system``
-The ``trusted_headers`` option is needed to configure which client information
-should be trusted (e.g. their host) when running Symfony behind a load balancer
-or a reverse proxy. See :doc:`/deployment/proxies`.
+The cache adapter used by the ``cache.system`` service. It supports the same
+adapters available for the ``cache.app`` service.
-.. _reference-framework-trusted-proxies:
+.. _reference-framework-csrf-protection:
-trusted_proxies
+csrf_protection
~~~~~~~~~~~~~~~
-The ``trusted_proxies`` option is needed to get precise information about the
-client (e.g. their IP address) when running Symfony behind a load balancer or a
-reverse proxy. See :doc:`/deployment/proxies`.
-
-ide
-~~~
-
-**type**: ``string`` **default**: ``%env(default::SYMFONY_IDE)%``
+.. seealso::
-Symfony turns file paths seen in variable dumps and exception messages into
-links that open those files right inside your browser. If you prefer to open
-those files in your favorite IDE or text editor, set this option to any of the
-following values: ``phpstorm``, ``sublime``, ``textmate``, ``macvim``, ``emacs``,
-``atom`` and ``vscode``.
+ For more information about CSRF protection, see :doc:`/security/csrf`.
-.. note::
+enabled
+.......
- The ``phpstorm`` option is supported natively by PhpStorm on macOS and
- Windows; Linux requires installing `phpstorm-url-handler`_.
+**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
-If you use another editor, the expected configuration value is a URL template
-that contains an ``%f`` placeholder where the file path is expected and ``%l``
-placeholder for the line number (percentage signs (``%``) must be escaped by
-doubling them to prevent Symfony from interpreting them as container parameters).
+This option can be used to disable CSRF protection on *all* forms. But you
+can also :ref:`disable CSRF protection on individual forms `.
.. configuration-block::
@@ -254,7 +821,8 @@ doubling them to prevent Symfony from interpreting them as container parameters)
# config/packages/framework.yaml
framework:
- ide: 'myide://open?url=file://%%f&line=%%l'
+ # ...
+ csrf_protection: true
.. code-block:: xml
@@ -265,83 +833,64 @@ doubling them to prevent Symfony from interpreting them as container parameters)
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services
https://symfony.com/schema/dic/services/services-1.0.xsd
- http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
-
-
+ http://symfony.com/schema/dic/symfony
+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
+
+
+
.. code-block:: php
// config/packages/framework.php
use Symfony\Config\FrameworkConfig;
-
return static function (FrameworkConfig $framework): void {
- $framework->ide('myide://open?url=file://%%f&line=%%l');
+ $framework->csrfProtection()
+ ->enabled(true)
+ ;
};
-Since every developer uses a different IDE, the recommended way to enable this
-feature is to configure it on a system level. First, you can define this option
-in the ``SYMFONY_IDE`` environment variable, which Symfony reads automatically
-when ``framework.ide`` config is not set.
-
-Another alternative is to set the ``xdebug.file_link_format`` option in your
-``php.ini`` configuration file. The format to use is the same as for the
-``framework.ide`` option, but without the need to escape the percent signs
-(``%``) by doubling them:
-
-.. code-block:: ini
-
- // example for PhpStorm
- xdebug.file_link_format="phpstorm://open?file=%f&line=%l"
-
- // example for PhpStorm with Jetbrains Toolbox
- xdebug.file_link_format="jetbrains://phpstorm/navigate/reference?project=example&path=%f:%l"
-
- // example for Sublime Text
- xdebug.file_link_format="subl://open?url=file://%f&line=%l"
+If you're using forms, but want to avoid starting your session (e.g. using
+forms in an API-only website), ``csrf_protection`` will need to be set to
+``false``.
-.. note::
+stateless_token_ids
+...................
- If both ``framework.ide`` and ``xdebug.file_link_format`` are defined,
- Symfony uses the value of the ``xdebug.file_link_format`` option.
+**type**: ``array`` **default**: ``[]``
-.. tip::
+The list of CSRF token ids that will use :ref:`stateless CSRF protection `.
- Setting the ``xdebug.file_link_format`` ini option works even if the Xdebug
- extension is not enabled.
+.. versionadded:: 7.2
-.. tip::
+ The ``stateless_token_ids`` option was introduced in Symfony 7.2.
- When running your app in a container or in a virtual machine, you can tell
- Symfony to map files from the guest to the host by changing their prefix.
- This map should be specified at the end of the URL template, using ``&`` and
- ``>`` as guest-to-host separators:
+check_header
+............
- .. code-block:: text
+**type**: ``integer`` or ``bool`` **default**: ``false``
- // /path/to/guest/.../file will be opened
- // as /path/to/host/.../file on the host
- // and /var/www/app/ as /projects/my_project/ also
- 'myide://%%f:%%l&/path/to/guest/>/path/to/host/&/var/www/app/>/projects/my_project/&...'
+Whether to check the CSRF token in an HTTP header in addition to the cookie when
+using :ref:`stateless CSRF protection `. You can also set
+this to ``2`` (the value of the ``CHECK_ONLY_HEADER`` constant on the
+:class:`Symfony\\Component\\Security\\Csrf\\SameOriginCsrfTokenManager` class)
+to check only the header and ignore the cookie.
- // example for PhpStorm
- 'phpstorm://open?file=%%f&line=%%l&/var/www/app/>/projects/my_project/'
+.. versionadded:: 7.2
-.. _reference-framework-test:
+ The ``check_header`` option was introduced in Symfony 7.2.
-test
-~~~~
+cookie_name
+...........
-**type**: ``boolean``
+**type**: ``string`` **default**: ``csrf-token``
-If this configuration setting is present (and not ``false``), then the services
-related to testing your application (e.g. ``test.client``) are loaded. This
-setting should be present in your ``test`` environment (usually via
-``config/packages/test/framework.yaml``).
+The name of the cookie (and HTTP header) to use for the double-submit when using
+:ref:`stateless CSRF protection `.
-.. seealso::
+.. versionadded:: 7.2
- For more information, see :doc:`/testing`.
+ The ``cookie_name`` option was introduced in Symfony 7.2.
.. _config-framework-default_locale:
@@ -443,31 +992,36 @@ If ``true``, Symfony adds a ``X-Robots-Tag: noindex`` HTTP tag to all responses
This option is a protection measure in case you accidentally publish your site
in debug mode.
-.. _configuration-framework-trusted-hosts:
+.. _config-framework-error_controller:
-trusted_hosts
-~~~~~~~~~~~~~
+error_controller
+~~~~~~~~~~~~~~~~
-**type**: ``array`` | ``string`` **default**: ``[]``
+**type**: ``string`` **default**: ``error_controller``
-A lot of different attacks have been discovered relying on inconsistencies
-in handling the ``Host`` header by various software (web servers, reverse
-proxies, web frameworks, etc.). Basically, every time the framework is
-generating an absolute URL (when sending an email to reset a password for
-instance), the host might have been manipulated by an attacker.
+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`)
+renders specific templates under different error conditions (see
+:doc:`/controller/error_pages`).
+
+esi
+~~~
.. seealso::
- You can read `HTTP Host header attacks`_ for more information about
- these kinds of attacks.
+ You can read more about Edge Side Includes (ESI) in :ref:`edge-side-includes`.
-The Symfony :method:`Request::getHost() `
-method might be vulnerable to some of these attacks because it depends on
-the configuration of your web server. One simple solution to avoid these
-attacks is to configure a list of hosts that your Symfony application can respond
-to. That's the purpose of this ``trusted_hosts`` option. If the incoming
-request's hostname doesn't match one of the regular expressions in this list,
-the application won't respond and the user will receive a 400 response.
+.. _reference-esi-enabled:
+
+enabled
+.......
+
+**type**: ``boolean`` **default**: ``false``
+
+Whether to enable the edge side includes support in the framework.
+
+You can also set ``esi`` to ``true`` to enable it:
.. configuration-block::
@@ -475,7 +1029,7 @@ the application won't respond and the user will receive a 400 response.
# config/packages/framework.yaml
framework:
- trusted_hosts: ['^example\.com$', '^example\.org$']
+ esi: true
.. code-block:: xml
@@ -489,9 +1043,7 @@ the application won't respond and the user will receive a 400 response.
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
- ^example\.com$
- ^example\.org$
-
+
@@ -501,182 +1053,187 @@ the application won't respond and the user will receive a 400 response.
use Symfony\Config\FrameworkConfig;
return static function (FrameworkConfig $framework): void {
- $framework->trustedHosts(['^example\.com$', '^example\.org$']);
+ $framework->esi()->enabled(true);
};
-Hosts can also be configured to respond to any subdomain, via
-``^(.+\.)?example\.com$`` for instance.
-
-In addition, you can also set the trusted hosts in the front controller
-using the ``Request::setTrustedHosts()`` method::
-
- // public/index.php
- Request::setTrustedHosts(['^(.+\.)?example\.com$', '^(.+\.)?example\.org$']);
-
-The default value for this option is an empty array, meaning that the application
-can respond to any given host.
-
-.. seealso::
+.. _framework_exceptions:
- Read more about this in the `Security Advisory Blog post`_.
+exceptions
+~~~~~~~~~~
-.. _reference-framework-form:
+**type**: ``array``
-form
-~~~~
+Defines the :ref:`log level `, :ref:`log channel `
+and HTTP status code applied to the exceptions that match the given exception class:
-.. _reference-form-enabled:
+.. configuration-block::
-enabled
-.......
+ .. code-block:: yaml
-**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
+ # config/packages/exceptions.yaml
+ framework:
+ exceptions:
+ Symfony\Component\HttpKernel\Exception\BadRequestHttpException:
+ log_level: 'debug'
+ status_code: 422
+ log_channel: 'custom_channel'
-Whether to enable the form services or not in the service container. If
-you don't use forms, setting this to ``false`` may increase your application's
-performance because less services will be loaded into the container.
+ .. code-block:: xml
-This option will automatically be set to ``true`` when one of the child
-settings is configured.
+
+
+
-.. note::
+
+
+
+
+
- This will automatically enable the `validation`_.
+ .. code-block:: php
-.. seealso::
+ // config/packages/exceptions.php
+ use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+ use Symfony\Config\FrameworkConfig;
- For more details, see :doc:`/forms`.
+ return static function (FrameworkConfig $framework): void {
+ $framework->exception(BadRequestHttpException::class)
+ ->logLevel('debug')
+ ->statusCode(422)
+ ->logChannel('custom_channel')
+ ;
+ };
-.. _reference-form-field-name:
+.. versionadded:: 7.3
-field_name
-..........
+ The ``log_channel`` option was introduced in Symfony 7.3.
-**type**: ``string`` **default**: ``_token``
+The order in which you configure exceptions is important because Symfony will
+use the configuration of the first exception that matches ``instanceof``:
-This is the field name that you should give to the CSRF token field of your forms.
+.. code-block:: yaml
-.. _reference-framework-csrf-protection:
+ # config/packages/exceptions.yaml
+ framework:
+ exceptions:
+ Exception:
+ log_level: 'debug'
+ status_code: 404
+ # The following configuration will never be used because \RuntimeException extends \Exception
+ RuntimeException:
+ log_level: 'debug'
+ status_code: 422
-csrf_protection
-~~~~~~~~~~~~~~~
+You can map a status code and a set of headers to an exception thanks
+to the ``#[WithHttpStatus]`` attribute on the exception class::
-.. seealso::
+ namespace App\Exception;
- For more information about CSRF protection, see :doc:`/security/csrf`.
+ use Symfony\Component\HttpKernel\Attribute\WithHttpStatus;
-.. _reference-csrf_protection-enabled:
+ #[WithHttpStatus(422, [
+ 'Retry-After' => 10,
+ 'X-Custom-Header' => 'header-value',
+ ])]
+ class CustomException extends \Exception
+ {
+ }
-enabled
-.......
+It is also possible to map a log level on a custom exception class using
+the ``#[WithLogLevel]`` attribute::
-**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
+ namespace App\Exception;
-This option can be used to disable CSRF protection on *all* forms. But you
-can also :ref:`disable CSRF protection on individual forms `.
+ use Psr\Log\LogLevel;
+ use Symfony\Component\HttpKernel\Attribute\WithLogLevel;
-.. configuration-block::
+ #[WithLogLevel(LogLevel::WARNING)]
+ class CustomException extends \Exception
+ {
+ }
- .. code-block:: yaml
+The attributes can also be added to interfaces directly::
- # config/packages/framework.yaml
- framework:
- # ...
- csrf_protection: true
+ namespace App\Exception;
- .. code-block:: xml
+ use Symfony\Component\HttpKernel\Attribute\WithHttpStatus;
-
-
-
-
-
-
-
+ #[WithHttpStatus(422)]
+ interface CustomExceptionInterface
+ {
+ }
- .. code-block:: php
+ class CustomException extends \Exception implements CustomExceptionInterface
+ {
+ }
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
- return static function (FrameworkConfig $framework): void {
- $framework->csrfProtection()
- ->enabled(true)
- ;
- };
+.. versionadded:: 7.1
-If you're using forms, but want to avoid starting your session (e.g. using
-forms in an API-only website), ``csrf_protection`` will need to be set to
-``false``.
+ Support to use ``#[WithHttpStatus]`` and ``#[WithLogLevel]`` attributes
+ on interfaces was introduced in Symfony 7.1.
-.. _config-framework-error_controller:
+.. _reference-framework-form:
-error_controller
-~~~~~~~~~~~~~~~~
+form
+~~~~
-**type**: ``string`` **default**: ``error_controller``
+.. _reference-form-enabled:
-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`)
-renders specific templates under different error conditions (see
-:doc:`/controller/error_pages`).
+enabled
+.......
-esi
-~~~
+**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
-.. seealso::
+Whether to enable the form services or not in the service container. If
+you don't use forms, setting this to ``false`` may increase your application's
+performance because less services will be loaded into the container.
- You can read more about Edge Side Includes (ESI) in :ref:`edge-side-includes`.
+This option will automatically be set to ``true`` when one of the child
+settings is configured.
-.. _reference-esi-enabled:
+.. note::
-enabled
-.......
+ This will automatically enable the `validation`_.
-**type**: ``boolean`` **default**: ``false``
+.. seealso::
-Whether to enable the edge side includes support in the framework.
+ For more details, see :doc:`/forms`.
-You can also set ``esi`` to ``true`` to enable it:
+csrf_protection
+...............
-.. configuration-block::
+field_name
+''''''''''
- .. code-block:: yaml
+**type**: ``string`` **default**: ``_token``
- # config/packages/framework.yaml
- framework:
- esi: true
+This is the field name that you should give to the CSRF token field of your forms.
- .. code-block:: xml
+field_attr
+''''''''''
-
-
-
+**type**: ``array`` **default**: ``['data-controller' => 'csrf-protection']``
-
-
-
-
+HTML attributes to add to the CSRF token field of your forms.
- .. code-block:: php
+token_id
+''''''''
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
+**type**: ``string`` **default**: ``null``
- return static function (FrameworkConfig $framework): void {
- $framework->esi()->enabled(true);
- };
+The CSRF token ID used to validate the CSRF tokens of your forms. This setting
+applies only to form types that use :ref:`service autoconfiguration `,
+which typically means your own form types, not those registered by third-party bundles.
fragments
~~~~~~~~~
@@ -721,6 +1278,120 @@ path
The path prefix for fragments. The fragment listener will only be executed
when the request starts with this path.
+handle_all_throwables
+~~~~~~~~~~~~~~~~~~~~~
+
+**type**: ``boolean`` **default**: ``true``
+
+When set to ``true``, the Symfony kernel will catch all ``\Throwable`` exceptions
+thrown by the application and will turn them into HTTP responses.
+
+html_sanitizer
+~~~~~~~~~~~~~~
+
+The ``html_sanitizer`` option (and its children) are used to configure
+custom HTML sanitizers. Read more about the options in the
+:ref:`HTML sanitizer documentation `.
+
+.. _configuration-framework-http_cache:
+
+http_cache
+~~~~~~~~~~
+
+allow_reload
+............
+
+**type**: ``boolean`` **default**: ``false``
+
+Specifies whether the client can force a cache reload by including a
+Cache-Control "no-cache" directive in the request. Set it to ``true``
+for compliance with RFC 2616.
+
+allow_revalidate
+................
+
+**type**: ``boolean`` **default**: ``false``
+
+Specifies whether the client can force a cache revalidate by including a
+Cache-Control "max-age=0" directive in the request. Set it to ``true``
+for compliance with RFC 2616.
+
+debug
+.....
+
+**type**: ``boolean`` **default**: ``%kernel.debug%``
+
+If true, exceptions are thrown when things go wrong. Otherwise, the cache will
+try to carry on and deliver a meaningful response.
+
+default_ttl
+...........
+
+**type**: ``integer`` **default**: ``0``
+
+The number of seconds that a cache entry should be considered fresh when no
+explicit freshness information is provided in a response. Explicit
+Cache-Control or Expires headers override this value.
+
+enabled
+.......
+
+**type**: ``boolean`` **default**: ``false``
+
+private_headers
+...............
+
+**type**: ``array`` **default**: ``['Authorization', 'Cookie']``
+
+Set of request headers that trigger "private" cache-control behavior on responses
+that don't explicitly state whether the response is public or private via a
+Cache-Control directive.
+
+skip_response_headers
+.....................
+
+**type**: ``array`` **default**: ``Set-Cookie``
+
+Set of response headers that will never be cached even when the response is cacheable
+and public.
+
+stale_if_error
+..............
+
+**type**: ``integer`` **default**: ``60``
+
+Specifies the default number of seconds (the granularity is the second) during
+which the cache can serve a stale response when an error is encountered.
+This setting is overridden by the stale-if-error HTTP
+Cache-Control extension (see RFC 5861).
+
+stale_while_revalidate
+......................
+
+**type**: ``integer`` **default**: ``2``
+
+Specifies the default number of seconds (the granularity is the second as the
+Response TTL precision is a second) during which the cache can immediately return
+a stale response while it revalidates it in the background.
+This setting is overridden by the stale-while-revalidate HTTP Cache-Control
+extension (see RFC 5861).
+
+trace_header
+............
+
+**type**: ``string`` **default**: ``'X-Symfony-Cache'``
+
+Header name to use for traces.
+
+trace_level
+...........
+
+**type**: ``string`` **possible values**: ``'none'``, ``'short'`` or ``'full'``
+
+For 'short', a concise trace of the main request will be added as an HTTP header.
+'full' will add traces for all requests (including ESI subrequests).
+(default: ``'full'`` if in debug; ``'none'`` otherwise)
+
.. _reference-http-client:
http_client
@@ -1071,6 +1742,8 @@ max_duration
The maximum execution time, in seconds, that the request and the response are
allowed to take. A value lower than or equal to 0 means it is unlimited.
+.. _reference-http-client-max-host-connections:
+
max_host_connections
....................
@@ -1251,7 +1924,7 @@ timeout
**type**: ``float`` **default**: depends on your PHP config
-Time, in seconds, to wait for a response. If the response takes longer, a
+Time, in seconds, to wait for network activity. If the connection is idle for longer, a
:class:`Symfony\\Component\\HttpClient\\Exception\\TransportException` is thrown.
Its default value is the same as the value of PHP's `default_socket_timeout`_
config option.
@@ -1275,147 +1948,64 @@ connection is verified for authenticity. Authenticating the certificate is not
enough to be sure about the server, so you should combine this with the
``verify_host`` option.
-html_sanitizer
-~~~~~~~~~~~~~~
-
-The ``html_sanitizer`` option (and its children) are used to configure
-custom HTML sanitizers. Read more about the options in the
-:ref:`HTML sanitizer documentation `.
-
-profiler
-~~~~~~~~
-
-.. _reference-profiler-enabled:
-
-enabled
-.......
-
-**type**: ``boolean`` **default**: ``false``
-
-The profiler can be enabled by setting this option to ``true``. When you
-install it using Symfony Flex, the profiler is enabled in the ``dev``
-and ``test`` environments.
-
-.. note::
-
- The profiler works independently from the Web Developer Toolbar, see
- the :doc:`WebProfilerBundle configuration `
- on how to disable/enable the toolbar.
-
-collect
-.......
-
-**type**: ``boolean`` **default**: ``true``
-
-This option configures the way the profiler behaves when it is enabled. If set
-to ``true``, the profiler collects data for all requests. If you want to only
-collect information on-demand, you can set the ``collect`` flag to ``false`` and
-activate the data collectors manually::
-
- $profiler->enable();
-
-collect_parameter
-.................
-
-**type**: ``string`` **default**: ``null``
-
-This specifies name of a query parameter, a body parameter or a request attribute
-used to enable or disable collection of data by the profiler for each request.
-Combine it with the ``collect`` option to enable/disable the profiler on demand:
-
-* If the ``collect`` option is set to ``true`` but this parameter exists in a
- request and has any value other than ``true``, ``yes``, ``on`` or ``1``, the
- request data will not be collected;
-* If the ``collect`` option is set to ``false``, but this parameter exists in a
- request and has value of ``true``, ``yes``, ``on`` or ``1``, the request data
- will be collected.
-
-only_exceptions
-...............
-
-**type**: ``boolean`` **default**: ``false``
-
-When this is set to ``true``, the profiler will only be enabled when an
-exception is thrown during the handling of the request.
-
-.. _only_master_requests:
-
-only_main_requests
-..................
-
-**type**: ``boolean`` **default**: ``false``
-
-When this is set to ``true``, the profiler will only be enabled on the main
-requests (and not on the subrequests).
-
-.. _profiler-dsn:
-
-dsn
-...
-
-**type**: ``string`` **default**: ``file:%kernel.cache_dir%/profiler``
-
-The DSN where to store the profiling information.
-
-.. _collect_serializer_data:
+ .. _configuration-framework-http_method_override:
-collect_serializer_data
-.......................
+http_method_override
+~~~~~~~~~~~~~~~~~~~~
**type**: ``boolean`` **default**: ``false``
-Set this option to ``true`` to enable the serializer data collector and its
-profiler panel. When this option is ``true``, all normalizers and encoders are
-decorated by traceable implementations that collect profiling information about them.
-
-rate_limiter
-~~~~~~~~~~~~
-
-.. _reference-rate-limiter-name:
-
-name
-....
+This determines whether the ``_method`` request parameter is used as the
+intended HTTP method on POST requests. If enabled, the
+:method:`Request::enableHttpMethodParameterOverride `
+method gets called automatically. It becomes the service container parameter
+named ``kernel.http_method_override``.
-**type**: ``prototype``
+.. seealso::
-Name of the rate limiter you want to create.
+ :ref:`Changing the Action and HTTP Method ` of
+ Symfony forms.
-lock_factory
-""""""""""""
+.. warning::
-**type**: ``string`` **default:** ``lock.factory``
+ If you're using the :ref:`HttpCache Reverse Proxy `
+ with this option, the kernel will ignore the ``_method`` parameter,
+ which could lead to errors.
-The service that is used to create a lock. The service has to be an instance of
-the :class:`Symfony\\Component\\Lock\\LockFactory` class.
+ To fix this, invoke the ``enableHttpMethodParameterOverride()`` method
+ before creating the ``Request`` object::
-policy
-""""""
+ // public/index.php
-**type**: ``string`` **required**
+ // ...
+ $kernel = new CacheKernel($kernel);
-The name of the rate limiting algorithm to use. Example names are ``fixed_window``,
-``sliding_window`` and ``no_limit``. See :ref:`Rate Limiter Policies `)
-for more information.
+ Request::enableHttpMethodParameterOverride(); // <-- add this line
+ $request = Request::createFromGlobals();
+ // ...
-request
-~~~~~~~
+.. _reference-framework-ide:
-formats
-.......
+ide
+~~~
-**type**: ``array`` **default**: ``[]``
+**type**: ``string`` **default**: ``%env(default::SYMFONY_IDE)%``
-This setting is used to associate additional request formats (e.g. ``html``)
-to one or more mime types (e.g. ``text/html``), which will allow you to use the
-format & mime types to call
-:method:`Request::getFormat($mimeType) ` or
-:method:`Request::getMimeType($format) `.
+Symfony turns file paths seen in variable dumps and exception messages into
+links that open those files right inside your browser. If you prefer to open
+those files in your favorite IDE or text editor, set this option to any of the
+following values: ``phpstorm``, ``sublime``, ``textmate``, ``macvim``, ``emacs``,
+``atom`` and ``vscode``.
-In practice, this is important because Symfony uses it to automatically set the
-``Content-Type`` header on the ``Response`` (if you don't explicitly set one).
-If you pass an array of mime types, the first will be used for the header.
+.. note::
-To configure a ``jsonp`` format:
+ The ``phpstorm`` option is supported natively by PhpStorm on macOS and
+ Windows; Linux requires installing `phpstorm-url-handler`_.
+
+If you use another editor, the expected configuration value is a URL template
+that contains an ``%f`` placeholder where the file path is expected and ``%l``
+placeholder for the line number (percentage signs (``%``) must be escaped by
+doubling them to prevent Symfony from interpreting them as container parameters).
.. configuration-block::
@@ -1423,30 +2013,20 @@ To configure a ``jsonp`` format:
# config/packages/framework.yaml
framework:
- request:
- formats:
- jsonp: 'application/javascript'
+ ide: 'myide://open?url=file://%%f&line=%%l'
.. code-block:: xml
-
+ http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
-
-
-
- application/javascript
-
-
-
+
.. code-block:: php
@@ -1455,193 +2035,98 @@ To configure a ``jsonp`` format:
use Symfony\Config\FrameworkConfig;
return static function (FrameworkConfig $framework): void {
- $framework->request()
- ->format('jsonp', 'application/javascript');
+ $framework->ide('myide://open?url=file://%%f&line=%%l');
};
-router
-~~~~~~
-
-resource
-........
-
-**type**: ``string`` **required**
-
-The path the main routing resource (e.g. a YAML file) that contains the
-routes and imports the router should load.
-
-.. _reference-router-type:
-
-type
-....
-
-**type**: ``string``
-
-The type of the resource to hint the loaders about the format. This isn't
-needed when you use the default routers with the expected file extensions
-(``.xml``, ``.yaml``, ``.php``).
-
-default_uri
-...........
-
-**type**: ``string``
-
-The default URI used to generate URLs in a non-HTTP context (see
-:ref:`Generating URLs in Commands `).
-
-http_port
-.........
-
-**type**: ``integer`` **default**: ``80``
-
-The port for normal http requests (this is used when matching the scheme).
+Since every developer uses a different IDE, the recommended way to enable this
+feature is to configure it on a system level. First, you can define this option
+in the ``SYMFONY_IDE`` environment variable, which Symfony reads automatically
+when ``framework.ide`` config is not set.
-https_port
-..........
+Another alternative is to set the ``xdebug.file_link_format`` option in your
+``php.ini`` configuration file. The format to use is the same as for the
+``framework.ide`` option, but without the need to escape the percent signs
+(``%``) by doubling them:
-**type**: ``integer`` **default**: ``443``
+.. code-block:: ini
-The port for https requests (this is used when matching the scheme).
+ // example for PhpStorm
+ xdebug.file_link_format="phpstorm://open?file=%f&line=%l"
-strict_requirements
-...................
+ // example for PhpStorm with Jetbrains Toolbox
+ xdebug.file_link_format="jetbrains://phpstorm/navigate/reference?project=example&path=%f:%l"
-**type**: ``mixed`` **default**: ``true``
+ // example for Sublime Text
+ xdebug.file_link_format="subl://open?url=file://%f&line=%l"
-Determines the routing generator behavior. When generating a route that
-has specific :ref:`parameter requirements `, the generator
-can behave differently in case the used parameters do not meet these requirements.
+.. note::
-The value can be one of:
+ If both ``framework.ide`` and ``xdebug.file_link_format`` are defined,
+ Symfony uses the value of the ``xdebug.file_link_format`` option.
-``true``
- Throw an exception when the requirements are not met;
-``false``
- Disable exceptions when the requirements are not met and return ``''``
- instead;
-``null``
- Disable checking the requirements (thus, match the route even when the
- requirements don't match).
+.. tip::
-``true`` is recommended in the development environment, while ``false``
-or ``null`` might be preferred in production.
+ Setting the ``xdebug.file_link_format`` ini option works even if the Xdebug
+ extension is not enabled.
-utf8
-....
+.. tip::
-**type**: ``boolean`` **default**: ``true``
+ When running your app in a container or in a virtual machine, you can tell
+ Symfony to map files from the guest to the host by changing their prefix.
+ This map should be specified at the end of the URL template, using ``&`` and
+ ``>`` as guest-to-host separators:
-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
-when using ``.``, instead of matching only a single byte.
+ .. code-block:: text
-If the charset of your application is UTF-8 (as defined in the
-:ref:`getCharset() method ` of your kernel) it's
-recommended setting it to ``true``. This will make non-UTF8 URLs to generate 404
-errors.
+ // /path/to/guest/.../file will be opened
+ // as /path/to/host/.../file on the host
+ // and /var/www/app/ as /projects/my_project/ also
+ 'myide://%%f:%%l&/path/to/guest/>/path/to/host/&/var/www/app/>/projects/my_project/&...'
-cache_dir
-.........
+ // example for PhpStorm
+ 'phpstorm://open?file=%%f&line=%%l&/var/www/app/>/projects/my_project/'
-**type**: ``string`` **default**: ``%kernel.cache_dir%``
+.. _reference-lock:
-The directory where routing information will be cached. Can be set to
-``~`` (``null``) to disable route caching.
+lock
+~~~~
-.. deprecated:: 7.1
+**type**: ``string`` | ``array``
- Setting the ``cache_dir`` option is deprecated since Symfony 7.1. The routes
- are now always cached in the ``%kernel.build_dir%`` directory.
+The default lock adapter. If not defined, the value is set to ``semaphore`` when
+available, or to ``flock`` otherwise. Store's DSN are also allowed.
-secrets
-~~~~~~~
+.. _reference-lock-enabled:
enabled
.......
**type**: ``boolean`` **default**: ``true``
-Whether to enable or not secrets managements.
-
-decryption_env_var
-..................
-
-**type**: ``string`` **default**: ``base64:default::SYMFONY_DECRYPTION_SECRET``
-
-The env var name that contains the vault decryption secret. By default, this
-value will be decoded from base64.
-
-local_dotenv_file
-.................
-
-**type**: ``string`` **default**: ``%kernel.project_dir%/.env.%kernel.environment%.local``
-
-The path to the local ``.env`` file. This file must contain the vault
-decryption key, given by the ``decryption_env_var`` option.
-
-vault_directory
-...............
-
-**type**: ``string`` **default**: ``%kernel.project_dir%/config/secrets/%kernel.runtime_environment%``
-
-The directory to store the secret vault. By default, the path includes the value
-of the :ref:`kernel.runtime_environment `
-parameter.
-
-.. _config-framework-session:
-
-session
-~~~~~~~
-
-.. _storage_id:
-
-storage_factory_id
-..................
-
-**type**: ``string`` **default**: ``session.storage.factory.native``
-
-The service ID used for creating the ``SessionStorageInterface`` that stores
-the session. This service is available in the Symfony application via the
-``session.storage.factory`` service alias. The class has to implement
-:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageFactoryInterface`.
-To see a list of all available storages, run:
-
-.. code-block:: terminal
-
- $ php bin/console debug:container session.storage.factory.
-
-.. _config-framework-session-handler-id:
+Whether to enable the support for lock or not. This setting is
+automatically set to ``true`` when one of the child settings is configured.
-handler_id
-..........
+.. _reference-lock-resources:
-**type**: ``string`` | ``null`` **default**: ``null``
+resources
+.........
-If ``framework.session.save_path`` is not set, the default value of this option
-is ``null``, which means to use the session handler configured in php.ini. If the
-``framework.session.save_path`` option is set, then Symfony stores sessions using
-the native file session handler.
+**type**: ``array``
-It is possible to :ref:`store sessions in a database `,
-and also to configure the session handler with a DSN:
+A map of lock stores to be created by the framework extension, with
+the name as key and DSN or service id as value:
.. configuration-block::
- .. code-block:: yaml
-
- # config/packages/framework.yaml
- framework:
- session:
- # a few possible examples
- handler_id: 'redis://localhost'
- handler_id: '%env(REDIS_URL)%'
- handler_id: '%env(DATABASE_URL)%'
- handler_id: 'file://%kernel.project_dir%/var/sessions'
-
+ .. code-block:: yaml
+
+ # config/packages/lock.yaml
+ framework:
+ lock: '%env(LOCK_DSN)%'
+
.. code-block:: xml
-
+
+
-
-
+
+ %env(LOCK_DSN)%
+
.. code-block:: php
- // config/packages/framework.php
- use function Symfony\Component\DependencyInjection\Loader\Configurator\env;
+ // config/packages/lock.php
use Symfony\Config\FrameworkConfig;
return static function (FrameworkConfig $framework): void {
- // ...
-
- $framework->session()
- // a few possible examples
- ->handlerId('redis://localhost')
- ->handlerId(env('REDIS_URL'))
- ->handlerId(env('DATABASE_URL'))
- ->handlerId('file://%kernel.project_dir%/var/sessions');
+ $framework->lock()
+ ->resource('default', [env('LOCK_DSN')]);
};
-.. note::
-
- Supported DSN protocols are the following:
+.. seealso::
- * ``file``
- * ``redis``
- * ``rediss`` (Redis over TLS)
- * ``memcached`` (requires :doc:`symfony/cache `)
- * ``pdo_oci`` (requires :doc:`doctrine/dbal `)
- * ``mssql``
- * ``mysql``
- * ``mysql2``
- * ``pgsql``
- * ``postgres``
- * ``postgresql``
- * ``sqlsrv``
- * ``sqlite``
- * ``sqlite3``
+ For more details, see :doc:`/lock`.
-.. _name:
+.. _reference-lock-resources-name:
name
-....
-
-**type**: ``string``
-
-This specifies the name of the session cookie.
-
-If not set, ``php.ini``'s `session.name`_ directive will be relied on.
-
-cookie_lifetime
-...............
+""""
-**type**: ``integer``
+**type**: ``prototype``
-This determines the lifetime of the session - in seconds.
-Setting this value to ``0`` means the cookie is valid for
-the length of the browser session.
+Name of the lock you want to create.
-If not set, ``php.ini``'s `session.cookie_lifetime`_ directive will be relied on.
+mailer
+~~~~~~
-cookie_path
-...........
+.. _mailer-dsn:
-**type**: ``string``
+dsn
+...
-This determines the path to set in the session cookie.
+**type**: ``string`` **default**: ``null``
-If not set, ``php.ini``'s `session.cookie_path`_ directive will be relied on.
+The DSN used by the mailer. When several DSN may be used, use
+``transports`` option (see below) instead.
-cache_limiter
-.............
+envelope
+........
-**type**: ``string`` **default**: ``0``
+recipients
+""""""""""
-If set to ``0``, Symfony won't set any particular header related to the cache
-and it will rely on ``php.ini``'s `session.cache_limiter`_ directive.
+**type**: ``array``
-Unlike the other session options, ``cache_limiter`` is set as a regular
-:ref:`container parameter `:
+The "envelope recipient" which is used as the value of ``RCPT TO`` during the
+the `SMTP session`_. This value overrides any other recipient set in the code.
.. configuration-block::
.. code-block:: yaml
- # config/services.yaml
- parameters:
- session.storage.options:
- cache_limiter: 0
+ # config/packages/mailer.yaml
+ framework:
+ mailer:
+ dsn: 'smtp://localhost:25'
+ envelope:
+ recipients: ['admin@symfony.com', 'lead@symfony.com']
.. code-block:: xml
-
+
-
-
-
- 0
-
-
+ 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">
+
+
+
+ admin@symfony.com
+ lead@symfony.com
+
+
+
.. code-block:: php
- // config/services.php
- $container->setParameter('session.storage.options', [
- 'cache_limiter' => 0,
- ]);
+ // config/packages/mailer.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
-Be aware that if you configure it, you'll have to set other session-related options
-as parameters as well.
+ return static function (ContainerConfigurator $container): void {
+ $container->extension('framework', [
+ 'mailer' => [
+ 'dsn' => 'smtp://localhost:25',
+ 'envelope' => [
+ 'recipients' => [
+ 'admin@symfony.com',
+ 'lead@symfony.com',
+ ],
+ ],
+ ],
+ ]);
+ };
-cookie_domain
-.............
+sender
+""""""
**type**: ``string``
-This determines the domain to set in the session cookie.
-
-If not set, ``php.ini``'s `session.cookie_domain`_ directive will be relied on.
-
-cookie_samesite
-...............
-
-**type**: ``string`` or ``null`` **default**: ``null``
-
-It controls the way cookies are sent when the HTTP request did not originate
-from the same domain that is associated with the cookies. Setting this option is
-recommended to mitigate `CSRF security attacks`_.
-
-By default, browsers send all cookies related to the domain of the HTTP request.
-This may be a problem for example when you visit a forum and some malicious
-comment includes a link like ``https://some-bank.com/?send_money_to=attacker&amount=1000``.
-If you were previously logged into your bank website, the browser will send all
-those cookies when making that HTTP request.
+The "envelope sender" which is used as the value of ``MAIL FROM`` during the
+`SMTP session`_. This value overrides any other sender set in the code.
-The possible values for this option are:
+.. _mailer-headers:
-* ``null``, use ``php.ini``'s `session.cookie_samesite`_ directive.
-* ``'none'`` (or the ``Symfony\Component\HttpFoundation\Cookie::SAMESITE_NONE`` constant), use it to allow
- sending of cookies when the HTTP request originated from a different domain
- (previously this was the default behavior of null, but in newer browsers ``'lax'``
- would be applied when the header has not been set)
-* ``'strict'`` (or the ``Cookie::SAMESITE_STRICT`` constant), use it to never
- send any cookie when the HTTP request did not originate from the same domain.
-* ``'lax'`` (or the ``Cookie::SAMESITE_LAX`` constant), use it to allow sending
- cookies when the request originated from a different domain, but only when the
- user consciously made the request (by clicking a link or submitting a form
- with the ``GET`` method).
+headers
+.......
-cookie_secure
-.............
+**type**: ``array``
-**type**: ``boolean`` or ``'auto'``
+Headers to add to emails. The key (``name`` attribute in xml format) is the
+header name and value the header value.
-This determines whether cookies should only be sent over secure connections. In
-addition to ``true`` and ``false``, there's a special ``'auto'`` value that
-means ``true`` for HTTPS requests and ``false`` for HTTP requests.
+.. seealso::
-If not set, ``php.ini``'s `session.cookie_secure`_ directive will be relied on.
+ For more information, see :ref:`Configuring Emails Globally `
-cookie_httponly
-...............
+message_bus
+...........
-**type**: ``boolean`` **default**: ``true``
+**type**: ``string`` **default**: ``null`` or default bus if Messenger component is installed
-This determines whether cookies should only be accessible through the HTTP
-protocol. This means that the cookie won't be accessible by scripting
-languages, such as JavaScript. This setting can effectively help to reduce
-identity theft through :ref:`XSS attacks `.
+Service identifier of the message bus to use when using the
+:doc:`Messenger component ` (e.g. ``messenger.default_bus``).
-gc_divisor
+transports
..........
-**type**: ``integer``
-
-See `gc_probability`_.
-
-If not set, ``php.ini``'s `session.gc_divisor`_ directive will be relied on.
-
-gc_probability
-..............
-
-**type**: ``integer`` **default**: ``1``
-
-This defines the probability that the garbage collector (GC) process is
-started on every session initialization. The probability is calculated by
-using ``gc_probability`` / ``gc_divisor``, e.g. 1/100 means there is a 1%
-chance that the GC process will start on each request.
-
-gc_maxlifetime
-..............
-
-**type**: ``integer``
-
-This determines the number of seconds after which data will be seen as "garbage"
-and potentially cleaned up. Garbage collection may occur during session
-start and depends on `gc_divisor`_ and `gc_probability`_.
-
-If not set, ``php.ini``'s `session.gc_maxlifetime`_ directive will be relied on.
+**type**: ``array``
-sid_length
-..........
+A :ref:`list of DSN ` that can be used by the
+mailer. A transport name is the key and the dsn is the value.
-**type**: ``integer``
+messenger
+~~~~~~~~~
-This determines the length of session ID string, which can be an integer between
-``22`` and ``256`` (both inclusive), ``32`` being the recommended value. Longer
-session IDs are harder to guess.
+enabled
+.......
-If not set, ``php.ini``'s `session.sid_length`_ directive will be relied on.
+**type**: ``boolean`` **default**: ``true``
-sid_bits_per_character
-......................
+Whether to enable or not Messenger.
-**type**: ``integer``
+.. seealso::
-This determines the number of bits in the encoded session ID character. The possible
-values are ``4`` (0-9, a-f), ``5`` (0-9, a-v), and ``6`` (0-9, a-z, A-Z, "-", ",").
-The more bits results in stronger session ID. ``5`` is recommended value for
-most environments.
+ For more details, see the :doc:`Messenger component `
+ documentation.
-If not set, ``php.ini``'s `session.sid_bits_per_character`_ directive will be relied on.
+php_errors
+~~~~~~~~~~
-save_path
-.........
+log
+...
-**type**: ``string`` | ``null`` **default**: ``%kernel.cache_dir%/sessions``
+**type**: ``boolean``, ``int`` or ``array`` **default**: ``true``
-This determines the argument to be passed to the save handler. If you choose
-the default file handler, this is the path where the session files are created.
+Use the application logger instead of the PHP logger for logging PHP errors.
+When an integer value is used, it defines a bitmask of PHP errors that will
+be logged. Those integer values must be the same used in the
+`error_reporting PHP option`_. The default log levels will be used for each
+PHP error.
+When a boolean value is used, ``true`` enables logging for all PHP errors
+while ``false`` disables logging entirely.
-If ``null``, ``php.ini``'s `session.save_path`_ directive will be relied on:
+This option also accepts a map of PHP errors to log levels:
.. configuration-block::
@@ -1900,8 +2316,23 @@ If ``null``, ``php.ini``'s `session.save_path`_ directive will be relied on:
# config/packages/framework.yaml
framework:
- session:
- save_path: ~
+ php_errors:
+ log:
+ !php/const \E_DEPRECATED: !php/const Psr\Log\LogLevel::ERROR
+ !php/const \E_USER_DEPRECATED: !php/const Psr\Log\LogLevel::ERROR
+ !php/const \E_NOTICE: !php/const Psr\Log\LogLevel::ERROR
+ !php/const \E_USER_NOTICE: !php/const Psr\Log\LogLevel::ERROR
+ !php/const \E_STRICT: !php/const Psr\Log\LogLevel::ERROR
+ !php/const \E_WARNING: !php/const Psr\Log\LogLevel::ERROR
+ !php/const \E_USER_WARNING: !php/const Psr\Log\LogLevel::ERROR
+ !php/const \E_COMPILE_WARNING: !php/const Psr\Log\LogLevel::ERROR
+ !php/const \E_CORE_WARNING: !php/const Psr\Log\LogLevel::ERROR
+ !php/const \E_USER_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
+ !php/const \E_RECOVERABLE_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
+ !php/const \E_COMPILE_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
+ !php/const \E_PARSE: !php/const Psr\Log\LogLevel::CRITICAL
+ !php/const \E_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
+ !php/const \E_CORE_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
.. code-block:: xml
@@ -1915,282 +2346,241 @@ If ``null``, ``php.ini``'s `session.save_path`_ directive will be relied on:
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
-
+
+
+
+
.. code-block:: php
// config/packages/framework.php
+ use Psr\Log\LogLevel;
use Symfony\Config\FrameworkConfig;
return static function (FrameworkConfig $framework): void {
- $framework->session()
- ->savePath(null);
+ $framework->phpErrors()->log(\E_DEPRECATED, LogLevel::ERROR);
+ $framework->phpErrors()->log(\E_USER_DEPRECATED, LogLevel::ERROR);
+ // ...
};
-.. _reference-session-metadata-update-threshold:
-
-metadata_update_threshold
-.........................
+throw
+.....
-**type**: ``integer`` **default**: ``0``
+**type**: ``boolean`` **default**: ``%kernel.debug%``
-This is how many seconds to wait between updating/writing the session metadata.
-This can be useful if, for some reason, you want to limit the frequency at which
-the session persists, instead of doing that on every request.
+Throw PHP errors as ``\ErrorException`` instances. The parameter
+``debug.error_handler.throw_at`` controls the threshold.
-.. _reference-session-enabled:
+profiler
+~~~~~~~~
-enabled
+collect
.......
**type**: ``boolean`` **default**: ``true``
-Whether to enable the session support in the framework.
+This option configures the way the profiler behaves when it is enabled. If set
+to ``true``, the profiler collects data for all requests. If you want to only
+collect information on-demand, you can set the ``collect`` flag to ``false`` and
+activate the data collectors manually::
-.. configuration-block::
+ $profiler->enable();
- .. code-block:: yaml
+collect_parameter
+.................
- # config/packages/framework.yaml
- framework:
- session:
- enabled: true
+**type**: ``string`` **default**: ``null``
- .. code-block:: xml
+This specifies name of a query parameter, a body parameter or a request attribute
+used to enable or disable collection of data by the profiler for each request.
+Combine it with the ``collect`` option to enable/disable the profiler on demand:
-
-
-
+* If the ``collect`` option is set to ``true`` but this parameter exists in a
+ request and has any value other than ``true``, ``yes``, ``on`` or ``1``, the
+ request data will not be collected;
+* If the ``collect`` option is set to ``false``, but this parameter exists in a
+ request and has value of ``true``, ``yes``, ``on`` or ``1``, the request data
+ will be collected.
-
-
-
-
+.. _collect_serializer_data:
- .. code-block:: php
+collect_serializer_data
+.......................
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
+**type**: ``boolean`` **default**: ``false``
- return static function (FrameworkConfig $framework): void {
- $framework->session()
- ->enabled(true);
- };
+When this option is ``true``, all normalizers and encoders are
+decorated by traceable implementations that collect profiling information about them.
-use_cookies
-...........
+.. deprecated:: 7.3
-**type**: ``boolean``
+ Setting the ``collect_serializer_data`` option to ``false`` is deprecated
+ since Symfony 7.3.
-This specifies if the session ID is stored on the client side using cookies or
-not.
+.. _profiler-dsn:
-If not set, ``php.ini``'s `session.use_cookies`_ directive will be relied on.
+dsn
+...
-ssi
-~~~
+**type**: ``string`` **default**: ``file:%kernel.cache_dir%/profiler``
+
+The DSN where to store the profiling information.
+
+.. _reference-profiler-enabled:
enabled
.......
**type**: ``boolean`` **default**: ``false``
-Whether to enable or not SSI support in your application.
-
-assets
-~~~~~~
-
-.. _reference-assets-base-path:
+The profiler can be enabled by setting this option to ``true``. When you
+install it using Symfony Flex, the profiler is enabled in the ``dev``
+and ``test`` environments.
-base_path
-.........
+.. note::
-**type**: ``string``
+ The profiler works independently from the Web Developer Toolbar, see
+ the :doc:`WebProfilerBundle configuration `
+ on how to disable/enable the toolbar.
-This option allows you to define a base path to be used for assets:
+only_exceptions
+...............
-.. configuration-block::
+**type**: ``boolean`` **default**: ``false``
- .. code-block:: yaml
+When this is set to ``true``, the profiler will only be enabled when an
+exception is thrown during the handling of the request.
- # config/packages/framework.yaml
- framework:
- # ...
- assets:
- base_path: '/images'
+.. _only_master_requests:
- .. code-block:: xml
+only_main_requests
+..................
-
-
-
+**type**: ``boolean`` **default**: ``false``
-
-
-
-
+When this is set to ``true``, the profiler will only be enabled on the main
+requests (and not on the subrequests).
- .. code-block:: php
+property_access
+~~~~~~~~~~~~~~~
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
+magic_call
+..........
- return static function (FrameworkConfig $framework): void {
- // ...
- $framework->assets()
- ->basePath('/images');
- };
+**type**: ``boolean`` **default**: ``false``
-.. _reference-templating-base-urls:
-.. _reference-assets-base-urls:
+When enabled, the ``property_accessor`` service uses PHP's
+:ref:`magic __call() method ` when
+its ``getValue()`` method is called.
-base_urls
+magic_get
.........
-**type**: ``array``
-
-This option allows you to define base URLs to be used for assets.
-If multiple base URLs are provided, Symfony will select one from the
-collection each time it generates an asset's path:
+**type**: ``boolean`` **default**: ``true``
-.. configuration-block::
+When enabled, the ``property_accessor`` service uses PHP's
+:ref:`magic __get() method ` when
+its ``getValue()`` method is called.
- .. code-block:: yaml
+magic_set
+.........
- # config/packages/framework.yaml
- framework:
- # ...
- assets:
- base_urls:
- - 'http://cdn.example.com/'
+**type**: ``boolean`` **default**: ``true``
- .. code-block:: xml
+When enabled, the ``property_accessor`` service uses PHP's
+:ref:`magic __set() method ` when
+its ``setValue()`` method is called.
-
-
-
+throw_exception_on_invalid_index
+................................
-
-
-
-
+**type**: ``boolean`` **default**: ``false``
- .. code-block:: php
+When enabled, the ``property_accessor`` service throws an exception when you
+try to access an invalid index of an array.
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
+throw_exception_on_invalid_property_path
+........................................
- return static function (FrameworkConfig $framework): void {
- // ...
- $framework->assets()
- ->baseUrls(['http://cdn.example.com/']);
- };
+**type**: ``boolean`` **default**: ``true``
-.. _reference-framework-assets-packages:
+When enabled, the ``property_accessor`` service throws an exception when you
+try to access an invalid property path of an object.
-packages
-........
+property_info
+~~~~~~~~~~~~~
-You can group assets into packages, to specify different base URLs for them:
+.. _reference-property-info-enabled:
-.. configuration-block::
+enabled
+.......
- .. code-block:: yaml
+**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
- # config/packages/framework.yaml
- framework:
- # ...
- assets:
- packages:
- avatars:
- base_urls: 'http://static_cdn.example.com/avatars'
+with_constructor_extractor
+..........................
- .. code-block:: xml
+**type**: ``boolean`` **default**: ``false``
-
-
-
+Configures the ``property_info`` service to extract property information from the constructor arguments
+using the :ref:`ConstructorExtractor `.
-
-
-
-
-
-
+.. versionadded:: 7.3
- .. code-block:: php
+ The ``with_constructor_extractor`` option was introduced in Symfony 7.3.
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
+rate_limiter
+~~~~~~~~~~~~
- return static function (FrameworkConfig $framework): void {
- // ...
- $framework->assets()
- ->package('avatars')
- ->baseUrls(['http://static_cdn.example.com/avatars']);
- };
+.. _reference-rate-limiter-name:
-Now you can use the ``avatars`` package in your templates:
+name
+....
-.. code-block:: html+twig
+**type**: ``prototype``
-
+Name of the rate limiter you want to create.
-Each package can configure the following options:
+lock_factory
+""""""""""""
-* :ref:`base_path `
-* :ref:`base_urls `
-* :ref:`version_strategy `
-* :ref:`version `
-* :ref:`version_format `
-* :ref:`json_manifest_path `
-* :ref:`strict_mode `
+**type**: ``string`` **default:** ``lock.factory``
-.. _reference-framework-assets-version:
-.. _ref-framework-assets-version:
+The service that is used to create a lock. The service has to be an instance of
+the :class:`Symfony\\Component\\Lock\\LockFactory` class.
-version
-.......
+policy
+""""""
-**type**: ``string``
+**type**: ``string`` **required**
-This option is used to *bust* the cache on assets by globally adding a query
-parameter to all rendered asset paths (e.g. ``/images/logo.png?v2``). This
-applies only to assets rendered via the Twig ``asset()`` function (or PHP
-equivalent).
+The name of the rate limiting algorithm to use. Example names are ``fixed_window``,
+``sliding_window`` and ``no_limit``. See :ref:`Rate Limiter Policies `)
+for more information.
-For example, suppose you have the following:
+request
+~~~~~~~
-.. code-block:: html+twig
+formats
+.......
-
+**type**: ``array`` **default**: ``[]``
-By default, this will render a path to your image such as ``/images/logo.png``.
-Now, activate the ``version`` option:
+This setting is used to associate additional request formats (e.g. ``html``)
+to one or more mime types (e.g. ``text/html``), which will allow you to use the
+format & mime types to call
+:method:`Request::getFormat($mimeType) ` or
+:method:`Request::getMimeType($format) `.
+
+In practice, this is important because Symfony uses it to automatically set the
+``Content-Type`` header on the ``Response`` (if you don't explicitly set one).
+If you pass an array of mime types, the first will be used for the header.
+
+To configure a ``jsonp`` format:
.. configuration-block::
@@ -2198,23 +2588,29 @@ Now, activate the ``version`` option:
# config/packages/framework.yaml
framework:
- # ...
- assets:
- version: 'v2'
+ request:
+ formats:
+ jsonp: 'application/javascript'
.. code-block:: xml
+
+ http://symfony.com/schema/dic/symfony
+ https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
-
+
+
+ application/javascript
+
+
@@ -2224,591 +2620,465 @@ Now, activate the ``version`` option:
use Symfony\Config\FrameworkConfig;
return static function (FrameworkConfig $framework): void {
- // ...
- $framework->assets()
- ->version('v2');
+ $framework->request()
+ ->format('jsonp', 'application/javascript');
};
-Now, the same asset will be rendered as ``/images/logo.png?v2`` If you use
-this feature, you **must** manually increment the ``version`` value
-before each deployment so that the query parameters change.
-
-You can also control how the query string works via the `version_format`_
-option.
-
-.. note::
-
- This parameter cannot be set at the same time as ``version_strategy`` or ``json_manifest_path``.
-
-.. tip::
-
- As with all settings, you can use a parameter as value for the
- ``version``. This makes it easier to increment the cache on each
- deployment.
-
-.. _reference-templating-version-format:
-.. _reference-assets-version-format:
-
-version_format
-..............
-
-**type**: ``string`` **default**: ``%%s?%%s``
-
-This specifies a :phpfunction:`sprintf` pattern that will be used with the
-`version`_ option to construct an asset's path. By default, the pattern
-adds the asset's version as a query string. For example, if
-``version_format`` is set to ``%%s?version=%%s`` and ``version``
-is set to ``5``, the asset's path would be ``/images/logo.png?version=5``.
-
-.. note::
-
- All percentage signs (``%``) in the format string must be doubled to
- escape the character. Without escaping, values might inadvertently be
- interpreted as :ref:`service-container-parameters`.
+router
+~~~~~~
-.. tip::
+cache_dir
+.........
- Some CDN's do not support cache-busting via query strings, so injecting
- the version into the actual file path is necessary. Thankfully,
- ``version_format`` is not limited to producing versioned query
- strings.
+**type**: ``string`` **default**: ``%kernel.cache_dir%``
- The pattern receives the asset's original path and version as its first
- and second parameters, respectively. Since the asset's path is one
- parameter, you cannot modify it in-place (e.g. ``/images/logo-v5.png``);
- however, you can prefix the asset's path using a pattern of
- ``version-%%2$s/%%1$s``, which would result in the path
- ``version-5/images/logo.png``.
+The directory where routing information will be cached. Can be set to
+``~`` (``null``) to disable route caching.
- URL rewrite rules could then be used to disregard the version prefix
- before serving the asset. Alternatively, you could copy assets to the
- appropriate version path as part of your deployment process and forgot
- any URL rewriting. The latter option is useful if you would like older
- asset versions to remain accessible at their original URL.
+.. deprecated:: 7.1
-.. _reference-assets-version-strategy:
-.. _reference-templating-version-strategy:
+ Setting the ``cache_dir`` option is deprecated since Symfony 7.1. The routes
+ are now always cached in the ``%kernel.build_dir%`` directory.
-version_strategy
-................
+default_uri
+...........
-**type**: ``string`` **default**: ``null``
+**type**: ``string``
-The service id of the :doc:`asset version strategy `
-applied to the assets. This option can be set globally for all assets and
-individually for each asset package:
+The default URI used to generate URLs in a non-HTTP context (see
+:ref:`Generating URLs in Commands `).
-.. configuration-block::
+http_port
+.........
- .. code-block:: yaml
+**type**: ``integer`` **default**: ``80``
- # config/packages/framework.yaml
- framework:
- assets:
- # this strategy is applied to every asset (including packages)
- version_strategy: 'app.asset.my_versioning_strategy'
- packages:
- foo_package:
- # this package removes any versioning (its assets won't be versioned)
- version: ~
- bar_package:
- # this package uses its own strategy (the default strategy is ignored)
- version_strategy: 'app.asset.another_version_strategy'
- baz_package:
- # this package inherits the default strategy
- base_path: '/images'
+The port for normal http requests (this is used when matching the scheme).
- .. code-block:: xml
+https_port
+..........
-
-
-
+**type**: ``integer`` **default**: ``443``
-
-
-
-
-
-
-
-
-
-
-
+The port for https requests (this is used when matching the scheme).
- .. code-block:: php
+resource
+........
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
+**type**: ``string`` **required**
- return static function (FrameworkConfig $framework): void {
- // ...
- $framework->assets()
- ->versionStrategy('app.asset.my_versioning_strategy');
+The path the main routing resource (e.g. a YAML file) that contains the
+routes and imports the router should load.
- $framework->assets()->package('foo_package')
- // this package removes any versioning (its assets won't be versioned)
- ->version(null);
+strict_requirements
+...................
- $framework->assets()->package('bar_package')
- // this package uses its own strategy (the default strategy is ignored)
- ->versionStrategy('app.asset.another_version_strategy');
+**type**: ``mixed`` **default**: ``true``
- $framework->assets()->package('baz_package')
- // this package inherits the default strategy
- ->basePath('/images');
- };
+Determines the routing generator behavior. When generating a route that
+has specific :ref:`parameter requirements `, the generator
+can behave differently in case the used parameters do not meet these requirements.
-.. note::
+The value can be one of:
- This parameter cannot be set at the same time as ``version`` or ``json_manifest_path``.
+``true``
+ Throw an exception when the requirements are not met;
+``false``
+ Disable exceptions when the requirements are not met and return ``''``
+ instead;
+``null``
+ Disable checking the requirements (thus, match the route even when the
+ requirements don't match).
-.. _reference-assets-json-manifest-path:
-.. _reference-templating-json-manifest-path:
+``true`` is recommended in the development environment, while ``false``
+or ``null`` might be preferred in production.
-json_manifest_path
-..................
+.. _reference-router-type:
-**type**: ``string`` **default**: ``null``
+type
+....
-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.
+**type**: ``string``
-.. tip::
+The type of the resource to hint the loaders about the format. This isn't
+needed when you use the default routers with the expected file extensions
+(``.xml``, ``.yaml``, ``.php``).
- Symfony's :ref:`Webpack Encore ` supports
- :ref:`outputting hashed assets `. Moreover, this
- can be incorporated into many other workflows, including Webpack and
- Gulp using `webpack-manifest-plugin`_ and `gulp-rev`_, respectively.
+utf8
+....
-This option can be set globally for all assets and individually for each asset
-package:
+**type**: ``boolean`` **default**: ``true``
-.. configuration-block::
+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
+when using ``.``, instead of matching only a single byte.
- .. code-block:: yaml
+If the charset of your application is UTF-8 (as defined in the
+:ref:`getCharset() method ` of your kernel) it's
+recommended setting it to ``true``. This will make non-UTF8 URLs to generate 404
+errors.
- # config/packages/framework.yaml
- framework:
- 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)
- json_manifest_path: "%kernel.project_dir%/public/build/a_different_manifest.json"
- # Throws an exception when an asset is not found in the manifest
- strict_mode: %kernel.debug%
- bar_package:
- # this package uses the global manifest (the default file is used)
- base_path: '/images'
+.. _configuration-framework-secret:
- .. code-block:: xml
+secret
+~~~~~~
-
-
-
+**type**: ``string`` **required**
-
-
-
-
-
-
-
-
-
-
-
-
-
+This is a string that should be unique to your application and it's commonly
+used to add more entropy to security related operations. Its value should
+be a series of characters, numbers and symbols chosen randomly and the
+recommended length is around 32 characters.
- .. code-block:: php
+In practice, Symfony uses this value for encrypting the cookies used
+in the :doc:`remember me functionality ` and for
+creating signed URIs when using :ref:`ESI (Edge Side Includes) `.
+That's why you should treat this value as if it were a sensitive credential and
+**never make it public**.
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
+This option becomes the service container parameter named ``kernel.secret``,
+which you can use whenever the application needs an immutable random string
+to add more entropy.
- return static function (FrameworkConfig $framework): void {
- // ...
- $framework->assets()
- // this manifest is applied to every asset (including packages)
- ->jsonManifestPath('%kernel.project_dir%/public/build/manifest.json');
+As with any other security-related parameter, it is a good practice to change
+this value from time to time. However, keep in mind that changing this value
+will invalidate all signed URIs and Remember Me cookies. That's why, after
+changing this value, you should regenerate the application cache and log
+out all the application users.
- // you can use absolute URLs too and Symfony will download them automatically
- // 'json_manifest_path' => 'https://cdn.example.com/manifest.json',
- $framework->assets()->package('foo_package')
- // this package uses its own manifest (the default file is ignored)
- ->jsonManifestPath('%kernel.project_dir%/public/build/a_different_manifest.json')
- // Throws an exception when an asset is not found in the manifest
- ->setStrictMode('%kernel.debug%');
+secrets
+~~~~~~~
- $framework->assets()->package('bar_package')
- // this package uses the global manifest (the default file is used)
- ->basePath('/images');
- };
+decryption_env_var
+..................
-.. note::
+**type**: ``string`` **default**: ``base64:default::SYMFONY_DECRYPTION_SECRET``
- This parameter cannot be set at the same time as ``version`` or ``version_strategy``.
- Additionally, this option cannot be nullified at the package scope if a global manifest
- file is specified.
+The env var name that contains the vault decryption secret. By default, this
+value will be decoded from base64.
-.. tip::
+enabled
+.......
- If you request an asset that is *not found* in the ``manifest.json`` file, the original -
- *unmodified* - asset path will be returned.
- You can set ``strict_mode`` to ``true`` to get an exception when an asset is *not found*.
+**type**: ``boolean`` **default**: ``true``
-.. note::
+Whether to enable or not secrets managements.
- If a URL is set, the JSON manifest is downloaded on each request using the `http_client`_.
+local_dotenv_file
+.................
-.. _reference-assets-strict-mode:
+**type**: ``string`` **default**: ``%kernel.project_dir%/.env.%kernel.environment%.local``
-strict_mode
-...........
+The path to the local ``.env`` file. This file must contain the vault
+decryption key, given by the ``decryption_env_var`` option.
-**type**: ``boolean`` **default**: ``false``
+vault_directory
+...............
-When enabled, the strict mode asserts that all requested assets are in the
-manifest file. This option is useful to detect typos or missing assets, the
-recommended value is ``%kernel.debug%``.
+**type**: ``string`` **default**: ``%kernel.project_dir%/config/secrets/%kernel.runtime_environment%``
-translator
-~~~~~~~~~~
+The directory to store the secret vault. By default, the path includes the value
+of the :ref:`kernel.runtime_environment `
+parameter.
-cache_dir
-.........
+semaphore
+~~~~~~~~~
-**type**: ``string`` | ``null`` **default**: ``%kernel.cache_dir%/translations``
+**type**: ``string`` | ``array``
-Defines the directory where the translation cache is stored. Use ``null`` to
-disable this cache.
+The default semaphore adapter. Store's DSN are also allowed.
-.. _reference-translator-enabled:
+.. _reference-semaphore-enabled:
enabled
.......
-**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
+**type**: ``boolean`` **default**: ``true``
-Whether or not to enable the ``translator`` service in the service container.
+Whether to enable the support for semaphore or not. This setting is
+automatically set to ``true`` when one of the child settings is configured.
-.. _fallback:
+.. _reference-semaphore-resources:
-fallbacks
+resources
.........
-**type**: ``string|array`` **default**: value of `default_locale`_
+**type**: ``array``
-This option is used when the translation key for the current locale wasn't
-found.
+A map of semaphore stores to be created by the framework extension, with
+the name as key and DSN or service id as value:
-.. seealso::
+.. configuration-block::
- For more details, see :doc:`/translation`.
+ .. code-block:: yaml
-.. _reference-framework-translator-logging:
+ # config/packages/semaphore.yaml
+ framework:
+ semaphore: '%env(SEMAPHORE_DSN)%'
-logging
-.......
+ .. code-block:: xml
+
+
+
+
+
+
+
+ %env(SEMAPHORE_DSN)%
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/semaphore.php
+ use Symfony\Config\FrameworkConfig;
+ use function Symfony\Component\DependencyInjection\Loader\Configurator\env;
-**default**: ``true`` when the debug mode is enabled, ``false`` otherwise.
+ return static function (FrameworkConfig $framework): void {
+ $framework->semaphore()
+ ->resource('default', [env('SEMAPHORE_DSN')]);
+ };
-When ``true``, a log entry is made whenever the translator cannot find a translation
-for a given key. The logs are made to the ``translation`` channel at the
-``debug`` level for keys where there is a translation in the fallback
-locale, and the ``warning`` level if there is no translation to use at all.
+.. _reference-semaphore-resources-name:
-.. _reference-framework-translator-formatter:
+name
+""""
-formatter
-.........
+**type**: ``prototype``
-**type**: ``string`` **default**: ``translator.formatter.default``
+Name of the semaphore you want to create.
-The ID of the service used to format translation messages. The service class
-must implement the :class:`Symfony\\Component\\Translation\\Formatter\\MessageFormatterInterface`.
+.. _configuration-framework-serializer:
-.. _reference-translator-paths:
+serializer
+~~~~~~~~~~
-paths
-.....
+.. _reference-serializer-circular_reference_handler:
-**type**: ``array`` **default**: ``[]``
+circular_reference_handler
+..........................
-This option allows to define an array of paths where the component will look
-for translation files. The later a path is added, the more priority it has
-(translations from later paths overwrite earlier ones). Translations from the
-:ref:`default_path ` have more priority than
-translations from all these paths.
+**type** ``string``
-.. _reference-translator-default_path:
+The service id that is used as the circular reference handler of the default
+serializer. The service has to implement the magic ``__invoke($object)``
+method.
-default_path
-............
+.. seealso::
-**type**: ``string`` **default**: ``%kernel.project_dir%/translations``
+ For more information, see
+ :ref:`component-serializer-handling-circular-references`.
-This option allows to define the path where the application translations files
-are stored.
+default_context
+...............
-.. _reference-translator-providers:
+**type**: ``array`` **default**: ``[]``
-providers
-.........
+A map with default context options that will be used with each ``serialize`` and ``deserialize``
+call. This can be used for example to set the json encoding behavior by setting ``json_encode_options``
+to a `json_encode flags bitmask`_.
-**type**: ``array`` **default**: ``[]``
+You can inspect the :ref:`serializer context builders `
+to discover the available settings.
-This option enables and configures :ref:`translation providers `
-to push and pull your translations to/from third party translation services.
+.. _reference-serializer-enable_annotations:
-property_access
-~~~~~~~~~~~~~~~
+enable_attributes
+.................
-magic_call
-..........
+**type**: ``boolean`` **default**: ``true``
-**type**: ``boolean`` **default**: ``false``
+Enables support for `PHP attributes`_ in the serializer component.
-When enabled, the ``property_accessor`` service uses PHP's
-:ref:`magic __call() method ` when
-its ``getValue()`` method is called.
+.. seealso::
-magic_get
-.........
+ See :ref:`the reference ` for a list of supported annotations.
-**type**: ``boolean`` **default**: ``true``
+.. _reference-serializer-enabled:
-When enabled, the ``property_accessor`` service uses PHP's
-:ref:`magic __get() method ` when
-its ``getValue()`` method is called.
+enabled
+.......
-magic_set
-.........
+**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
-**type**: ``boolean`` **default**: ``true``
+Whether to enable the ``serializer`` service or not in the service container.
-When enabled, the ``property_accessor`` service uses PHP's
-:ref:`magic __set() method ` when
-its ``setValue()`` method is called.
+.. _reference-serializer-mapping:
-throw_exception_on_invalid_index
-................................
+mapping
+.......
-**type**: ``boolean`` **default**: ``false``
+.. _reference-serializer-mapping-paths:
-When enabled, the ``property_accessor`` service throws an exception when you
-try to access an invalid index of an array.
+paths
+"""""
-throw_exception_on_invalid_property_path
-........................................
+**type**: ``array`` **default**: ``[]``
-**type**: ``boolean`` **default**: ``true``
+This option allows to define an array of paths with files or directories where
+the component will look for additional serialization files.
-When enabled, the ``property_accessor`` service throws an exception when you
-try to access an invalid property path of an object.
+.. _reference-serializer-name_converter:
-property_info
-~~~~~~~~~~~~~
+name_converter
+..............
-.. _reference-property-info-enabled:
+**type**: ``string``
-enabled
-.......
+The name converter to use.
+The :class:`Symfony\\Component\\Serializer\\NameConverter\\CamelCaseToSnakeCaseNameConverter`
+name converter can enabled by using the ``serializer.name_converter.camel_case_to_snake_case``
+value.
-**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
+.. seealso::
-.. _reference-validation:
+ For more information, see :ref:`serializer-name-conversion`.
-validation
-~~~~~~~~~~
+.. _config-framework-session:
-.. _reference-validation-auto-mapping:
+session
+~~~~~~~
-auto_mapping
-............
+cache_limiter
+.............
-**type**: ``array`` **default**: ``[]``
+**type**: ``string`` **default**: ``0``
-Defines the Doctrine entities that will be introspected to add
-:ref:`automatic validation constraints ` to them:
+If set to ``0``, Symfony won't set any particular header related to the cache
+and it will rely on ``php.ini``'s `session.cache_limiter`_ directive.
+
+Unlike the other session options, ``cache_limiter`` is set as a regular
+:ref:`container parameter `:
.. configuration-block::
.. code-block:: yaml
- framework:
- validation:
- auto_mapping:
- # an empty array means that all entities that belong to that
- # namespace will add automatic validation
- 'App\Entity\': []
- 'Foo\': ['Foo\Some\Entity', 'Foo\Another\Entity']
+ # config/services.yaml
+ parameters:
+ session.storage.options:
+ cache_limiter: 0
.. code-block:: xml
-
+
-
-
-
-
-
+ https://symfony.com/schema/dic/services/services-1.0.xsd">
- Foo\Some\Entity
- Foo\Another\Entity
-
-
-
+
+
+ 0
+
+
.. code-block:: php
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
-
- return static function (FrameworkConfig $framework): void {
- $framework->validation()
- ->autoMapping()
- ->paths([
- 'App\\Entity\\' => [],
- 'Foo\\' => ['Foo\\Some\\Entity', 'Foo\\Another\\Entity'],
- ]);
- };
-
-.. _reference-validation-enabled:
+ // config/services.php
+ $container->setParameter('session.storage.options', [
+ 'cache_limiter' => 0,
+ ]);
-enabled
-.......
+Be aware that if you configure it, you'll have to set other session-related options
+as parameters as well.
-**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
+cookie_domain
+.............
-Whether or not to enable validation support.
+**type**: ``string``
-This option will automatically be set to ``true`` when one of the child
-settings is configured.
+This determines the domain to set in the session cookie.
-.. _reference-validation-enable_annotations:
+If not set, ``php.ini``'s `session.cookie_domain`_ directive will be relied on.
-enable_attributes
-.................
+cookie_httponly
+...............
**type**: ``boolean`` **default**: ``true``
-If this option is enabled, validation constraints can be defined using `PHP attributes`_.
-
-translation_domain
-..................
-
-**type**: ``string | false`` **default**: ``validators``
-
-The translation domain that is used when translating validation constraint
-error messages. Use false to disable translations.
-
-.. _reference-validation-not-compromised-password:
-
-not_compromised_password
-........................
-
-The :doc:`NotCompromisedPassword `
-constraint makes HTTP requests to a public API to check if the given password
-has been compromised in a data breach.
-
-.. _reference-validation-not-compromised-password-enabled:
+This determines whether cookies should only be accessible through the HTTP
+protocol. This means that the cookie won't be accessible by scripting
+languages, such as JavaScript. This setting can effectively help to reduce
+identity theft through :ref:`XSS attacks `.
-enabled
-"""""""
+cookie_lifetime
+...............
-**type**: ``boolean`` **default**: ``true``
+**type**: ``integer``
-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
-continuous integration servers.
+This determines the lifetime of the session - in seconds.
+Setting this value to ``0`` means the cookie is valid for
+the length of the browser session.
-endpoint
-""""""""
+If not set, ``php.ini``'s `session.cookie_lifetime`_ directive will be relied on.
-**type**: ``string`` **default**: ``null``
+cookie_path
+...........
-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
-checks. It's useful for example when the Symfony application is run in an
-intranet without public access to the internet.
+**type**: ``string``
-static_method
-.............
+This determines the path to set in the session cookie.
-**type**: ``string | array`` **default**: ``['loadValidatorMetadata']``
+If not set, ``php.ini``'s `session.cookie_path`_ directive will be relied on.
-Defines the name of the static method which is called to load the validation
-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.
+cookie_samesite
+...............
-.. _reference-validation-password-strength:
+**type**: ``string`` or ``null`` **default**: ``null``
-password_strength
-.................
+It controls the way cookies are sent when the HTTP request did not originate
+from the same domain that is associated with the cookies. Setting this option is
+recommended to mitigate `CSRF security attacks`_.
-The :doc:`PasswordStrength `
-constraint verifies the submitted string entropy is matching the minimum entropy score.
+By default, browsers send all cookies related to the domain of the HTTP request.
+This may be a problem for example when you visit a forum and some malicious
+comment includes a link like ``https://some-bank.com/?send_money_to=attacker&amount=1000``.
+If you were previously logged into your bank website, the browser will send all
+those cookies when making that HTTP request.
-.. _reference-validation-email_validation_mode:
+The possible values for this option are:
-email_validation_mode
-.....................
+* ``null``, use ``php.ini``'s `session.cookie_samesite`_ directive.
+* ``'none'`` (or the ``Symfony\Component\HttpFoundation\Cookie::SAMESITE_NONE`` constant), use it to allow
+ sending of cookies when the HTTP request originated from a different domain
+ (previously this was the default behavior of null, but in newer browsers ``'lax'``
+ would be applied when the header has not been set)
+* ``'strict'`` (or the ``Cookie::SAMESITE_STRICT`` constant), use it to never
+ send any cookie when the HTTP request did not originate from the same domain.
+* ``'lax'`` (or the ``Cookie::SAMESITE_LAX`` constant), use it to allow sending
+ cookies when the request originated from a different domain, but only when the
+ user consciously made the request (by clicking a link or submitting a form
+ with the ``GET`` method).
-**type**: ``string`` **default**: ``html5``
+cookie_secure
+.............
-Sets the default value for the
-:ref:`"mode" option of the Email validator `.
+**type**: ``boolean`` or ``'auto'``
-.. _reference-validation-mapping:
+This determines whether cookies should only be sent over secure connections. In
+addition to ``true`` and ``false``, there's a special ``'auto'`` value that
+means ``true`` for HTTPS requests and ``false`` for HTTP requests.
-mapping
-.......
+If not set, ``php.ini``'s `session.cookie_secure`_ directive will be relied on.
-.. _reference-validation-mapping-paths:
+.. _reference-session-enabled:
-paths
-"""""
+enabled
+.......
-**type**: ``array`` **default**: ``['config/validation/']``
+**type**: ``boolean`` **default**: ``true``
-This option allows to define an array of paths with files or directories where
-the component will look for additional validation files:
+Whether to enable the session support in the framework.
.. configuration-block::
@@ -2816,10 +3086,8 @@ the component will look for additional validation files:
# config/packages/framework.yaml
framework:
- validation:
- mapping:
- paths:
- - "%kernel.project_dir%/config/validation/"
+ session:
+ enabled: true
.. code-block:: xml
@@ -2833,11 +3101,7 @@ the component will look for additional validation files:
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
-
-
- %kernel.project_dir%/config/validation/
-
-
+
@@ -2847,148 +3111,163 @@ the component will look for additional validation files:
use Symfony\Config\FrameworkConfig;
return static function (FrameworkConfig $framework): void {
- $framework->validation()
- ->mapping()
- ->paths(['%kernel.project_dir%/config/validation/']);
+ $framework->session()
+ ->enabled(true);
};
-annotations
-~~~~~~~~~~~
-
-.. _reference-annotations-cache:
-
-cache
-.....
+gc_divisor
+..........
-**type**: ``string`` **default**: ``php_array``
+**type**: ``integer``
-This option can be one of the following values:
+See `gc_probability`_.
-php_array
- Use a PHP array to cache annotations in memory
-file
- Use the filesystem to cache annotations
-none
- Disable the caching of annotations
+If not set, ``php.ini``'s `session.gc_divisor`_ directive will be relied on.
-file_cache_dir
+gc_maxlifetime
..............
-**type**: ``string`` **default**: ``%kernel.cache_dir%/annotations``
-
-The directory to store cache files for annotations, in case
-``annotations.cache`` is set to ``'file'``.
-
-debug
-.....
-
-**type**: ``boolean`` **default**: ``%kernel.debug%``
-
-Whether to enable debug mode for caching. If enabled, the cache will
-automatically update when the original file is changed (both with code and
-annotation changes). For performance reasons, it is recommended to disable
-debug mode in production, which will happen automatically if you use the
-default value.
-
-.. _configuration-framework-serializer:
-
-serializer
-~~~~~~~~~~
+**type**: ``integer``
-.. _reference-serializer-enabled:
+This determines the number of seconds after which data will be seen as "garbage"
+and potentially cleaned up. Garbage collection may occur during session
+start and depends on `gc_divisor`_ and `gc_probability`_.
-enabled
-.......
+If not set, ``php.ini``'s `session.gc_maxlifetime`_ directive will be relied on.
-**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
+gc_probability
+..............
-Whether to enable the ``serializer`` service or not in the service container.
+**type**: ``integer``
-.. _reference-serializer-enable_annotations:
+This defines the probability that the garbage collector (GC) process is
+started on every session initialization. The probability is calculated by
+using ``gc_probability`` / ``gc_divisor``, e.g. 1/100 means there is a 1%
+chance that the GC process will start on each request.
-enable_attributes
-.................
+If not set, Symfony will use the value of the `session.gc_probability`_ directive
+in the ``php.ini`` configuration file.
-**type**: ``boolean`` **default**: ``true``
+.. versionadded:: 7.2
-Enables support for `PHP attributes`_ in the serializer component.
+ Relying on ``php.ini``'s directive as default for ``gc_probability`` was
+ introduced in Symfony 7.2.
-.. seealso::
+.. _config-framework-session-handler-id:
- See :ref:`the reference ` for a list of supported annotations.
+handler_id
+..........
-.. _reference-serializer-name_converter:
+**type**: ``string`` | ``null`` **default**: ``null``
-name_converter
-..............
+If ``framework.session.save_path`` is not set, the default value of this option
+is ``null``, which means to use the session handler configured in php.ini. If the
+``framework.session.save_path`` option is set, then Symfony stores sessions using
+the native file session handler.
-**type**: ``string``
+It is possible to :ref:`store sessions in a database `,
+and also to configure the session handler with a DSN:
-The name converter to use.
-The :class:`Symfony\\Component\\Serializer\\NameConverter\\CamelCaseToSnakeCaseNameConverter`
-name converter can enabled by using the ``serializer.name_converter.camel_case_to_snake_case``
-value.
+.. configuration-block::
-.. seealso::
+ .. code-block:: yaml
- For more information, see :ref:`serializer-name-conversion`.
+ # config/packages/framework.yaml
+ framework:
+ session:
+ # a few possible examples
+ handler_id: 'redis://localhost'
+ handler_id: '%env(REDIS_URL)%'
+ handler_id: '%env(DATABASE_URL)%'
+ handler_id: 'file://%kernel.project_dir%/var/sessions'
-.. _reference-serializer-circular_reference_handler:
+ .. code-block:: xml
-circular_reference_handler
-..........................
+
+
+
+
+
+
+
+
-**type** ``string``
+ .. code-block:: php
-The service id that is used as the circular reference handler of the default
-serializer. The service has to implement the magic ``__invoke($object)``
-method.
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
+ use function Symfony\Component\DependencyInjection\Loader\Configurator\env;
-.. seealso::
+ return static function (FrameworkConfig $framework): void {
+ // ...
- For more information, see
- :ref:`component-serializer-handling-circular-references`.
+ $framework->session()
+ // a few possible examples
+ ->handlerId('redis://localhost')
+ ->handlerId(env('REDIS_URL'))
+ ->handlerId(env('DATABASE_URL'))
+ ->handlerId('file://%kernel.project_dir%/var/sessions');
+ };
-.. _reference-serializer-mapping:
+.. note::
-mapping
-.......
+ Supported DSN protocols are the following:
-.. _reference-serializer-mapping-paths:
+ * ``file``
+ * ``redis``
+ * ``rediss`` (Redis over TLS)
+ * ``memcached`` (requires :doc:`symfony/cache `)
+ * ``pdo_oci`` (requires :doc:`doctrine/dbal `)
+ * ``mssql``
+ * ``mysql``
+ * ``mysql2``
+ * ``pgsql``
+ * ``postgres``
+ * ``postgresql``
+ * ``sqlsrv``
+ * ``sqlite``
+ * ``sqlite3``
-paths
-"""""
+.. _reference-session-metadata-update-threshold:
-**type**: ``array`` **default**: ``[]``
+metadata_update_threshold
+.........................
-This option allows to define an array of paths with files or directories where
-the component will look for additional serialization files.
+**type**: ``integer`` **default**: ``0``
-default_context
-...............
+This is how many seconds to wait between updating/writing the session metadata.
+This can be useful if, for some reason, you want to limit the frequency at which
+the session persists, instead of doing that on every request.
-**type**: ``array`` **default**: ``[]``
+.. _name:
-A map with default context options that will be used with each ``serialize`` and ``deserialize``
-call. This can be used for example to set the json encoding behavior by setting ``json_encode_options``
-to a `json_encode flags bitmask`_.
+name
+....
-You can inspect the :ref:`serializer context builders `
-to discover the available settings.
+**type**: ``string``
-php_errors
-~~~~~~~~~~
+This specifies the name of the session cookie.
-log
-...
+If not set, ``php.ini``'s `session.name`_ directive will be relied on.
-**type**: ``boolean`` | ``int`` **default**: ``true``
+save_path
+.........
-Use the application logger instead of the PHP logger for logging PHP errors.
-When an integer value is used, it also sets the log level. Those integer
-values must be the same used in the `error_reporting PHP option`_.
+**type**: ``string`` | ``null`` **default**: ``%kernel.cache_dir%/sessions``
-This option also accepts a map of PHP errors to log levels:
+This determines the argument to be passed to the save handler. If you choose
+the default file handler, this is the path where the session files are created.
+
+If ``null``, ``php.ini``'s `session.save_path`_ directive will be relied on:
.. configuration-block::
@@ -2996,23 +3275,8 @@ This option also accepts a map of PHP errors to log levels:
# config/packages/framework.yaml
framework:
- php_errors:
- log:
- !php/const \E_DEPRECATED: !php/const Psr\Log\LogLevel::ERROR
- !php/const \E_USER_DEPRECATED: !php/const Psr\Log\LogLevel::ERROR
- !php/const \E_NOTICE: !php/const Psr\Log\LogLevel::ERROR
- !php/const \E_USER_NOTICE: !php/const Psr\Log\LogLevel::ERROR
- !php/const \E_STRICT: !php/const Psr\Log\LogLevel::ERROR
- !php/const \E_WARNING: !php/const Psr\Log\LogLevel::ERROR
- !php/const \E_USER_WARNING: !php/const Psr\Log\LogLevel::ERROR
- !php/const \E_COMPILE_WARNING: !php/const Psr\Log\LogLevel::ERROR
- !php/const \E_CORE_WARNING: !php/const Psr\Log\LogLevel::ERROR
- !php/const \E_USER_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
- !php/const \E_RECOVERABLE_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
- !php/const \E_COMPILE_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
- !php/const \E_PARSE: !php/const Psr\Log\LogLevel::CRITICAL
- !php/const \E_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
- !php/const \E_CORE_ERROR: !php/const Psr\Log\LogLevel::CRITICAL
+ session:
+ save_path: ~
.. code-block:: xml
@@ -3026,322 +3290,267 @@ This option also accepts a map of PHP errors to log levels:
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
-
-
-
-
+
.. code-block:: php
// config/packages/framework.php
- use Psr\Log\LogLevel;
use Symfony\Config\FrameworkConfig;
return static function (FrameworkConfig $framework): void {
- $framework->phpErrors()->log(\E_DEPRECATED, LogLevel::ERROR);
- $framework->phpErrors()->log(\E_USER_DEPRECATED, LogLevel::ERROR);
- // ...
+ $framework->session()
+ ->savePath(null);
};
-throw
-.....
-
-**type**: ``boolean`` **default**: ``%kernel.debug%``
-
-Throw PHP errors as ``\ErrorException`` instances. The parameter
-``debug.error_handler.throw_at`` controls the threshold.
-
-.. _reference-cache:
-
-cache
-~~~~~
-
-.. _reference-cache-app:
-
-app
-...
-
-**type**: ``string`` **default**: ``cache.adapter.filesystem``
+sid_bits_per_character
+......................
-The cache adapter used by the ``cache.app`` service. The FrameworkBundle
-ships with multiple adapters: ``cache.adapter.apcu``, ``cache.adapter.system``,
-``cache.adapter.filesystem``, ``cache.adapter.psr6``, ``cache.adapter.redis``,
-``cache.adapter.memcached``, ``cache.adapter.pdo`` and
-``cache.adapter.doctrine_dbal``.
+**type**: ``integer``
-There's also a special adapter called ``cache.adapter.array`` which stores
-contents in memory using a PHP array and it's used to disable caching (mostly on
-the ``dev`` environment).
+This determines the number of bits in the encoded session ID character. The possible
+values are ``4`` (0-9, a-f), ``5`` (0-9, a-v), and ``6`` (0-9, a-z, A-Z, "-", ",").
+The more bits results in stronger session ID. ``5`` is recommended value for
+most environments.
-.. tip::
+If not set, ``php.ini``'s `session.sid_bits_per_character`_ directive will be relied on.
- It might be tough to understand at the beginning, so to avoid confusion
- remember that all pools perform the same actions but on different medium
- given the adapter they are based on. Internally, a pool wraps the definition
- of an adapter.
+.. deprecated:: 7.2
-.. _reference-cache-system:
+ The ``sid_bits_per_character`` option was deprecated in Symfony 7.2. No alternative
+ is provided as PHP 8.4 has deprecated the related option.
-system
-......
+sid_length
+..........
-**type**: ``string`` **default**: ``cache.adapter.system``
+**type**: ``integer``
-The cache adapter used by the ``cache.system`` service. It supports the same
-adapters available for the ``cache.app`` service.
+This determines the length of session ID string, which can be an integer between
+``22`` and ``256`` (both inclusive), ``32`` being the recommended value. Longer
+session IDs are harder to guess.
-directory
-.........
+If not set, ``php.ini``'s `session.sid_length`_ directive will be relied on.
-**type**: ``string`` **default**: ``%kernel.cache_dir%/pools``
+.. deprecated:: 7.2
-The path to the cache directory used by services inheriting from the
-``cache.adapter.filesystem`` adapter (including ``cache.app``).
+ The ``sid_length`` option was deprecated in Symfony 7.2. No alternative is
+ provided as PHP 8.4 has deprecated the related option.
-default_doctrine_provider
-.........................
+.. _storage_id:
-**type**: ``string``
+storage_factory_id
+..................
-The service name to use as your default Doctrine provider. The provider is
-available as the ``cache.default_doctrine_provider`` service.
+**type**: ``string`` **default**: ``session.storage.factory.native``
-default_psr6_provider
-.....................
+The service ID used for creating the ``SessionStorageInterface`` that stores
+the session. This service is available in the Symfony application via the
+``session.storage.factory`` service alias. The class has to implement
+:class:`Symfony\\Component\\HttpFoundation\\Session\\Storage\\SessionStorageFactoryInterface`.
+To see a list of all available storages, run:
-**type**: ``string``
+.. code-block:: terminal
-The service name to use as your default PSR-6 provider. It is available as
-the ``cache.default_psr6_provider`` service.
+ $ php bin/console debug:container session.storage.factory.
-default_redis_provider
-......................
+use_cookies
+...........
-**type**: ``string`` **default**: ``redis://localhost``
+**type**: ``boolean``
-The DSN to use by the Redis provider. The provider is available as the ``cache.default_redis_provider``
-service.
+This specifies if the session ID is stored on the client side using cookies or
+not.
-default_memcached_provider
-..........................
+If not set, ``php.ini``'s `session.use_cookies`_ directive will be relied on.
-**type**: ``string`` **default**: ``memcached://localhost``
+ssi
+~~~
-The DSN to use by the Memcached provider. The provider is available as the ``cache.default_memcached_provider``
-service.
+enabled
+.......
-default_pdo_provider
-....................
+**type**: ``boolean`` **default**: ``false``
-**type**: ``string`` **default**: ``doctrine.dbal.default_connection``
+Whether to enable or not SSI support in your application.
-The service id of the database connection, which should be either a PDO or a
-Doctrine DBAL instance. The provider is available as the ``cache.default_pdo_provider``
-service.
+.. _reference-framework-test:
-pools
-.....
+test
+~~~~
-**type**: ``array``
+**type**: ``boolean``
-A list of cache pools to be created by the framework extension.
+If this configuration setting is present (and not ``false``), then the services
+related to testing your application (e.g. ``test.client``) are loaded. This
+setting should be present in your ``test`` environment (usually via
+``config/packages/test/framework.yaml``).
.. seealso::
- For more information about how pools work, see :ref:`cache pools `.
-
-To configure a Redis cache pool with a default lifetime of 1 hour, do the following:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # config/packages/framework.yaml
- framework:
- cache:
- pools:
- cache.mycache:
- adapter: cache.adapter.redis
- default_lifetime: 3600
-
- .. code-block:: xml
-
-
-
-
+ For more information, see :doc:`/testing`.
-
-
-
-
-
-
-
+translator
+~~~~~~~~~~
- .. code-block:: php
+cache_dir
+.........
- // config/packages/framework.php
- use Symfony\Config\FrameworkConfig;
+**type**: ``string`` | ``null`` **default**: ``%kernel.cache_dir%/translations``
- return static function (FrameworkConfig $framework): void {
- $framework->cache()
- ->pool('cache.mycache')
- ->adapters(['cache.adapter.redis'])
- ->defaultLifetime(3600);
- };
+Defines the directory where the translation cache is stored. Use ``null`` to
+disable this cache.
-.. _reference-cache-pools-name:
+.. _reference-translator-default_path:
-name
-""""
+default_path
+............
-**type**: ``prototype``
+**type**: ``string`` **default**: ``%kernel.project_dir%/translations``
-Name of the pool you want to create.
+This option allows to define the path where the application translations files
+are stored.
-.. note::
+.. _reference-translator-enabled:
- Your pool name must differ from ``cache.app`` or ``cache.system``.
+enabled
+.......
-adapter
-"""""""
+**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
-**type**: ``string`` **default**: ``cache.app``
+Whether or not to enable the ``translator`` service in the service container.
-The service name of the adapter to use. You can specify one of the default
-services that follow the pattern ``cache.adapter.[type]``. Alternatively you
-can specify another cache pool as base, which will make this pool inherit the
-settings from the base pool as defaults.
+.. _fallback:
-.. note::
+fallbacks
+.........
- Your service needs to implement the ``Psr\Cache\CacheItemPoolInterface`` interface.
+**type**: ``string|array`` **default**: value of `default_locale`_
-public
-""""""
+This option is used when the translation key for the current locale wasn't
+found.
-**type**: ``boolean`` **default**: ``false``
+.. seealso::
-Whether your service should be public or not.
+ For more details, see :doc:`/translation`.
-tags
-""""
+.. _reference-framework-translator-formatter:
-**type**: ``boolean`` | ``string`` **default**: ``null``
+formatter
+.........
-Whether your service should be able to handle tags or not.
-Can also be the service id of another cache pool where tags will be stored.
+**type**: ``string`` **default**: ``translator.formatter.default``
-default_lifetime
-""""""""""""""""
+The ID of the service used to format translation messages. The service class
+must implement the :class:`Symfony\\Component\\Translation\\Formatter\\MessageFormatterInterface`.
-**type**: ``integer`` | ``string``
+.. _reference-framework-translator-logging:
-Default lifetime of your cache items. Give an integer value to set the default
-lifetime in seconds. A string value could be ISO 8601 time interval, like ``"PT5M"``
-or a PHP date expression that is accepted by ``strtotime()``, like ``"5 minutes"``.
+logging
+.......
-If no value is provided, the cache adapter will fallback to the default value on
-the actual cache storage.
+**default**: ``true`` when the debug mode is enabled, ``false`` otherwise.
-provider
-""""""""
+When ``true``, a log entry is made whenever the translator cannot find a translation
+for a given key. The logs are made to the ``translation`` channel at the
+``debug`` level for keys where there is a translation in the fallback
+locale, and the ``warning`` level if there is no translation to use at all.
-**type**: ``string``
+.. _reference-translator-paths:
-Overwrite the default service name or DSN respectively, if you do not want to
-use what is configured as ``default_X_provider`` under ``cache``. See the
-description of the default provider setting above for information on how to
-specify your specific provider.
+paths
+.....
-clearer
-"""""""
+**type**: ``array`` **default**: ``[]``
-**type**: ``string``
+This option allows to define an array of paths where the component will look
+for translation files. The later a path is added, the more priority it has
+(translations from later paths overwrite earlier ones). Translations from the
+:ref:`default_path ` have more priority than
+translations from all these paths.
-The cache clearer used to clear your PSR-6 cache.
+.. _reference-translator-providers:
-.. seealso::
+providers
+.........
- For more information, see :class:`Symfony\\Component\\HttpKernel\\CacheClearer\\Psr6CacheClearer`.
+**type**: ``array`` **default**: ``[]``
-.. _reference-cache-prefix-seed:
+This option enables and configures :ref:`translation providers `
+to push and pull your translations to/from third party translation services.
-prefix_seed
-...........
+trust_x_sendfile_type_header
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-**type**: ``string`` **default**: ``_%kernel.project_dir%.%kernel.container_class%``
+**type**: ``boolean`` **default**: ``%env(bool:default::SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER)%``
-This value is used as part of the "namespace" generated for the
-cache item keys. A common practice is to use the unique name of the application
-(e.g. ``symfony.com``) because that prevents naming collisions when deploying
-multiple applications into the same path (on different servers) that share the
-same cache backend.
+.. versionadded:: 7.2
-It's also useful when using `blue/green deployment`_ strategies and more
-generally, when you need to abstract out the actual deployment directory (for
-example, when warming caches offline).
+ In Symfony 7.2, the default value of this option was changed from ``false`` to the
+ value stored in the ``SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER`` environment variable.
-.. note::
+``X-Sendfile`` is a special HTTP header that tells web servers to replace the
+response contents by the file that is defined in that header. This improves
+performance because files are no longer served by your application but directly
+by the web server.
- The ``prefix_seed`` option is used at compile time. This means
- that any change made to this value after container's compilation
- will have no effect.
+This configuration option determines whether to trust ``x-sendfile`` header for
+BinaryFileResponse. If enabled, Symfony calls the
+:method:`BinaryFileResponse::trustXSendfileTypeHeader `
+method automatically. It becomes the service container parameter named
+``kernel.trust_x_sendfile_type_header``.
-.. _reference-lock:
+.. _reference-framework-trusted-headers:
-lock
-~~~~
+trusted_headers
+~~~~~~~~~~~~~~~
-**type**: ``string`` | ``array``
+The ``trusted_headers`` option is needed to configure which client information
+should be trusted (e.g. their host) when running Symfony behind a load balancer
+or a reverse proxy. See :doc:`/deployment/proxies`.
-The default lock adapter. If not defined, the value is set to ``semaphore`` when
-available, or to ``flock`` otherwise. Store's DSN are also allowed.
+.. _configuration-framework-trusted-hosts:
-.. _reference-lock-enabled:
+trusted_hosts
+~~~~~~~~~~~~~
-enabled
-.......
+**type**: ``array`` | ``string`` **default**: ``['%env(default::SYMFONY_TRUSTED_HOSTS)%']``
-**type**: ``boolean`` **default**: ``true``
+.. versionadded:: 7.2
-Whether to enable the support for lock or not. This setting is
-automatically set to ``true`` when one of the child settings is configured.
+ In Symfony 7.2, the default value of this option was changed from ``[]`` to the
+ value stored in the ``SYMFONY_TRUSTED_HOSTS`` environment variable.
-.. _reference-lock-resources:
+A lot of different attacks have been discovered relying on inconsistencies
+in handling the ``Host`` header by various software (web servers, reverse
+proxies, web frameworks, etc.). Basically, every time the framework is
+generating an absolute URL (when sending an email to reset a password for
+instance), the host might have been manipulated by an attacker.
-resources
-.........
+.. seealso::
-**type**: ``array``
+ You can read `HTTP Host header attacks`_ for more information about
+ these kinds of attacks.
-A map of lock stores to be created by the framework extension, with
-the name as key and DSN as value:
+The Symfony :method:`Request::getHost() `
+method might be vulnerable to some of these attacks because it depends on
+the configuration of your web server. One simple solution to avoid these
+attacks is to configure a list of hosts that your Symfony application can respond
+to. That's the purpose of this ``trusted_hosts`` option. If the incoming
+request's hostname doesn't match one of the regular expressions in this list,
+the application won't respond and the user will receive a 400 response.
.. configuration-block::
.. code-block:: yaml
- # config/packages/lock.yaml
+ # config/packages/framework.yaml
framework:
- lock: '%env(LOCK_DSN)%'
+ trusted_hosts: ['^example\.com$', '^example\.org$']
.. code-block:: xml
-
+
-
- %env(LOCK_DSN)%
-
+ ^example\.com$
+ ^example\.org$
+
.. code-block:: php
- // config/packages/lock.php
+ // config/packages/framework.php
use Symfony\Config\FrameworkConfig;
return static function (FrameworkConfig $framework): void {
- $framework->lock()
- ->resource('default', [env('LOCK_DSN')]);
+ $framework->trustedHosts(['^example\.com$', '^example\.org$']);
};
-.. seealso::
-
- For more details, see :doc:`/lock`.
-
-.. _reference-lock-resources-name:
+Hosts can also be configured to respond to any subdomain, via
+``^(.+\.)?example\.com$`` for instance.
-name
-""""
+In addition, you can also set the trusted hosts in the front controller
+using the ``Request::setTrustedHosts()`` method::
-**type**: ``prototype``
+ // public/index.php
+ Request::setTrustedHosts(['^(.+\.)?example\.com$', '^(.+\.)?example\.org$']);
-Name of the lock you want to create.
+The default value for this option is an empty array, meaning that the application
+can respond to any given host.
-semaphore
-~~~~~~~~~
+.. seealso::
-**type**: ``string`` | ``array``
+ Read more about this in the `Security Advisory Blog post`_.
-The default semaphore adapter. Store's DSN are also allowed.
+.. _reference-framework-trusted-proxies:
-.. _reference-semaphore-enabled:
+trusted_proxies
+~~~~~~~~~~~~~~~
-enabled
-.......
+The ``trusted_proxies`` option is needed to get precise information about the
+client (e.g. their IP address) when running Symfony behind a load balancer or a
+reverse proxy. See :doc:`/deployment/proxies`.
-**type**: ``boolean`` **default**: ``true``
+.. _reference-validation:
-Whether to enable the support for semaphore or not. This setting is
-automatically set to ``true`` when one of the child settings is configured.
+validation
+~~~~~~~~~~
-.. _reference-semaphore-resources:
+.. _reference-validation-auto-mapping:
-resources
-.........
+auto_mapping
+............
-**type**: ``array``
+**type**: ``array`` **default**: ``[]``
-A map of semaphore stores to be created by the framework extension, with
-the name as key and DSN as value:
+Defines the Doctrine entities that will be introspected to add
+:ref:`automatic validation constraints ` to them:
.. configuration-block::
.. code-block:: yaml
- # config/packages/semaphore.yaml
framework:
- semaphore: '%env(SEMAPHORE_DSN)%'
+ validation:
+ auto_mapping:
+ # an empty array means that all entities that belong to that
+ # namespace will add automatic validation
+ 'App\Entity\': []
+ 'Foo\': ['Foo\Some\Entity', 'Foo\Another\Entity']
.. code-block:: xml
-
+
-
- %env(SEMAPHORE_DSN)%
-
+
+
+
+
+ Foo\Some\Entity
+ Foo\Another\Entity
+
+
.. code-block:: php
- // config/packages/semaphore.php
+ // config/packages/framework.php
use Symfony\Config\FrameworkConfig;
return static function (FrameworkConfig $framework): void {
- $framework->semaphore()
- ->resource('default', ['%env(SEMAPHORE_DSN)%']);
+ $framework->validation()
+ ->autoMapping()
+ ->paths([
+ 'App\\Entity\\' => [],
+ 'Foo\\' => ['Foo\\Some\\Entity', 'Foo\\Another\\Entity'],
+ ]);
};
-.. _reference-semaphore-resources-name:
+.. _reference-validation-disable_translation:
-name
-""""
+disable_translation
+...................
-**type**: ``prototype``
+**type**: ``boolean`` **default**: ``false``
-Name of the semaphore you want to create.
+Validation error messages are automatically translated to the current application
+locale. Set this option to ``true`` to disable translation of validation messages.
+This is useful to avoid "missing translation" errors in applications that use
+only a single language.
-mailer
-~~~~~~
+.. versionadded:: 7.3
-.. _mailer-dsn:
+ The ``disable_translation`` option was introduced in Symfony 7.3.
-dsn
-...
+.. _reference-validation-email_validation_mode:
-**type**: ``string`` **default**: ``null``
+email_validation_mode
+.....................
-The DSN used by the mailer. When several DSN may be used, use
-``transports`` option (see below) instead.
+**type**: ``string`` **default**: ``html5``
+
+Sets the default value for the
+:ref:`"mode" option of the Email validator `.
+
+.. _reference-validation-enable_annotations:
-transports
-..........
+enable_attributes
+.................
-**type**: ``array``
+**type**: ``boolean`` **default**: ``true``
-A :ref:`list of DSN ` that can be used by the
-mailer. A transport name is the key and the dsn is the value.
+If this option is enabled, validation constraints can be defined using `PHP attributes`_.
-message_bus
-...........
+.. _reference-validation-enabled:
-**type**: ``string`` **default**: ``null`` or default bus if Messenger component is installed
+enabled
+.......
-Service identifier of the message bus to use when using the
-:doc:`Messenger component ` (e.g. ``messenger.default_bus``).
+**type**: ``boolean`` **default**: ``true`` or ``false`` depending on your installation
-envelope
-........
+Whether or not to enable validation support.
-sender
-""""""
+This option will automatically be set to ``true`` when one of the child
+settings is configured.
-**type**: ``string``
+.. _reference-validation-mapping:
-The "envelope sender" which is used as the value of ``MAIL FROM`` during the
-`SMTP session`_. This value overrides any other sender set in the code.
+mapping
+.......
-recipients
-""""""""""
+.. _reference-validation-mapping-paths:
-**type**: ``array``
+paths
+"""""
-The "envelope recipient" which is used as the value of ``RCPT TO`` during the
-the `SMTP session`_. This value overrides any other recipient set in the code.
+**type**: ``array`` **default**: ``['config/validation/']``
+
+This option allows to define an array of paths with files or directories where
+the component will look for additional validation files:
.. configuration-block::
.. code-block:: yaml
- # config/packages/mailer.yaml
+ # config/packages/framework.yaml
framework:
- mailer:
- dsn: 'smtp://localhost:25'
- envelope:
- recipients: ['admin@symfony.com', 'lead@symfony.com']
+ validation:
+ mapping:
+ paths:
+ - "%kernel.project_dir%/config/validation/"
.. code-block:: xml
-
+
+
-
-
- admin@symfony.com
- lead@symfony.com
-
-
+
+
+ %kernel.project_dir%/config/validation/
+
+
.. code-block:: php
- // config/packages/mailer.php
- namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
- return static function (ContainerConfigurator $container): void {
- $container->extension('framework', [
- 'mailer' => [
- 'dsn' => 'smtp://localhost:25',
- 'envelope' => [
- 'recipients' => [
- 'admin@symfony.com',
- 'lead@symfony.com',
- ],
- ],
- ],
- ]);
+ return static function (FrameworkConfig $framework): void {
+ $framework->validation()
+ ->mapping()
+ ->paths(['%kernel.project_dir%/config/validation/']);
};
-.. _mailer-headers:
+.. _reference-validation-not-compromised-password:
-headers
-.......
+not_compromised_password
+........................
-**type**: ``array``
+The :doc:`NotCompromisedPassword `
+constraint makes HTTP requests to a public API to check if the given password
+has been compromised in a data breach.
-Headers to add to emails. The key (``name`` attribute in xml format) is the
-header name and value the header value.
+static_method
+.............
-.. seealso::
+**type**: ``string | array`` **default**: ``['loadValidatorMetadata']``
- For more information, see :ref:`Configuring Emails Globally `
+Defines the name of the static method which is called to load the validation
+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.
-messenger
-~~~~~~~~~
+translation_domain
+..................
+
+**type**: ``string | false`` **default**: ``validators``
+
+The translation domain that is used when translating validation constraint
+error messages. Use false to disable translations.
+
+
+.. _reference-validation-not-compromised-password-enabled:
enabled
-.......
+"""""""
**type**: ``boolean`` **default**: ``true``
-Whether to enable or not Messenger.
+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
+continuous integration servers.
-.. seealso::
+endpoint
+""""""""
- For more details, see the :doc:`Messenger component `
- documentation.
+**type**: ``string`` **default**: ``null``
+
+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
+checks. It's useful for example when the Symfony application is run in an
+intranet without public access to the internet.
web_link
~~~~~~~~
@@ -3749,125 +3989,6 @@ Defines the kind of workflow that is going to be created, which can be either
a normal workflow or a state machine. Read :doc:`this article `
to know their differences.
-.. _framework_exceptions:
-
-exceptions
-~~~~~~~~~~
-
-**type**: ``array``
-
-Defines the :ref:`log level ` and HTTP status code applied to the
-exceptions that match the given exception class:
-
-.. configuration-block::
-
- .. code-block:: yaml
-
- # config/packages/exceptions.yaml
- framework:
- exceptions:
- Symfony\Component\HttpKernel\Exception\BadRequestHttpException:
- log_level: 'debug'
- status_code: 422
-
- .. code-block:: xml
-
-
-
-
-
-
-
-
-
-
-
- .. code-block:: php
-
- // config/packages/exceptions.php
- use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
- use Symfony\Config\FrameworkConfig;
-
- return static function (FrameworkConfig $framework): void {
- $framework->exception(BadRequestHttpException::class)
- ->logLevel('debug')
- ->statusCode(422)
- ;
- };
-
-The order in which you configure exceptions is important because Symfony will
-use the configuration of the first exception that matches ``instanceof``:
-
-.. code-block:: yaml
-
- # config/packages/exceptions.yaml
- framework:
- exceptions:
- Exception:
- log_level: 'debug'
- status_code: 404
- # The following configuration will never be used because \RuntimeException extends \Exception
- RuntimeException:
- log_level: 'debug'
- status_code: 422
-
-You can map a status code and a set of headers to an exception thanks
-to the ``#[WithHttpStatus]`` attribute on the exception class::
-
- namespace App\Exception;
-
- use Symfony\Component\HttpKernel\Attribute\WithHttpStatus;
-
- #[WithHttpStatus(422, [
- 'Retry-After' => 10,
- 'X-Custom-Header' => 'header-value',
- ])]
- class CustomException extends \Exception
- {
- }
-
-It is also possible to map a log level on a custom exception class using
-the ``#[WithLogLevel]`` attribute::
-
- namespace App\Exception;
-
- use Psr\Log\LogLevel;
- use Symfony\Component\HttpKernel\Attribute\WithLogLevel;
-
- #[WithLogLevel(LogLevel::WARNING)]
- class CustomException extends \Exception
- {
- }
-
-The attributes can also be added to interfaces directly::
-
- namespace App\Exception;
-
- use Symfony\Component\HttpKernel\Attribute\WithHttpStatus;
-
- #[WithHttpStatus(422)]
- interface CustomExceptionInterface
- {
- }
-
- class CustomException extends \Exception implements CustomExceptionInterface
- {
- }
-
-.. versionadded:: 7.1
-
- Support to use ``#[WithHttpStatus]`` and ``#[WithLogLevel]`` attributes
- on interfaces was introduced in Symfony 7.1.
-
.. _`HTTP Host header attacks`: https://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html
.. _`Security Advisory Blog post`: https://symfony.com/blog/security-releases-symfony-2-0-24-2-1-12-2-2-5-and-2-3-3-released#cve-2013-4752-request-gethost-poisoning
.. _`phpstorm-url-handler`: https://github.com/sanduhrs/phpstorm-url-handler
@@ -3890,6 +4011,7 @@ The attributes can also be added to interfaces directly::
.. _`session.cookie_samesite`: https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-samesite
.. _`session.cookie_secure`: https://www.php.net/manual/en/session.configuration.php#ini.session.cookie-secure
.. _`session.gc_divisor`: https://www.php.net/manual/en/session.configuration.php#ini.session.gc-divisor
+.. _`session.gc_probability`: https://www.php.net/manual/en/session.configuration.php#ini.session.gc-probability
.. _`session.gc_maxlifetime`: https://www.php.net/manual/en/session.configuration.php#ini.session.gc-maxlifetime
.. _`session.sid_length`: https://www.php.net/manual/en/session.configuration.php#ini.session.sid-length
.. _`session.sid_bits_per_character`: https://www.php.net/manual/en/session.configuration.php#ini.session.sid-bits-per-character
diff --git a/reference/configuration/security.rst b/reference/configuration/security.rst
index 757dc7313cd..ef7247e330e 100644
--- a/reference/configuration/security.rst
+++ b/reference/configuration/security.rst
@@ -19,14 +19,12 @@ key in your application configuration.
namespace and the related XSD schema is available at:
``https://symfony.com/schema/dic/services/services-1.0.xsd``
-Configuration
--------------
-
**Basic Options**:
* `access_denied_url`_
* `erase_credentials`_
-* `hide_user_not_found`_
+* `expose_security_errors`_
+* `hide_user_not_found`_ (deprecated)
* `session_fixation_strategy`_
**Advanced Options**:
@@ -41,96 +39,71 @@ separate articles:
* `role_hierarchy`_
access_denied_url
-~~~~~~~~~~~~~~~~~
+-----------------
**type**: ``string`` **default**: ``null``
Defines the URL where the user is redirected after a ``403`` HTTP error (unless
you define a custom access denial handler). Example: ``/no-permission``
-delete_cookies
-~~~~~~~~~~~~~~
+erase_credentials
+-----------------
-**type**: ``array`` **default**: ``[]``
+**type**: ``boolean`` **default**: ``true``
-Lists the names (and other optional features) of the cookies to delete when the
-user logs out::
+If ``true``, the ``eraseCredentials()`` method of the user object is called
+after authentication::
-.. configuration-block::
+ use Symfony\Component\Security\Core\User\UserInterface;
- .. code-block:: yaml
+ class User implements UserInterface
+ {
+ // ...
- # config/packages/security.yaml
- security:
- # ...
+ public function eraseCredentials(): void
+ {
+ // If you store any temporary, sensitive data on the user, clear it here
+ // $this->plainPassword = null;
+ }
+ }
- firewalls:
- main:
- # ...
- logout:
- delete_cookies:
- cookie1-name: null
- cookie2-name:
- path: '/'
- cookie3-name:
- path: null
- domain: example.com
+.. deprecated:: 7.3
- .. code-block:: xml
+ Since Symfony 7.3, ``eraseCredentials()`` methods are deprecated and are
+ not called if they have the ``#[\Deprecated]`` attribute.
-
-
-
+expose_security_errors
+----------------------
-
-
+**type**: ``string`` **default**: ``'none'``
-
-
-
-
-
-
-
-
-
-
+.. versionadded:: 7.3
- .. code-block:: php
+ The ``expose_security_errors`` option was introduced in Symfony 7.3
- // config/packages/security.php
+User enumeration is a common security issue where attackers infer valid usernames
+based on error messages. For example, a message like "This user does not exist"
+shown by your login form reveals whether a username exists.
- // ...
+This option lets you hide some or all errors related to user accounts
+(e.g. blocked or expired accounts) to prevent this issue. Instead, these
+errors will trigger a generic ``BadCredentialsException``. The value of this
+option can be one of the following:
- return static function (SecurityConfig $securityConfig): void {
- // ...
+* ``'none'``: hides all user-related security exceptions;
+* ``'account_status'``: shows account-related exceptions (e.g. blocked or expired
+ accounts) but only for users who provided the correct password;
+* ``'all'``: shows all security-related exceptions.
- $securityConfig->firewall('main')
- ->logout()
- ->deleteCookie('cookie1-name')
- ->deleteCookie('cookie2-name')
- ->path('/')
- ->deleteCookie('cookie3-name')
- ->path(null)
- ->domain('example.com');
- };
-
-erase_credentials
-~~~~~~~~~~~~~~~~~
+hide_user_not_found
+-------------------
**type**: ``boolean`` **default**: ``true``
-If ``true``, the ``eraseCredentials()`` method of the user object is called
-after authentication.
+.. deprecated:: 7.3
-hide_user_not_found
-~~~~~~~~~~~~~~~~~~~
-
-**type**: ``boolean`` **default**: ``true``
+ The ``hide_user_not_found`` option was deprecated in favor of the
+ ``expose_security_errors`` option in Symfony 7.3.
If ``true``, when a user is not found a generic exception of type
:class:`Symfony\\Component\\Security\\Core\\Exception\\BadCredentialsException`
@@ -141,7 +114,7 @@ If ``false``, the exception thrown is of type
and it includes the given not found user identifier.
session_fixation_strategy
-~~~~~~~~~~~~~~~~~~~~~~~~~
+-------------------------
**type**: ``string`` **default**: ``SessionAuthenticationStrategy::MIGRATE``
@@ -430,7 +403,7 @@ delete_cookies
**type**: ``array`` **default**: ``[]``
Lists the names (and other optional features) of the cookies to delete when the
-user logs out::
+user logs out:
.. configuration-block::
@@ -1063,6 +1036,58 @@ the session must not be used when authenticating users:
// ...
};
+.. _reference-security-lazy:
+
+lazy
+~~~~
+
+Firewalls can configure a ``lazy`` boolean option to load the user and start the
+session only if the application actually accesses the User object, (e.g. calling
+``is_granted()`` in a template or ``isGranted()`` in a controller or service):
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/security.yaml
+ security:
+ # ...
+
+ firewalls:
+ main:
+ # ...
+ lazy: true
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/security.php
+ use Symfony\Config\SecurityConfig;
+
+ return static function (SecurityConfig $security): void {
+ $security->firewall('main')
+ ->lazy(true);
+ // ...
+ };
+
User Checkers
~~~~~~~~~~~~~
diff --git a/reference/configuration/twig.rst b/reference/configuration/twig.rst
index 596d70d8a2b..360309fef8f 100644
--- a/reference/configuration/twig.rst
+++ b/reference/configuration/twig.rst
@@ -19,9 +19,6 @@ under the ``twig`` key in your application configuration.
namespace and the related XSD schema is available at:
``https://symfony.com/schema/dic/twig/twig-1.0.xsd``
-Configuration
--------------
-
auto_reload
~~~~~~~~~~~
@@ -74,16 +71,27 @@ application harder to maintain.
cache
~~~~~
-**type**: ``string`` | ``false`` **default**: ``%kernel.cache_dir%/twig``
+**type**: ``string`` | ``boolean`` **default**: ``true``
Before using the Twig templates to render some contents, they are compiled into
regular PHP code. Compilation is a costly process, so the result is cached in
the directory defined by this configuration option.
+You can either specify a custom path where the cache should be stored (as a
+string) or use ``true`` to let Symfony decide the default path. When set to
+``true``, the cache is stored in ``%kernel.cache_dir%/twig`` by default. However,
+if ``auto_reload`` is disabled and ``%kernel.build_dir%`` differs from
+``%kernel.cache_dir%``, the cache will be stored in ``%kernel.build_dir%/twig`` instead.
+
Set this option to ``false`` to disable Twig template compilation. However, this
-is not recommended; not even in the ``dev`` environment, because the
-``auto_reload`` option ensures that cached templates which have changed get
-compiled again.
+is not recommended, not even in the ``dev`` environment, because the ``auto_reload``
+option ensures that cached templates which have changed get compiled again.
+
+.. versionadded:: 7.3
+
+ Support for using ``true`` as a value was introduced in Symfony 7.3. It also
+ became the default value for this option, replacing the explicit path
+ ``%kernel.cache_dir%/twig``.
charset
~~~~~~~
@@ -282,7 +290,7 @@ mailer
html_to_text_converter
......................
-**type**: ``string`` **default**: ````
+**type**: ``string`` **default**: ``null``
The service implementing
:class:`Symfony\\Component\\Mime\\HtmlToTextConverter\\HtmlToTextConverterInterface`
diff --git a/reference/configuration/web_profiler.rst b/reference/configuration/web_profiler.rst
index 93c65621999..c3b57d37c55 100644
--- a/reference/configuration/web_profiler.rst
+++ b/reference/configuration/web_profiler.rst
@@ -24,9 +24,6 @@ under the ``web_profiler`` key in your application configuration.
The web debug toolbar is not available for responses of type ``StreamedResponse``.
-Configuration
--------------
-
excluded_ajax_paths
~~~~~~~~~~~~~~~~~~~
@@ -56,8 +53,21 @@ on the given link to perform the redirect.
toolbar
~~~~~~~
+enabled
+.......
**type**: ``boolean`` **default**: ``false``
It enables and disables the toolbar entirely. Usually you set this to ``true``
in the ``dev`` and ``test`` environments and to ``false`` in the ``prod``
environment.
+
+ajax_replace
+............
+**type**: ``boolean`` **default**: ``false``
+
+If you set this option to ``true``, the toolbar is replaced on AJAX requests.
+This only works in combination with an enabled toolbar.
+
+.. versionadded:: 7.3
+
+ The ``ajax_replace`` configuration option was introduced in Symfony 7.3.
diff --git a/reference/constraints/All.rst b/reference/constraints/All.rst
index 3aa05b1d2d0..43ff4d6ac9d 100644
--- a/reference/constraints/All.rst
+++ b/reference/constraints/All.rst
@@ -79,12 +79,12 @@ entry in that array:
{
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('favoriteColors', new Assert\All([
- 'constraints' => [
+ $metadata->addPropertyConstraint('favoriteColors', new Assert\All(
+ constraints: [
new Assert\NotBlank(),
- new Assert\Length(['min' => 5]),
+ new Assert\Length(min: 5),
],
- ]));
+ ));
}
}
@@ -97,7 +97,7 @@ Options
``constraints``
~~~~~~~~~~~~~~~
-**type**: ``array`` [:ref:`default option `]
+**type**: ``array``
This required option is the array of validation constraints that you want
to apply to each element of the underlying array.
diff --git a/reference/constraints/AtLeastOneOf.rst b/reference/constraints/AtLeastOneOf.rst
index 0a6ab618aa5..fecbe617f5a 100644
--- a/reference/constraints/AtLeastOneOf.rst
+++ b/reference/constraints/AtLeastOneOf.rst
@@ -115,23 +115,23 @@ The following constraints ensure that:
{
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('password', new Assert\AtLeastOneOf([
- 'constraints' => [
- new Assert\Regex(['pattern' => '/#/']),
- new Assert\Length(['min' => 10]),
+ $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' => [
+ $metadata->addPropertyConstraint('grades', new Assert\AtLeastOneOf(
+ constraints: [
+ new Assert\Count(min: 3),
+ new Assert\All(
+ constraints: [
new Assert\GreaterThanOrEqual(5),
],
- ]),
+ ),
],
- ]));
+ ));
}
}
@@ -141,7 +141,7 @@ Options
constraints
~~~~~~~~~~~
-**type**: ``array`` [:ref:`default option `]
+**type**: ``array``
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.
diff --git a/reference/constraints/Bic.rst b/reference/constraints/Bic.rst
index 69ce35248f3..6cde4a11bac 100644
--- a/reference/constraints/Bic.rst
+++ b/reference/constraints/Bic.rst
@@ -121,4 +121,19 @@ Parameter Description
.. include:: /reference/constraints/_payload-option.rst.inc
+``mode``
+~~~~~~~~
+
+**type**: ``string`` **default**: ``Bic::VALIDATION_MODE_STRICT``
+
+This option defines how the BIC is validated. The possible values are available
+as constants in the :class:`Symfony\\Component\\Validator\\Constraints\\Bic` class:
+
+* ``Bic::VALIDATION_MODE_STRICT`` validates the given value without any modification;
+* ``Bic::VALIDATION_MODE_CASE_INSENSITIVE`` converts the given value to uppercase before validating it.
+
+.. versionadded:: 7.2
+
+ The ``mode`` option was introduced in Symfony 7.2.
+
.. _`Business Identifier Code (BIC)`: https://en.wikipedia.org/wiki/Business_Identifier_Code
diff --git a/reference/constraints/Callback.rst b/reference/constraints/Callback.rst
index f4c78a9642a..017b9435cff 100644
--- a/reference/constraints/Callback.rst
+++ b/reference/constraints/Callback.rst
@@ -259,7 +259,7 @@ Options
``callback``
~~~~~~~~~~~~
-**type**: ``string``, ``array`` or ``Closure`` [:ref:`default option `]
+**type**: ``string``, ``array`` or ``Closure``
The callback option accepts three different formats for specifying the
callback method:
diff --git a/reference/constraints/CardScheme.rst b/reference/constraints/CardScheme.rst
index 6e98e6fab98..a2ed9c568c3 100644
--- a/reference/constraints/CardScheme.rst
+++ b/reference/constraints/CardScheme.rst
@@ -77,12 +77,12 @@ on an object that will contain a credit card number.
{
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('cardNumber', new Assert\CardScheme([
- 'schemes' => [
+ $metadata->addPropertyConstraint('cardNumber', new Assert\CardScheme(
+ schemes: [
Assert\CardScheme::VISA,
],
- 'message' => 'Your credit card number is invalid.',
- ]));
+ message: 'Your credit card number is invalid.',
+ ));
}
}
@@ -114,7 +114,7 @@ Parameter Description
``schemes``
~~~~~~~~~~~
-**type**: ``mixed`` [:ref:`default option `]
+**type**: ``mixed``
This option is required and represents the name of the number scheme used
to validate the credit card number, it can either be a string or an array.
diff --git a/reference/constraints/Choice.rst b/reference/constraints/Choice.rst
index 5a9c365be37..72e1ae6ecf7 100644
--- a/reference/constraints/Choice.rst
+++ b/reference/constraints/Choice.rst
@@ -100,10 +100,10 @@ If your valid choice list is simple, you can pass them in directly via the
new Assert\Choice(['New York', 'Berlin', 'Tokyo'])
);
- $metadata->addPropertyConstraint('genre', new Assert\Choice([
- 'choices' => ['fiction', 'non-fiction'],
- 'message' => 'Choose a valid genre.',
- ]));
+ $metadata->addPropertyConstraint('genre', new Assert\Choice(
+ choices: ['fiction', 'non-fiction'],
+ message: 'Choose a valid genre.',
+ ));
}
}
@@ -182,9 +182,9 @@ constraint.
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('genre', new Assert\Choice([
- 'callback' => 'getGenres',
- ]));
+ $metadata->addPropertyConstraint('genre', new Assert\Choice(
+ callback: 'getGenres',
+ ));
}
}
@@ -250,9 +250,9 @@ you can pass the class name and the method as an array.
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('genre', new Assert\Choice([
- 'callback' => [Genre::class, 'getGenres'],
- ]));
+ $metadata->addPropertyConstraint('genre', new Assert\Choice(
+ callback: [Genre::class, 'getGenres'],
+ ));
}
}
@@ -271,7 +271,7 @@ to return the choices array. See
``choices``
~~~~~~~~~~~
-**type**: ``array`` [:ref:`default option `]
+**type**: ``array``
A required option (unless `callback`_ is specified) - this is the array
of options that should be considered in the valid set. The input value
@@ -304,6 +304,7 @@ Parameter Description
================= ============================================================
``{{ choices }}`` A comma-separated list of available choices
``{{ value }}`` The current (invalid) value
+``{{ limit }}`` The maximum number of selectable choices
================= ============================================================
match
@@ -358,6 +359,7 @@ Parameter Description
================= ============================================================
``{{ choices }}`` A comma-separated list of available choices
``{{ value }}`` The current (invalid) value
+``{{ limit }}`` The minimum number of selectable choices
================= ============================================================
``multiple``
@@ -381,11 +383,11 @@ is not in the array of valid choices.
You can use the following parameters in this message:
-=============== ==============================================================
-Parameter Description
-=============== ==============================================================
-``{{ value }}`` The current (invalid) value
-``{{ label }}`` Corresponding form field label
-=============== ==============================================================
+================= ============================================================
+Parameter Description
+================= ============================================================
+``{{ choices }}`` A comma-separated list of available choices
+``{{ value }}`` The current (invalid) value
+================= ============================================================
.. include:: /reference/constraints/_payload-option.rst.inc
diff --git a/reference/constraints/Collection.rst b/reference/constraints/Collection.rst
index 2d16d201b17..c35a0103581 100644
--- a/reference/constraints/Collection.rst
+++ b/reference/constraints/Collection.rst
@@ -139,8 +139,8 @@ following:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('profileData', new Assert\Collection([
- 'fields' => [
+ $metadata->addPropertyConstraint('profileData', new Assert\Collection(
+ fields: [
'personal_email' => new Assert\Email(),
'short_bio' => [
new Assert\NotBlank(),
@@ -150,8 +150,8 @@ following:
]),
],
],
- 'allowMissingFields' => true,
- ]));
+ allowMissingFields: true,
+ ));
}
}
@@ -267,15 +267,15 @@ you can do the following:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('profileData', new Assert\Collection([
- 'fields' => [
+ $metadata->addPropertyConstraint('profileData', new Assert\Collection(
+ fields: [
'personal_email' => new Assert\Required([
new Assert\NotBlank(),
new Assert\Email(),
]),
'alternate_email' => new Assert\Optional(new Assert\Email()),
],
- ]));
+ ));
}
}
@@ -291,28 +291,28 @@ groups. Take the following example::
use Symfony\Component\Validator\Constraints as Assert;
- $constraint = new Assert\Collection([
- 'fields' => [
+ $constraint = new Assert\Collection(
+ fields: [
'name' => new Assert\NotBlank(['groups' => 'basic']),
'email' => new Assert\NotBlank(['groups' => 'contact']),
],
- ]);
+ );
This will result in the following configuration::
- $constraint = new Assert\Collection([
- 'fields' => [
- 'name' => new Assert\Required([
- 'constraints' => new Assert\NotBlank(['groups' => 'basic']),
- 'groups' => ['basic', 'strict'],
- ]),
- 'email' => new Assert\Required([
- "constraints" => new Assert\NotBlank(['groups' => 'contact']),
- 'groups' => ['basic', 'strict'],
- ]),
+ $constraint = new Assert\Collection(
+ fields: [
+ 'name' => new Assert\Required(
+ constraints: new Assert\NotBlank(groups: ['basic']),
+ groups: ['basic', 'strict'],
+ ),
+ 'email' => new Assert\Required(
+ constraints: new Assert\NotBlank(groups: ['contact']),
+ groups: ['basic', 'strict'],
+ ),
],
- 'groups' => ['basic', 'strict'],
- ]);
+ groups: ['basic', 'strict'],
+ );
The default ``allowMissingFields`` option requires the fields in all groups.
So when validating in ``contact`` group, ``$name`` can be empty but the key is
@@ -360,7 +360,7 @@ Parameter Description
``fields``
~~~~~~~~~~
-**type**: ``array`` [:ref:`default option `]
+**type**: ``array``
This option is required and is an associative array defining all of the
keys in the collection and, for each key, exactly which validator(s) should
diff --git a/reference/constraints/Compound.rst b/reference/constraints/Compound.rst
index fc8081f5917..4d2c7743176 100644
--- a/reference/constraints/Compound.rst
+++ b/reference/constraints/Compound.rst
@@ -35,9 +35,9 @@ you can create your own named set or requirements to be reused consistently ever
return [
new Assert\NotBlank(),
new Assert\Type('string'),
- new Assert\Length(['min' => 12]),
+ new Assert\Length(min: 12),
new Assert\NotCompromisedPassword(),
- new Assert\PasswordStrength(['minScore' => 4]),
+ new Assert\PasswordStrength(minScore: 4),
];
}
}
@@ -102,6 +102,50 @@ You can now use it anywhere you need it:
}
}
+Validation groups and payload can be passed via constructor:
+
+.. configuration-block::
+
+ .. code-block:: php-attributes
+
+ // src/Entity/User.php
+ namespace App\Entity\User;
+
+ use App\Validator\Constraints as Assert;
+
+ class User
+ {
+ #[Assert\PasswordRequirements(
+ groups: ['registration'],
+ payload: ['severity' => 'error'],
+ )]
+ public string $plainPassword;
+ }
+
+ .. code-block:: php
+
+ // src/Entity/User.php
+ namespace App\Entity\User;
+
+ use App\Validator\Constraints as Assert;
+ use Symfony\Component\Validator\Mapping\ClassMetadata;
+
+ class User
+ {
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('plainPassword', new Assert\PasswordRequirements(
+ groups: ['registration'],
+ payload: ['severity' => 'error'],
+ ));
+ }
+ }
+
+.. versionadded:: 7.2
+
+ Support for passing validation groups and the payload to the constructor
+ of the ``Compound`` class was introduced in Symfony 7.2.
+
Options
-------
diff --git a/reference/constraints/Count.rst b/reference/constraints/Count.rst
index 0bf40aca8e9..d33c54c0812 100644
--- a/reference/constraints/Count.rst
+++ b/reference/constraints/Count.rst
@@ -82,12 +82,12 @@ you might add the following:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('emails', new Assert\Count([
- 'min' => 1,
- 'max' => 5,
- 'minMessage' => 'You must specify at least one email',
- 'maxMessage' => 'You cannot specify more than {{ limit }} emails',
- ]));
+ $metadata->addPropertyConstraint('emails', new Assert\Count(
+ min: 1,
+ max: 5,
+ minMessage: 'You must specify at least one email',
+ maxMessage: 'You cannot specify more than {{ limit }} emails',
+ ));
}
}
diff --git a/reference/constraints/CssColor.rst b/reference/constraints/CssColor.rst
index 88a4eb4be9f..b9c78ec25ac 100644
--- a/reference/constraints/CssColor.rst
+++ b/reference/constraints/CssColor.rst
@@ -2,7 +2,7 @@ CssColor
========
Validates that a value is a valid CSS color. The underlying value is
-casted to a string before being validated.
+cast to a string before being validated.
========== ===================================================================
Applies to :ref:`property or method `
@@ -110,15 +110,15 @@ the named CSS colors:
{
$metadata->addPropertyConstraint('defaultColor', new Assert\CssColor());
- $metadata->addPropertyConstraint('accentColor', new Assert\CssColor([
- 'formats' => Assert\CssColor::HEX_LONG,
- 'message' => 'The accent color must be a 6-character hexadecimal color.',
- ]));
+ $metadata->addPropertyConstraint('accentColor', new Assert\CssColor(
+ formats: Assert\CssColor::HEX_LONG,
+ message: 'The accent color must be a 6-character hexadecimal color.',
+ ));
- $metadata->addPropertyConstraint('currentColor', new Assert\CssColor([
- 'formats' => [Assert\CssColor::BASIC_NAMED_COLORS, Assert\CssColor::EXTENDED_NAMED_COLORS],
- 'message' => 'The color "{{ value }}" is not a valid CSS color name.',
- ]));
+ $metadata->addPropertyConstraint('currentColor', new Assert\CssColor(
+ formats: [Assert\CssColor::BASIC_NAMED_COLORS, Assert\CssColor::EXTENDED_NAMED_COLORS],
+ message: 'The color "{{ value }}" is not a valid CSS color name.',
+ ));
}
}
diff --git a/reference/constraints/DateTime.rst b/reference/constraints/DateTime.rst
index f6bcce7e5f5..ffcfbf55dda 100644
--- a/reference/constraints/DateTime.rst
+++ b/reference/constraints/DateTime.rst
@@ -99,11 +99,16 @@ This message is shown if the underlying data is not a valid datetime.
You can use the following parameters in this message:
-=============== ==============================================================
-Parameter Description
-=============== ==============================================================
-``{{ value }}`` The current (invalid) value
-``{{ label }}`` Corresponding form field label
-=============== ==============================================================
+================ ==============================================================
+Parameter Description
+================ ==============================================================
+``{{ value }}`` The current (invalid) value
+``{{ label }}`` Corresponding form field label
+``{{ format }}`` The date format defined in ``format``
+================ ==============================================================
+
+.. versionadded:: 7.3
+
+ The ``{{ format }}`` parameter was introduced in Symfony 7.3.
.. include:: /reference/constraints/_payload-option.rst.inc
diff --git a/reference/constraints/DivisibleBy.rst b/reference/constraints/DivisibleBy.rst
index dd90ad9a0fd..23b36023cff 100644
--- a/reference/constraints/DivisibleBy.rst
+++ b/reference/constraints/DivisibleBy.rst
@@ -92,9 +92,9 @@ The following constraints ensure that:
{
$metadata->addPropertyConstraint('weight', new Assert\DivisibleBy(0.25));
- $metadata->addPropertyConstraint('quantity', new Assert\DivisibleBy([
- 'value' => 5,
- ]));
+ $metadata->addPropertyConstraint('quantity', new Assert\DivisibleBy(
+ value: 5,
+ ));
}
}
diff --git a/reference/constraints/Email.rst b/reference/constraints/Email.rst
index 516d6d07dca..41012e5e935 100644
--- a/reference/constraints/Email.rst
+++ b/reference/constraints/Email.rst
@@ -70,9 +70,9 @@ Basic Usage
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('email', new Assert\Email([
- 'message' => 'The email "{{ value }}" is not a valid email.',
- ]));
+ $metadata->addPropertyConstraint('email', new Assert\Email(
+ message: 'The email "{{ value }}" is not a valid email.',
+ ));
}
}
diff --git a/reference/constraints/EqualTo.rst b/reference/constraints/EqualTo.rst
index d5d78f60a0f..fdc402b1a97 100644
--- a/reference/constraints/EqualTo.rst
+++ b/reference/constraints/EqualTo.rst
@@ -91,9 +91,9 @@ and that the ``age`` is ``20``, you could do the following:
{
$metadata->addPropertyConstraint('firstName', new Assert\EqualTo('Mary'));
- $metadata->addPropertyConstraint('age', new Assert\EqualTo([
- 'value' => 20,
- ]));
+ $metadata->addPropertyConstraint('age', new Assert\EqualTo(
+ value: 20,
+ ));
}
}
diff --git a/reference/constraints/Expression.rst b/reference/constraints/Expression.rst
index bf015d17573..518c5c1f160 100644
--- a/reference/constraints/Expression.rst
+++ b/reference/constraints/Expression.rst
@@ -111,10 +111,10 @@ One way to accomplish this is with the Expression constraint:
{
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addConstraint(new Assert\Expression([
- 'expression' => 'this.getCategory() in ["php", "symfony"] or !this.isTechnicalPost()',
- 'message' => 'If this is a tech post, the category should be either php or symfony!',
- ]));
+ $metadata->addConstraint(new Assert\Expression(
+ expression: 'this.getCategory() in ["php", "symfony"] or !this.isTechnicalPost()',
+ message: 'If this is a tech post, the category should be either php or symfony!',
+ ));
}
// ...
@@ -200,10 +200,10 @@ assert that the expression must return ``true`` for validation to fail.
{
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('isTechnicalPost', new Assert\Expression([
- 'expression' => 'this.getCategory() in ["php", "symfony"] or value == false',
- 'message' => 'If this is a tech post, the category should be either php or symfony!',
- ]));
+ $metadata->addPropertyConstraint('isTechnicalPost', new Assert\Expression(
+ expression: 'this.getCategory() in ["php", "symfony"] or value == false',
+ message: 'If this is a tech post, the category should be either php or symfony!',
+ ));
}
// ...
@@ -227,7 +227,7 @@ Options
``expression``
~~~~~~~~~~~~~~
-**type**: ``string`` [:ref:`default option `]
+**type**: ``string``
The expression that will be evaluated. If the expression evaluates to a false
value (using ``==``, not ``===``), validation will fail. Learn more about the
@@ -343,10 +343,10 @@ type (numeric, boolean, strings, null, etc.)
{
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('metric', new Assert\Expression([
- 'expression' => 'value + error_margin < threshold',
- 'values' => ['error_margin' => 0.25, 'threshold' => 1.5],
- ]));
+ $metadata->addPropertyConstraint('metric', new Assert\Expression(
+ expression: 'value + error_margin < threshold',
+ values: ['error_margin' => 0.25, 'threshold' => 1.5],
+ ));
}
// ...
diff --git a/reference/constraints/ExpressionSyntax.rst b/reference/constraints/ExpressionSyntax.rst
index c1d086790c1..37e0ad7de4a 100644
--- a/reference/constraints/ExpressionSyntax.rst
+++ b/reference/constraints/ExpressionSyntax.rst
@@ -90,9 +90,9 @@ The following constraints ensure that:
{
$metadata->addPropertyConstraint('promotion', new Assert\ExpressionSyntax());
- $metadata->addPropertyConstraint('shippingOptions', new Assert\ExpressionSyntax([
- 'allowedVariables' => ['user', 'shipping_centers'],
- ]));
+ $metadata->addPropertyConstraint('shippingOptions', new Assert\ExpressionSyntax(
+ allowedVariables: ['user', 'shipping_centers'],
+ ));
}
}
diff --git a/reference/constraints/File.rst b/reference/constraints/File.rst
index 6d9b72d17b8..62efa6cc08e 100644
--- a/reference/constraints/File.rst
+++ b/reference/constraints/File.rst
@@ -119,13 +119,13 @@ below a certain file size and a valid PDF, add the following:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('bioFile', new Assert\File([
- 'maxSize' => '1024k',
- 'extensions' => [
+ $metadata->addPropertyConstraint('bioFile', new Assert\File(
+ maxSize: '1024k',
+ extensions: [
'pdf',
],
- 'extensionsMessage' => 'Please upload a valid PDF',
- ]));
+ extensionsMessage: 'Please upload a valid PDF',
+ ));
}
}
@@ -274,6 +274,31 @@ You can find a list of existing mime types on the `IANA website`_.
If set, the validator will check that the filename of the underlying file
doesn't exceed a certain length.
+``filenameCountUnit``
+~~~~~~~~~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``File::FILENAME_COUNT_BYTES``
+
+The character count unit to use for the filename max length check.
+By default :phpfunction:`strlen` is used, which counts the length of the string in bytes.
+
+Can be one of the following constants of the
+:class:`Symfony\\Component\\Validator\\Constraints\\File` class:
+
+* ``FILENAME_COUNT_BYTES``: Uses :phpfunction:`strlen` counting the length of the
+ string in bytes.
+* ``FILENAME_COUNT_CODEPOINTS``: Uses :phpfunction:`mb_strlen` counting the length
+ of the string in Unicode code points. Simple (multibyte) Unicode characters count
+ as 1 character, while for example ZWJ sequences of composed emojis count as
+ multiple characters.
+* ``FILENAME_COUNT_GRAPHEMES``: Uses :phpfunction:`grapheme_strlen` counting the
+ length of the string in graphemes, i.e. even emojis and ZWJ sequences of composed
+ emojis count as 1 character.
+
+.. versionadded:: 7.3
+
+ The ``filenameCountUnit`` option was introduced in Symfony 7.3.
+
``filenameTooLongMessage``
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -290,6 +315,35 @@ Parameter Description
``{{ filename_max_length }}`` Maximum number of characters allowed
============================== ==============================================================
+``filenameCharset``
+~~~~~~~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``null``
+
+The charset to be used when computing value's filename max length with the
+:phpfunction:`mb_check_encoding` and :phpfunction:`mb_strlen`
+PHP functions.
+
+``filenameCharsetMessage``
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``This filename does not match the expected charset.``
+
+The message that will be shown if the value is not using the given `filenameCharsetMessage`_.
+
+You can use the following parameters in this message:
+
+================= ============================================================
+Parameter Description
+================= ============================================================
+``{{ charset }}`` The expected charset
+``{{ name }}`` The current (invalid) value
+================= ============================================================
+
+.. versionadded:: 7.3
+
+ The ``filenameCharset`` and ``filenameCharsetMessage`` options were introduced in Symfony 7.3.
+
``extensionsMessage``
~~~~~~~~~~~~~~~~~~~~~
diff --git a/reference/constraints/GreaterThan.rst b/reference/constraints/GreaterThan.rst
index 4f2e34bcbfa..d1b79028acd 100644
--- a/reference/constraints/GreaterThan.rst
+++ b/reference/constraints/GreaterThan.rst
@@ -89,9 +89,9 @@ The following constraints ensure that:
{
$metadata->addPropertyConstraint('siblings', new Assert\GreaterThan(5));
- $metadata->addPropertyConstraint('age', new Assert\GreaterThan([
- 'value' => 18,
- ]));
+ $metadata->addPropertyConstraint('age', new Assert\GreaterThan(
+ value: 18,
+ ));
}
}
diff --git a/reference/constraints/GreaterThanOrEqual.rst b/reference/constraints/GreaterThanOrEqual.rst
index e5a71e5f788..63c2ade6197 100644
--- a/reference/constraints/GreaterThanOrEqual.rst
+++ b/reference/constraints/GreaterThanOrEqual.rst
@@ -88,9 +88,9 @@ The following constraints ensure that:
{
$metadata->addPropertyConstraint('siblings', new Assert\GreaterThanOrEqual(5));
- $metadata->addPropertyConstraint('age', new Assert\GreaterThanOrEqual([
- 'value' => 18,
- ]));
+ $metadata->addPropertyConstraint('age', new Assert\GreaterThanOrEqual(
+ value: 18,
+ ));
}
}
diff --git a/reference/constraints/Hostname.rst b/reference/constraints/Hostname.rst
index 95b10d1736e..58ac0364669 100644
--- a/reference/constraints/Hostname.rst
+++ b/reference/constraints/Hostname.rst
@@ -72,9 +72,9 @@ will contain a host name.
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('name', new Assert\Hostname([
- 'message' => 'The server name must be a valid hostname.',
- ]));
+ $metadata->addPropertyConstraint('name', new Assert\Hostname(
+ message: 'The server name must be a valid hostname.',
+ ));
}
}
diff --git a/reference/constraints/Iban.rst b/reference/constraints/Iban.rst
index 3cf800200e2..fdc955c81b0 100644
--- a/reference/constraints/Iban.rst
+++ b/reference/constraints/Iban.rst
@@ -77,14 +77,23 @@ will contain an International Bank Account Number.
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('bankAccountNumber', new Assert\Iban([
- 'message' => 'This is not a valid International Bank Account Number (IBAN).',
- ]));
+ $metadata->addPropertyConstraint('bankAccountNumber', new Assert\Iban(
+ message: 'This is not a valid International Bank Account Number (IBAN).',
+ ));
}
}
.. include:: /reference/constraints/_empty-values-are-valid.rst.inc
+.. note::
+
+ For convenience, the IBAN validator accepts values with various types of
+ whitespace (e.g., regular, non-breaking, and narrow non-breaking spaces),
+ which are automatically removed before validation. However, this flexibility
+ can cause issues when storing IBANs or sending them to APIs that expect a
+ strict format. To ensure compatibility, normalize IBANs by removing
+ whitespace and converting them to uppercase before storing or processing.
+
Options
-------
diff --git a/reference/constraints/IdenticalTo.rst b/reference/constraints/IdenticalTo.rst
index 5b6d853dc0b..f8844f90a72 100644
--- a/reference/constraints/IdenticalTo.rst
+++ b/reference/constraints/IdenticalTo.rst
@@ -94,9 +94,9 @@ The following constraints ensure that:
{
$metadata->addPropertyConstraint('firstName', new Assert\IdenticalTo('Mary'));
- $metadata->addPropertyConstraint('age', new Assert\IdenticalTo([
- 'value' => 20,
- ]));
+ $metadata->addPropertyConstraint('age', new Assert\IdenticalTo(
+ value: 20,
+ ));
}
}
diff --git a/reference/constraints/Image.rst b/reference/constraints/Image.rst
index 042c6041423..5dd270c44f8 100644
--- a/reference/constraints/Image.rst
+++ b/reference/constraints/Image.rst
@@ -116,12 +116,12 @@ that it is between a certain size, add the following:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('headshot', new Assert\Image([
- 'minWidth' => 200,
- 'maxWidth' => 400,
- 'minHeight' => 200,
- 'maxHeight' => 400,
- ]));
+ $metadata->addPropertyConstraint('headshot', new Assert\Image(
+ minWidth: 200,
+ maxWidth: 400,
+ minHeight: 200,
+ maxHeight: 400,
+ ));
}
}
@@ -187,10 +187,10 @@ following code:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('headshot', new Assert\Image([
- 'allowLandscape' => false,
- 'allowPortrait' => false,
- ]));
+ $metadata->addPropertyConstraint('headshot', new Assert\Image(
+ allowLandscape: false,
+ allowPortrait: false,
+ ));
}
}
@@ -210,10 +210,9 @@ add several other options.
If this option is false, the image cannot be landscape oriented.
-.. note::
+.. versionadded:: 7.3
- This option does not apply to SVG files. If you use it with SVG files,
- you'll see the error message defined in the ``sizeNotDetectedMessage`` option.
+ The ``allowLandscape`` option support for SVG files was introduced in Symfony 7.3.
``allowLandscapeMessage``
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -239,10 +238,9 @@ Parameter Description
If this option is false, the image cannot be portrait oriented.
-.. note::
+.. versionadded:: 7.3
- This option does not apply to SVG files. If you use it with SVG files,
- you'll see the error message defined in the ``sizeNotDetectedMessage`` option.
+ The ``allowPortrait`` option support for SVG files was introduced in Symfony 7.3.
``allowPortraitMessage``
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -270,10 +268,9 @@ If this option is false, the image cannot be a square. If you want to force
a square image, then leave this option as its default ``true`` value
and set `allowLandscape`_ and `allowPortrait`_ both to ``false``.
-.. note::
+.. versionadded:: 7.3
- This option does not apply to SVG files. If you use it with SVG files,
- you'll see the error message defined in the ``sizeNotDetectedMessage`` option.
+ The ``allowSquare`` option support for SVG files was introduced in Symfony 7.3.
``allowSquareMessage``
~~~~~~~~~~~~~~~~~~~~~~
@@ -373,10 +370,9 @@ Parameter Description
If set, the aspect ratio (``width / height``) of the image file must be less
than or equal to this value.
-.. note::
+.. versionadded:: 7.3
- This option does not apply to SVG files. If you use it with SVG files,
- you'll see the error message defined in the ``sizeNotDetectedMessage`` option.
+ The ``maxRatio`` option support for SVG files was introduced in Symfony 7.3.
``maxRatioMessage``
~~~~~~~~~~~~~~~~~~~
@@ -497,10 +493,9 @@ Parameter Description
If set, the aspect ratio (``width / height``) of the image file must be greater
than or equal to this value.
-.. note::
+.. versionadded:: 7.3
- This option does not apply to SVG files. If you use it with SVG files,
- you'll see the error message defined in the ``sizeNotDetectedMessage`` option.
+ The ``minRatio`` option support for SVG files was introduced in Symfony 7.3.
``minRatioMessage``
~~~~~~~~~~~~~~~~~~~
@@ -555,11 +550,5 @@ options has been set.
This message has no parameters.
-.. note::
-
- Detecting the size of SVG images is not supported. This error message will
- be displayed if you use any of the following options: ``allowLandscape``,
- ``allowPortrait``, ``allowSquare``, ``maxRatio``, and ``minRatio``.
-
.. _`IANA website`: https://www.iana.org/assignments/media-types/media-types.xhtml
.. _`PHP GD extension`: https://www.php.net/manual/en/book.image.php
diff --git a/reference/constraints/IsFalse.rst b/reference/constraints/IsFalse.rst
index 0b9ebe77491..3d0a1665944 100644
--- a/reference/constraints/IsFalse.rst
+++ b/reference/constraints/IsFalse.rst
@@ -93,9 +93,9 @@ method returns **false**:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addGetterConstraint('stateInvalid', new Assert\IsFalse([
- 'message' => "You've entered an invalid state.",
- ]));
+ $metadata->addGetterConstraint('stateInvalid', new Assert\IsFalse(
+ message: "You've entered an invalid state.",
+ ));
}
public function isStateInvalid(): bool
diff --git a/reference/constraints/IsTrue.rst b/reference/constraints/IsTrue.rst
index 678371f6e69..b50ba4f3e8b 100644
--- a/reference/constraints/IsTrue.rst
+++ b/reference/constraints/IsTrue.rst
@@ -97,9 +97,9 @@ Then you can validate this method with ``IsTrue`` as follows:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addGetterConstraint('tokenValid', new IsTrue([
- 'message' => 'The token is invalid.',
- ]));
+ $metadata->addGetterConstraint('tokenValid', new IsTrue(
+ message: 'The token is invalid.',
+ ));
}
public function isTokenValid(): bool
diff --git a/reference/constraints/Isbn.rst b/reference/constraints/Isbn.rst
index 954bff233d5..52d10565fe5 100644
--- a/reference/constraints/Isbn.rst
+++ b/reference/constraints/Isbn.rst
@@ -76,10 +76,10 @@ on an object that will contain an ISBN.
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('isbn', new Assert\Isbn([
- 'type' => Assert\Isbn::ISBN_10,
- 'message' => 'This value is not valid.',
- ]));
+ $metadata->addPropertyConstraint('isbn', new Assert\Isbn(
+ type: Assert\Isbn::ISBN_10,
+ message: 'This value is not valid.',
+ ));
}
}
diff --git a/reference/constraints/Json.rst b/reference/constraints/Json.rst
index 28e15976f3c..337b2dc6a1e 100644
--- a/reference/constraints/Json.rst
+++ b/reference/constraints/Json.rst
@@ -69,9 +69,9 @@ The ``Json`` constraint can be applied to a property or a "getter" method:
{
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('chapters', new Assert\Json([
- 'message' => 'You\'ve entered an invalid Json.',
- ]));
+ $metadata->addPropertyConstraint('chapters', new Assert\Json(
+ message: 'You\'ve entered an invalid Json.',
+ ));
}
}
diff --git a/reference/constraints/Length.rst b/reference/constraints/Length.rst
index 9a4478f509b..c1a8575070b 100644
--- a/reference/constraints/Length.rst
+++ b/reference/constraints/Length.rst
@@ -85,12 +85,12 @@ and ``50``, you might add the following:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('firstName', new Assert\Length([
- 'min' => 2,
- 'max' => 50,
- 'minMessage' => 'Your first name must be at least {{ limit }} characters long',
- 'maxMessage' => 'Your first name cannot be longer than {{ limit }} characters',
- ]));
+ $metadata->addPropertyConstraint('firstName', new Assert\Length(
+ min: 2,
+ max: 50,
+ minMessage: 'Your first name must be at least {{ limit }} characters long',
+ maxMessage: 'Your first name cannot be longer than {{ limit }} characters',
+ ));
}
}
diff --git a/reference/constraints/LessThan.rst b/reference/constraints/LessThan.rst
index 964bfbb3527..3d23bcda445 100644
--- a/reference/constraints/LessThan.rst
+++ b/reference/constraints/LessThan.rst
@@ -89,9 +89,9 @@ The following constraints ensure that:
{
$metadata->addPropertyConstraint('siblings', new Assert\LessThan(5));
- $metadata->addPropertyConstraint('age', new Assert\LessThan([
- 'value' => 80,
- ]));
+ $metadata->addPropertyConstraint('age', new Assert\LessThan(
+ value: 80,
+ ));
}
}
diff --git a/reference/constraints/LessThanOrEqual.rst b/reference/constraints/LessThanOrEqual.rst
index 9420c3e4376..ac66c62d7d0 100644
--- a/reference/constraints/LessThanOrEqual.rst
+++ b/reference/constraints/LessThanOrEqual.rst
@@ -88,9 +88,9 @@ The following constraints ensure that:
{
$metadata->addPropertyConstraint('siblings', new Assert\LessThanOrEqual(5));
- $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual([
- 'value' => 80,
- ]));
+ $metadata->addPropertyConstraint('age', new Assert\LessThanOrEqual(
+ value: 80,
+ ));
}
}
diff --git a/reference/constraints/Locale.rst b/reference/constraints/Locale.rst
index 49edd473d05..4bba45ae12b 100644
--- a/reference/constraints/Locale.rst
+++ b/reference/constraints/Locale.rst
@@ -78,9 +78,9 @@ Basic Usage
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('locale', new Assert\Locale([
- 'canonicalize' => true,
- ]));
+ $metadata->addPropertyConstraint('locale', new Assert\Locale(
+ canonicalize: true,
+ ));
}
}
diff --git a/reference/constraints/Luhn.rst b/reference/constraints/Luhn.rst
index 8f5ef34c4ba..0c835204091 100644
--- a/reference/constraints/Luhn.rst
+++ b/reference/constraints/Luhn.rst
@@ -72,9 +72,9 @@ will contain a credit card number.
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('cardNumber', new Assert\Luhn([
- 'message' => 'Please check your credit card number',
- ]));
+ $metadata->addPropertyConstraint('cardNumber', new Assert\Luhn(
+ message: 'Please check your credit card number',
+ ));
}
}
diff --git a/reference/constraints/NotEqualTo.rst b/reference/constraints/NotEqualTo.rst
index b8ee4cac32f..dd3f633b4a1 100644
--- a/reference/constraints/NotEqualTo.rst
+++ b/reference/constraints/NotEqualTo.rst
@@ -93,9 +93,9 @@ the following:
{
$metadata->addPropertyConstraint('firstName', new Assert\NotEqualTo('Mary'));
- $metadata->addPropertyConstraint('age', new Assert\NotEqualTo([
- 'value' => 15,
- ]));
+ $metadata->addPropertyConstraint('age', new Assert\NotEqualTo(
+ value: 15,
+ ));
}
}
diff --git a/reference/constraints/NotIdenticalTo.rst b/reference/constraints/NotIdenticalTo.rst
index 9ea93dc4b86..b2c20027292 100644
--- a/reference/constraints/NotIdenticalTo.rst
+++ b/reference/constraints/NotIdenticalTo.rst
@@ -94,9 +94,9 @@ The following constraints ensure that:
{
$metadata->addPropertyConstraint('age', new Assert\NotIdenticalTo('Mary'));
- $metadata->addPropertyConstraint('age', new Assert\NotIdenticalTo([
- 'value' => 15,
- ]));
+ $metadata->addPropertyConstraint('age', new Assert\NotIdenticalTo(
+ value: 15,
+ ));
}
}
diff --git a/reference/constraints/PasswordStrength.rst b/reference/constraints/PasswordStrength.rst
index 989ffddd100..0b242cacf08 100644
--- a/reference/constraints/PasswordStrength.rst
+++ b/reference/constraints/PasswordStrength.rst
@@ -101,9 +101,9 @@ or by a custom password strength estimator.
class User
{
- #[Assert\PasswordStrength([
- 'minScore' => PasswordStrength::STRENGTH_VERY_STRONG, // Very strong password required
- ])]
+ #[Assert\PasswordStrength(
+ minScore: PasswordStrength::STRENGTH_VERY_STRONG, // Very strong password required
+ )]
protected $rawPassword;
}
@@ -123,8 +123,92 @@ The default message supplied when the password does not reach the minimum requir
class User
{
- #[Assert\PasswordStrength([
- 'message' => 'Your password is too easy to guess. Company\'s security policy requires to use a stronger password.'
- ])]
+ #[Assert\PasswordStrength(
+ message: 'Your password is too easy to guess. Company\'s security policy requires to use a stronger password.'
+ )]
protected $rawPassword;
}
+
+Customizing the Password Strength Estimation
+--------------------------------------------
+
+.. versionadded:: 7.2
+
+ The feature to customize the password strength estimation was introduced in Symfony 7.2.
+
+By default, this constraint calculates the strength of a password based on its
+length and the number of unique characters used. You can get the calculated
+password strength (e.g. to display it in the user interface) using the following
+static function::
+
+ use Symfony\Component\Validator\Constraints\PasswordStrengthValidator;
+
+ $passwordEstimatedStrength = PasswordStrengthValidator::estimateStrength($password);
+
+If you need to override the default password strength estimation algorithm, you
+can pass a ``Closure`` to the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator`
+constructor (e.g. using the :doc:`service closures `).
+
+First, create a custom password strength estimation algorithm within a dedicated
+callable class::
+
+ namespace App\Validator;
+
+ class CustomPasswordStrengthEstimator
+ {
+ /**
+ * @return PasswordStrength::STRENGTH_*
+ */
+ public function __invoke(string $password): int
+ {
+ // Your custom password strength estimation algorithm
+ }
+ }
+
+Then, configure the :class:`Symfony\\Component\\Validator\\Constraints\\PasswordStrengthValidator`
+service to use your own estimator:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/services.yaml
+ services:
+ custom_password_strength_estimator:
+ class: App\Validator\CustomPasswordStrengthEstimator
+
+ Symfony\Component\Validator\Constraints\PasswordStrengthValidator:
+ arguments: [!closure '@custom_password_strength_estimator']
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+ use Symfony\Component\Validator\Constraints\PasswordStrengthValidator;
+
+ return function (ContainerConfigurator $container): void {
+ $services = $container->services();
+
+ $services->set('custom_password_strength_estimator', CustomPasswordStrengthEstimator::class);
+
+ $services->set(PasswordStrengthValidator::class)
+ ->args([closure('custom_password_strength_estimator')]);
+ };
diff --git a/reference/constraints/Range.rst b/reference/constraints/Range.rst
index edd199c48b9..46a9e3799b3 100644
--- a/reference/constraints/Range.rst
+++ b/reference/constraints/Range.rst
@@ -78,11 +78,11 @@ you might add the following:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('height', new Assert\Range([
- 'min' => 120,
- 'max' => 180,
- 'notInRangeMessage' => 'You must be between {{ min }}cm and {{ max }}cm tall to enter',
- ]));
+ $metadata->addPropertyConstraint('height', new Assert\Range(
+ min: 120,
+ max: 180,
+ notInRangeMessage: 'You must be between {{ min }}cm and {{ max }}cm tall to enter',
+ ));
}
}
@@ -154,10 +154,10 @@ date must lie within the current year like this:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('startDate', new Assert\Range([
- 'min' => 'first day of January',
- 'max' => 'first day of January next year',
- ]));
+ $metadata->addPropertyConstraint('startDate', new Assert\Range(
+ min: 'first day of January',
+ max: 'first day of January next year',
+ ));
}
}
@@ -224,10 +224,10 @@ dates. If you want to fix the timezone, append it to the date string:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('startDate', new Assert\Range([
- 'min' => 'first day of January UTC',
- 'max' => 'first day of January next year UTC',
- ]));
+ $metadata->addPropertyConstraint('startDate', new Assert\Range(
+ min: 'first day of January UTC',
+ max: 'first day of January next year UTC',
+ ));
}
}
@@ -294,10 +294,10 @@ can check that a delivery date starts within the next five hours like this:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('deliveryDate', new Assert\Range([
- 'min' => 'now',
- 'max' => '+5 hours',
- ]));
+ $metadata->addPropertyConstraint('deliveryDate', new Assert\Range(
+ min: 'now',
+ max: '+5 hours',
+ ));
}
}
diff --git a/reference/constraints/Regex.rst b/reference/constraints/Regex.rst
index 2e11a8d04fc..e3b4d4711b2 100644
--- a/reference/constraints/Regex.rst
+++ b/reference/constraints/Regex.rst
@@ -71,9 +71,9 @@ more word characters at the beginning of your string:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('description', new Assert\Regex([
- 'pattern' => '/^\w+/',
- ]));
+ $metadata->addPropertyConstraint('description', new Assert\Regex(
+ pattern: '/^\w+/',
+ ));
}
}
@@ -145,11 +145,11 @@ it a custom message:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('firstName', new Assert\Regex([
- 'pattern' => '/\d/',
- 'match' => false,
- 'message' => 'Your name cannot contain a number',
- ]));
+ $metadata->addPropertyConstraint('firstName', new Assert\Regex(
+ pattern: '/\d/',
+ match: false,
+ message: 'Your name cannot contain a number',
+ ));
}
}
@@ -236,10 +236,10 @@ need to specify the HTML5 compatible pattern in the ``htmlPattern`` option:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('name', new Assert\Regex([
- 'pattern' => '/^[a-z]+$/i',
- 'htmlPattern' => '[a-zA-Z]+',
- ]));
+ $metadata->addPropertyConstraint('name', new Assert\Regex(
+ pattern: '/^[a-z]+$/i',
+ htmlPattern: '[a-zA-Z]+',
+ ));
}
}
@@ -275,7 +275,7 @@ Parameter Description
``pattern``
~~~~~~~~~~~
-**type**: ``string`` [:ref:`default option `]
+**type**: ``string``
This required option is the regular expression pattern that the input will
be matched against. By default, this validator will fail if the input string
diff --git a/reference/constraints/Sequentially.rst b/reference/constraints/Sequentially.rst
index 7620997f0a3..078be338cdf 100644
--- a/reference/constraints/Sequentially.rst
+++ b/reference/constraints/Sequentially.rst
@@ -110,7 +110,7 @@ You can validate each of these constraints sequentially to solve these issues:
$metadata->addPropertyConstraint('address', new Assert\Sequentially([
new Assert\NotNull(),
new Assert\Type('string'),
- new Assert\Length(['min' => 10]),
+ new Assert\Length(min: 10),
new Assert\Regex(self::ADDRESS_REGEX),
new AcmeAssert\Geolocalizable(),
]));
@@ -123,7 +123,7 @@ Options
``constraints``
~~~~~~~~~~~~~~~
-**type**: ``array`` [:ref:`default option `]
+**type**: ``array``
This required option is the array of validation constraints that you want
to apply sequentially.
diff --git a/reference/constraints/Twig.rst b/reference/constraints/Twig.rst
new file mode 100644
index 00000000000..e38b4507d7a
--- /dev/null
+++ b/reference/constraints/Twig.rst
@@ -0,0 +1,130 @@
+Twig Constraint
+===============
+
+.. versionadded:: 7.3
+
+ The ``Twig`` constraint was introduced in Symfony 7.3.
+
+Validates that a given string contains valid :ref:`Twig syntax `.
+This is particularly useful when template content is user-generated or
+configurable, and you want to ensure it can be rendered by the Twig engine.
+
+.. note::
+
+ Using this constraint requires having the ``symfony/twig-bridge`` package
+ installed in your application (e.g. by running ``composer require symfony/twig-bridge``).
+
+========== ===================================================================
+Applies to :ref:`property or method `
+Class :class:`Symfony\\Bridge\\Twig\\Validator\\Constraints\\Twig`
+Validator :class:`Symfony\\Bridge\\Twig\\Validator\\Constraints\\TwigValidator`
+========== ===================================================================
+
+Basic Usage
+-----------
+
+Apply the ``Twig`` constraint to validate the contents of any property or the
+returned value of any method::
+
+ use Symfony\Bridge\Twig\Validator\Constraints\Twig;
+
+ class Template
+ {
+ #[Twig]
+ private string $templateCode;
+ }
+
+.. configuration-block::
+
+ .. code-block:: php-attributes
+
+ // src/Entity/Page.php
+ namespace App\Entity;
+
+ use Symfony\Bridge\Twig\Validator\Constraints\Twig;
+
+ class Page
+ {
+ #[Twig]
+ private string $templateCode;
+ }
+
+ .. code-block:: yaml
+
+ # config/validator/validation.yaml
+ App\Entity\Page:
+ properties:
+ templateCode:
+ - Symfony\Bridge\Twig\Validator\Constraints\Twig: ~
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // src/Entity/Page.php
+ namespace App\Entity;
+
+ use Symfony\Bridge\Twig\Validator\Constraints\Twig;
+ use Symfony\Component\Validator\Mapping\ClassMetadata;
+
+ class Page
+ {
+ // ...
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('templateCode', new Twig());
+ }
+ }
+
+Constraint Options
+------------------
+
+``message``
+~~~~~~~~~~~
+
+**type**: ``message`` **default**: ``This value is not a valid Twig template.``
+
+This is the message displayed when the given string does *not* contain valid Twig syntax::
+
+ // ...
+
+ class Page
+ {
+ #[Twig(message: 'Check this Twig code; it contains errors.')]
+ private string $templateCode;
+ }
+
+This message has no parameters.
+
+``skipDeprecations``
+~~~~~~~~~~~~~~~~~~~~
+
+**type**: ``boolean`` **default**: ``true``
+
+If ``true``, Twig deprecation warnings are ignored during validation. Set it to
+``false`` to trigger validation errors when the given Twig code contains any deprecations::
+
+ // ...
+
+ class Page
+ {
+ #[Twig(skipDeprecations: false)]
+ private string $templateCode;
+ }
+
+This can be helpful when enforcing stricter template rules or preparing for major
+Twig version upgrades.
diff --git a/reference/constraints/Type.rst b/reference/constraints/Type.rst
index b99e8ce1c54..b49536dff8b 100644
--- a/reference/constraints/Type.rst
+++ b/reference/constraints/Type.rst
@@ -127,14 +127,14 @@ The following example checks if ``emailAddress`` is an instance of ``Symfony\Com
$metadata->addPropertyConstraint('firstName', new Assert\Type('string'));
- $metadata->addPropertyConstraint('age', new Assert\Type([
- 'type' => 'integer',
- 'message' => 'The value {{ value }} is not a valid {{ type }}.',
- ]));
-
- $metadata->addPropertyConstraint('accessCode', new Assert\Type([
- 'type' => ['alpha', 'digit'],
- ]));
+ $metadata->addPropertyConstraint('age', new Assert\Type(
+ type: 'integer',
+ message: 'The value {{ value }} is not a valid {{ type }}.',
+ ));
+
+ $metadata->addPropertyConstraint('accessCode', new Assert\Type(
+ type: ['alpha', 'digit'],
+ ));
}
}
@@ -169,7 +169,7 @@ Parameter Description
``type``
~~~~~~~~
-**type**: ``string`` or ``array`` [:ref:`default option `]
+**type**: ``string`` or ``array``
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
diff --git a/reference/constraints/Ulid.rst b/reference/constraints/Ulid.rst
index ed7dfe7ed96..4094bab98f5 100644
--- a/reference/constraints/Ulid.rst
+++ b/reference/constraints/Ulid.rst
@@ -73,6 +73,21 @@ Basic Usage
Options
-------
+``format``
+~~~~~~~~~~
+
+**type**: ``string`` **default**: ``Ulid::FORMAT_BASE_32``
+
+The format of the ULID to validate. The following formats are available:
+
+* ``Ulid::FORMAT_BASE_32``: The ULID is encoded in `base32`_ (default)
+* ``Ulid::FORMAT_BASE_58``: The ULID is encoded in `base58`_
+* ``Ulid::FORMAT_RFC4122``: The ULID is encoded in the `RFC 4122 format`_
+
+.. versionadded:: 7.2
+
+ The ``format`` option was introduced in Symfony 7.2.
+
.. include:: /reference/constraints/_groups-option.rst.inc
``message``
@@ -96,3 +111,6 @@ Parameter Description
.. include:: /reference/constraints/_payload-option.rst.inc
.. _`Universally Unique Lexicographically Sortable Identifier (ULID)`: https://github.com/ulid/spec
+.. _`base32`: https://en.wikipedia.org/wiki/Base32
+.. _`base58`: https://en.wikipedia.org/wiki/Binary-to-text_encoding#Base58
+.. _`RFC 4122 format`: https://datatracker.ietf.org/doc/html/rfc4122
diff --git a/reference/constraints/Unique.rst b/reference/constraints/Unique.rst
index 8954f455086..9ce84139cd5 100644
--- a/reference/constraints/Unique.rst
+++ b/reference/constraints/Unique.rst
@@ -162,14 +162,30 @@ collection::
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('coordinates', new Assert\Unique([
- 'fields' => ['latitude', 'longitude'],
- ]));
+ $metadata->addPropertyConstraint('coordinates', new Assert\Unique(
+ fields: ['latitude', 'longitude'],
+ ));
}
}
.. include:: /reference/constraints/_groups-option.rst.inc
+``errorPath``
+~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``null``
+
+.. versionadded:: 7.2
+
+ The ``errorPath`` option was introduced in Symfony 7.2.
+
+If a validation error occurs, the error message is, by default, bound to the
+first element in the collection. Use this option to bind the error message to a
+specific field within the first item of the collection.
+
+The value of this option must use any :doc:`valid PropertyAccess syntax `
+(e.g. ``'point_of_interest'``, ``'user.email'``).
+
``message``
~~~~~~~~~~~
@@ -200,4 +216,17 @@ trailing whitespace during validation.
.. include:: /reference/constraints/_payload-option.rst.inc
+``stopOnFirstError``
+~~~~~~~~~~~~~~~~~~~~
+
+**type**: ``boolean`` **default**: ``true``
+
+By default, this constraint stops at the first violation. If this option is set
+to ``false``, validation continues on all elements and returns all detected
+:class:`Symfony\\Component\\Validator\\ConstraintViolation` objects.
+
+.. versionadded:: 7.3
+
+ The ``stopOnFirstError`` option was introduced in Symfony 7.3.
+
.. _`PHP callable`: https://www.php.net/callable
diff --git a/reference/constraints/UniqueEntity.rst b/reference/constraints/UniqueEntity.rst
index d4fbfeb8666..0ab2c0a8cbd 100644
--- a/reference/constraints/UniqueEntity.rst
+++ b/reference/constraints/UniqueEntity.rst
@@ -95,9 +95,9 @@ between all of the rows in your user table:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addConstraint(new UniqueEntity([
- 'fields' => 'email',
- ]));
+ $metadata->addConstraint(new UniqueEntity(
+ fields: 'email',
+ ));
$metadata->addPropertyConstraint('email', new Assert\Email());
}
@@ -260,7 +260,7 @@ Now, the message would be bound to the ``port`` field with this configuration.
``fields``
~~~~~~~~~~
-**type**: ``array`` | ``string`` [:ref:`default option `]
+**type**: ``array`` | ``string``
This required option is the field (or list of fields) on which this entity
should be unique. For example, if you specified both the ``email`` and ``name``
@@ -277,7 +277,7 @@ each with a single field.
``ignoreNull``
~~~~~~~~~~~~~~
-**type**: ``boolean`` | ``string`` | ``array`` **default**: ``true``
+**type**: ``boolean``, ``string`` or ``array`` **default**: ``true``
If this option is set to ``true``, then the constraint will allow multiple
entities to have a ``null`` value for a field without failing validation.
@@ -346,10 +346,10 @@ this option to specify one or more fields to only ignore ``null`` values on them
{
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
- $metadata->addConstraint(new UniqueEntity([
- 'fields' => ['email', 'phoneNumber'],
- 'ignoreNull' => 'phoneNumber',
- ]));
+ $metadata->addConstraint(new UniqueEntity(
+ fields: ['email', 'phoneNumber'],
+ ignoreNull: 'phoneNumber',
+ ));
// ...
}
diff --git a/reference/constraints/Url.rst b/reference/constraints/Url.rst
index 74f0d750dfd..c3fac520f96 100644
--- a/reference/constraints/Url.rst
+++ b/reference/constraints/Url.rst
@@ -152,9 +152,9 @@ Parameter Description
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('bioUrl', new Assert\Url([
- 'message' => 'The url "{{ value }}" is not a valid url.',
- ]));
+ $metadata->addPropertyConstraint('bioUrl', new Assert\Url(
+ message: 'The url "{{ value }}" is not a valid url.',
+ ));
}
}
@@ -231,9 +231,9 @@ the ``ftp://`` type URLs to be valid, redefine the ``protocols`` array, listing
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('bioUrl', new Assert\Url([
- 'protocols' => ['http', 'https', 'ftp'],
- ]));
+ $metadata->addPropertyConstraint('bioUrl', new Assert\Url(
+ protocols: ['http', 'https', 'ftp'],
+ ));
}
}
@@ -302,9 +302,9 @@ also relative URLs that contain no protocol (e.g. ``//example.com``).
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('bioUrl', new Assert\Url([
- 'relativeProtocol' => true,
- ]));
+ $metadata->addPropertyConstraint('bioUrl', new Assert\Url(
+ relativeProtocol: true,
+ ));
}
}
@@ -323,7 +323,7 @@ also relative URLs that contain no protocol (e.g. ``//example.com``).
and will default to ``true`` in Symfony 8.0.
By default, URLs like ``https://aaa`` or ``https://foobar`` are considered valid
-because they are tecnically correct according to the `URL spec`_. If you set this option
+because they are technically correct according to the `URL spec`_. If you set this option
to ``true``, the host part of the URL will have to include a TLD (top-level domain
name): e.g. ``https://example.com`` will be valid but ``https://example`` won't.
@@ -414,10 +414,10 @@ Parameter Description
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('homepageUrl', new Assert\Url([
- 'requireTld' => true,
- 'tldMessage' => 'Add at least one TLD to the {{ value }} URL.',
- ]));
+ $metadata->addPropertyConstraint('homepageUrl', new Assert\Url(
+ requireTld: true,
+ tldMessage: 'Add at least one TLD to the {{ value }} URL.',
+ ));
}
}
diff --git a/reference/constraints/Valid.rst b/reference/constraints/Valid.rst
index 1f99b666419..61a2c1d992c 100644
--- a/reference/constraints/Valid.rst
+++ b/reference/constraints/Valid.rst
@@ -149,7 +149,7 @@ stores an ``Address`` instance in the ``$address`` property::
{
$metadata->addPropertyConstraint('street', new Assert\NotBlank());
$metadata->addPropertyConstraint('zipCode', new Assert\NotBlank());
- $metadata->addPropertyConstraint('zipCode', new Assert\Length(['max' => 5]));
+ $metadata->addPropertyConstraint('zipCode', new Assert\Length(max: 5));
}
}
@@ -166,7 +166,7 @@ stores an ``Address`` instance in the ``$address`` property::
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
$metadata->addPropertyConstraint('firstName', new Assert\NotBlank());
- $metadata->addPropertyConstraint('firstName', new Assert\Length(['min' => 4]));
+ $metadata->addPropertyConstraint('firstName', new Assert\Length(min: 4));
$metadata->addPropertyConstraint('lastName', new Assert\NotBlank());
}
}
diff --git a/reference/constraints/Week.rst b/reference/constraints/Week.rst
new file mode 100644
index 00000000000..b3c1b0ca122
--- /dev/null
+++ b/reference/constraints/Week.rst
@@ -0,0 +1,172 @@
+Week
+====
+
+.. versionadded:: 7.2
+
+ The ``Week`` constraint was introduced in Symfony 7.2.
+
+Validates that a given string (or an object implementing the ``Stringable`` PHP
+interface) represents a valid week number according to the `ISO-8601`_ standard
+(e.g. ``2025-W01``).
+
+========== =======================================================================
+Applies to :ref:`property or method `
+Class :class:`Symfony\\Component\\Validator\\Constraints\\Week`
+Validator :class:`Symfony\\Component\\Validator\\Constraints\\WeekValidator`
+========== =======================================================================
+
+Basic Usage
+-----------
+
+If you wanted to ensure that the ``startWeek`` property of an ``OnlineCourse``
+class is between the first and the twentieth week of the year 2022, you could do
+the following:
+
+.. configuration-block::
+
+ .. code-block:: php-attributes
+
+ // src/Entity/OnlineCourse.php
+ namespace App\Entity;
+
+ use Symfony\Component\Validator\Constraints as Assert;
+
+ class OnlineCourse
+ {
+ #[Assert\Week(min: '2022-W01', max: '2022-W20')]
+ protected string $startWeek;
+ }
+
+ .. code-block:: yaml
+
+ # config/validator/validation.yaml
+ App\Entity\OnlineCourse:
+ properties:
+ startWeek:
+ - Week:
+ min: '2022-W01'
+ max: '2022-W20'
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // src/Entity/OnlineCourse.php
+ namespace App\Entity;
+
+ use Symfony\Component\Validator\Constraints as Assert;
+ use Symfony\Component\Validator\Mapping\ClassMetadata;
+
+ class OnlineCourse
+ {
+ // ...
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('startWeek', new Assert\Week(
+ min: '2022-W01',
+ max: '2022-W20',
+ ));
+ }
+ }
+
+This constraint not only checks that the value matches the week number pattern,
+but it also verifies that the specified week actually exists in the calendar.
+According to the ISO-8601 standard, years can have either 52 or 53 weeks. For example,
+``2022-W53`` is not valid because 2022 only had 52 weeks; but ``2020-W53`` is
+valid because 2020 had 53 weeks.
+
+Options
+-------
+
+``min``
+~~~~~~~
+
+**type**: ``string`` **default**: ``null``
+
+The minimum week number that the value must match.
+
+``max``
+~~~~~~~
+
+**type**: ``string`` **default**: ``null``
+
+The maximum week number that the value must match.
+
+.. include:: /reference/constraints/_groups-option.rst.inc
+
+``invalidFormatMessage``
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``This value does not represent a valid week in the ISO 8601 format.``
+
+This is the message that will be shown if the value does not match the ISO 8601
+week format.
+
+``invalidWeekNumberMessage``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``The week "{{ value }}" is not a valid week.``
+
+This is the message that will be shown if the value does not match a valid week
+number.
+
+You can use the following parameters in this message:
+
+================ ==================================================
+Parameter Description
+================ ==================================================
+``{{ value }}`` The value that was passed to the constraint
+================ ==================================================
+
+``tooLowMessage``
+~~~~~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``The value should not be before week "{{ min }}".``
+
+This is the message that will be shown if the value is lower than the minimum
+week number.
+
+You can use the following parameters in this message:
+
+================ ==================================================
+Parameter Description
+================ ==================================================
+``{{ min }}`` The minimum week number
+================ ==================================================
+
+``tooHighMessage``
+~~~~~~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``The value should not be after week "{{ max }}".``
+
+This is the message that will be shown if the value is higher than the maximum
+week number.
+
+You can use the following parameters in this message:
+
+================ ==================================================
+Parameter Description
+================ ==================================================
+``{{ max }}`` The maximum week number
+================ ==================================================
+
+.. include:: /reference/constraints/_payload-option.rst.inc
+
+.. _`ISO-8601`: https://en.wikipedia.org/wiki/ISO_8601
diff --git a/reference/constraints/When.rst b/reference/constraints/When.rst
index e1e8ac895ce..6eca8b4895f 100644
--- a/reference/constraints/When.rst
+++ b/reference/constraints/When.rst
@@ -9,6 +9,7 @@ Applies to :ref:`class `
or :ref:`property/method `
Options - `expression`_
- `constraints`_
+ _ `otherwise`_
- `groups`_
- `payload`_
- `values`_
@@ -47,7 +48,7 @@ properties::
To validate the object, you have some requirements:
A) If ``type`` is ``percent``, then ``value`` must be less than or equal 100;
-B) If ``type`` is ``absolute``, then ``value`` can be anything;
+B) If ``type`` is not ``percent``, then ``value`` must be less than 9999;
C) No matter the value of ``type``, the ``value`` must be greater than 0.
One way to accomplish this is with the When constraint:
@@ -69,6 +70,9 @@ One way to accomplish this is with the When constraint:
constraints: [
new Assert\LessThanOrEqual(100, message: 'The value should be between 1 and 100!')
],
+ otherwise: [
+ new Assert\LessThan(9999, message: 'The value should be less than 9999!')
+ ],
)]
private ?int $value;
@@ -88,6 +92,10 @@ One way to accomplish this is with the When constraint:
- LessThanOrEqual:
value: 100
message: "The value should be between 1 and 100!"
+ otherwise:
+ - LessThan:
+ value: 9999
+ message: "The value should be less than 9999!"
.. code-block:: xml
@@ -109,6 +117,12 @@ One way to accomplish this is with the When constraint:
+
@@ -127,15 +141,21 @@ One way to accomplish this is with the When constraint:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
$metadata->addPropertyConstraint('value', new Assert\GreaterThan(0));
- $metadata->addPropertyConstraint('value', new Assert\When([
- 'expression' => 'this.getType() == "percent"',
- 'constraints' => [
- new Assert\LessThanOrEqual([
- 'value' => 100,
- 'message' => 'The value should be between 1 and 100!',
- ]),
+ $metadata->addPropertyConstraint('value', new Assert\When(
+ expression: 'this.getType() == "percent"',
+ constraints: [
+ new Assert\LessThanOrEqual(
+ value: 100,
+ message: 'The value should be between 1 and 100!',
+ ),
],
- ]));
+ otherwise: [
+ new Assert\LessThan(
+ value: 9999,
+ message: 'The value should be less than 9999!',
+ ),
+ ],
+ ));
}
// ...
@@ -154,26 +174,36 @@ Options
``expression``
~~~~~~~~~~~~~~
-**type**: ``string``
-
-The condition written with the expression language syntax that will be evaluated.
-If the expression evaluates to a falsey value (i.e. using ``==``, not ``===``),
-validation of constraints won't be triggered.
+**type**: ``string|Closure``
-To learn more about the expression language syntax, see
-:doc:`/reference/formats/expression_language`.
+The condition evaluated to decide if the constraint is applied or not. It can be
+defined as a closure or a string using the :doc:`expression language syntax `.
+If the result is a falsey value (``false``, ``null``, ``0``, an empty string or
+an empty array) the constraints defined in the ``constraints`` option won't be
+applied but the constraints defined in ``otherwise`` option (if provided) will be applied.
-Depending on how you use the constraint, you have access to 1 or 2 variables
-in your expression:
+**When using an expression**, you access to the following variables:
``this``
The object being validated (e.g. an instance of Discount).
``value``
The value of the property being validated (only available when
the constraint is applied to a property).
+``context``
+ The :class:`Symfony\\Component\\Validator\\Context\\ExecutionContextInterface`
+ object that provides information such as the currently validated class, the
+ name of the currently validated property, the list of violations, etc.
-The ``value`` variable can be used when you want to execute more complex
-validation based on its value:
+.. versionadded:: 7.2
+
+ The ``context`` variable in expressions was introduced in Symfony 7.2.
+
+**When using a closure**, the first argument is the object being validated.
+
+.. versionadded:: 7.3
+
+ The support for closures in the ``expression`` option was introduced in Symfony 7.3
+ and requires PHP 8.5.
.. configuration-block::
@@ -187,11 +217,21 @@ validation based on its value:
class Discount
{
+ // either using an expression...
#[Assert\When(
expression: 'value == "percent"',
constraints: [new Assert\Callback('doComplexValidation')],
)]
+
+ // ... or using a closure
+ #[Assert\When(
+ expression: static function (Discount $discount) {
+ return $discount->getType() === 'percent';
+ },
+ constraints: [new Assert\Callback('doComplexValidation')],
+ )]
private ?string $type;
+
// ...
public function doComplexValidation(ExecutionContextInterface $context, $payload): void
@@ -248,12 +288,12 @@ validation based on its value:
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addPropertyConstraint('type', new Assert\When([
- 'expression' => 'value == "percent"',
- 'constraints' => [
+ $metadata->addPropertyConstraint('type', new Assert\When(
+ expression: 'value == "percent"',
+ constraints: [
new Assert\Callback('doComplexValidation'),
],
- ]));
+ ));
}
public function doComplexValidation(ExecutionContextInterface $context, $payload): void
@@ -271,6 +311,17 @@ You can also pass custom variables using the `values`_ option.
One or multiple constraints that are applied if the expression returns true.
+``otherwise``
+~~~~~~~~~~~~~
+
+**type**: ``array|Constraint``
+
+One or multiple constraints that are applied if the expression returns false.
+
+.. versionadded:: 7.3
+
+ The ``otherwise`` option was introduced in Symfony 7.3.
+
.. include:: /reference/constraints/_groups-option.rst.inc
.. include:: /reference/constraints/_payload-option.rst.inc
diff --git a/reference/constraints/WordCount.rst b/reference/constraints/WordCount.rst
new file mode 100644
index 00000000000..392f8a5bcb7
--- /dev/null
+++ b/reference/constraints/WordCount.rst
@@ -0,0 +1,150 @@
+WordCount
+=========
+
+.. versionadded:: 7.2
+
+ The ``WordCount`` constraint was introduced in Symfony 7.2.
+
+Validates that a string (or an object implementing the ``Stringable`` PHP interface)
+contains a given number of words. Internally, this constraint uses the
+:phpclass:`IntlBreakIterator` class to count the words depending on your locale.
+
+========== =======================================================================
+Applies to :ref:`property or method `
+Class :class:`Symfony\\Component\\Validator\\Constraints\\WordCount`
+Validator :class:`Symfony\\Component\\Validator\\Constraints\\WordCountValidator`
+========== =======================================================================
+
+Basic Usage
+-----------
+
+If you wanted to ensure that the ``content`` property of a ``BlogPostDTO``
+class contains between 100 and 200 words, you could do the following:
+
+.. configuration-block::
+
+ .. code-block:: php-attributes
+
+ // src/Entity/BlogPostDTO.php
+ namespace App\Entity;
+
+ use Symfony\Component\Validator\Constraints as Assert;
+
+ class BlogPostDTO
+ {
+ #[Assert\WordCount(min: 100, max: 200)]
+ protected string $content;
+ }
+
+ .. code-block:: yaml
+
+ # config/validator/validation.yaml
+ App\Entity\BlogPostDTO:
+ properties:
+ content:
+ - WordCount:
+ min: 100
+ max: 200
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+ 100
+ 200
+
+
+
+
+
+ .. code-block:: php
+
+ // src/Entity/BlogPostDTO.php
+ namespace App\Entity;
+
+ use Symfony\Component\Validator\Constraints as Assert;
+ use Symfony\Component\Validator\Mapping\ClassMetadata;
+
+ class BlogPostDTO
+ {
+ // ...
+
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('content', new Assert\WordCount(
+ min: 100,
+ max: 200,
+ ));
+ }
+ }
+
+Options
+-------
+
+``min``
+~~~~~~~
+
+**type**: ``integer`` **default**: ``null``
+
+The minimum number of words that the value must contain.
+
+``max``
+~~~~~~~
+
+**type**: ``integer`` **default**: ``null``
+
+The maximum number of words that the value must contain.
+
+``locale``
+~~~~~~~~~~
+
+**type**: ``string`` **default**: ``null``
+
+The locale to use for counting the words by using the :phpclass:`IntlBreakIterator`
+class. The default value (``null``) means that the constraint uses the current
+user locale.
+
+.. include:: /reference/constraints/_groups-option.rst.inc
+
+``minMessage``
+~~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``This value is too short. It should contain at least one word.|This value is too short. It should contain at least {{ min }} words.``
+
+This is the message that will be shown if the value does not contain at least
+the minimum number of words.
+
+You can use the following parameters in this message:
+
+================ ==================================================
+Parameter Description
+================ ==================================================
+``{{ min }}`` The minimum number of words
+``{{ count }}`` The actual number of words
+================ ==================================================
+
+``maxMessage``
+~~~~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``This value is too long. It should contain one word.|This value is too long. It should contain {{ max }} words or less.``
+
+This is the message that will be shown if the value contains more than the
+maximum number of words.
+
+You can use the following parameters in this message:
+
+================ ==================================================
+Parameter Description
+================ ==================================================
+``{{ max }}`` The maximum number of words
+``{{ count }}`` The actual number of words
+================ ==================================================
+
+.. include:: /reference/constraints/_payload-option.rst.inc
diff --git a/reference/constraints/Yaml.rst b/reference/constraints/Yaml.rst
new file mode 100644
index 00000000000..0d1564f4f8a
--- /dev/null
+++ b/reference/constraints/Yaml.rst
@@ -0,0 +1,152 @@
+Yaml
+====
+
+Validates that a value has valid `YAML`_ syntax.
+
+.. versionadded:: 7.2
+
+ The ``Yaml`` constraint was introduced in Symfony 7.2.
+
+========== ===================================================================
+Applies to :ref:`property or method `
+Class :class:`Symfony\\Component\\Validator\\Constraints\\Yaml`
+Validator :class:`Symfony\\Component\\Validator\\Constraints\\YamlValidator`
+========== ===================================================================
+
+Basic Usage
+-----------
+
+The ``Yaml`` constraint can be applied to a property or a "getter" method:
+
+.. configuration-block::
+
+ .. code-block:: php-attributes
+
+ // src/Entity/Report.php
+ namespace App\Entity;
+
+ use Symfony\Component\Validator\Constraints as Assert;
+
+ class Report
+ {
+ #[Assert\Yaml(
+ message: "Your configuration doesn't have valid YAML syntax."
+ )]
+ private string $customConfiguration;
+ }
+
+ .. code-block:: yaml
+
+ # config/validator/validation.yaml
+ App\Entity\Report:
+ properties:
+ customConfiguration:
+ - Yaml:
+ message: Your configuration doesn't have valid YAML syntax.
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+ Your configuration doesn't have valid YAML syntax.
+
+
+
+
+
+ .. code-block:: php
+
+ // src/Entity/Report.php
+ namespace App\Entity;
+
+ use Symfony\Component\Validator\Constraints as Assert;
+ use Symfony\Component\Validator\Mapping\ClassMetadata;
+
+ class Report
+ {
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('customConfiguration', new Assert\Yaml(
+ message: 'Your configuration doesn\'t have valid YAML syntax.',
+ ));
+ }
+ }
+
+Options
+-------
+
+``flags``
+~~~~~~~~~
+
+**type**: ``integer`` **default**: ``0``
+
+This option enables optional features of the YAML parser when validating contents.
+Its value is a combination of one or more of the :ref:`flags defined by the Yaml component `:
+
+.. configuration-block::
+
+ .. code-block:: php-attributes
+
+ // src/Entity/Report.php
+ namespace App\Entity;
+
+ use Symfony\Component\Validator\Constraints as Assert;
+ use Symfony\Component\Yaml\Yaml;
+
+ class Report
+ {
+ #[Assert\Yaml(
+ message: "Your configuration doesn't have valid YAML syntax.",
+ flags: Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS | Yaml::PARSE_DATETIME,
+ )]
+ private string $customConfiguration;
+ }
+
+ .. code-block:: php
+
+ // src/Entity/Report.php
+ namespace App\Entity;
+
+ use Symfony\Component\Validator\Constraints as Assert;
+ use Symfony\Component\Validator\Mapping\ClassMetadata;
+ use Symfony\Component\Yaml\Yaml;
+
+ class Report
+ {
+ public static function loadValidatorMetadata(ClassMetadata $metadata): void
+ {
+ $metadata->addPropertyConstraint('customConfiguration', new Assert\Yaml(
+ message: 'Your configuration doesn\'t have valid YAML syntax.',
+ flags: Yaml::PARSE_CONSTANT | Yaml::PARSE_CUSTOM_TAGS | Yaml::PARSE_DATETIME,
+ ));
+ }
+ }
+
+``message``
+~~~~~~~~~~~
+
+**type**: ``string`` **default**: ``This value is not valid YAML.``
+
+This message shown if the underlying data is not a valid YAML value.
+
+You can use the following parameters in this message:
+
+=============== ==============================================================
+Parameter Description
+=============== ==============================================================
+``{{ error }}`` The full error message from the YAML parser
+``{{ line }}`` The line where the YAML syntax error happened
+=============== ==============================================================
+
+.. include:: /reference/constraints/_groups-option.rst.inc
+
+.. include:: /reference/constraints/_payload-option.rst.inc
+
+.. _`YAML`: https://en.wikipedia.org/wiki/YAML
diff --git a/reference/constraints/_comparison-value-option.rst.inc b/reference/constraints/_comparison-value-option.rst.inc
index c8abdfb5af0..91ab28a2e94 100644
--- a/reference/constraints/_comparison-value-option.rst.inc
+++ b/reference/constraints/_comparison-value-option.rst.inc
@@ -1,7 +1,7 @@
``value``
~~~~~~~~~
-**type**: ``mixed`` [:ref:`default option `]
+**type**: ``mixed``
This option is required. It defines the comparison value. It can be a
string, number or object.
diff --git a/reference/constraints/map.rst.inc b/reference/constraints/map.rst.inc
index 706742dfde5..06680e42207 100644
--- a/reference/constraints/map.rst.inc
+++ b/reference/constraints/map.rst.inc
@@ -33,10 +33,13 @@ String Constraints
* :doc:`NotCompromisedPassword `
* :doc:`PasswordStrength `
* :doc:`Regex `
+* :doc:`Twig `
* :doc:`Ulid `
* :doc:`Url `
* :doc:`UserPassword `
* :doc:`Uuid `
+* :doc:`WordCount `
+* :doc:`Yaml `
Comparison Constraints
~~~~~~~~~~~~~~~~~~~~~~
@@ -70,6 +73,7 @@ Date Constraints
* :doc:`DateTime `
* :doc:`Time `
* :doc:`Timezone `
+* :doc:`Week `
Choice Constraints
~~~~~~~~~~~~~~~~~~
diff --git a/reference/formats/expression_language.rst b/reference/formats/expression_language.rst
index a23440c5715..dfed9c74398 100644
--- a/reference/formats/expression_language.rst
+++ b/reference/formats/expression_language.rst
@@ -20,6 +20,11 @@ The component supports:
* **booleans** - ``true`` and ``false``
* **null** - ``null``
* **exponential** - also known as scientific (e.g. ``1.99E+3`` or ``1e-2``)
+* **comments** - using ``/*`` and ``*/`` (e.g. ``/* this is a comment */``)
+
+.. versionadded:: 7.2
+
+ The support for comments inside expressions was introduced in Symfony 7.2.
.. warning::
@@ -124,11 +129,10 @@ returns the right-hand side. Expressions can chain multiple coalescing operators
* ``foo[3] ?? 'no'``
* ``foo.baz ?? foo['baz'] ?? 'no'``
-.. note::
+.. versionadded:: 7.2
- The main difference with the `null-coalescing operator in PHP`_ is that
- ExpressionLanguage will throw an exception when trying to access a
- non-existent variable.
+ Starting from Symfony 7.2, no exception is thrown when trying to access a
+ non-existent variable. This is the same behavior as the `null-coalescing operator in PHP`_.
.. _component-expression-functions:
@@ -280,6 +284,14 @@ Bitwise Operators
* ``&`` (and)
* ``|`` (or)
* ``^`` (xor)
+* ``~`` (not)
+* ``<<`` (left shift)
+* ``>>`` (right shift)
+
+.. versionadded:: 7.2
+
+ Support for the ``~``, ``<<`` and ``>>`` bitwise operators was introduced
+ in Symfony 7.2.
Comparison Operators
~~~~~~~~~~~~~~~~~~~~
@@ -333,6 +345,11 @@ Logical Operators
* ``not`` or ``!``
* ``and`` or ``&&``
* ``or`` or ``||``
+* ``xor``
+
+.. versionadded:: 7.2
+
+ Support for the ``xor`` logical operator was introduced in Symfony 7.2.
For example::
@@ -445,38 +462,40 @@ parentheses in your expressions (e.g. ``(1 + 2) * 4`` or ``1 + (2 * 4)``.
The following table summarizes the operators and their associativity from the
**highest to the lowest precedence**:
-+----------------------------------------------------------+---------------+
-| Operators | Associativity |
-+==========================================================+===============+
-| ``-`` , ``+`` (unary operators that add the number sign) | none |
-+----------------------------------------------------------+---------------+
-| ``**`` | right |
-+----------------------------------------------------------+---------------+
-| ``*``, ``/``, ``%`` | left |
-+----------------------------------------------------------+---------------+
-| ``not``, ``!`` | none |
-+----------------------------------------------------------+---------------+
-| ``~`` | left |
-+----------------------------------------------------------+---------------+
-| ``+``, ``-`` | left |
-+----------------------------------------------------------+---------------+
-| ``..`` | left |
-+----------------------------------------------------------+---------------+
-| ``==``, ``===``, ``!=``, ``!==``, | left |
-| ``<``, ``>``, ``>=``, ``<=``, | |
-| ``not in``, ``in``, ``contains``, | |
-| ``starts with``, ``ends with``, ``matches`` | |
-+----------------------------------------------------------+---------------+
-| ``&`` | left |
-+----------------------------------------------------------+---------------+
-| ``^`` | left |
-+----------------------------------------------------------+---------------+
-| ``|`` | left |
-+----------------------------------------------------------+---------------+
-| ``and``, ``&&`` | left |
-+----------------------------------------------------------+---------------+
-| ``or``, ``||`` | left |
-+----------------------------------------------------------+---------------+
++-----------------------------------------------------------------+---------------+
+| Operators | Associativity |
++=================================================================+===============+
+| ``-`` , ``+``, ``~`` (unary operators that add the number sign) | none |
++-----------------------------------------------------------------+---------------+
+| ``**`` | right |
++-----------------------------------------------------------------+---------------+
+| ``*``, ``/``, ``%`` | left |
++-----------------------------------------------------------------+---------------+
+| ``not``, ``!`` | none |
++-----------------------------------------------------------------+---------------+
+| ``~`` | left |
++-----------------------------------------------------------------+---------------+
+| ``+``, ``-`` | left |
++-----------------------------------------------------------------+---------------+
+| ``..``, ``<<``, ``>>`` | left |
++-----------------------------------------------------------------+---------------+
+| ``==``, ``===``, ``!=``, ``!==``, | left |
+| ``<``, ``>``, ``>=``, ``<=``, | |
+| ``not in``, ``in``, ``contains``, | |
+| ``starts with``, ``ends with``, ``matches`` | |
++-----------------------------------------------------------------+---------------+
+| ``&`` | left |
++-----------------------------------------------------------------+---------------+
+| ``^`` | left |
++-----------------------------------------------------------------+---------------+
+| ``|`` | left |
++-----------------------------------------------------------------+---------------+
+| ``and``, ``&&`` | left |
++-----------------------------------------------------------------+---------------+
+| ``xor`` | left |
++-----------------------------------------------------------------+---------------+
+| ``or``, ``||`` | left |
++-----------------------------------------------------------------+---------------+
Built-in Objects and Variables
------------------------------
diff --git a/reference/formats/xliff.rst b/reference/formats/xliff.rst
index acb9af36014..b5dc99b4186 100644
--- a/reference/formats/xliff.rst
+++ b/reference/formats/xliff.rst
@@ -29,7 +29,7 @@ loaded/dumped inside a Symfony application:
true
user login
-
+
original-content
translated-content
@@ -37,4 +37,8 @@ loaded/dumped inside a Symfony application:
+.. versionadded:: 7.2
+
+ The support of attributes in the ```` element was introduced in Symfony 7.2.
+
.. _XLIFF: https://docs.oasis-open.org/xliff/xliff-core/v2.1/xliff-core-v2.1.html
diff --git a/reference/forms/types/choice.rst b/reference/forms/types/choice.rst
index 5ec8aac0ff8..9f61fb768bd 100644
--- a/reference/forms/types/choice.rst
+++ b/reference/forms/types/choice.rst
@@ -178,6 +178,8 @@ correct types will be assigned to the model.
.. include:: /reference/forms/types/options/choice_loader.rst.inc
+.. include:: /reference/forms/types/options/choice_lazy.rst.inc
+
.. include:: /reference/forms/types/options/choice_name.rst.inc
.. include:: /reference/forms/types/options/choice_translation_domain_enabled.rst.inc
diff --git a/reference/forms/types/date.rst b/reference/forms/types/date.rst
index f28aae474b9..210fff5dd0d 100644
--- a/reference/forms/types/date.rst
+++ b/reference/forms/types/date.rst
@@ -153,6 +153,20 @@ values for the year, month and day fields::
.. include:: /reference/forms/types/options/view_timezone.rst.inc
+``calendar``
+~~~~~~~~~~~~
+
+**type**: ``integer`` or ``\IntlCalendar`` **default**: ``null``
+
+The calendar to use for formatting and parsing the date. The value should be
+an ``integer`` from :phpclass:`IntlDateFormatter` calendar constants or an instance
+of the :phpclass:`IntlCalendar` to use. By default, the Gregorian calendar
+with the application default locale is used.
+
+.. versionadded:: 7.2
+
+ The ``calendar`` option was introduced in Symfony 7.2.
+
.. include:: /reference/forms/types/options/date_widget.rst.inc
.. include:: /reference/forms/types/options/years.rst.inc
diff --git a/reference/forms/types/enum.rst b/reference/forms/types/enum.rst
index 875c0808108..b786fb68125 100644
--- a/reference/forms/types/enum.rst
+++ b/reference/forms/types/enum.rst
@@ -94,6 +94,22 @@ Inherited Options
These options inherit from the :doc:`ChoiceType `:
+.. include:: /reference/forms/types/options/choice_attr.rst.inc
+
+.. include:: /reference/forms/types/options/choice_filter.rst.inc
+
+.. include:: /reference/forms/types/options/choice_label.rst.inc
+
+.. include:: /reference/forms/types/options/choice_loader.rst.inc
+
+.. include:: /reference/forms/types/options/choice_name.rst.inc
+
+.. include:: /reference/forms/types/options/choice_translation_domain_enabled.rst.inc
+
+.. include:: /reference/forms/types/options/choice_translation_parameters.rst.inc
+
+.. include:: /reference/forms/types/options/choice_value.rst.inc
+
.. include:: /reference/forms/types/options/error_bubbling.rst.inc
.. include:: /reference/forms/types/options/error_mapping.rst.inc
diff --git a/reference/forms/types/money.rst b/reference/forms/types/money.rst
index a02b695abd4..967fe9e4ce4 100644
--- a/reference/forms/types/money.rst
+++ b/reference/forms/types/money.rst
@@ -83,6 +83,9 @@ input
By default, the money value is converted to a ``float`` PHP type. If you need the
value to be converted into an integer (e.g. because some library needs money
values stored in cents as integers) set this option to ``integer``.
+You can also set this option to ``string``, it can be useful if the underlying
+data is a string for precision reasons (for example, Doctrine uses strings for
+the decimal type).
.. versionadded:: 7.1
diff --git a/reference/forms/types/options/choice_lazy.rst.inc b/reference/forms/types/options/choice_lazy.rst.inc
new file mode 100644
index 00000000000..08fbe953e41
--- /dev/null
+++ b/reference/forms/types/options/choice_lazy.rst.inc
@@ -0,0 +1,31 @@
+``choice_lazy``
+~~~~~~~~~~~~~~~
+
+**type**: ``boolean`` **default**: ``false``
+
+.. versionadded:: 7.2
+
+ The ``choice_lazy`` option was introduced in Symfony 7.2.
+
+The ``choice_lazy`` option is particularly useful when dealing with a large set
+of choices, where loading them all at once could cause performance issues or
+delays::
+
+ use App\Entity\User;
+ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
+
+ $builder->add('user', EntityType::class, [
+ 'class' => User::class,
+ 'choice_lazy' => true,
+ ]);
+
+When set to ``true`` and used alongside the ``choice_loader`` option, the form
+will only load and render the choices that are preset as default values or
+submitted. This defers the loading of the full list of choices, helping to
+improve your form's performance.
+
+.. warning::
+
+ Keep in mind that when using ``choice_lazy``, you are responsible for
+ providing the user interface for selecting choices, typically through a
+ JavaScript plugin capable of dynamically loading choices.
diff --git a/reference/forms/types/options/choice_value.rst.inc b/reference/forms/types/options/choice_value.rst.inc
index 4b3668074a9..137ca8a6df0 100644
--- a/reference/forms/types/options/choice_value.rst.inc
+++ b/reference/forms/types/options/choice_value.rst.inc
@@ -9,7 +9,7 @@ You don't normally need to worry about this, but it might be handy when processi
an API request (since you can configure the value that will be sent in the API request).
This can be a callable or a property path. By default, the choices are used if they
-can be casted to strings. Otherwise an incrementing integer is used (starting at ``0``).
+can be cast to strings. Otherwise an incrementing integer is used (starting at ``0``).
If you pass a callable, it will receive one argument: the choice itself. When using
the :doc:`/reference/forms/types/entity`, the argument will be the entity object
diff --git a/reference/forms/types/options/validation_groups.rst.inc b/reference/forms/types/options/validation_groups.rst.inc
index 1f5c9a597a3..6957bf203a3 100644
--- a/reference/forms/types/options/validation_groups.rst.inc
+++ b/reference/forms/types/options/validation_groups.rst.inc
@@ -1,59 +1,14 @@
``validation_groups``
~~~~~~~~~~~~~~~~~~~~~
-**type**: ``array``, ``string``, ``callable``, :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence` or ``null`` **default**: ``null``
+**type**: ``array``, ``string``, ``callable``, :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence`, or ``null`` **default**: ``null``
-This option is only valid on the root form and is used to specify which
-groups will be used by the validator.
+This option is only valid on the root form. It specifies which validation groups
+will be used by the validator.
-For ``null`` the validator will just use the ``Default`` group.
-
-If you specify the groups as an array or string they will be used by the
-validator as they are::
-
- public function configureOptions(OptionsResolver $resolver): void
- {
- $resolver->setDefaults([
- 'validation_groups' => 'Registration',
- ]);
- }
-
-This is equivalent to passing the group as array::
-
- 'validation_groups' => ['Registration'],
-
-The form's data will be :doc:`validated against all given groups `.
-
-If the validation groups depend on the form's data a callable may be passed to
-the option. Symfony will then pass the form when calling it::
-
- use Symfony\Component\Form\FormInterface;
- use Symfony\Component\OptionsResolver\OptionsResolver;
-
- // ...
- public function configureOptions(OptionsResolver $resolver): void
- {
- $resolver->setDefaults([
- 'validation_groups' => function (FormInterface $form): array {
- $entity = $form->getData();
-
- return $entity->isUser() ? ['User'] : ['Company'];
- },
- ]);
- }
-
-.. seealso::
-
- You can read more about this in :doc:`/form/data_based_validation`.
-
-.. note::
-
- When your form contains multiple submit buttons, you can change the
- validation group depending on :doc:`which button is used `
- to submit the form.
-
- If you need advanced logic to determine the validation groups have
- a look at :doc:`/form/validation_group_service_resolver`.
+If set to ``null``, the validator will use only the ``Default`` group. For the
+other possible values, see the main article about
+:doc:`using validation groups in Symfony forms `
In some cases, you want to validate your groups step by step. To do this, you
can pass a :class:`Symfony\\Component\\Validator\\Constraints\\GroupSequence`
diff --git a/reference/forms/types/submit.rst b/reference/forms/types/submit.rst
index 70fa429685a..5d863fbe8b4 100644
--- a/reference/forms/types/submit.rst
+++ b/reference/forms/types/submit.rst
@@ -102,28 +102,8 @@ validation_groups
**type**: ``array`` **default**: ``null``
When your form contains multiple submit buttons, you can change the validation
-group based on the button which was used to submit the form. Imagine a registration
-form wizard with buttons to go to the previous or the next step::
-
- use Symfony\Component\Form\Extension\Core\Type\SubmitType;
- // ...
-
- $form = $this->createFormBuilder($user)
- ->add('previousStep', SubmitType::class, [
- 'validation_groups' => false,
- ])
- ->add('nextStep', SubmitType::class, [
- 'validation_groups' => ['Registration'],
- ])
- ->getForm();
-
-The special ``false`` ensures that no validation is performed when the previous
-step button is clicked. When the second button is clicked, all constraints
-from the "Registration" are validated.
-
-.. seealso::
-
- You can read more about this in :doc:`/form/data_based_validation`.
+group based on the clicked button. Read the article about
+:doc:`using validation groups in Symfony forms `.
Form Variables
--------------
diff --git a/reference/twig_reference.rst b/reference/twig_reference.rst
index d8b802dd73e..633d4c7f0c6 100644
--- a/reference/twig_reference.rst
+++ b/reference/twig_reference.rst
@@ -96,6 +96,11 @@ Returns an instance of ``ControllerReference`` to be used with functions
like :ref:`render() ` and
:ref:`render_esi() `.
+.. code-block:: twig
+
+ {{ render(controller('App\\Controller\\BlogController:latest', {max: 3})) }}
+ {# output: the content returned by the controller method; e.g. a rendered Twig template #}
+
.. _reference-twig-function-asset:
asset
@@ -110,6 +115,22 @@ asset
``packageName`` *(optional)*
**type**: ``string`` | ``null`` **default**: ``null``
+.. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ # ...
+ assets:
+ packages:
+ foo_package:
+ base_path: /avatars
+
+.. code-block:: twig
+
+ {# the image lives at "public/avatars/avatar.png" #}
+ {{ asset(path = 'avatar.png', packageName = 'foo_package') }}
+ {# output: /avatars/avatar.png #}
+
Returns the public path of the given asset path (which can be a CSS file, a
JavaScript file, an image path, etc.). This function takes into account where
the application is installed (e.g. in case the project is accessed in a host
@@ -155,25 +176,52 @@ csrf_token
Renders a CSRF token. Use this function if you want :doc:`CSRF protection `
in a regular HTML form not managed by the Symfony Form component.
+.. code-block:: twig
+
+ {{ csrf_token('my_form') }}
+ {# output: a random alphanumeric string like:
+ a.YOosAd0fhT7BEuUCFbROzrvgkW8kpEmBDQ_DKRMUi2o.Va8ZQKt5_2qoa7dLW-02_PLYwDBx9nnWOluUHUFCwC5Zo0VuuVfQCqtngg #}
+
is_granted
~~~~~~~~~~
.. code-block:: twig
- {{ is_granted(role, object = null, field = null) }}
+ {{ is_granted(role, object = null) }}
``role``
**type**: ``string``
``object`` *(optional)*
**type**: ``object``
-``field`` *(optional)*
- **type**: ``string``
Returns ``true`` if the current user has the given role.
Optionally, an object can be passed to be used by the voter. More information
can be found in :ref:`security-template`.
+is_granted_for_user
+~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 7.3
+
+ The ``is_granted_for_user()`` function was introduced in Symfony 7.3.
+
+.. code-block:: twig
+
+ {{ is_granted_for_user(user, attribute, subject = null) }}
+
+``user``
+ **type**: ``object``
+``attribute``
+ **type**: ``string``
+``subject`` *(optional)*
+ **type**: ``object``
+
+Returns ``true`` if the user is authorized for the specified attribute.
+
+Optionally, an object can be passed to be used by the voter. More information
+can be found in :ref:`security-template`.
+
logout_path
~~~~~~~~~~~
@@ -187,6 +235,30 @@ logout_path
Generates a relative logout URL for the given firewall. If no key is provided,
the URL is generated for the current firewall the user is logged into.
+.. code-block:: yaml
+
+ # config/packages/security.yaml
+ security:
+ # ...
+
+ firewalls:
+ main:
+ # ...
+ logout:
+ path: '/logout'
+ othername:
+ # ...
+ logout:
+ path: '/other/logout'
+
+.. code-block:: twig
+
+ {{ logout_path(key = 'main') }}
+ {# output: /logout #}
+
+ {{ logout_path(key = 'othername') }}
+ {# output: /other/logout #}
+
logout_url
~~~~~~~~~~
@@ -200,6 +272,30 @@ logout_url
Equal to the `logout_path`_ function, but it'll generate an absolute URL
instead of a relative one.
+.. code-block:: yaml
+
+ # config/packages/security.yaml
+ security:
+ # ...
+
+ firewalls:
+ main:
+ # ...
+ logout:
+ path: '/logout'
+ othername:
+ # ...
+ logout:
+ path: '/other/logout'
+
+.. code-block:: twig
+
+ {{ logout_url(key = 'main') }}
+ {# output: http://example.org/logout #}
+
+ {{ logout_url(key = 'othername') }}
+ {# output: http://example.org/other/logout #}
+
path
~~~~
@@ -217,6 +313,16 @@ path
Returns the relative URL (without the scheme and host) for the given route.
If ``relative`` is enabled, it'll create a path relative to the current path.
+.. code-block:: twig
+
+ {# consider that the app defines an 'app_blog' route with the path '/blog/{page}' #}
+
+ {{ path(name = 'app_blog', parameters = {page: 3}, relative = false) }}
+ {# output: /blog/3 #}
+
+ {{ path(name = 'app_blog', parameters = {page: 3}, relative = true) }}
+ {# output: blog/3 #}
+
.. seealso::
Read more about :doc:`Symfony routing ` and about
@@ -239,6 +345,16 @@ url
Returns the absolute URL (with scheme and host) for the given route. If
``schemeRelative`` is enabled, it'll create a scheme-relative URL.
+.. code-block:: twig
+
+ {# consider that the app defines an 'app_blog' route with the path '/blog/{page}' #}
+
+ {{ url(name = 'app_blog', parameters = {page: 3}, schemeRelative = false) }}
+ {# output: http://example.org/blog/3 #}
+
+ {{ url(name = 'app_blog', parameters = {page: 3}, schemeRelative = true) }}
+ {# output: //example.org/blog/3 #}
+
.. seealso::
Read more about :doc:`Symfony routing ` and about
@@ -290,6 +406,11 @@ expression
Creates an :class:`Symfony\\Component\\ExpressionLanguage\\Expression` related
to the :doc:`ExpressionLanguage component `.
+.. code-block:: twig
+
+ {{ expression(1 + 2) }}
+ {# output: 3 #}
+
impersonation_path
~~~~~~~~~~~~~~~~~~
@@ -365,6 +486,42 @@ t
Creates a ``Translatable`` object that can be passed to the
:ref:`trans filter `.
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # translations/blog.en.yaml
+ message: Hello %name%
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+ message
+ Hello %name%
+
+
+
+
+
+ .. code-block:: php
+
+ // translations/blog.en.php
+ return [
+ 'message' => "Hello %name%",
+ ];
+
+Using the filter will be rendered as:
+
+.. code-block:: twig
+
+ {{ t(message = 'message', parameters = {'%name%': 'John'}, domain = 'blog')|trans }}
+ {# output: Hello John #}
+
importmap
~~~~~~~~~
@@ -387,6 +544,7 @@ explained in the article about :doc:`customizing form rendering `
* :ref:`form_rest() `
* :ref:`field_name() `
+* :ref:`field_id() `
* :ref:`field_value() `
* :ref:`field_label() `
* :ref:`field_help() `
@@ -444,6 +602,42 @@ trans
Translates the text into the current language. More information in
:ref:`Translation Filters `.
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # translations/messages.en.yaml
+ message: Hello %name%
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+ message
+ Hello %name%
+
+
+
+
+
+ .. code-block:: php
+
+ // translations/messages.en.php
+ return [
+ 'message' => "Hello %name%",
+ ];
+
+Using the filter will be rendered as:
+
+.. code-block:: twig
+
+ {{ 'message'|trans(arguments = {'%name%': 'John'}, domain = 'messages', locale = 'en') }}
+ {# output: Hello John #}
+
sanitize_html
~~~~~~~~~~~~~
@@ -581,6 +775,16 @@ abbr_class
Generates an ```` element with the short name of a PHP class (the
FQCN will be shown in a tooltip when a user hovers over the element).
+.. code-block:: twig
+
+ {{ 'App\\Entity\\Product'|abbr_class }}
+
+The above example will be rendered as:
+
+.. code-block:: html
+
+ Product
+
abbr_method
~~~~~~~~~~~
@@ -595,6 +799,16 @@ Generates an ```` element using the ``FQCN::method()`` syntax. If
``method`` is ``Closure``, ``Closure`` will be used instead and if ``method``
doesn't have a class name, it's shown as a function (``method()``).
+.. code-block:: twig
+
+ {{ 'App\\Controller\\ProductController::list'|abbr_method }}
+
+The above example will be rendered as:
+
+.. code-block:: html
+
+ ProductController::list()
+
format_args
~~~~~~~~~~~
@@ -637,6 +851,32 @@ Generates an excerpt of a code file around the given ``line`` number. The
``srcContext`` argument defines the total number of lines to display around the
given line number (use ``-1`` to display the whole file).
+Consider the following as the content of ``file.txt``:
+
+.. code-block:: text
+
+ a
+ b
+ c
+ d
+ e
+
+.. code-block:: html+twig
+
+ {{ '/path/to/file.txt'|file_excerpt(line = 4, srcContext = 1) }}
+ {# output: #}
+
+ c
+ d
+ e
+
+
+ {{ '/path/to/file.txt'|file_excerpt(line = 1, srcContext = 0) }}
+ {# output: #}
+
+ a
+
+
format_file
~~~~~~~~~~~
@@ -655,6 +895,27 @@ Generates the file path inside an ```` element. If the path is inside
the kernel root directory, the kernel root directory path is replaced by
``kernel.project_dir`` (showing the full path in a tooltip on hover).
+.. code-block:: html+twig
+
+ {{ '/path/to/file.txt'|format_file(line = 1, text = "my_text") }}
+ {# output: #}
+ my_text at line 1
+
+
+ {{ "/path/to/file.txt"|format_file(line = 3) }}
+ {# output: #}
+ /path/to/file.txt at line 3
+
+
+.. tip::
+
+ If you set the :ref:`framework.ide ` option, the
+ generated links will change to open the file in that IDE/editor. For example,
+ when using PhpStorm, the ```
and returns a serialized string in the specified ``format``.
+For example::
+
+ $object = new \stdClass();
+ $object->foo = 'bar';
+ $object->content = [];
+ $object->createdAt = new \DateTime('2024-11-30');
+
+.. code-block:: twig
+
+ {{ object|serialize(format = 'json', context = {
+ 'datetime_format': 'D, Y-m-d',
+ 'empty_array_as_object': true,
+ }) }}
+ {# output: {"foo":"bar","content":{},"createdAt":"Sat, 2024-11-30"} #}
+
.. _reference-twig-filter-emojify:
emojify
diff --git a/routing.rst b/routing.rst
index df1f861c554..4f31e70da64 100644
--- a/routing.rst
+++ b/routing.rst
@@ -18,12 +18,13 @@ your favorite.
:ref:`Symfony recommends attributes `
because it's convenient to put the route and controller in the same place.
+.. _routing-route-attributes:
+
Creating Routes as Attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PHP attributes allow to define routes next to the code of the
-:doc:`controllers ` associated to those routes. Attributes are
-native in PHP 8 and higher versions, so you can use them right away.
+:doc:`controllers ` associated to those routes.
You need to add a bit of configuration to your project before using them. If your
project uses :ref:`Symfony Flex `, this file is already created for you.
@@ -248,6 +249,68 @@ Use the ``methods`` option to restrict the verbs each route should respond to:
automatically for you when the :ref:`framework.http_method_override `
option is ``true``.
+Matching Environments
+~~~~~~~~~~~~~~~~~~~~~
+
+Use the ``env`` option to register a route only when the current
+:ref:`configuration environment ` matches the
+given value:
+
+.. configuration-block::
+
+ .. code-block:: php-attributes
+
+ // src/Controller/DefaultController.php
+ namespace App\Controller;
+
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\Routing\Attribute\Route;
+
+ class DefaultController extends AbstractController
+ {
+ #[Route('/tools', name: 'tools', env: 'dev')]
+ public function developerTools(): Response
+ {
+ // ...
+ }
+ }
+
+ .. code-block:: yaml
+
+ # config/routes.yaml
+ tools:
+ path: /tools
+ controller: App\Controller\DefaultController::developerTools
+ env: dev
+
+ .. code-block:: xml
+
+
+
+
+
+
+ dev
+
+
+
+ .. code-block:: php
+
+ // config/routes.php
+ use App\Controller\DefaultController;
+ use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
+
+ return function (RoutingConfigurator $routes): void {
+ $routes->add('tools', '/tools')
+ ->controller([DefaultController::class, 'developerTools'])
+ ->env('dev')
+ ;
+ };
+
.. _routing-matching-expressions:
Matching Expressions
@@ -434,6 +497,18 @@ evaluates them:
blog_show ANY ANY ANY /blog/{slug}
---------------- ------- ------- ----- --------------------------------------------
+ # pass this option to also display all the defined route aliases
+ $ php bin/console debug:router --show-aliases
+
+ # pass this option to only display routes that match the given HTTP method
+ # (you can use the special value ANY to see routes that match any method)
+ $ php bin/console debug:router --method=GET
+ $ php bin/console debug:router --method=ANY
+
+.. versionadded:: 7.3
+
+ The ``--method`` option was introduced in Symfony 7.3.
+
Pass the name (or part of the name) of some route to this argument to print the
route details:
@@ -451,11 +526,6 @@ route details:
| | utf8: true |
+-------------+---------------------------------------------------------+
-.. tip::
-
- Use the ``--show-aliases`` option to show all available aliases for a given
- route.
-
The other command is called ``router:match`` and it shows which route will match
the given URL. It's useful to find out why some URL is not executing the
controller action that you expect:
@@ -578,7 +648,7 @@ the ``{page}`` parameter using the ``requirements`` option:
}
#[Route('/blog/{slug}', name: 'blog_show')]
- public function show($slug): Response
+ public function show(string $slug): Response
{
// ...
}
@@ -707,12 +777,6 @@ URL Route Parameters
matches any uppercase character in any language, ``\p{Greek}`` matches any
Greek characters, etc.
-.. note::
-
- When using regular expressions in route parameters, you can set the ``utf8``
- route option to ``true`` to make any ``.`` character match any UTF-8
- characters instead of just a single byte.
-
If you prefer, requirements can be inlined in each parameter using the syntax
``{parameter_name}``. This feature makes configuration more
concise, but it can decrease route readability when requirements are complex:
@@ -948,6 +1012,7 @@ optional ``priority`` parameter in those routes to control their priority:
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
@@ -997,7 +1062,7 @@ controller action. Instead of ``string $slug``, add ``BlogPost $post``::
{
// ...
- #[Route('/blog/{slug}', name: 'blog_show')]
+ #[Route('/blog/{slug:post}', name: 'blog_show')]
public function show(BlogPost $post): Response
{
// $post is the object whose slug matches the routing parameter
@@ -1011,9 +1076,37 @@ this case), the "param converter" makes a database request to find the object
using the request parameters (``slug`` in this case). If no object is found,
Symfony generates a 404 response automatically.
+The ``{slug:post}`` syntax maps the route parameter named ``slug`` to the controller
+argument named ``$post``. It also hints the "param converter" to look up the
+corresponding ``BlogPost`` object from the database using the slug.
+
+.. versionadded:: 7.1
+
+ Route parameter mapping was introduced in Symfony 7.1.
+
+When mapping multiple entities from route parameters, name collisions can occur.
+In this example, the route tries to define two mappings: one for an author and one
+for a category; both using the same ``name`` parameter. This isn't allowed because
+the route ends up declaring ``name`` twice::
+
+ #[Route('/search-book/{name:author}/{name:category}')]
+
+Such routes should instead be defined using the following syntax::
+
+ #[Route('/search-book/{authorName:author.name}/{categoryName:category.name}')]
+
+This way, the route parameter names are unique (``authorName`` and ``categoryName``),
+and the "param converter" can correctly map them to controller arguments (``$author``
+and ``$category``), loading them both by their name.
+
+.. versionadded:: 7.3
+
+ This more advanced style of route parameter mapping was introduced in Symfony 7.3.
+
+More advanced mappings can be achieved using the ``#[MapEntity]`` attribute.
Check out the :ref:`Doctrine param conversion documentation `
-to learn about the ``#[MapEntity]`` attribute that can be used to customize the
-database queries used to fetch the object from the route parameter.
+to learn how to customize the database queries used to fetch the object from the route
+parameter.
Backed Enum Parameters
~~~~~~~~~~~~~~~~~~~~~~
@@ -1305,15 +1398,35 @@ A possible solution is to change the parameter requirements to be more permissiv
Route Aliasing
--------------
-Route alias allow you to have multiple name for the same route:
+Route alias allows you to have multiple names for the same route
+and can be used to provide backward compatibility for routes that
+have been renamed. Let's say you have a route called ``product_show``:
.. configuration-block::
+ .. code-block:: php-attributes
+
+ // src/Controller/ProductController.php
+ namespace App\Controller;
+
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\Routing\Attribute\Route;
+
+ class ProductController
+ {
+ #[Route('/product/{id}', name: 'product_show')]
+ public function show(): Response
+ {
+ // ...
+ }
+ }
+
.. code-block:: yaml
# config/routes.yaml
- new_route_name:
- alias: original_route_name
+ product_show:
+ path: /product/{id}
+ controller: App\Controller\ProductController::show
.. code-block:: xml
@@ -1324,7 +1437,7 @@ Route alias allow you to have multiple name for the same route:
xsi:schemaLocation="http://symfony.com/schema/routing
https://symfony.com/schema/routing/routing-1.0.xsd">
-
+
.. code-block:: php
@@ -1333,38 +1446,169 @@ Route alias allow you to have multiple name for the same route:
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
return static function (RoutingConfigurator $routes): void {
- $routes->alias('new_route_name', 'original_route_name');
+ $routes->add('product_show', '/product/{id}')
+ ->controller('App\Controller\ProductController::show');
};
-In this example, both ``original_route_name`` and ``new_route_name`` routes can
+Now, let's say you want to create a new route called ``product_details``
+that acts exactly the same as ``product_show``.
+
+Instead of duplicating the original route, you can create an alias for it.
+
+.. configuration-block::
+
+ .. code-block:: php-attributes
+
+ // src/Controller/ProductController.php
+ namespace App\Controller;
+
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\Routing\Attribute\Route;
+
+ class ProductController
+ {
+ // the "alias" argument assigns an alternate name to this route;
+ // the alias will point to the actual route "product_show"
+ #[Route('/product/{id}', name: 'product_show', alias: ['product_details'])]
+ public function show(): Response
+ {
+ // ...
+ }
+ }
+
+ .. code-block:: yaml
+
+ # config/routes.yaml
+ product_show:
+ path: /product/{id}
+ controller: App\Controller\ProductController::show
+
+ product_details:
+ # "alias" option refers to the name of the route declared above
+ alias: product_show
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/routes.php
+ use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
+
+ return static function (RoutingConfigurator $routes): void {
+ $routes->add('product_show', '/product/{id}')
+ ->controller('App\Controller\ProductController::show');
+ // second argument refers to the name of the route declared above
+ $routes->alias('product_details', 'product_show');
+ };
+
+.. versionadded:: 7.3
+
+ Support for route aliases in PHP attributes was introduced in Symfony 7.3.
+
+In this example, both ``product_show`` and ``product_details`` routes can
be used in the application and will produce the same result.
+.. note::
+
+ YAML, XML, and PHP configuration formats are the only ways to define an alias
+ for a route that you do not own. You can't do this when using PHP attributes.
+
+ This allows you for example to use your own route name for URL generation,
+ while still targeting a route defined by a third-party bundle. The alias and
+ the original route do not need to be declared in the same file or format.
+
.. _routing-alias-deprecation:
Deprecating Route Aliases
~~~~~~~~~~~~~~~~~~~~~~~~~
-If some route alias should no longer be used (because it is outdated or
-you decided not to maintain it anymore), you can deprecate its definition:
+Route aliases can be used to provide backward compatibility for routes that
+have been renamed.
+
+Now, let's say you want to replace the ``product_show`` route in favor of
+``product_details`` and mark the old one as deprecated.
+
+In the previous example, the alias ``product_details`` was pointing to
+``product_show`` route.
+
+To mark the ``product_show`` route as deprecated, you need to "switch" the alias.
+The ``product_show`` become the alias, and will now point to the ``product_details`` route.
+This way, the ``product_show`` alias could be deprecated.
.. configuration-block::
+ .. code-block:: php-attributes
+
+ // src/Controller/ProductController.php
+ namespace App\Controller;
+
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\Routing\Attribute\Route;
+
+ class ProductController
+ {
+ // this outputs the following generic deprecation message:
+ // Since acme/package 1.2: The "product_show" route alias is deprecated. You should stop using it, as it will be removed in the future.
+ #[Route('/product/{id}',
+ name: 'product_details',
+ alias: new DeprecatedAlias(
+ aliasName: 'product_show',
+ package: 'acme/package',
+ version: '1.2',
+ ),
+ )]
+ // Or, you can also define a custom deprecation message (%alias_id% placeholder is available)
+ #[Route('/product/{id}',
+ name: 'product_details',
+ alias: new DeprecatedAlias(
+ aliasName: 'product_show',
+ package: 'acme/package',
+ version: '1.2',
+ message: 'The "%alias_id%" route alias is deprecated. Please use "product_details" instead.',
+ ),
+ )]
+ public function show(): Response
+ {
+ // ...
+ }
+ }
+
.. code-block:: yaml
- new_route_name:
- alias: original_route_name
+ # Move the concrete route definition under ``product_details``
+ product_details:
+ path: /product/{id}
+ controller: App\Controller\ProductController::show
+
+ # Define the alias and the deprecation under the ``product_show`` definition
+ product_show:
+ alias: product_details
# this outputs the following generic deprecation message:
- # Since acme/package 1.2: The "new_route_name" route alias is deprecated. You should stop using it, as it will be removed in the future.
+ # Since acme/package 1.2: The "product_show" route alias is deprecated. You should stop using it, as it will be removed in the future.
deprecated:
package: 'acme/package'
version: '1.2'
- # you can also define a custom deprecation message (%alias_id% placeholder is available)
+ # or
+
+ # you can define a custom deprecation message (%alias_id% placeholder is available)
deprecated:
package: 'acme/package'
version: '1.2'
- message: 'The "%alias_id%" route alias is deprecated. Do not use it anymore.'
+ message: 'The "%alias_id%" route alias is deprecated. Please use "product_details" instead.'
.. code-block:: xml
@@ -1374,35 +1618,46 @@ you decided not to maintain it anymore), you can deprecate its definition:
xsi:schemaLocation="http://symfony.com/schema/routing
https://symfony.com/schema/routing/routing-1.0.xsd">
-
+
+
+
+
+
+ Since acme/package 1.2: The "product_show" route alias is deprecated. You should stop using it, as it will be removed in the future. -->
-
+
+
+
- The "%alias_id%" route alias is deprecated. Do not use it anymore.
+ The "%alias_id%" route alias is deprecated. Please use "product_details" instead.
.. code-block:: php
- $routes->alias('new_route_name', 'original_route_name')
+ $routes->add('product_details', '/product/{id}')
+ ->controller('App\Controller\ProductController::show');
+
+ $routes->alias('product_show', 'product_details')
// this outputs the following generic deprecation message:
- // Since acme/package 1.2: The "new_route_name" route alias is deprecated. You should stop using it, as it will be removed in the future.
+ // Since acme/package 1.2: The "product_show" route alias is deprecated. You should stop using it, as it will be removed in the future.
->deprecate('acme/package', '1.2', '')
- // you can also define a custom deprecation message (%alias_id% placeholder is available)
+ // or
+
+ // you can define a custom deprecation message (%alias_id% placeholder is available)
->deprecate(
'acme/package',
'1.2',
- 'The "%alias_id%" route alias is deprecated. Do not use it anymore.'
+ 'The "%alias_id%" route alias is deprecated. Please use "product_details" instead.'
)
;
-In this example, every time the ``new_route_name`` alias is used, a deprecation
-warning is triggered, advising you to stop using that alias.
+In this example, every time the ``product_show`` alias is used, a deprecation
+warning is triggered, advising you to stop using this route and prefer using ``product_details``.
The message is actually a message template, which replaces occurrences of the
``%alias_id%`` placeholder by the route alias name. You **must** have
@@ -2476,23 +2731,23 @@ The solution is to configure the ``default_uri`` option to define the
Now you'll get the expected results when generating URLs in your commands::
- // src/Command/SomeCommand.php
+ // src/Command/MyCommand.php
namespace App\Command;
- use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Output\OutputInterface;
+ use Symfony\Component\Console\Attribute\AsCommand;
+ use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
// ...
- class SomeCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- public function __construct(private UrlGeneratorInterface $urlGenerator)
- {
- parent::__construct();
+ public function __construct(
+ private UrlGeneratorInterface $urlGenerator,
+ ) {
}
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(SymfonyStyle $io): int
{
// generate a URL with no route arguments
$signUpPage = $this->urlGenerator->generate('sign_up');
@@ -2802,11 +3057,41 @@ argument of :method:`Symfony\\Component\\HttpFoundation\\UriSigner::sign`::
The feature to add an expiration date for a signed URI was introduced in Symfony 7.1.
-.. note::
+If you need to know the reason why a signed URI is invalid, you can use the
+``verify()`` method which throws exceptions on failure::
+
+ use Symfony\Component\HttpFoundation\Exception\ExpiredSignedUriException;
+ use Symfony\Component\HttpFoundation\Exception\UnsignedUriException;
+ use Symfony\Component\HttpFoundation\Exception\UnverifiedSignedUriException;
+
+ // ...
+
+ try {
+ $uriSigner->verify($uri); // $uri can be a string or Request object
+
+ // the URI is valid
+ } catch (UnsignedUriException) {
+ // the URI isn't signed
+ } catch (UnverifiedSignedUriException) {
+ // the URI is signed but the signature is invalid
+ } catch (ExpiredSignedUriException) {
+ // the URI is signed but expired
+ }
+
+.. versionadded:: 7.3
+
+ The ``verify()`` method was introduced in Symfony 7.3.
+
+.. tip::
+
+ If ``symfony/clock`` is installed, it will be used to create and verify
+ expirations. This allows you to :ref:`mock the current time in your tests
+ `.
+
+.. versionadded:: 7.3
- The generated URI hashes may include the ``/`` and ``+`` characters, which
- can cause issues with certain clients. If you encounter this problem, replace
- them using the following: ``strtr($hash, ['/' => '_', '+' => '-'])``.
+ Support for :doc:`Symfony Clock ` in ``UriSigner`` was
+ introduced in Symfony 7.3.
Troubleshooting
---------------
diff --git a/scheduler.rst b/scheduler.rst
index ddc40aa4952..ed6ada8b5ed 100644
--- a/scheduler.rst
+++ b/scheduler.rst
@@ -1,6 +1,11 @@
Scheduler
=========
+.. admonition:: Screencast
+ :class: screencast
+
+ Like video tutorials? Check out this `Scheduler quick-start screencast`_.
+
The scheduler component manages task scheduling within your PHP application, like
running a task each night at 3 AM, every two weeks except for holidays or any
other custom schedule you might need.
@@ -15,17 +20,16 @@ stack Symfony application.
Installation
------------
-In applications using :ref:`Symfony Flex `, run this command to
-install the scheduler component:
+Run this command to install the scheduler component:
.. code-block:: terminal
$ composer require symfony/scheduler
-.. tip::
+.. note::
- Starting in `MakerBundle`_ ``v1.58.0``, you can run ``php bin/console make:schedule``
- to generate a basic schedule, that you can customize to create your own Scheduler.
+ In applications using :ref:`Symfony Flex `, installing the component
+ also creates an initial schedule that's ready to start adding your tasks.
Symfony Scheduler Basics
------------------------
@@ -223,7 +227,7 @@ this will create a very long running list of schedules at that exact time.
This may cause an issue if a task has a memory leak.
You can add a hash symbol (``#``) in expressions to generate random values.
-Athough the values are random, they are predictable and consistent because they
+Although the values are random, they are predictable and consistent because they
are generated based on the message. A message with string representation ``my task``
and a defined frequency of ``# # * * *`` will have an idempotent frequency
of ``56 20 * * *`` (every day at 8:56pm).
@@ -272,14 +276,30 @@ defined by PHP datetime functions::
RecurringMessage::every('3 weeks', new Message());
RecurringMessage::every('first Monday of next month', new Message());
- $from = new \DateTimeImmutable('13:47', new \DateTimeZone('Europe/Paris'));
- $until = '2023-06-12';
- RecurringMessage::every('first Monday of next month', new Message(), $from, $until);
-
.. tip::
You can also define periodic tasks using :ref:`the AsPeriodicTask attribute