diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml
index 5c3d2fd7687..dfb85021586 100644
--- a/.doctor-rst.yaml
+++ b/.doctor-rst.yaml
@@ -75,16 +75,16 @@ rules:
yaml_instead_of_yml_suffix: ~
versionadded_directive_major_version:
- major_version: 6
+ major_version: 7
versionadded_directive_min_version:
- min_version: '6.0'
+ min_version: '7.0'
deprecated_directive_major_version:
- major_version: 6
+ major_version: 7
deprecated_directive_min_version:
- min_version: '6.0'
+ min_version: '7.0'
exclude_rule_for_file:
- path: configuration/multiple_kernels.rst
diff --git a/_build/build.php b/_build/build.php
index b80d8e0e51e..b684700a848 100755
--- a/_build/build.php
+++ b/_build/build.php
@@ -27,7 +27,7 @@
$outputDir = __DIR__.'/output';
$buildConfig = (new BuildConfig())
- ->setSymfonyVersion('6.4')
+ ->setSymfonyVersion('7.1')
->setContentDir(__DIR__.'/..')
->setOutputDir($outputDir)
->setImagesDir(__DIR__.'/output/_images')
diff --git a/_build/redirection_map b/_build/redirection_map
index 8d121257713..c30723eac58 100644
--- a/_build/redirection_map
+++ b/_build/redirection_map
@@ -530,7 +530,7 @@
/serializer/normalizers /serializer#serializer-built-in-normalizers
/logging/monolog_regex_based_excludes /logging/monolog_exclude_http_codes
/security/named_encoders /security/named_hashers
-/components/inflector /components/string#inflector
+/components/inflector /string#inflector
/security/experimental_authenticators /security
/security/user_provider /security/user_providers
/security/reset_password /security/passwords#reset-password
@@ -568,6 +568,11 @@
/messenger/dispatch_after_current_bus /messenger#messenger-transactional-messages
/messenger/multiple_buses /messenger#messenger-multiple-buses
/frontend/encore/server-data /frontend/server-data
+/components/string /string
+/testing/http_authentication /testing#testing_logging_in_users
+/doctrine/registration_form /security#security-make-registration-form
+/form/form_dependencies /form/create_custom_field_type
+/doctrine/reverse_engineering /doctrine#doctrine-adding-mapping
/components/serializer /serializer
/serializer/custom_encoder /serializer/encoders#serializer-custom-encoder
/components/string /string
diff --git a/_includes/_annotation_loader_tip.rst.inc b/_includes/_annotation_loader_tip.rst.inc
deleted file mode 100644
index 0f4267b07f5..00000000000
--- a/_includes/_annotation_loader_tip.rst.inc
+++ /dev/null
@@ -1,19 +0,0 @@
-.. note::
-
- In order to use the annotation loader, you should have installed the
- ``doctrine/annotations`` and ``doctrine/cache`` packages with Composer.
-
-.. tip::
-
- Annotation classes aren't loaded automatically, so you must load them
- using a class loader like this::
-
- use Composer\Autoload\ClassLoader;
- use Doctrine\Common\Annotations\AnnotationRegistry;
-
- /** @var ClassLoader $loader */
- $loader = require __DIR__.'/../vendor/autoload.php';
-
- AnnotationRegistry::registerLoader([$loader, 'loadClass']);
-
- return $loader;
diff --git a/bundles.rst b/bundles.rst
index 93a9ceafc0e..3e590a4e2aa 100644
--- a/bundles.rst
+++ b/bundles.rst
@@ -58,11 +58,6 @@ Start by creating a new class called ``AcmeBlogBundle``::
{
}
-.. versionadded:: 6.1
-
- The :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle` was
- introduced in Symfony 6.1.
-
.. warning::
If your bundle must be compatible with previous Symfony versions you have to
diff --git a/bundles/best_practices.rst b/bundles/best_practices.rst
index 99d254b7929..8049ebb9a1c 100644
--- a/bundles/best_practices.rst
+++ b/bundles/best_practices.rst
@@ -232,10 +232,10 @@ with Symfony Flex to install a specific Symfony version:
.. code-block:: bash
- # this requires Symfony 6.x for all Symfony packages
- export SYMFONY_REQUIRE=6.*
+ # this requires Symfony 7.x for all Symfony packages
+ export SYMFONY_REQUIRE=7.*
# alternatively you can run this command to update composer.json config
- # composer config extra.symfony.require "6.*"
+ # composer config extra.symfony.require "7.*"
# install Symfony Flex in the CI environment
composer global config --no-plugins allow-plugins.symfony/flex true
diff --git a/bundles/configuration.rst b/bundles/configuration.rst
index ab15675105f..dedfada2ea2 100644
--- a/bundles/configuration.rst
+++ b/bundles/configuration.rst
@@ -61,10 +61,6 @@ There are two different ways of creating friendly configuration for a bundle:
Using the AbstractBundle Class
------------------------------
-.. versionadded:: 6.1
-
- The ``AbstractBundle`` class was introduced in Symfony 6.1.
-
In bundles extending the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
class, you can add all the logic related to processing the configuration in that class::
diff --git a/bundles/extension.rst b/bundles/extension.rst
index 607ca1404fb..d2792efc477 100644
--- a/bundles/extension.rst
+++ b/bundles/extension.rst
@@ -20,10 +20,6 @@ There are two different ways of doing it:
Loading Services Directly in your Bundle Class
----------------------------------------------
-.. versionadded:: 6.1
-
- The ``AbstractBundle`` class was introduced in Symfony 6.1.
-
In bundles extending the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
class, you can define the :method:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle::loadExtension`
method to load service definitions from configuration files::
diff --git a/bundles/prepend_extension.rst b/bundles/prepend_extension.rst
index afde2595e98..e4099d9f81a 100644
--- a/bundles/prepend_extension.rst
+++ b/bundles/prepend_extension.rst
@@ -154,10 +154,6 @@ registered and the ``entity_manager_name`` setting for ``acme_hello`` is set to
Prepending Extension in the Bundle Class
----------------------------------------
-.. versionadded:: 6.1
-
- The ``AbstractBundle`` class was introduced in Symfony 6.1.
-
You can also prepend extension configuration directly in your
Bundle class if you extend from the :class:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle`
class and define the :method:`Symfony\\Component\\HttpKernel\\Bundle\\AbstractBundle::prependExtension`
@@ -175,6 +171,9 @@ method::
$containerBuilder->prependExtensionConfig('framework', [
'cache' => ['prefix_seed' => 'foo/bar'],
]);
+
+ // prepend config from a file
+ $containerConfigurator->import('../config/packages/cache.php');
}
}
@@ -182,6 +181,40 @@ method::
The ``prependExtension()`` method, like ``prepend()``, is called only at compile time.
+.. versionadded:: 7.1
+
+ Starting from Symfony 7.1, calling the :method:`Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator::import`
+ method inside ``prependExtension()`` will prepend the given configuration.
+ In previous Symfony versions, this method appended the configuration.
+
+Alternatively, you can use the ``prepend`` parameter of the
+:method:`Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator::extension`
+method::
+
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+ use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
+
+ class FooBundle extends AbstractBundle
+ {
+ public function prependExtension(ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void
+ {
+ // ...
+
+ $containerConfigurator->extension('framework', [
+ 'cache' => ['prefix_seed' => 'foo/bar'],
+ ], prepend: true);
+
+ // ...
+ }
+ }
+
+.. versionadded:: 7.1
+
+ The ``prepend`` parameter of the
+ :method:`Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ContainerConfigurator::extension`
+ method was added in Symfony 7.1.
+
More than one Bundle using PrependExtensionInterface
----------------------------------------------------
diff --git a/cache.rst b/cache.rst
index 71fe56c2b33..9379511fde8 100644
--- a/cache.rst
+++ b/cache.rst
@@ -132,12 +132,7 @@ Some of these adapters could be configured via shortcuts.
default_psr6_provider: 'app.my_psr6_service'
default_redis_provider: 'redis://localhost'
default_memcached_provider: 'memcached://localhost'
- default_pdo_provider: 'app.my_pdo_service'
-
- services:
- app.my_pdo_service:
- class: \PDO
- arguments: ['pgsql:host=localhost']
+ default_pdo_provider: 'pgsql:host=localhost'
.. code-block:: xml
@@ -158,24 +153,17 @@ Some of these adapters could be configured via shortcuts.
default-psr6-provider="app.my_psr6_service"
default-redis-provider="redis://localhost"
default-memcached-provider="memcached://localhost"
- default-pdo-provider="app.my_pdo_service"
+ default-pdo-provider="pgsql:host=localhost"
/>
-
-
-
- pgsql:host=localhost
-
-
.. code-block:: php
// config/packages/cache.php
- use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Config\FrameworkConfig;
- return static function (FrameworkConfig $framework, ContainerConfigurator $container): void {
+ return static function (FrameworkConfig $framework): void {
$framework->cache()
// Only used with cache.adapter.filesystem
->directory('%kernel.cache_dir%/pools')
@@ -184,15 +172,14 @@ Some of these adapters could be configured via shortcuts.
->defaultPsr6Provider('app.my_psr6_service')
->defaultRedisProvider('redis://localhost')
->defaultMemcachedProvider('memcached://localhost')
- ->defaultPdoProvider('app.my_pdo_service')
- ;
-
- $container->services()
- ->set('app.my_pdo_service', \PDO::class)
- ->args(['pgsql:host=localhost'])
+ ->defaultPdoProvider('pgsql:host=localhost')
;
};
+.. versionadded:: 7.1
+
+ Using a DSN as the provider for the PDO adapter was introduced in Symfony 7.1.
+
.. _cache-create-pools:
Creating Custom (Namespaced) Pools
@@ -551,6 +538,8 @@ Symfony stores the item automatically in all the missing pools.
;
};
+.. _cache-using-cache-tags:
+
Using Cache Tags
----------------
@@ -743,20 +732,12 @@ Clear all cache pools:
$ php bin/console cache:pool:clear --all
-.. versionadded:: 6.3
-
- The ``--all`` option was introduced in Symfony 6.3.
-
Clear all cache pools except some:
.. code-block:: terminal
$ php bin/console cache:pool:clear --all --exclude=my_cache_pool --exclude=another_cache_pool
-.. versionadded:: 6.4
-
- The ``--exclude`` option was introduced in Symfony 6.4.
-
Clear all caches everywhere:
.. code-block:: terminal
@@ -765,10 +746,6 @@ Clear all caches everywhere:
Clear cache by tag(s):
-.. versionadded:: 6.1
-
- The ``cache:pool:invalidate-tags`` command was introduced in Symfony 6.1.
-
.. code-block:: terminal
# invalidate tag1 from all taggable pools
diff --git a/components/browser_kit.rst b/components/browser_kit.rst
index 21ceaf5a095..8cf0772298c 100644
--- a/components/browser_kit.rst
+++ b/components/browser_kit.rst
@@ -130,10 +130,6 @@ on a link::
// ... and `clickLink()`
$crawler = $client->clickLink('Go elsewhere...', ['X-Custom-Header' => 'Some data']);
-.. versionadded:: 6.4
-
- The ``serverParameters`` parameter was introduced in Symfony 6.4.
-
Submitting Forms
~~~~~~~~~~~~~~~~
@@ -403,10 +399,6 @@ to call ``json_decode()`` explicitly::
$response = $browser->getResponse()->toArray();
// $response is a PHP array of the decoded JSON contents
-.. versionadded:: 6.1
-
- The ``toArray()`` method was introduced in Symfony 6.1.
-
Learn more
----------
diff --git a/components/cache.rst b/components/cache.rst
index f5a76f2119d..44ba8b5c151 100644
--- a/components/cache.rst
+++ b/components/cache.rst
@@ -84,6 +84,69 @@ generate and return the value::
Use cache tags to delete more than one key at the time. Read more at
:doc:`/components/cache/cache_invalidation`.
+Creating Sub-Namespaces
+-----------------------
+
+.. versionadded:: 7.3
+
+ Cache sub-namespaces were introduced in Symfony 7.3.
+
+Sometimes you need to create context-dependent variations of data that should be
+cached. For example, the data used to render a dashboard page may be expensive
+to generate and unique per user, so you can't cache the same data for everyone.
+
+In such cases, Symfony allows you to create different cache contexts using
+namespaces. A cache namespace is an arbitrary string that identifies a set of
+related cache items. All cache adapters provided by the component implement the
+:class:`Symfony\\Contracts\\Cache\\NamespacedPoolInterface`, which provides the
+:method:`Symfony\\Contracts\\Cache\\NamespacedPoolInterface::withSubNamespace`
+method.
+
+This method allows you to namespace cached items by transparently prefixing their keys::
+
+ $userCache = $cache->withSubNamespace(sprintf('user-%d', $user->getId()));
+
+ $userCache->get('dashboard_data', function (ItemInterface $item): string {
+ $item->expiresAfter(3600);
+
+ return '...';
+ });
+
+In this example, the cache item uses the ``dashboard_data`` key, but it will be
+stored internally under a namespace based on the current user ID. This is handled
+automatically, so you **don't** need to manually prefix keys like ``user-27.dashboard_data``.
+
+There are no guidelines or restrictions on how to define cache namespaces.
+You can make them as granular or as generic as your application requires::
+
+ $localeCache = $cache->withSubNamespace($request->getLocale());
+
+ $flagCache = $cache->withSubNamespace(
+ $featureToggle->isEnabled('new_checkout') ? 'checkout-v2' : 'checkout-v1'
+ );
+
+ $channel = $request->attributes->get('_route')?->startsWith('api_') ? 'api' : 'web';
+ $channelCache = $cache->withSubNamespace($channel);
+
+.. tip::
+
+ You can combine cache namespaces with :ref:`cache tags `
+ for more advanced needs.
+
+There is no built-in way to invalidate caches by namespace. Instead, the recommended
+approach is to change the namespace itself. For this reason, it's common to include
+static or dynamic versioning data in the cache namespace::
+
+ // for simple applications, an incrementing static version number may be enough
+ $userCache = $cache->withSubNamespace(sprintf('v1-user-%d', $user->getId()));
+
+ // other applications may use dynamic versioning based on the date (e.g. monthly)
+ $userCache = $cache->withSubNamespace(sprintf('%s-user-%d', date('Ym'), $user->getId()));
+
+ // or even invalidate the cache when the user data changes
+ $checksum = hash('xxh128', $user->getUpdatedAt()->format(DATE_ATOM));
+ $userCache = $cache->withSubNamespace(sprintf('user-%d-%s', $user->getId(), $checksum));
+
.. _cache_stampede-prevention:
Stampede Prevention
@@ -208,16 +271,27 @@ Symfony uses *marshallers* (classes which implement
the cache items before storing them.
The :class:`Symfony\\Component\\Cache\\Marshaller\\DefaultMarshaller` uses PHP's
-``serialize()`` or ``igbinary_serialize()`` if the `Igbinary extension`_ is installed.
-There are other *marshallers* that can encrypt or compress the data before storing it::
+``serialize()`` function by default, but you can optionally use the ``igbinary_serialize()``
+function from the `Igbinary extension`_::
use Symfony\Component\Cache\Adapter\RedisAdapter;
- use Symfony\Component\Cache\DefaultMarshaller;
- use Symfony\Component\Cache\DeflateMarshaller;
+ use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
+ use Symfony\Component\Cache\Marshaller\DeflateMarshaller;
$marshaller = new DeflateMarshaller(new DefaultMarshaller());
+ // you can optionally use the Igbinary extension if you have it installed
+ // $marshaller = new DeflateMarshaller(new DefaultMarshaller(useIgbinarySerialize: true));
+
$cache = new RedisAdapter(new \Redis(), 'namespace', 0, $marshaller);
+There are other *marshallers* that can encrypt or compress the data before storing it.
+
+.. versionadded:: 7.2
+
+ In Symfony versions prior to 7.2, the ``igbinary_serialize()`` function was
+ used by default when the Igbinary extension was installed. Starting from
+ Symfony 7.2, you have to enable Igbinary support explicitly.
+
Advanced Usage
--------------
diff --git a/components/cache/adapters/array_cache_adapter.rst b/components/cache/adapters/array_cache_adapter.rst
index f903771e468..08f8276db3d 100644
--- a/components/cache/adapters/array_cache_adapter.rst
+++ b/components/cache/adapters/array_cache_adapter.rst
@@ -24,5 +24,14 @@ method::
// the maximum number of items that can be stored in the cache. When the limit
// is reached, cache follows the LRU model (least recently used items are deleted)
- $maxItems = 0
+ $maxItems = 0,
+
+ // optional implementation of the Psr\Clock\ClockInterface that will be used
+ // to calculate the lifetime of cache items (for example to get predictable
+ // lifetimes in tests)
+ $clock = null,
);
+
+.. versionadded:: 7.2
+
+ The optional ``$clock`` argument was introduced in Symfony 7.2.
diff --git a/components/cache/adapters/couchbasebucket_adapter.rst b/components/cache/adapters/couchbasebucket_adapter.rst
index 970dabe2cd9..29c9e26f83c 100644
--- a/components/cache/adapters/couchbasebucket_adapter.rst
+++ b/components/cache/adapters/couchbasebucket_adapter.rst
@@ -1,6 +1,12 @@
Couchbase Bucket Cache Adapter
==============================
+.. deprecated:: 7.1
+
+ The ``CouchbaseBucketAdapter`` is deprecated since Symfony 7.1, use the
+ :doc:`CouchbaseCollectionAdapter `
+ instead.
+
This adapter stores the values in-memory using one (or more) `Couchbase server`_
instances. Unlike the :doc:`APCu adapter `, and similarly to the
:doc:`Memcached adapter `, it is not limited to the current server's
diff --git a/components/cache/adapters/doctrine_dbal_adapter.rst b/components/cache/adapters/doctrine_dbal_adapter.rst
index fc04410bffc..68732ddd3fa 100644
--- a/components/cache/adapters/doctrine_dbal_adapter.rst
+++ b/components/cache/adapters/doctrine_dbal_adapter.rst
@@ -39,5 +39,22 @@ optional arguments::
necessary to detect the database engine and version without opening the
connection.
+The adapter uses SQL syntax that is optimized for database server that it is connected to.
+The following database servers are known to be compatible:
+
+* MySQL 5.7 and newer
+* MariaDB 10.2 and newer
+* Oracle 10g and newer
+* SQL Server 2012 and newer
+* SQLite 3.24 or later
+* PostgreSQL 9.5 or later
+
+.. note::
+
+ Newer releases of Doctrine DBAL might increase these minimal versions. Check
+ the manual page on `Doctrine DBAL Platforms`_ if your database server is
+ compatible with the installed Doctrine DBAL version.
+
.. _`Doctrine DBAL Connection`: https://github.com/doctrine/dbal/blob/master/src/Connection.php
-.. _`Doctrine DBAL URL`: https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
+.. _`Doctrine DBAL URL`: https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/configuration.html#connecting-using-a-url
+.. _`Doctrine DBAL Platforms`: https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/platforms.html
diff --git a/components/cache/adapters/redis_adapter.rst b/components/cache/adapters/redis_adapter.rst
index 503fc42454d..c0295b487a0 100644
--- a/components/cache/adapters/redis_adapter.rst
+++ b/components/cache/adapters/redis_adapter.rst
@@ -8,7 +8,8 @@ Redis Cache Adapter
:ref:`Symfony Cache configuration `
article if you are using it in a Symfony application.
-This adapter stores the values in-memory using one (or more) `Redis server`_ instances.
+This adapter stores the values in-memory using one (or more) `Redis server`_
+of `Valkey`_ server instances.
Unlike the :doc:`APCu adapter `, and similarly to the
:doc:`Memcached adapter `, it is not limited to the current server's
@@ -19,9 +20,9 @@ to utilize a cluster of servers to provide redundancy and/or fail-over is also a
**Requirements:** At least one `Redis server`_ must be installed and running to use this
adapter. Additionally, this adapter requires a compatible extension or library that implements
- ``\Redis``, ``\RedisArray``, ``RedisCluster``, ``\Relay\Relay`` or ``\Predis``.
+ ``\Redis``, ``\RedisArray``, ``RedisCluster``, ``\Relay\Relay``, ``\Relay\Cluster`` or ``\Predis``.
-This adapter expects a `Redis`_, `RedisArray`_, `RedisCluster`_, `Relay`_ or `Predis`_ instance to be
+This adapter expects a `Redis`_, `RedisArray`_, `RedisCluster`_, `Relay`_, `RelayCluster`_ or `Predis`_ instance to be
passed as the first parameter. A namespace and default cache lifetime can optionally be passed
as the second and third parameters::
@@ -47,9 +48,9 @@ as the second and third parameters::
?MarshallerInterface $marshaller = null
);
-.. versionadded:: 6.3
+.. versionadded:: 7.3
- Support for `Relay`_ was introduced in Symfony 6.3.
+ Support for ``Relay\Cluster`` was introduced in Symfony 7.3.
Configure the Connection
------------------------
@@ -65,6 +66,11 @@ helper method allows creating and configuring the Redis client class instance us
'redis://localhost'
);
+.. versionadded:: 7.3
+
+ Starting in Symfony 7.3, when using Valkey servers you can use the
+ ``valkey[s]:`` scheme instead of the ``redis[s]:`` one in your DSNs.
+
The DSN can specify either an IP/host (and an optional port) or a socket path, as well as a
password and a database index. To enable TLS for connections, the scheme ``redis`` must be
replaced by ``rediss`` (the second ``s`` means "secure").
@@ -169,10 +175,6 @@ array of ``key => value`` pairs representing option names and their respective v
Available Options
~~~~~~~~~~~~~~~~~
-.. versionadded:: 6.3
-
- ``\Relay\Relay`` support was introduced in Symfony 6.3.
-
``class`` (type: ``string``, default: ``null``)
Specifies the connection library to return, either ``\Redis``, ``\Relay\Relay`` or ``\Predis\Client``.
If none is specified, fallback value is in following order, depending which one is available first:
@@ -214,6 +216,9 @@ Available Options
``redis_sentinel`` (type: ``string``, default: ``null``)
Specifies the master name connected to the sentinels.
+``sentinel_master`` (type: ``string``, default: ``null``)
+ Alias of ``redis_sentinel`` option.
+
``dbindex`` (type: ``int``, default: ``0``)
Specifies the database index to select.
@@ -225,6 +230,34 @@ Available Options
``ssl`` (type: ``array``, default: ``null``)
SSL context options. See `php.net/context.ssl`_ for more information.
+``relay_cluster_context`` (type: ``array``, default: ``[]``)
+ Defines configuration options specific to ``\Relay\Cluster``. For example, to
+ user a self-signed certificate for testing in local environment::
+
+ $options = [
+ // ...
+ 'relay_cluster_context' => [
+ // ...
+ 'stream' => [
+ 'verify_peer' => false,
+ 'verify_peer_name' => false,
+ 'allow_self_signed' => true,
+ 'local_cert' => '/valkey.crt',
+ 'local_pk' => '/valkey.key',
+ 'cafile' => '/valkey.crt',
+ ],
+ ],
+ ];
+
+.. versionadded:: 7.1
+
+ The option ``sentinel_master`` as an alias for ``redis_sentinel`` was introduced
+ in Symfony 7.1.
+
+.. versionadded:: 7.3
+
+ The ``relay_cluster_context`` option was introduced in Symfony 7.3.
+
.. note::
When using the `Predis`_ library some additional Predis-specific options are available.
@@ -348,10 +381,12 @@ Supports key rotation, ensuring secure decryption with both old and new keys::
.. _`Data Source Name (DSN)`: https://en.wikipedia.org/wiki/Data_source_name
.. _`Redis server`: https://redis.io/
+.. _`Valkey`: https://valkey.io/
.. _`Redis`: https://github.com/phpredis/phpredis
.. _`RedisArray`: https://github.com/phpredis/phpredis/blob/develop/arrays.md
.. _`RedisCluster`: https://github.com/phpredis/phpredis/blob/develop/cluster.md
.. _`Relay`: https://relay.so/
+.. _`RelayCluster`: https://relay.so/docs/1.x/connections#cluster
.. _`Predis`: https://packagist.org/packages/predis/predis
.. _`Predis Connection Parameters`: https://github.com/nrk/predis/wiki/Connection-Parameters#list-of-connection-parameters
.. _`TCP-keepalive`: https://redis.io/topics/clients#tcp-keepalive
diff --git a/components/clock.rst b/components/clock.rst
index 1ae56775b77..c4ac88e9092 100644
--- a/components/clock.rst
+++ b/components/clock.rst
@@ -1,10 +1,6 @@
The Clock Component
===================
-.. versionadded:: 6.2
-
- The Clock component was introduced in Symfony 6.2
-
The Clock component decouples applications from the system clock. This allows
you to fix time to improve testability of time-sensitive logic.
@@ -79,16 +75,6 @@ When using the Clock component, you manipulate
:class:`Symfony\\Component\\Clock\\DatePoint` instances. You can learn more
about it in :ref:`the dedicated section `.
-.. versionadded:: 6.3
-
- The :class:`Symfony\\Component\\Clock\\Clock` class and ``now()`` function
- were introduced in Symfony 6.3.
-
-.. versionadded:: 6.4
-
- The ``modifier`` argument of the ``now()`` function was introduced in
- Symfony 6.4.
-
Available Clocks Implementations
--------------------------------
@@ -208,10 +194,6 @@ you can set the current time arbitrarily without having to change your service c
This will help you test every case of your method without the need of actually
being in a month or another.
-.. versionadded:: 6.3
-
- The :class:`Symfony\\Component\\Clock\\ClockAwareTrait` was introduced in Symfony 6.3.
-
.. _clock_date-point:
The ``DatePoint`` Class
@@ -247,16 +229,73 @@ The constructor also allows setting a timezone or custom referenced date::
$referenceDate = new \DateTimeImmutable();
$relativeDate = new DatePoint('+1month', reference: $referenceDate);
+The ``DatePoint`` class also provides a named constructor to create dates from
+timestamps::
+
+ $dateOfFirstCommitToSymfonyProject = DatePoint::createFromTimestamp(1129645656);
+ // equivalent to:
+ // $dateOfFirstCommitToSymfonyProject = (new \DateTimeImmutable())->setTimestamp(1129645656);
+
+ // negative timestamps (for dates before January 1, 1970) and float timestamps
+ // (for high precision sub-second datetimes) are also supported
+ $dateOfFirstMoonLanding = DatePoint::createFromTimestamp(-14182940);
+
+.. versionadded:: 7.1
+
+ The ``createFromTimestamp()`` method was introduced in Symfony 7.1.
+
.. note::
In addition ``DatePoint`` offers stricter return types and provides consistent
error handling across versions of PHP, thanks to polyfilling `PHP 8.3's behavior`_
on the topic.
-.. versionadded:: 6.4
+``DatePoint`` also allows to set and get the microsecond part of the date and time::
+
+ $datePoint = new DatePoint();
+ $datePoint->setMicrosecond(345);
+ $microseconds = $datePoint->getMicrosecond();
+
+.. note::
+
+ This feature polyfills PHP 8.4's behavior on the topic, as microseconds manipulation
+ is not available in previous versions of PHP.
+
+.. versionadded:: 7.1
- The :class:`Symfony\\Component\\Clock\\DatePoint` class was introduced
- in Symfony 6.4.
+ The :method:`Symfony\\Component\\Clock\\DatePoint::setMicrosecond` and
+ :method:`Symfony\\Component\\Clock\\DatePoint::getMicrosecond` methods were
+ introduced in Symfony 7.1.
+
+Storing DatePoints in the Database
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you :doc:`use Doctrine ` to work with databases, consider using the
+``date_point`` Doctrine type, which converts to/from ``DatePoint`` objects automatically::
+
+ // src/Entity/Product.php
+ namespace App\Entity;
+
+ use Doctrine\ORM\Mapping as ORM;
+ use Symfony\Component\Clock\DatePoint;
+
+ #[ORM\Entity]
+ class Product
+ {
+ // if you don't define the Doctrine type explicitly, Symfony will autodetect it:
+ #[ORM\Column]
+ private DatePoint $createdAt;
+
+ // if you prefer to define the Doctrine type explicitly:
+ #[ORM\Column(type: 'date_point')]
+ private DatePoint $updatedAt;
+
+ // ...
+ }
+
+.. versionadded:: 7.3
+
+ The ``DatePointType`` was introduced in Symfony 7.3.
.. _clock_writing-tests:
@@ -314,10 +353,6 @@ By combining the :class:`Symfony\\Component\\Clock\\ClockAwareTrait` and
:class:`Symfony\\Component\\Clock\\Test\\ClockSensitiveTrait`, you have full
control on your time-sensitive code's behavior.
-.. versionadded:: 6.3
-
- The :class:`Symfony\\Component\\Clock\\Test\\ClockSensitiveTrait` was introduced in Symfony 6.3.
-
Exceptions Management
---------------------
@@ -338,11 +373,6 @@ These exceptions are available starting from PHP 8.3. However, thanks to the
`symfony/polyfill-php83`_ dependency required by the Clock component, you can
use them even if your project doesn't use PHP 8.3 yet.
-.. versionadded:: 6.4
-
- The support for ``DateMalformedStringException`` and
- ``DateInvalidTimeZoneException`` was introduced in Symfony 6.4.
-
.. _`PSR-20`: https://www.php-fig.org/psr/psr-20/
.. _`accepted by the DateTime constructor`: https://www.php.net/manual/en/datetime.formats.php
.. _`PHP DateTime exceptions`: https://wiki.php.net/rfc/datetime-exceptions
diff --git a/components/config/caching.rst b/components/config/caching.rst
index f30128a5f9d..80e23a4fdfb 100644
--- a/components/config/caching.rst
+++ b/components/config/caching.rst
@@ -55,3 +55,17 @@ the cache file itself. This ``.meta`` file contains the serialized resources,
whose timestamps are used to determine if the cache is still fresh. When
not in debug mode, the cache is considered to be "fresh" as soon as it exists,
and therefore no ``.meta`` file will be generated.
+
+You can explicitly define the absolute path to the meta file::
+
+ use Symfony\Component\Config\ConfigCache;
+ use Symfony\Component\Config\Resource\FileResource;
+
+ $cachePath = __DIR__.'/cache/appUserMatcher.php';
+
+ // the third optional argument indicates the absolute path to the meta file
+ $userMatcherCache = new ConfigCache($cachePath, true, '/my/absolute/path/to/cache.meta');
+
+.. versionadded:: 7.1
+
+ The argument to customize the meta file path was introduced in Symfony 7.1.
diff --git a/components/config/definition.rst b/components/config/definition.rst
index 96f0c177aaa..2b1841bc24a 100644
--- a/components/config/definition.rst
+++ b/components/config/definition.rst
@@ -83,9 +83,19 @@ reflect the real structure of the configuration values::
->scalarNode('default_connection')
->defaultValue('mysql')
->end()
+ ->stringNode('username')
+ ->defaultValue('root')
+ ->end()
+ ->stringNode('password')
+ ->defaultValue('root')
+ ->end()
->end()
;
+.. versionadded:: 7.2
+
+ The ``stringNode()`` method was introduced in Symfony 7.2.
+
The root node itself is an array node, and has children, like the boolean
node ``auto_connect`` and the scalar node ``default_connection``. In general:
after defining a node, a call to ``end()`` takes you one step up in the
@@ -100,6 +110,7 @@ node definition. Node types are available for:
* scalar (generic type that includes booleans, strings, integers, floats
and ``null``)
* boolean
+* string
* integer
* float
* enum (similar to scalar, but it only allows a finite set of values)
@@ -109,6 +120,10 @@ node definition. Node types are available for:
and are created with ``node($name, $type)`` or their associated shortcut
``xxxxNode($name)`` method.
+.. versionadded:: 7.2
+
+ Support for the ``string`` type was introduced in Symfony 7.2.
+
Numeric Node Constraints
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -171,10 +186,24 @@ The configuration can now be written like this::
->end()
;
-.. versionadded:: 6.3
+You can also use the ``enumFqcn()`` method to pass the FQCN of an enum
+class to the node. This will automatically set the values of the node to
+the cases of the enum::
+
+ $rootNode
+ ->children()
+ ->enumNode('delivery')
+ ->enumFqcn(Delivery::class)
+ ->end()
+ ->end()
+ ;
+
+When using a backed enum, the values provided to the node will be cast
+to one of the enum cases if possible.
- The support of enum values in ``enumNode()`` was introduced
- in Symfony 6.3.
+.. versionadded:: 7.3
+
+ The ``enumFqcn()`` method was introduced in Symfony 7.3.
Array Nodes
~~~~~~~~~~~
@@ -517,6 +546,30 @@ and in XML:
+You can also provide a URL to a full documentation page::
+
+ $rootNode
+ ->docUrl('Full documentation is available at https://example.com/docs/{version:major}.{version:minor}/reference.html')
+ ->children()
+ ->integerNode('entries_per_page')
+ ->defaultValue(25)
+ ->end()
+ ->end()
+ ;
+
+A few placeholders are available to customize the URL:
+
+* ``{version:major}``: The major version of the package currently installed
+* ``{version:minor}``: The minor version of the package currently installed
+* ``{package}``: The name of the package
+
+The placeholders will be replaced when printing the configuration tree with the
+``config:dump-reference`` command.
+
+.. versionadded:: 7.3
+
+ The ``docUrl()`` method was introduced in Symfony 7.3.
+
Optional Sections
-----------------
@@ -805,6 +858,7 @@ A validation rule always has an "if" part. You can specify this part in
the following ways:
- ``ifTrue()``
+- ``ifFalse()``
- ``ifString()``
- ``ifNull()``
- ``ifEmpty()``
@@ -823,6 +877,10 @@ A validation rule also requires a "then" part:
Usually, "then" is a closure. Its return value will be used as a new value
for the node, instead of the node's original value.
+.. versionadded:: 7.3
+
+ The ``ifFalse()`` method was introduced in Symfony 7.3.
+
Configuring the Node Path Separator
-----------------------------------
diff --git a/components/console/changing_default_command.rst b/components/console/changing_default_command.rst
index c69995ea395..2195bbd2697 100644
--- a/components/console/changing_default_command.rst
+++ b/components/console/changing_default_command.rst
@@ -9,20 +9,14 @@ name to the ``setDefaultCommand()`` method::
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Output\OutputInterface;
+ use Symfony\Component\Console\Style\SymfonyStyle;
- #[AsCommand(name: 'hello:world')]
+ #[AsCommand(name: 'hello:world', description: 'Outputs "Hello World"')]
class HelloWorldCommand extends Command
{
- protected function configure(): void
+ public function __invoke(SymfonyStyle $io): int
{
- $this->setDescription('Outputs "Hello World"');
- }
-
- protected function execute(InputInterface $input, OutputInterface $output): int
- {
- $output->writeln('Hello World');
+ $io->writeln('Hello World');
return Command::SUCCESS;
}
diff --git a/components/console/events.rst b/components/console/events.rst
index ab497068979..699ba444747 100644
--- a/components/console/events.rst
+++ b/components/console/events.rst
@@ -156,11 +156,6 @@ Listeners receive a
a signal. You can learn more about signals in the
:ref:`the dedicated section `.
- .. versionadded:: 6.4
-
- Dispatching the ``ConsoleEvents::TERMINATE`` event on exit
- signal was introduced in Symfony 6.4.
-
.. _console-events_signal:
The ``ConsoleEvents::SIGNAL`` Event
@@ -207,11 +202,6 @@ method::
$event->abortExit();
});
-.. versionadded:: 6.3
-
- The ``setExitCode()``, ``getExitCode()`` and ``abortExit()`` methods were introduced
- in Symfony 6.3.
-
.. tip::
All the available signals (``SIGINT``, ``SIGQUIT``, etc.) are defined as
@@ -219,36 +209,32 @@ method::
for these constants to be available.
If you use the Console component inside a Symfony application, commands can
-handle signals themselves. To do so, implement the
-:class:`Symfony\\Component\\Console\\Command\\SignalableCommandInterface` and subscribe to one or more signals::
+handle signals themselves by subscribing to the :class:`Symfony\\Component\\Console\\Event\\ConsoleSignalEvent` event::
- // src/Command/SomeCommand.php
+ // src/Command/MyCommand.php
namespace App\Command;
- use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Command\SignalableCommandInterface;
+ use Symfony\Component\Console\Attribute\AsCommand;
+ use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
- class SomeCommand extends Command implements SignalableCommandInterface
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
// ...
- public function getSubscribedSignals(): array
- {
- // return here any of the constants defined by PCNTL extension
- return [\SIGINT, \SIGTERM];
- }
-
- public function handleSignal(int $signal): int|false
+ #[AsEventListener(ConsoleSignalEvent::class)]
+ public function handleSignal(ConsoleSignalEvent $event): void
{
- if (\SIGINT === $signal) {
+ // set here any of the constants defined by PCNTL extension
+ if (in_array($event->getHandlingSignal(), [\SIGINT, \SIGTERM], true)) {
// ...
}
// ...
- // return an integer to set the exit code, or
+ // set an integer exit code, or
// false to continue normal execution
- return 0;
+ $event->setExitCode(0);
}
}
@@ -263,21 +249,6 @@ handle all signals e.g. to do some tasks before terminating the command.
:method:`Symfony\\Component\\Console\\SignalRegistry\\SignalMap::getSignalName`
method.
- .. versionadded:: 6.4
-
- The :class:`Symfony\\Component\\Console\\SignalRegistry\\SignalMap` class was
- introduced in Symfony 6.4.
-
-.. deprecated:: 6.3
-
- In Symfony versions previous to 6.3, all signals (except ``SIGUSR1`` and
- ``SIGUSR2``) would terminate the script by calling ``exit(0)``. Starting
- from Symfony 6.3, no more signal is automatically calling ``exit(0)``.
-
-.. deprecated:: 6.3
-
- Not returning a value in ``handleSignal()`` is deprecated since Symfony 6.3.
-
.. _`reserved exit codes`: https://www.tldp.org/LDP/abs/html/exitcodes.html
.. _`Signals`: https://en.wikipedia.org/wiki/Signal_(IPC)
.. _`constants of the PCNTL PHP extension`: https://www.php.net/manual/en/pcntl.constants.php
diff --git a/components/console/helpers/cursor.rst b/components/console/helpers/cursor.rst
index c5cab6c6d0b..63045f178ad 100644
--- a/components/console/helpers/cursor.rst
+++ b/components/console/helpers/cursor.rst
@@ -13,16 +13,16 @@ of the output:
// src/Command/MyCommand.php
namespace App\Command;
- use Symfony\Component\Console\Command\Command;
+ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Cursor;
- use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
- class MyCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(OutputInterface $output): int
{
// ...
diff --git a/components/console/helpers/debug_formatter.rst b/components/console/helpers/debug_formatter.rst
index 10d3c67a79a..8fa59c319c9 100644
--- a/components/console/helpers/debug_formatter.rst
+++ b/components/console/helpers/debug_formatter.rst
@@ -10,15 +10,14 @@ this:
.. image:: /_images/components/console/debug_formatter.png
:alt: Console output, with the first line showing "RUN Running figlet", followed by lines showing the output of the command prefixed with "OUT" and "RES Finished the command" as last line in the output.
-Using the debug_formatter
+Using the Debug Formatter
-------------------------
-The formatter is included in the default helper set and you can get it by
-calling :method:`Symfony\\Component\\Console\\Command\\Command::getHelper`::
+The debug formatter helper can be instantiated directly as shown::
- $debugFormatter = $this->getHelper('debug_formatter');
+ $debugFormatter = new DebugFormatterHelper();
-The formatter accepts strings and returns a formatted string, which you then
+It accepts strings and returns a formatted string, which you then
output to the console (or even log the information or do anything else).
All methods of this helper have an identifier as the first argument. This is a
diff --git a/components/console/helpers/formatterhelper.rst b/components/console/helpers/formatterhelper.rst
index 1ff6bff189b..cf9bacdeb9c 100644
--- a/components/console/helpers/formatterhelper.rst
+++ b/components/console/helpers/formatterhelper.rst
@@ -1,15 +1,11 @@
Formatter Helper
================
-The Formatter helper provides functions to format the output with colors.
-You can do more advanced things with this helper than you can in
-:doc:`/console/coloring`.
+The :class:`Symfony\\Component\\Console\\Helper\\FormatterHelper` helper provides
+functions to format the output with colors. You can do more advanced things with
+this helper than you can with the :doc:`basic colors and styles `::
-The :class:`Symfony\\Component\\Console\\Helper\\FormatterHelper` is included
-in the default helper set and you can get it by calling
-:method:`Symfony\\Component\\Console\\Command\\Command::getHelper`::
-
- $formatter = $this->getHelper('formatter');
+ $formatter = new FormatterHelper();
The methods return a string, which you'll usually render to the console by
passing it to the
@@ -129,14 +125,16 @@ Sometimes you want to format seconds to time. This is possible with the
The first argument is the seconds to format and the second argument is the
precision (default ``1``) of the result::
- Helper::formatTime(42); // 42 secs
- Helper::formatTime(125); // 2 mins
- Helper::formatTime(125, 2); // 2 mins, 5 secs
- Helper::formatTime(172799, 4); // 1 day, 23 hrs, 59 mins, 59 secs
+ Helper::formatTime(0.001); // 1 ms
+ Helper::formatTime(42); // 42 s
+ Helper::formatTime(125); // 2 min
+ Helper::formatTime(125, 2); // 2 min, 5 s
+ Helper::formatTime(172799, 4); // 1 d, 23 h, 59 min, 59 s
+ Helper::formatTime(172799.056, 5); // 1 d, 23 h, 59 min, 59 s, 56 ms
-.. versionadded:: 6.4
+.. versionadded:: 7.3
- The support for exact times was introduced in Symfony 6.4.
+ Support for formatting up to milliseconds was introduced in Symfony 7.3.
Formatting Memory
-----------------
diff --git a/components/console/helpers/map.rst.inc b/components/console/helpers/map.rst.inc
index 41f0667c40d..73d5d4da7a0 100644
--- a/components/console/helpers/map.rst.inc
+++ b/components/console/helpers/map.rst.inc
@@ -4,5 +4,6 @@
* :doc:`/components/console/helpers/progressindicator`
* :doc:`/components/console/helpers/questionhelper`
* :doc:`/components/console/helpers/table`
+* :doc:`/components/console/helpers/tree`
* :doc:`/components/console/helpers/debug_formatter`
* :doc:`/components/console/helpers/cursor`
diff --git a/components/console/helpers/processhelper.rst b/components/console/helpers/processhelper.rst
index b46d9f2e95f..df9a8efe45b 100644
--- a/components/console/helpers/processhelper.rst
+++ b/components/console/helpers/processhelper.rst
@@ -11,7 +11,7 @@ a very verbose verbosity (e.g. ``-vv``)::
use Symfony\Component\Process\Process;
- $helper = $this->getHelper('process');
+ $helper = new ProcessHelper();
$process = new Process(['figlet', 'Symfony']);
$helper->run($output, $process);
diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst
index 445fb1dda88..19e2d0daef5 100644
--- a/components/console/helpers/progressbar.rst
+++ b/components/console/helpers/progressbar.rst
@@ -69,10 +69,6 @@ that starting point::
// displays the progress bar starting at 25 completed units
$progressBar->start(null, 25);
-.. versionadded:: 6.2
-
- The option to start a progress bar at a certain point was introduced in Symfony 6.2.
-
.. tip::
If your platform doesn't support ANSI codes, updates to the progress
@@ -375,10 +371,6 @@ with the ``setPlaceholderFormatter`` method::
return $progressBar->getMaxSteps() - $progressBar->getProgress();
});
-.. versionadded:: 6.3
-
- The ``setPlaceholderFormatter()`` method was introduced in Symfony 6.3.
-
Custom Messages
~~~~~~~~~~~~~~~
diff --git a/components/console/helpers/progressindicator.rst b/components/console/helpers/progressindicator.rst
index d64ec6367b7..0defe7c83fd 100644
--- a/components/console/helpers/progressindicator.rst
+++ b/components/console/helpers/progressindicator.rst
@@ -44,18 +44,21 @@ level of verbosity of the ``OutputInterface`` instance:
| Processing...
/ Processing...
- Processing...
+ ✔ Finished
# OutputInterface::VERBOSITY_VERBOSE (-v)
\ Processing... (1 sec)
| Processing... (1 sec)
/ Processing... (1 sec)
- Processing... (1 sec)
+ ✔ Finished (1 sec)
# OutputInterface::VERBOSITY_VERY_VERBOSE (-vv) and OutputInterface::VERBOSITY_DEBUG (-vvv)
\ Processing... (1 sec, 6.0 MiB)
| Processing... (1 sec, 6.0 MiB)
/ Processing... (1 sec, 6.0 MiB)
- Processing... (1 sec, 6.0 MiB)
+ ✔ Finished (1 sec, 6.0 MiB)
.. tip::
@@ -94,6 +97,34 @@ The progress indicator will now look like this:
⠛ Processing...
⠹ Processing...
⢸ Processing...
+ ✔ Finished
+
+Once the progress finishes, it displays a special finished indicator (which defaults
+to ✔). You can replace it with your own::
+
+ $progressIndicator = new ProgressIndicator($output, finishedIndicatorValue: '🎉');
+
+ try {
+ /* do something */
+ $progressIndicator->finish('Finished');
+ } catch (\Exception) {
+ $progressIndicator->finish('Failed', '🚨');
+ }
+
+The progress indicator will now look like this:
+
+.. code-block:: text
+
+ \ Processing...
+ | Processing...
+ / Processing...
+ - Processing...
+ 🎉 Finished
+
+.. versionadded:: 7.2
+
+ The ``finishedIndicator`` parameter for the constructor was introduced in Symfony 7.2.
+ The ``finishedIndicator`` parameter for method ``finish()`` was introduced in Symfony 7.2.
Customize Placeholders
~~~~~~~~~~~~~~~~~~~~~~
diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst
index 3dc97d5c0d3..6d22a2de2af 100644
--- a/components/console/helpers/questionhelper.rst
+++ b/components/console/helpers/questionhelper.rst
@@ -2,11 +2,9 @@ Question Helper
===============
The :class:`Symfony\\Component\\Console\\Helper\\QuestionHelper` provides
-functions to ask the user for more information. It is included in the default
-helper set and you can get it by calling
-:method:`Symfony\\Component\\Console\\Command\\Command::getHelper`::
+functions to ask the user for more information::
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
The Question Helper has a single method
:method:`Symfony\\Component\\Console\\Helper\\QuestionHelper::ask` that needs an
@@ -27,18 +25,18 @@ Suppose you want to confirm an action before actually executing it. Add
the following to your command::
// ...
+ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
- class YourCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- // ...
-
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$question = new ConfirmationQuestion('Continue with this action?', false);
if (!$helper->ask($input, $output, $question)) {
@@ -91,7 +89,7 @@ if you want to know a bundle name, you can add this to your command::
use Symfony\Component\Console\Question\Question;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
$question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');
@@ -121,10 +119,10 @@ but ``red`` could be set instead (could be more explicit)::
use Symfony\Component\Console\Question\ChoiceQuestion;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$question = new ChoiceQuestion(
'Please select your favorite color (defaults to red)',
// choices can also be PHP objects that implement __toString() method
@@ -184,10 +182,10 @@ this use :method:`Symfony\\Component\\Console\\Question\\ChoiceQuestion::setMult
use Symfony\Component\Console\Question\ChoiceQuestion;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$question = new ChoiceQuestion(
'Please select your favorite colors (defaults to red and blue)',
['red', 'blue', 'yellow'],
@@ -218,10 +216,10 @@ will be autocompleted as the user types::
use Symfony\Component\Console\Question\Question;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$bundles = ['AcmeDemoBundle', 'AcmeBlogBundle', 'AcmeStoreBundle'];
$question = new Question('Please enter the name of a bundle', 'FooBundle');
@@ -241,9 +239,9 @@ provide a callback function to dynamically generate suggestions::
use Symfony\Component\Console\Question\Question;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
// This function is called whenever the input changes and new
// suggestions are needed.
@@ -282,10 +280,10 @@ You can also specify if you want to not trim the answer by setting it directly w
use Symfony\Component\Console\Question\Question;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$question = new Question('What is the name of the child?');
$question->setTrimmable(false);
@@ -308,10 +306,10 @@ the response to a question should allow multiline answers by passing ``true`` to
use Symfony\Component\Console\Question\Question;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$question = new Question('How do you solve world peace?');
$question->setMultiline(true);
@@ -335,10 +333,10 @@ convenient for passwords::
use Symfony\Component\Console\Question\Question;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$question = new Question('What is the database password?');
$question->setHidden(true);
@@ -372,10 +370,10 @@ convenient for passwords::
use Symfony\Component\Console\Question\ChoiceQuestion;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
QuestionHelper::disableStty();
// ...
@@ -396,10 +394,10 @@ method::
use Symfony\Component\Console\Question\Question;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');
$question->setNormalizer(function (string $value): string {
@@ -434,10 +432,10 @@ method::
use Symfony\Component\Console\Question\Question;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');
$question->setValidator(function (string $answer): string {
@@ -480,10 +478,10 @@ invalid answer and will only be able to proceed if their input is valid.
use Symfony\Component\Validator\Validation;
$question = new Question('Please enter the name of the bundle', 'AcmeDemoBundle');
- $validation = Validation::createCallable(new Regex([
- 'pattern' => '/^[a-zA-Z]+Bundle$/',
- 'message' => 'The name of the bundle should be suffixed with \'Bundle\'',
- ]));
+ $validation = Validation::createCallable(new Regex(
+ pattern: '/^[a-zA-Z]+Bundle$/',
+ message: 'The name of the bundle should be suffixed with \'Bundle\'',
+ ));
$question->setValidator($validation);
Validating a Hidden Response
@@ -494,10 +492,10 @@ You can also use a validator with a hidden question::
use Symfony\Component\Console\Question\Question;
// ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
// ...
- $helper = $this->getHelper('question');
+ $helper = new QuestionHelper();
$question = new Question('Please enter your password');
$question->setNormalizer(function (?string $value): string {
diff --git a/components/console/helpers/table.rst b/components/console/helpers/table.rst
index 6d48d92a98a..e36b1570b70 100644
--- a/components/console/helpers/table.rst
+++ b/components/console/helpers/table.rst
@@ -10,15 +10,16 @@ features, use the ``Table`` console helper explained in this article.
To display a table, use :class:`Symfony\\Component\\Console\\Helper\\Table`,
set the headers, set the rows and then render the table::
+ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
- use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
// ...
- class SomeCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(OutputInterface $output): int
{
$table = new Table($output);
$table
@@ -192,10 +193,6 @@ This outputs:
| Author: Charles Dickens |
+------------------------------+
-.. versionadded:: 6.1
-
- Support for vertical rendering was introduced in Symfony 6.1.
-
Customizing the Table Style
---------------------------
@@ -277,6 +274,26 @@ This outputs:
║ 80-902734-1-6 │ And Then There Were None │ Agatha Christie ║
╚═══════════════╧══════════════════════════╧══════════════════╝
+**Markdown**::
+
+ $table->setStyle('markdown');
+ $table->render();
+
+This outputs:
+
+.. code-block:: terminal
+
+ | ISBN | Title | Author |
+ |---------------|--------------------------|------------------|
+ | 99921-58-10-7 | Divine Comedy | Dante Alighieri |
+ | 9971-5-0210-0 | A Tale of Two Cities | Charles Dickens |
+ | 960-425-059-0 | The Lord of the Rings | J. R. R. Tolkien |
+ | 80-902734-1-6 | And Then There Were None | Agatha Christie |
+
+.. versionadded:: 7.3
+
+ The ``markdown`` style was introduced in Symfony 7.3.
+
Making a Custom Table Style
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -456,9 +473,10 @@ The only requirement to append rows is that the table must be rendered inside a
use Symfony\Component\Console\Helper\Table;
// ...
- class SomeCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(OutputInterface $output): int
{
$section = $output->section();
$table = new Table($section);
diff --git a/components/console/helpers/tree.rst b/components/console/helpers/tree.rst
new file mode 100644
index 00000000000..5e08e684e51
--- /dev/null
+++ b/components/console/helpers/tree.rst
@@ -0,0 +1,336 @@
+Tree Helper
+===========
+
+The Tree Helper allows you to build and display tree structures in the console.
+It's commonly used to render directory hierarchies, but you can also use it to render
+any tree-like content, such us organizational charts, product category trees, taxonomies, etc.
+
+.. versionadded:: 7.3
+
+ The ``TreeHelper`` class was introduced in Symfony 7.3.
+
+Rendering a Tree
+----------------
+
+The :method:`Symfony\\Component\\Console\\Helper\\TreeHelper::createTree` method
+creates a tree structure from an array and returns a :class:`Symfony\\Component\\Console\\Helper\\Tree`
+object that can be rendered in the console.
+
+Rendering a Tree from an Array
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can build a tree from an array by passing the array to the
+:method:`Symfony\\Component\\Console\\Helper\\TreeHelper::createTree` method
+inside your console command::
+
+ namespace App\Command;
+
+ use Symfony\Component\Console\Attribute\AsCommand;
+ use Symfony\Component\Console\Helper\TreeHelper;
+ use Symfony\Component\Console\Helper\TreeNode;
+ use Symfony\Component\Console\Style\SymfonyStyle;
+
+ #[AsCommand(name: 'app:my-command', description: '...')]
+ class MyCommand
+ {
+ // ...
+
+ public function __invoke(SymfonyStyle $io): int
+ {
+ $node = TreeNode::fromValues([
+ 'config/',
+ 'public/',
+ 'src/',
+ 'templates/',
+ 'tests/',
+ ]);
+
+ $tree = TreeHelper::createTree($io, $node);
+ $tree->render();
+
+ // ...
+ }
+ }
+
+This exampe would output the following:
+
+.. code-block:: terminal
+
+ ├── config/
+ ├── public/
+ ├── src/
+ ├── templates/
+ └── tests/
+
+The given contents can be defined in a multi-dimensional array::
+
+ $tree = TreeHelper::createTree($io, null, [
+ 'src' => [
+ 'Command',
+ 'Controller' => [
+ 'DefaultController.php',
+ ],
+ 'Kernel.php',
+ ],
+ 'templates' => [
+ 'base.html.twig',
+ ],
+ ]);
+
+ $tree->render();
+
+The above code will output the following tree:
+
+.. code-block:: terminal
+
+ ├── src
+ │ ├── Command
+ │ ├── Controller
+ │ │ └── DefaultController.php
+ │ └── Kernel.php
+ └── templates
+ └── base.html.twig
+
+Building a Tree Programmatically
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you don't know the tree elements beforehand, you can build the tree programmatically
+by creating a new instance of the :class:`Symfony\\Component\\Console\\Helper\\Tree`
+class and adding nodes to it::
+
+ use Symfony\Component\Console\Helper\TreeHelper;
+ use Symfony\Component\Console\Helper\TreeNode;
+
+ $root = new TreeNode('my-project/');
+ // you can pass a string directly or create a TreeNode object
+ $root->addChild('src/');
+ $root->addChild(new TreeNode('templates/'));
+
+ // create nested structures by adding child nodes to other nodes
+ $testsNode = new TreeNode('tests/');
+ $functionalTestsNode = new TreeNode('Functional/');
+ $testsNode->addChild($functionalTestsNode);
+ $root->addChild($testsNode);
+
+ $tree = TreeHelper::createTree($io, $root);
+ $tree->render();
+
+This example outputs:
+
+.. code-block:: terminal
+
+ my-project/
+ ├── src/
+ ├── templates/
+ └── tests/
+ └── Functional/
+
+If you prefer, you can build the array of elements programmatically and then
+create and render the tree like this::
+
+ $tree = TreeHelper::createTree($io, null, $array);
+ $tree->render();
+
+You can also build part of the tree from an array and then add other nodes::
+
+ $node = TreeNode::fromValues($array);
+ $node->addChild('templates');
+ // ...
+ $tree = TreeHelper::createTree($io, $node);
+ $tree->render();
+
+Customizing the Tree Style
+--------------------------
+
+Built-in Tree Styles
+~~~~~~~~~~~~~~~~~~~~
+
+The tree helper provides a few built-in styles that you can use to customize the
+output of the tree.
+
+**Default**::
+
+ TreeHelper::createTree($io, $node, [], TreeStyle::default());
+
+This outputs:
+
+.. code-block:: terminal
+
+ ├── config
+ │ ├── packages
+ │ └── routes
+ │ ├── framework.yaml
+ │ └── web_profiler.yaml
+ ├── src
+ │ ├── Command
+ │ ├── Controller
+ │ │ └── DefaultController.php
+ │ └── Kernel.php
+ └── templates
+ └── base.html.twig
+
+**Box**::
+
+ TreeHelper::createTree($io, $node, [], TreeStyle::box());
+
+This outputs:
+
+.. code-block:: terminal
+
+ ┃╸ config
+ ┃ ┃╸ packages
+ ┃ ┗╸ routes
+ ┃ ┃╸ framework.yaml
+ ┃ ┗╸ web_profiler.yaml
+ ┃╸ src
+ ┃ ┃╸ Command
+ ┃ ┃╸ Controller
+ ┃ ┃ ┗╸ DefaultController.php
+ ┃ ┗╸ Kernel.php
+ ┗╸ templates
+ ┗╸ base.html.twig
+
+**Double box**::
+
+ TreeHelper::createTree($io, $node, [], TreeStyle::doubleBox());
+
+This outputs:
+
+.. code-block:: terminal
+
+ ╠═ config
+ ║ ╠═ packages
+ ║ ╚═ routes
+ ║ ╠═ framework.yaml
+ ║ ╚═ web_profiler.yaml
+ ╠═ src
+ ║ ╠═ Command
+ ║ ╠═ Controller
+ ║ ║ ╚═ DefaultController.php
+ ║ ╚═ Kernel.php
+ ╚═ templates
+ ╚═ base.html.twig
+
+**Compact**::
+
+ TreeHelper::createTree($io, $node, [], TreeStyle::compact());
+
+This outputs:
+
+.. code-block:: terminal
+
+ ├ config
+ │ ├ packages
+ │ └ routes
+ │ ├ framework.yaml
+ │ └ web_profiler.yaml
+ ├ src
+ │ ├ Command
+ │ ├ Controller
+ │ │ └ DefaultController.php
+ │ └ Kernel.php
+ └ templates
+ └ base.html.twig
+
+**Light**::
+
+ TreeHelper::createTree($io, $node, [], TreeStyle::light());
+
+This outputs:
+
+.. code-block:: terminal
+
+ |-- config
+ | |-- packages
+ | `-- routes
+ | |-- framework.yaml
+ | `-- web_profiler.yaml
+ |-- src
+ | |-- Command
+ | |-- Controller
+ | | `-- DefaultController.php
+ | `-- Kernel.php
+ `-- templates
+ `-- base.html.twig
+
+**Minimal**::
+
+ TreeHelper::createTree($io, $node, [], TreeStyle::minimal());
+
+This outputs:
+
+.. code-block:: terminal
+
+ . config
+ . . packages
+ . . routes
+ . . framework.yaml
+ . . web_profiler.yaml
+ . src
+ . . Command
+ . . Controller
+ . . . DefaultController.php
+ . . Kernel.php
+ . templates
+ . base.html.twig
+
+**Rounded**::
+
+ TreeHelper::createTree($io, $node, [], TreeStyle::rounded());
+
+This outputs:
+
+.. code-block:: terminal
+
+ ├─ config
+ │ ├─ packages
+ │ ╰─ routes
+ │ ├─ framework.yaml
+ │ ╰─ web_profiler.yaml
+ ├─ src
+ │ ├─ Command
+ │ ├─ Controller
+ │ │ ╰─ DefaultController.php
+ │ ╰─ Kernel.php
+ ╰─ templates
+ ╰─ base.html.twig
+
+Making a Custom Tree Style
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You can create your own tree style by passing the characters to the constructor
+of the :class:`Symfony\\Component\\Console\\Helper\\TreeStyle` class::
+
+ use Symfony\Component\Console\Helper\TreeHelper;
+ use Symfony\Component\Console\Helper\TreeStyle;
+
+ $customStyle = new TreeStyle('🟣 ', '🟠 ', '🔵 ', '🟢 ', '🔴 ', '🟡 ');
+
+ // Pass the custom style to the createTree method
+
+ $tree = TreeHelper::createTree($io, null, [
+ 'src' => [
+ 'Command',
+ 'Controller' => [
+ 'DefaultController.php',
+ ],
+ 'Kernel.php',
+ ],
+ 'templates' => [
+ 'base.html.twig',
+ ],
+ ], $customStyle);
+
+ $tree->render();
+
+The above code will output the following tree:
+
+.. code-block:: terminal
+
+ 🔵 🟣 🟡 src
+ 🔵 🟢 🟣 🟡 Command
+ 🔵 🟢 🟣 🟡 Controller
+ 🔵 🟢 🟢 🟠 🟡 DefaultController.php
+ 🔵 🟢 🟠 🟡 Kernel.php
+ 🔵 🟠 🟡 templates
+ 🔵 🔴 🟠 🟡 base.html.twig
diff --git a/components/console/logger.rst b/components/console/logger.rst
index 4af0b9a3f2f..cc182821a0a 100644
--- a/components/console/logger.rst
+++ b/components/console/logger.rst
@@ -34,7 +34,6 @@ You can rely on the logger to use this dependency inside a command::
use Acme\MyDependency;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Logger\ConsoleLogger;
use Symfony\Component\Console\Output\OutputInterface;
@@ -42,16 +41,16 @@ You can rely on the logger to use this dependency inside a command::
name: 'my:command',
description: 'Use an external dependency requiring a PSR-3 logger'
)]
- class MyCommand extends Command
+ class MyCommand
{
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(OutputInterface $output): int
{
$logger = new ConsoleLogger($output);
$myDependency = new MyDependency($logger);
$myDependency->doStuff();
- // ...
+ return Command::SUCCESS;
}
}
diff --git a/components/console/single_command_tool.rst b/components/console/single_command_tool.rst
index 97cb09bf030..9c6b06537e2 100644
--- a/components/console/single_command_tool.rst
+++ b/components/console/single_command_tool.rst
@@ -9,19 +9,18 @@ it is possible to remove this need by declaring a single command application::
setName('My Super Command') // Optional
->setVersion('1.0.0') // Optional
- ->addArgument('foo', InputArgument::OPTIONAL, 'The directory')
- ->addOption('bar', null, InputOption::VALUE_REQUIRED)
- ->setCode(function (InputInterface $input, OutputInterface $output): int {
+ ->setCode(function (OutputInterface $output, #[Argument] string $foo = 'The directory', #[Option] string $bar = ''): int {
// output arguments and options
+
+ return 0;
})
->run();
diff --git a/components/console/usage.rst b/components/console/usage.rst
index d7725e8926e..591994948b8 100644
--- a/components/console/usage.rst
+++ b/components/console/usage.rst
@@ -65,9 +65,17 @@ You can suppress output with:
.. code-block:: terminal
+ # suppresses all output, including errors
+ $ php application.php list --silent
+
+ # suppresses all output except errors
$ php application.php list --quiet
$ php application.php list -q
+.. versionadded:: 7.2
+
+ The ``--silent`` option was introduced in Symfony 7.2.
+
You can get more verbose messages (if this is supported for a command)
with:
diff --git a/components/css_selector.rst b/components/css_selector.rst
index 94561cec9bd..1331a11e616 100644
--- a/components/css_selector.rst
+++ b/components/css_selector.rst
@@ -92,11 +92,11 @@ Pseudo-classes are partially supported:
* Not supported: ``*:first-of-type``, ``*:last-of-type``, ``*:nth-of-type`` and
``*:nth-last-of-type`` (all these work with an element name (e.g.
``li:first-of-type``) but not with the ``*`` selector).
-* Supported: ``*:only-of-type``, ``*:scope``.
+* Supported: ``*:only-of-type``, ``*:scope``, ``*:is`` and ``*:where``.
-.. versionadded:: 6.3
+.. versionadded:: 7.1
- The support for ``*:scope`` was introduced in Symfony 6.3.
+ The support for ``*:is`` and ``*:where`` was introduced in Symfony 7.1.
Learn more
----------
diff --git a/components/dependency_injection/compilation.rst b/components/dependency_injection/compilation.rst
index f3a48b0ee8a..7f991e85b72 100644
--- a/components/dependency_injection/compilation.rst
+++ b/components/dependency_injection/compilation.rst
@@ -278,10 +278,6 @@ The parameter being deprecated must be set before being declared as deprecated.
Otherwise a :class:`Symfony\\Component\\DependencyInjection\\Exception\\ParameterNotFoundException`
exception will be thrown.
-.. versionadded:: 6.3
-
- The ``ContainerBuilder::deprecateParameter()`` method was introduced in Symfony 6.3.
-
.. note::
Just registering an extension with the container is not enough to get
diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst
index 00e5d8795ea..630d301302a 100644
--- a/components/dom_crawler.rst
+++ b/components/dom_crawler.rst
@@ -229,11 +229,6 @@ Access the value of the first node of the current selection::
// but you can get the unchanged text by passing FALSE as argument
$text = $crawler->filterXPath('//body/p')->innerText(false);
-.. versionadded:: 6.3
-
- The removal of whitespace characters by default in ``innerText()`` was
- introduced in Symfony 6.3.
-
Access the attribute value of the first node of the current selection::
$class = $crawler->filterXPath('//body/p')->attr('class');
@@ -245,11 +240,6 @@ Access the attribute value of the first node of the current selection::
$class = $crawler->filterXPath('//body/p')->attr('class', 'my-default-class');
- .. versionadded:: 6.4
-
- The possibility to specify a default value to the ``attr()`` method
- was introduced in Symfony 6.4.
-
Extract attribute and/or node values from the list of nodes::
$attributes = $crawler
@@ -672,10 +662,6 @@ parser, set its ``useHtml5Parser`` constructor argument to ``true``::
By doing so, the crawler will use the HTML5 parser provided by the `masterminds/html5`_
library to parse the documents.
-.. versionadded:: 6.3
-
- The ``useHtml5Parser`` argument was introduced in Symfony 6.3.
-
Learn more
----------
diff --git a/components/expression_language.rst b/components/expression_language.rst
index 7133932da28..b0dd10b0f42 100644
--- a/components/expression_language.rst
+++ b/components/expression_language.rst
@@ -106,6 +106,33 @@ if the expression is not valid::
$expressionLanguage->lint('1 + 2', []); // doesn't throw anything
+ $expressionLanguage->lint('1 + a', []);
+ // throws a SyntaxError exception:
+ // "Variable "a" is not valid around position 5 for expression `1 + a`."
+
+The behavior of these methods can be configured with some flags defined in the
+:class:`Symfony\\Component\\ExpressionLanguage\\Parser` class:
+
+* ``IGNORE_UNKNOWN_VARIABLES``: don't throw an exception if a variable is not
+ defined in the expression;
+* ``IGNORE_UNKNOWN_FUNCTIONS``: don't throw an exception if a function is not
+ defined in the expression.
+
+This is how you can use these flags::
+
+ use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
+ use Symfony\Component\ExpressionLanguage\Parser;
+
+ $expressionLanguage = new ExpressionLanguage();
+
+ // does not throw a SyntaxError because the unknown variables and functions are ignored
+ $expressionLanguage->lint('unknown_var + unknown_function()', [], Parser::IGNORE_UNKNOWN_VARIABLES | Parser::IGNORE_UNKNOWN_FUNCTIONS);
+
+.. versionadded:: 7.1
+
+ The support for flags in the ``parse()`` and ``lint()`` methods
+ was introduced in Symfony 7.1.
+
Passing in Variables
--------------------
diff --git a/components/filesystem.rst b/components/filesystem.rst
index de1075f0b36..4eae6aaad27 100644
--- a/components/filesystem.rst
+++ b/components/filesystem.rst
@@ -313,6 +313,22 @@ contents at the end of some file::
If either the file or its containing directory doesn't exist, this method
creates them before appending the contents.
+``readFile``
+~~~~~~~~~~~~
+
+.. versionadded:: 7.1
+
+ The ``readFile()`` method was introduced in Symfony 7.1.
+
+:method:`Symfony\\Component\\Filesystem\\Filesystem::readFile` returns all the
+contents of a file as a string. Unlike the :phpfunction:`file_get_contents` function
+from PHP, it throws an exception when the given file path is not readable and
+when passing the path to a directory instead of a file::
+
+ $contents = $filesystem->readFile('/some/path/to/file.txt');
+
+The ``$contents`` variable now stores all the contents of the ``file.txt`` file.
+
Path Manipulation Utilities
---------------------------
diff --git a/components/finder.rst b/components/finder.rst
index 7cc580333e7..cecc597ac64 100644
--- a/components/finder.rst
+++ b/components/finder.rst
@@ -361,10 +361,6 @@ using a closure, return ``false`` for the directories which you want to prune.
Pruning directories early can improve performance significantly depending on the
file/directory hierarchy complexity and the number of excluded directories.
-.. versionadded:: 6.4
-
- The feature to prune directories was introduced in Symfony 6.4.
-
Sorting Results
---------------
@@ -376,11 +372,6 @@ Sort the results by name, extension, size or type (directories first, then files
$finder->sortBySize();
$finder->sortByType();
-.. versionadded:: 6.2
-
- The ``sortByCaseInsensitiveName()``, ``sortByExtension()`` and ``sortBySize()``
- methods were introduced in Symfony 6.2.
-
.. tip::
By default, the ``sortByName()`` method uses the :phpfunction:`strcmp` PHP
diff --git a/components/form.rst b/components/form.rst
index e4b1c9a67e9..44f407e4c8e 100644
--- a/components/form.rst
+++ b/components/form.rst
@@ -123,8 +123,7 @@ The following snippet adds CSRF protection to the form factory::
use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage;
// creates a RequestStack object using the current request
- $requestStack = new RequestStack();
- $requestStack->push($request);
+ $requestStack = new RequestStack([$request]);
$csrfGenerator = new UriSafeTokenGenerator();
$csrfStorage = new SessionTokenStorage($requestStack);
@@ -135,6 +134,11 @@ The following snippet adds CSRF protection to the form factory::
->addExtension(new CsrfExtension($csrfManager))
->getFormFactory();
+.. versionadded:: 7.2
+
+ Support for passing requests to the constructor of the ``RequestStack``
+ class was introduced in Symfony 7.2.
+
Internally, this extension will automatically add a hidden field to every
form (called ``_token`` by default) whose value is automatically generated by
the CSRF generator and validated when binding the form.
@@ -749,10 +753,11 @@ method to access the list of errors. It returns a
// "firstName" field
$errors = $form['firstName']->getErrors();
- // a FormErrorIterator instance in a flattened structure
+ // a FormErrorIterator instance including child forms in a flattened structure
+ // use getOrigin() to determine the form causing the error
$errors = $form->getErrors(true);
- // a FormErrorIterator instance representing the form tree structure
+ // a FormErrorIterator instance including child forms without flattening the output structure
$errors = $form->getErrors(true, false);
Clearing Form Errors
diff --git a/components/http_foundation.rst b/components/http_foundation.rst
index 14843bab346..1cb87aafb24 100644
--- a/components/http_foundation.rst
+++ b/components/http_foundation.rst
@@ -145,20 +145,12 @@ has some methods to filter the input values:
:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::getString`
Returns the parameter value as a string;
-.. versionadded:: 6.3
-
- The ``ParameterBag::getEnum()`` and ``ParameterBag::getString()`` methods
- were introduced in Symfony 6.3.
-
:method:`Symfony\\Component\\HttpFoundation\\ParameterBag::filter`
Filters the parameter by using the PHP :phpfunction:`filter_var` function.
-
- .. deprecated:: 6.3
-
- Ignoring invalid values when using ``filter()`` is deprecated and will throw
- a :class:`Symfony\\Component\\HttpKernel\\Exception\\BadRequestHttpException`
- in Symfony 7.0. You can use the ``FILTER_NULL_ON_FAILURE`` flag to keep
- ignoring them.
+ If invalid values are found, a
+ :class:`Symfony\\Component\\HttpKernel\\Exception\\BadRequestHttpException`
+ is thrown. The ``FILTER_NULL_ON_FAILURE`` flag can be used to ignore invalid
+ values.
All getters take up to two arguments: the first one is the parameter name
and the second one is the default value to return if the parameter does not
@@ -223,11 +215,6 @@ wrapping this data::
$data = $request->getPayload();
-.. versionadded:: 6.3
-
- The :method:`Symfony\\Component\\HttpFoundation\\Request::getPayload`
- method was introduced in Symfony 6.3.
-
Identifying a Request
~~~~~~~~~~~~~~~~~~~~~
@@ -375,6 +362,25 @@ analysis purposes. Use the ``anonymize()`` method from the
$anonymousIpv6 = IpUtils::anonymize($ipv6);
// $anonymousIpv6 = '2a01:198:603:10::'
+If you need even more anonymization, you can use the second and third parameters
+of the ``anonymize()`` method to specify the number of bytes that should be
+anonymized depending on the IP address format::
+
+ $ipv4 = '123.234.235.236';
+ $anonymousIpv4 = IpUtils::anonymize($ipv4, 3);
+ // $anonymousIpv4 = '123.0.0.0'
+
+ $ipv6 = '2a01:198:603:10:396e:4789:8e99:890f';
+ // (you must define the second argument (bytes to anonymize in IPv4 addresses)
+ // even when you are only anonymizing IPv6 addresses)
+ $anonymousIpv6 = IpUtils::anonymize($ipv6, 3, 10);
+ // $anonymousIpv6 = '2a01:198:603::'
+
+.. versionadded:: 7.2
+
+ The ``v4Bytes`` and ``v6Bytes`` parameters of the ``anonymize()`` method
+ were introduced in Symfony 7.2.
+
Check If an IP Belongs to a CIDR Subnet
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -410,10 +416,6 @@ use the ``isPrivateIp()`` method from the
$isPrivate = IpUtils::isPrivateIp($ipv6);
// $isPrivate = false
-.. versionadded:: 6.3
-
- The ``isPrivateIp()`` method was introduced in Symfony 6.3.
-
Matching a Request Against a Set of Rules
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -423,17 +425,18 @@ address, it uses a certain HTTP method, etc.):
* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\AttributesRequestMatcher`
* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\ExpressionRequestMatcher`
+* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\HeaderRequestMatcher`
* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\HostRequestMatcher`
* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\IpsRequestMatcher`
* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\IsJsonRequestMatcher`
* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\MethodRequestMatcher`
* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\PathRequestMatcher`
* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\PortRequestMatcher`
+* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\QueryParameterRequestMatcher`
* :class:`Symfony\\Component\\HttpFoundation\\RequestMatcher\\SchemeRequestMatcher`
You can use them individually or combine them using the
-:class:`Symfony\\Component\\HttpFoundation\\ChainRequestMatcher`
-class::
+:class:`Symfony\\Component\\HttpFoundation\\ChainRequestMatcher` class::
use Symfony\Component\HttpFoundation\ChainRequestMatcher;
use Symfony\Component\HttpFoundation\RequestMatcher\HostRequestMatcher;
@@ -456,6 +459,11 @@ class::
// ...
}
+.. versionadded:: 7.1
+
+ The ``HeaderRequestMatcher`` and ``QueryParameterRequestMatcher`` were
+ introduced in Symfony 7.1.
+
Accessing other Data
~~~~~~~~~~~~~~~~~~~~
@@ -555,12 +563,6 @@ your application to see which exceptions are thrown in listeners of the
more about it in
:ref:`the dedicated section about Kernel events `.
-.. versionadded:: 6.4
-
- The ``$flush`` parameter of the
- :method:`Symfony\\Component\\HttpFoundation\\Response::send` method
- was introduced in Symfony 6.4.
-
Setting Cookies
~~~~~~~~~~~~~~~
@@ -601,11 +603,6 @@ It is possible to define partitioned cookies, also known as `CHIPS`_, by using t
// you can also set the partitioned argument to true when using the `create()` factory method
$cookie = Cookie::create('name', 'value', partitioned: true);
-.. versionadded:: 6.4
-
- The :method:`Symfony\\Component\\HttpFoundation\\Cookie::withPartitioned`
- method was introduced in Symfony 6.4.
-
Managing the HTTP Cache
~~~~~~~~~~~~~~~~~~~~~~~
@@ -654,11 +651,6 @@ call::
'etag' => 'abcdef',
]);
-.. versionadded:: 6.1
-
- The ``stale_if_error`` and ``stale_while_revalidate`` options were
- introduced in Symfony 6.1.
-
To check if the Response validators (``ETag``, ``Last-Modified``) match a
conditional value specified in the client Request, use the
:method:`Symfony\\Component\\HttpFoundation\\Response::isNotModified`
@@ -689,8 +681,19 @@ Streaming a Response
~~~~~~~~~~~~~~~~~~~~
The :class:`Symfony\\Component\\HttpFoundation\\StreamedResponse` class allows
-you to stream the Response back to the client. The response content is
-represented by a PHP callable instead of a string::
+you to stream the Response back to the client. The response content can be
+represented by a string iterable::
+
+ use Symfony\Component\HttpFoundation\StreamedResponse;
+
+ $chunks = ['Hello', ' World'];
+
+ $response = new StreamedResponse();
+ $response->setChunks($chunks);
+ $response->send();
+
+For most complex use cases, the response content can be instead represented by
+a PHP callable::
use Symfony\Component\HttpFoundation\StreamedResponse;
@@ -718,13 +721,12 @@ represented by a PHP callable instead of a string::
// disables FastCGI buffering in nginx only for this response
$response->headers->set('X-Accel-Buffering', 'no');
-Streaming a JSON Response
-~~~~~~~~~~~~~~~~~~~~~~~~~
+.. versionadded:: 7.3
-.. versionadded:: 6.3
+ Support for using string iterables was introduced in Symfony 7.3.
- The :class:`Symfony\\Component\\HttpFoundation\\StreamedJsonResponse` class was
- introduced in Symfony 6.3.
+Streaming a JSON Response
+~~~~~~~~~~~~~~~~~~~~~~~~~
The :class:`Symfony\\Component\\HttpFoundation\\StreamedJsonResponse` allows to
stream large JSON responses using PHP generators to keep the used resources low.
@@ -738,9 +740,9 @@ containing JSON serializable data::
// any method or function returning a PHP Generator
function loadArticles(): \Generator {
- yield ['title' => 'Article 1'];
- yield ['title' => 'Article 2'];
- yield ['title' => 'Article 3'];
+ yield ['title' => 'Article 1'];
+ yield ['title' => 'Article 2'];
+ yield ['title' => 'Article 3'];
};
$response = new StreamedJsonResponse(
@@ -815,10 +817,6 @@ including generators::
return new StreamedJsonResponse(loadArticles());
}
-.. versionadded:: 6.4
-
- The ``StreamedJsonResponse`` support of iterables was introduced in Symfony 6.4.
-
.. _component-http-foundation-serving-files:
Serving Files
@@ -896,6 +894,23 @@ It is possible to delete the file after the response is sent with the
:method:`Symfony\\Component\\HttpFoundation\\BinaryFileResponse::deleteFileAfterSend` method.
Please note that this will not work when the ``X-Sendfile`` header is set.
+Alternatively, ``BinaryFileResponse`` supports instances of ``\SplTempFileObject``.
+This is useful when you want to serve a file that has been created in memory
+and that will be automatically deleted after the response is sent::
+
+ use Symfony\Component\HttpFoundation\BinaryFileResponse;
+
+ $file = new \SplTempFileObject();
+ $file->fwrite('Hello World');
+ $file->rewind();
+
+ $response = new BinaryFileResponse($file);
+
+.. versionadded:: 7.1
+
+ The support for ``\SplTempFileObject`` in ``BinaryFileResponse``
+ was introduced in Symfony 7.1.
+
If the size of the served file is unknown (e.g. because it's being generated on the fly,
or because a PHP stream filter is registered on it, etc.), you can pass a ``Stream``
instance to ``BinaryFileResponse``. This will disable ``Range`` and ``Content-Length``
diff --git a/components/http_kernel.rst b/components/http_kernel.rst
index 91643086a18..62d1e92d89b 100644
--- a/components/http_kernel.rst
+++ b/components/http_kernel.rst
@@ -261,18 +261,6 @@ on the request's information.
b) A new instance of your controller class is instantiated with no
constructor arguments.
- c) If the controller implements :class:`Symfony\\Component\\DependencyInjection\\ContainerAwareInterface`,
- ``setContainer()`` is called on the controller object and the container
- is passed to it. This step is also specific to the :class:`Symfony\\Bundle\\FrameworkBundle\\Controller\\ControllerResolver`
- sub-class used by the Symfony Framework.
-
-.. deprecated:: 6.4
-
- :class:`Symfony\\Component\\DependencyInjection\\ContainerAwareInterface` and
- :class:`Symfony\\Component\\DependencyInjection\\ContainerAwareTrait` are
- deprecated since Symfony 6.4. Dependency injection should be used instead to
- access the service container.
-
.. _component-http-kernel-kernel-controller:
3) The ``kernel.controller`` Event
@@ -293,10 +281,6 @@ Another typical use-case for this event is to retrieve the attributes from
the controller using the :method:`Symfony\\Component\\HttpKernel\\Event\\ControllerEvent::getAttributes`
method. See the Symfony section below for some examples.
-.. versionadded:: 6.2
-
- The ``ControllerEvent::getAttributes()`` method was introduced in Symfony 6.2.
-
Listeners to this event can also change the controller callable completely
by calling :method:`ControllerEvent::setController `
on the event object that's passed to listeners on this event.
@@ -356,13 +340,6 @@ of arguments that should be passed when executing that callable.
``ValueResolverInterface`` yourself and passing this to the
``ArgumentResolver``, you can extend this functionality.
- .. versionadded:: 6.2
-
- The ``ValueResolverInterface`` was introduced in Symfony 6.2. Prior to
- 6.2, you had to use the
- :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface`,
- which defines different methods.
-
.. _component-http-kernel-calling-controller:
5) Calling the Controller
@@ -543,6 +520,17 @@ comes with an :class:`Symfony\\Component\\HttpKernel\\EventListener\\ErrorListen
which if you choose to use, will do this and more by default (see the sidebar
below for more details).
+The :class:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent` exposes the
+:method:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent::isKernelTerminating`
+method, which you can use to determine if the kernel is currently terminating
+at the moment the exception was thrown.
+
+.. versionadded:: 7.1
+
+ The
+ :method:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent::isKernelTerminating`
+ method was introduced in Symfony 7.1.
+
.. note::
When setting a response for the ``kernel.exception`` event, the propagation
diff --git a/components/intl.rst b/components/intl.rst
index f69284c1a0e..ba3cbdcb959 100644
--- a/components/intl.rst
+++ b/components/intl.rst
@@ -28,7 +28,6 @@ This component provides the following ICU data:
* `Locales`_
* `Currencies`_
* `Timezones`_
-* `Emoji Transliteration`_
Language and Script Names
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -203,10 +202,6 @@ numeric country codes::
$exists = Countries::numericCodeExists('250');
// => true
-.. versionadded:: 6.4
-
- The support for numeric country codes was introduced in Symfony 6.4.
-
Locales
~~~~~~~
@@ -390,61 +385,22 @@ to catching the exception, you can also check if a given timezone ID is valid::
Emoji Transliteration
~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 6.2
-
- The Emoji transliteration feature was introduced in Symfony 6.2.
-
-The ``EmojiTransliterator`` class provides a utility to translate emojis into
-their textual representation in all languages based on the `Unicode CLDR dataset`_::
-
- use Symfony\Component\Intl\Transliterator\EmojiTransliterator;
-
- // describe emojis in English
- $transliterator = EmojiTransliterator::create('en');
- $transliterator->transliterate('Menus with 🍕 or 🍝');
- // => 'Menus with pizza or spaghetti'
-
- // describe emojis in Ukrainian
- $transliterator = EmojiTransliterator::create('uk');
- $transliterator->transliterate('Menus with 🍕 or 🍝');
- // => 'Menus with піца or спагеті'
-
-The ``EmojiTransliterator`` class also provides two extra catalogues: ``github``
-and ``slack`` that converts any emojis to the corresponding short code in those
-platforms::
+Symfony provides utilities to translate emojis into their textual representation
+in all languages. Read the documentation about :ref:`emoji transliteration `
+to learn more about this feature.
- use Symfony\Component\Intl\Transliterator\EmojiTransliterator;
-
- // describe emojis in Slack short code
- $transliterator = EmojiTransliterator::create('slack');
- $transliterator->transliterate('Menus with 🥗 or 🧆');
- // => 'Menus with :green_salad: or :falafel:'
-
- // describe emojis in Github short code
- $transliterator = EmojiTransliterator::create('github');
- $transliterator->transliterate('Menus with 🥗 or 🧆');
- // => 'Menus with :green_salad: or :falafel:'
-
-.. tip::
-
- Combine this emoji transliterator with the :ref:`Symfony String slugger `
- to improve the slugs of contents that include emojis (e.g. for URLs).
+Disk Space
+----------
-The data needed to store the transliteration of all emojis (~5,000) into all
-languages take a considerable disk space. If you need to save disk space (e.g.
-because you deploy to some service with tight size constraints), run this command
-(e.g. as an automated script after ``composer install``) to compress the internal
-Symfony emoji data files using the PHP ``zlib`` extension:
+If you need to save disk space (e.g. because you deploy to some service with tight size
+constraints), run this command (e.g. as an automated script after ``composer install``) to compress the
+internal Symfony Intl data files using the PHP ``zlib`` extension:
.. code-block:: terminal
# adjust the path to the 'compress' binary based on your application installation
$ php ./vendor/symfony/intl/Resources/bin/compress
-.. versionadded:: 6.3
-
- The ``compress`` binary was introduced in Symfony 6.3.
-
Learn more
----------
@@ -467,4 +423,3 @@ Learn more
.. _`daylight saving time (DST)`: https://en.wikipedia.org/wiki/Daylight_saving_time
.. _`ISO 639-1 alpha-2`: https://en.wikipedia.org/wiki/ISO_639-1
.. _`ISO 639-2 alpha-3 (2T)`: https://en.wikipedia.org/wiki/ISO_639-2
-.. _`Unicode CLDR dataset`: https://github.com/unicode-org/cldr
diff --git a/components/json_path.rst b/components/json_path.rst
new file mode 100644
index 00000000000..9db8e48885e
--- /dev/null
+++ b/components/json_path.rst
@@ -0,0 +1,330 @@
+The JsonPath Component
+======================
+
+.. versionadded:: 7.3
+
+ The JsonPath component was introduced in Symfony 7.3 as an
+ :doc:`experimental feature `.
+
+The JsonPath component lets you query and extract data from JSON structures.
+It implements the `RFC 9535 – JSONPath`_ standard, allowing you to navigate
+complex JSON data.
+
+Similar to the :doc:`DomCrawler component `, which lets
+you navigate and query HTML or XML documents with XPath, the JsonPath component
+offers the same convenience for traversing and searching JSON structures through
+JSONPath expressions. The component also provides an abstraction layer for data
+extraction.
+
+Installation
+------------
+
+You can install the component in your project using Composer:
+
+.. code-block:: terminal
+
+ $ composer require symfony/json-path
+
+.. include:: /components/require_autoload.rst.inc
+
+Usage
+-----
+
+To start querying a JSON document, first create a :class:`Symfony\\Component\\JsonPath\\JsonCrawler`
+object from a JSON string. The following examples use this sample "bookstore"
+JSON data::
+
+ use Symfony\Component\JsonPath\JsonCrawler;
+
+ $json = <<<'JSON'
+ {
+ "store": {
+ "book": [
+ {
+ "category": "reference",
+ "author": "Nigel Rees",
+ "title": "Sayings of the Century",
+ "price": 8.95
+ },
+ {
+ "category": "fiction",
+ "author": "Evelyn Waugh",
+ "title": "Sword of Honour",
+ "price": 12.99
+ },
+ {
+ "category": "fiction",
+ "author": "Herman Melville",
+ "title": "Moby Dick",
+ "isbn": "0-553-21311-3",
+ "price": 8.99
+ },
+ {
+ "category": "fiction",
+ "author": "John Ronald Reuel Tolkien",
+ "title": "The Lord of the Rings",
+ "isbn": "0-395-19395-8",
+ "price": 22.99
+ }
+ ],
+ "bicycle": {
+ "color": "red",
+ "price": 399
+ }
+ }
+ }
+ JSON;
+
+ $crawler = new JsonCrawler($json);
+
+Once you have the crawler instance, use its :method:`Symfony\\Component\\JsonPath\\JsonCrawler::find`
+method to start querying the data. This method returns an array of matching values.
+
+Querying with Expressions
+-------------------------
+
+The primary way to query the JSON is by passing a JSONPath expression string
+to the :method:`Symfony\\Component\\JsonPath\\JsonCrawler::find` method.
+
+Accessing a Specific Property
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Use dot notation for object keys and square brackets for array indices. The root
+of the document is represented by ``$``::
+
+ // get the title of the first book in the store
+ $titles = $crawler->find('$.store.book[0].title');
+
+ // $titles is ['Sayings of the Century']
+
+Dot notation is the default, but JSONPath provides other syntaxes for cases
+where it doesn't work. Use bracket notation (``['...']``) when a key contains
+spaces or special characters::
+
+ // this is equivalent to the previous example
+ $titles = $crawler->find('$["store"]["book"][0]["title"]');
+
+ // this expression requires brackets because some keys use dots or spaces
+ $titles = $crawler->find('$["store"]["book collection"][0]["title.original"]');
+
+ // you can combine both notations
+ $titles = $crawler->find('$["store"].book[0].title');
+
+Searching with the Descendant Operator
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The descendant operator (``..``) recursively searches for a given key, allowing
+you to find values without specifying the full path::
+
+ // get all authors from anywhere in the document
+ $authors = $crawler->find('$..author');
+
+ // $authors is ['Nigel Rees', 'Evelyn Waugh', 'Herman Melville', 'John Ronald Reuel Tolkien']
+
+Filtering Results
+~~~~~~~~~~~~~~~~~
+
+JSONPath includes a filter syntax (``?(expression)``) to select items based on
+a condition. The current item within the filter is referenced by ``@``::
+
+ // get all books with a price less than 10
+ $cheapBooks = $crawler->find('$.store.book[?(@.price < 10)]');
+
+Building Queries Programmatically
+---------------------------------
+
+For more dynamic or complex query building, use the fluent API provided
+by the :class:`Symfony\\Component\\JsonPath\\JsonPath` class. This lets you
+construct a query object step by step. The ``JsonPath`` object can then be passed
+to the crawler's :method:`Symfony\\Component\\JsonPath\\JsonCrawler::find` method.
+
+The main advantage of the programmatic builder is that it automatically handles
+escaping of keys and values, preventing syntax errors::
+
+ use Symfony\Component\JsonPath\JsonPath;
+
+ $path = (new JsonPath())
+ ->key('store') // selects the 'store' key
+ ->key('book') // then the 'book' key
+ ->index(1); // then the second item (indexes start at 0)
+
+ // the created $path object is equivalent to the string '$["store"]["book"][1]'
+ $book = $crawler->find($path);
+
+ // $book contains the book object for "Sword of Honour"
+
+The :class:`Symfony\\Component\\JsonPath\\JsonPath` class provides several
+methods to build your query:
+
+* :method:`Symfony\\Component\\JsonPath\\JsonPath::key`
+ Adds a key selector. The key name is properly escaped::
+
+ // creates the path '$["key\"with\"quotes"]'
+ $path = (new JsonPath())->key('key"with"quotes');
+
+* :method:`Symfony\\Component\\JsonPath\\JsonPath::deepScan`
+ Adds the descendant operator ``..`` to perform a recursive search from the
+ current point in the path::
+
+ // get all prices in the store: '$["store"]..["price"]'
+ $path = (new JsonPath())->key('store')->deepScan()->key('price');
+
+* :method:`Symfony\\Component\\JsonPath\\JsonPath::all`
+ Adds the wildcard operator ``[*]`` to select all items in an array or object::
+
+ // creates the path '$["store"]["book"][*]'
+ $path = (new JsonPath())->key('store')->key('book')->all();
+
+* :method:`Symfony\\Component\\JsonPath\\JsonPath::index`
+ Adds an array index selector. Index numbers start at ``0``.
+
+* :method:`Symfony\\Component\\JsonPath\\JsonPath::first` /
+ :method:`Symfony\\Component\\JsonPath\\JsonPath::last`
+ Shortcuts for ``index(0)`` and ``index(-1)`` respectively::
+
+ // get the last book: '$["store"]["book"][-1]'
+ $path = (new JsonPath())->key('store')->key('book')->last();
+
+* :method:`Symfony\\Component\\JsonPath\\JsonPath::slice`
+ Adds an array slice selector ``[start:end:step]``::
+
+ // get books from index 1 up to (but not including) index 3
+ // creates the path '$["store"]["book"][1:3]'
+ $path = (new JsonPath())->key('store')->key('book')->slice(1, 3);
+
+ // get every second book from the first four books
+ // creates the path '$["store"]["book"][0:4:2]'
+ $path = (new JsonPath())->key('store')->key('book')->slice(0, 4, 2);
+
+* :method:`Symfony\\Component\\JsonPath\\JsonPath::filter`
+ Adds a filter expression. The expression string is the part that goes inside
+ the ``?()`` syntax::
+
+ // get expensive books: '$["store"]["book"][?(@.price > 20)]'
+ $path = (new JsonPath())
+ ->key('store')
+ ->key('book')
+ ->filter('@.price > 20');
+
+Advanced Querying
+-----------------
+
+For a complete overview of advanced operators like wildcards and functions within
+filters, refer to the `Querying with Expressions`_ section above. All these
+features are supported and can be combined with the programmatic builder when
+appropriate (e.g., inside a ``filter()`` expression).
+
+Testing with JSON Assertions
+----------------------------
+
+The component provides a set of PHPUnit assertions to make testing JSON data
+more convenient. Use the :class:`Symfony\\Component\\JsonPath\\Test\\JsonPathAssertionsTrait`
+in your test class::
+
+ use PHPUnit\Framework\TestCase;
+ use Symfony\Component\JsonPath\Test\JsonPathAssertionsTrait;
+
+ class MyTest extends TestCase
+ {
+ use JsonPathAssertionsTrait;
+
+ public function testSomething(): void
+ {
+ $json = '{"books": [{"title": "A"}, {"title": "B"}]}';
+
+ self::assertJsonPathCount(2, '$.books[*]', $json);
+ }
+ }
+
+The trait provides the following assertion methods:
+
+* :method:`Symfony\\Component\\JsonPath\\Test\\JsonPathAssertionsTrait::assertJsonPathCount`
+ Asserts that the number of elements found by the JSONPath expression matches
+ an expected count::
+
+ $json = '{"a": [1, 2, 3]}';
+ self::assertJsonPathCount(3, '$.a[*]', $json);
+
+* :method:`Symfony\\Component\\JsonPath\\Test\\JsonPathAssertionsTrait::assertJsonPathEquals`
+ Asserts that the result of a JSONPath expression is equal to an expected
+ value. The comparison uses ``==`` (type coercion) instead of ``===``::
+
+ $json = '{"a": [1, 2, 3]}';
+
+ // passes because "1" == 1
+ self::assertJsonPathEquals(['1'], '$.a[0]', $json);
+
+* :method:`Symfony\\Component\\JsonPath\\Test\\JsonPathAssertionsTrait::assertJsonPathNotEquals`
+ Asserts that the result of a JSONPath expression is not equal to an expected
+ value. The comparison uses ``!=`` (type coercion) instead of ``!==``::
+
+ $json = '{"a": [1, 2, 3]}';
+ self::assertJsonPathNotEquals([42], '$.a[0]', $json);
+
+* :method:`Symfony\\Component\\JsonPath\\Test\\JsonPathAssertionsTrait::assertJsonPathSame`
+ Asserts that the result of a JSONPath expression is identical (``===``) to an
+ expected value. This is a strict comparison and does not perform type
+ coercion::
+
+ $json = '{"a": [1, 2, 3]}';
+
+ // fails because "1" !== 1
+ // self::assertJsonPathSame(['1'], '$.a[0]', $json);
+
+ self::assertJsonPathSame([1], '$.a[0]', $json);
+
+* :method:`Symfony\\Component\\JsonPath\\Test\\JsonPathAssertionsTrait::assertJsonPathNotSame`
+ Asserts that the result of a JSONPath expression is not identical (``!==``) to
+ an expected value::
+
+ $json = '{"a": [1, 2, 3]}';
+ self::assertJsonPathNotSame(['1'], '$.a[0]', $json);
+
+* :method:`Symfony\\Component\\JsonPath\\Test\\JsonPathAssertionsTrait::assertJsonPathContains`
+ Asserts that a given value is found within the array of results from the
+ JSONPath expression::
+
+ $json = '{"tags": ["php", "symfony", "json"]}';
+ self::assertJsonPathContains('symfony', '$.tags[*]', $json);
+
+* :method:`Symfony\\Component\\JsonPath\\Test\\JsonPathAssertionsTrait::assertJsonPathNotContains`
+ Asserts that a given value is NOT found within the array of results from the
+ JSONPath expression::
+
+ $json = '{"tags": ["php", "symfony", "json"]}';
+ self::assertJsonPathNotContains('java', '$.tags[*]', $json);
+
+Error Handling
+--------------
+
+The component throws specific exceptions for invalid input or queries:
+
+* :class:`Symfony\\Component\\JsonPath\\Exception\\InvalidArgumentException`:
+ Thrown if the input to the ``JsonCrawler`` constructor is not a valid JSON string;
+* :class:`Symfony\\Component\\JsonPath\\Exception\\InvalidJsonStringInputException`:
+ Thrown during a ``find()`` call if the JSON string is malformed (e.g., syntax error);
+* :class:`Symfony\\Component\\JsonPath\\Exception\\JsonCrawlerException`:
+ Thrown for errors within the JsonPath expression itself, such as using an
+ unknown function
+
+Example of handling errors::
+
+ use Symfony\Component\JsonPath\Exception\InvalidJsonStringInputException;
+ use Symfony\Component\JsonPath\Exception\JsonCrawlerException;
+
+ try {
+ // the following line contains malformed JSON
+ $crawler = new JsonCrawler('{"store": }');
+ $crawler->find('$..*');
+ } catch (InvalidJsonStringInputException $e) {
+ // ... handle error
+ }
+
+ try {
+ // the following line contains an invalid query
+ $crawler->find('$.store.book[?unknown_function(@.price)]');
+ } catch (JsonCrawlerException $e) {
+ // ... handle error
+ }
+
+.. _`RFC 9535 – JSONPath`: https://datatracker.ietf.org/doc/html/rfc9535
diff --git a/components/ldap.rst b/components/ldap.rst
index f5a142ced9f..e52a341986c 100644
--- a/components/ldap.rst
+++ b/components/ldap.rst
@@ -74,6 +74,19 @@ distinguished name (DN) and the password of a user::
When the LDAP server allows unauthenticated binds, a blank password will always be valid.
+You can also use the :method:`Symfony\\Component\\Ldap\\Ldap::saslBind` method
+for binding to an LDAP server using `SASL`_::
+
+ // this method defines other optional arguments like $mech, $realm, $authcId, etc.
+ $ldap->saslBind($dn, $password);
+
+After binding to the LDAP server, you can use the :method:`Symfony\\Component\\Ldap\\Ldap::whoami`
+method to get the distinguished name (DN) of the authenticated and authorized user.
+
+.. versionadded:: 7.2
+
+ The ``saslBind()`` and ``whoami()`` methods were introduced in Symfony 7.2.
+
Once bound (or if you enabled anonymous authentication on your
LDAP server), you may query the LDAP server using the
:method:`Symfony\\Component\\Ldap\\Ldap::query` method::
@@ -183,3 +196,5 @@ Possible operation types are ``LDAP_MODIFY_BATCH_ADD``, ``LDAP_MODIFY_BATCH_REMO
``LDAP_MODIFY_BATCH_REMOVE_ALL``, ``LDAP_MODIFY_BATCH_REPLACE``. Parameter
``$values`` must be ``NULL`` when using ``LDAP_MODIFY_BATCH_REMOVE_ALL``
operation type.
+
+.. _`SASL`: https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer
diff --git a/components/lock.rst b/components/lock.rst
index fb7efeb2b77..e9fe61ecd1a 100644
--- a/components/lock.rst
+++ b/components/lock.rst
@@ -403,8 +403,14 @@ Store Scope Blocking Ex
.. tip::
- A special ``InMemoryStore`` is available for saving locks in memory during
- a process, and can be useful for testing.
+ Symfony includes two other special stores that are mostly useful for testing:
+
+ * ``InMemoryStore`` (``LOCK_DSN=in-memory``), which saves locks in memory during a process;
+ * ``NullStore`` (``LOCK_DSN=null``) which doesn't persist anything.
+
+.. versionadded:: 7.2
+
+ The :class:`Symfony\\Component\\Lock\\Store\\NullStore` was introduced in Symfony 7.2.
.. _lock-store-flock:
@@ -473,21 +479,15 @@ avoid stalled locks::
The ``MongoDbStore`` takes the following ``$options`` (depending on the first parameter type):
-============== ================================================================================================
-Option Description
-============== ================================================================================================
-gcProbability Should a TTL Index be created expressed as a probability from 0.0 to 1.0 (Defaults to ``0.001``)
-gcProbablity Same as ``gcProbability``, see the deprecation note below
-database The name of the database
-collection The name of the collection
-uriOptions Array of URI options for `MongoDBClient::__construct`_
-driverOptions Array of driver options for `MongoDBClient::__construct`_
-============= ================================================================================================
-
-.. deprecated:: 6.3
-
- The ``gcProbablity`` option (notice the typo in its name) is deprecated since
- Symfony 6.3, use the ``gcProbability`` option instead.
+============= ================================================================================================
+Option Description
+============= ================================================================================================
+gcProbability Should a TTL Index be created expressed as a probability from 0.0 to 1.0 (Defaults to ``0.001``)
+database The name of the database
+collection The name of the collection
+uriOptions Array of URI options for `MongoDBClient::__construct`_
+driverOptions Array of driver options for `MongoDBClient::__construct`_
+============= ================================================================================================
When the first parameter is a:
@@ -561,11 +561,6 @@ the command:
$ php bin/console make:migration
-.. versionadded:: 6.3
-
- The automatic table generation when running the ``make:migration`` command
- was introduced in Symfony 6.3.
-
If you prefer to create the table yourself and it has not already been created, you can
create this table explicitly by calling the
:method:`Symfony\\Component\\Lock\\Store\\DoctrineDbalStore::createTable` method.
@@ -617,14 +612,10 @@ store locks and does not expire.
RedisStore
~~~~~~~~~~
-.. versionadded:: 6.3
-
- ``\Relay\Relay`` support was introduced in Symfony 6.3.
-
The RedisStore saves locks on a Redis server, it requires a Redis connection
-implementing the ``\Redis``, ``\RedisArray``, ``\RedisCluster``, ``\Relay\Relay`` or
-``\Predis`` classes. This store does not support blocking, and expects a TTL to
-avoid stalled locks::
+implementing the ``\Redis``, ``\RedisArray``, ``\RedisCluster``, ``\Relay\Relay``,
+``\Relay\Cluster`` or ``\Predis`` classes. This store does not support blocking,
+and expects a TTL to avoid stalled locks::
use Symfony\Component\Lock\Store\RedisStore;
@@ -633,6 +624,10 @@ avoid stalled locks::
$store = new RedisStore($redis);
+.. versionadded:: 7.3
+
+ Support for ``Relay\Cluster`` was introduced in Symfony 7.3.
+
.. _lock-store-semaphore:
SemaphoreStore
diff --git a/components/messenger.rst b/components/messenger.rst
index fe3d2926714..a8ff1e5290e 100644
--- a/components/messenger.rst
+++ b/components/messenger.rst
@@ -167,11 +167,6 @@ Here are some important envelope stamps that are shipped with the Symfony Messen
differentiate it from messages created "manually". You can learn more about it
in the :doc:`Scheduler documentation `.
-.. versionadded:: 6.4
-
- The :class:`Symfony\\Component\\Messenger\\Stamp\\ScheduledStamp` was
- introduced in Symfony 6.4.
-
.. note::
The :class:`Symfony\\Component\\Messenger\\Stamp\\ErrorDetailsStamp` stamp
diff --git a/components/options_resolver.rst b/components/options_resolver.rst
index 35e443d3d94..17ec46c2fc9 100644
--- a/components/options_resolver.rst
+++ b/components/options_resolver.rst
@@ -305,13 +305,21 @@ correctly. To validate the types of the options, call
// specify multiple allowed types
$resolver->setAllowedTypes('port', ['null', 'int']);
+ // if you prefer, you can also use the following equivalent syntax
+ $resolver->setAllowedTypes('port', 'int|null');
// check all items in an array recursively for a type
$resolver->setAllowedTypes('dates', 'DateTime[]');
$resolver->setAllowedTypes('ports', 'int[]');
+ // the following syntax means "an array of integers or an array of strings"
+ $resolver->setAllowedTypes('endpoints', '(int|string)[]');
}
}
+.. versionadded:: 7.3
+
+ Defining type unions with the ``|`` syntax was introduced in Symfony 7.3.
+
You can pass any type for which an ``is_()`` function is defined in PHP.
You may also pass fully qualified class or interface names (which is checked
using ``instanceof``). Additionally, you can validate all items in an array
@@ -386,7 +394,7 @@ returns ``true`` for acceptable values and ``false`` for invalid values::
// ...
$resolver->setAllowedValues('transport', Validation::createIsValidCallable(
- new Length(['min' => 10 ])
+ new Length(min: 10)
));
In sub-classes, you can use :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::addAllowedValues`
@@ -654,7 +662,7 @@ default value::
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver->setDefault('spool', function (OptionsResolver $spoolResolver): void {
+ $resolver->setOptions('spool', function (OptionsResolver $spoolResolver): void {
$spoolResolver->setDefaults([
'type' => 'file',
'path' => '/path/to/spool',
@@ -678,6 +686,16 @@ default value::
],
]);
+.. deprecated:: 7.3
+
+ Defining nested options via :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDefault`
+ is deprecated since Symfony 7.3. Use the :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setOptions`
+ method instead, which also allows defining default values for prototyped options.
+
+.. versionadded:: 7.3
+
+ The ``setOptions()`` method was introduced in Symfony 7.3.
+
Nested options also support required options, validation (type, value) and
normalization of their values. If the default value of a nested option depends
on another option defined in the parent level, add a second ``Options`` argument
@@ -690,7 +708,7 @@ to the closure to access to them::
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefault('sandbox', false);
- $resolver->setDefault('spool', function (OptionsResolver $spoolResolver, Options $parent): void {
+ $resolver->setOptions('spool', function (OptionsResolver $spoolResolver, Options $parent): void {
$spoolResolver->setDefaults([
'type' => $parent['sandbox'] ? 'memory' : 'file',
// ...
@@ -713,13 +731,13 @@ In same way, parent options can access to the nested options as normal arrays::
public function configureOptions(OptionsResolver $resolver): void
{
- $resolver->setDefault('spool', function (OptionsResolver $spoolResolver): void {
+ $resolver->setOptions('spool', function (OptionsResolver $spoolResolver): void {
$spoolResolver->setDefaults([
'type' => 'file',
// ...
]);
});
- $resolver->setDefault('profiling', function (Options $options): void {
+ $resolver->setOptions('profiling', function (Options $options): void {
return 'file' === $options['spool']['type'];
});
}
@@ -740,7 +758,7 @@ with ``host``, ``database``, ``user`` and ``password`` each.
The best way to implement this is to define the ``connections`` option as prototype::
- $resolver->setDefault('connections', function (OptionsResolver $connResolver): void {
+ $resolver->setOptions('connections', function (OptionsResolver $connResolver): void {
$connResolver
->setPrototype(true)
->setRequired(['host', 'database'])
@@ -869,10 +887,6 @@ if an unknown option is passed. You can ignore not defined options by using the
'version' => '1.2.3'
]);
-.. versionadded:: 6.3
-
- The ``ignoreUndefined()`` method was introduced in Symfony 6.3.
-
Chaining Option Configurations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst
index 5a2c508b68d..5ce4c003a11 100644
--- a/components/phpunit_bridge.rst
+++ b/components/phpunit_bridge.rst
@@ -291,10 +291,6 @@ Here is a summary that should help you pick the right configuration:
Ignoring Deprecations
.....................
-.. versionadded:: 6.1
-
- The ``ignoreFile`` feature was introduced in Symfony 6.1.
-
If your application has some deprecations that you can't fix for some reasons,
you can tell Symfony to ignore them.
@@ -435,11 +431,6 @@ configuration file:
Finally, if you want to avoid the bridge to force any locale, you can set the
``SYMFONY_PHPUNIT_LOCALE`` environment variable to ``0``.
-.. versionadded:: 6.4
-
- The support for the ``SYMFONY_PHPUNIT_LOCALE`` environment variable was
- introduced in Symfony 6.4.
-
.. _write-assertions-about-deprecations:
Write Assertions about Deprecations
@@ -579,10 +570,6 @@ allows you to mock the PHP's built-in time functions ``time()``, ``microtime()``
function ``date()`` is mocked so it uses the mocked time if no timestamp is
specified.
-.. versionadded:: 6.2
-
- Support for mocking the ``hrtime()`` function was introduced in Symfony 6.2.
-
Other functions with an optional timestamp parameter that defaults to ``time()``
will still use the system time instead of the mocked time. This means that you
may need to change some code in your tests. For example, instead of ``new DateTime()``,
@@ -779,10 +766,6 @@ reason, this component also provides mocks for these PHP functions:
* :phpfunction:`trait_exists`
* :phpfunction:`enum_exists`
-.. versionadded:: 6.3
-
- The ``enum_exists`` function was introduced in Symfony 6.3.
-
Use Case
~~~~~~~~
@@ -854,10 +837,6 @@ PHP 8.1 and later, calling ``class_exists`` on a enum will return ``true``.
That's why calling ``ClassExistsMock::withMockedEnums()`` will also register the enum
as a mocked class.
-.. versionadded:: 6.3
-
- The ``enum_exists`` function was introduced in Symfony 6.3.
-
Troubleshooting
---------------
diff --git a/components/process.rst b/components/process.rst
index 10e7e0777af..9c25c931510 100644
--- a/components/process.rst
+++ b/components/process.rst
@@ -114,6 +114,8 @@ You can configure the options passed to the ``other_options`` argument of
and ``suppress_errors``) are only supported on Windows operating systems.
Check out the `PHP documentation for proc_open()`_ before using them.
+.. _process-using-features-from-the-os-shell:
+
Using Features From the OS Shell
--------------------------------
@@ -418,10 +420,6 @@ instead::
Executing a PHP Child Process with the Same Configuration
---------------------------------------------------------
-.. versionadded:: 6.4
-
- The ``PhpSubprocess`` helper was introduced in Symfony 6.4.
-
When you start a PHP process, it uses the default configuration defined in
your ``php.ini`` file. You can bypass these options with the ``-d`` command line
option. For example, if ``memory_limit`` is set to ``256M``, you can disable this
@@ -432,11 +430,14 @@ However, if you run the command via the Symfony ``Process`` class, PHP will use
the settings defined in the ``php.ini`` file. You can solve this issue by using
the :class:`Symfony\\Component\\Process\\PhpSubprocess` class to run the command::
+ use Symfony\Component\Console\Attribute\AsCommand;
+ use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\Process;
- class MyCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(SymfonyStyle $io): int
{
// the memory_limit (and any other config option) of this command is
// the one defined in php.ini instead of the new values (optionally)
@@ -446,6 +447,8 @@ the :class:`Symfony\\Component\\Process\\PhpSubprocess` class to run the command
// the memory_limit (and any other config option) of this command takes
// into account the values (optionally) passed via the '-d' command option
$childProcess = new PhpSubprocess(['bin/console', 'cache:pool:prune']);
+
+ return 0;
}
}
@@ -515,6 +518,20 @@ When running a program asynchronously, you can send it POSIX signals with the
// will send a SIGKILL to the process
$process->signal(SIGKILL);
+You can make the process ignore signals by using the
+:method:`Symfony\\Component\\Process\\Process::setIgnoredSignals`
+method. The given signals won't be propagated to the child process::
+
+ use Symfony\Component\Process\Process;
+
+ $process = new Process(['find', '/', '-name', 'rabbit']);
+ $process->setIgnoredSignals([SIGKILL, SIGUSR1]);
+
+.. versionadded:: 7.1
+
+ The :method:`Symfony\\Component\\Process\\Process::setIgnoredSignals`
+ method was introduced in Symfony 7.1.
+
Process Pid
-----------
diff --git a/components/property_access.rst b/components/property_access.rst
index 9944ad05273..f608640fa9b 100644
--- a/components/property_access.rst
+++ b/components/property_access.rst
@@ -91,11 +91,6 @@ You can also use multi dimensional arrays::
Right square brackets ``]`` don't need to be escaped in array keys.
- .. versionadded:: 6.3
-
- Escaping dots and left square brackets in a property path was
- introduced in Symfony 6.3.
-
Reading from Objects
--------------------
@@ -238,10 +233,6 @@ is to mark all nullable properties with the nullsafe operator (``?``)::
// no longer evaluated and null is returned immediately without throwing an exception
var_dump($propertyAccessor->getValue($comment, 'person?.firstname')); // null
-.. versionadded:: 6.2
-
- The ``?`` nullsafe operator was introduced in Symfony 6.2.
-
.. _components-property-access-magic-get:
Magic ``__get()`` Method
diff --git a/components/property_info.rst b/components/property_info.rst
index 238da6396ab..865a36c5941 100644
--- a/components/property_info.rst
+++ b/components/property_info.rst
@@ -183,6 +183,26 @@ for a property::
See :ref:`components-property-info-type` for info about the ``Type`` class.
+Documentation Block
+~~~~~~~~~~~~~~~~~~~
+
+Extractors that implement :class:`Symfony\\Component\\PropertyInfo\\PropertyDocBlockExtractorInterface`
+can provide the full documentation block for a property as a string::
+
+ $docBlock = $propertyInfo->getDocBlock($class, $property);
+ /*
+ Example Result
+ --------------
+ string(79):
+ This is the subsequent paragraph in the DocComment.
+ It can span multiple lines.
+ */
+
+.. versionadded:: 7.1
+
+ The :class:`Symfony\\Component\\PropertyInfo\\PropertyDocBlockExtractorInterface`
+ interface was introduced in Symfony 7.1.
+
.. _property-info-description:
Description Information
@@ -229,12 +249,6 @@ works. It assumes camel case style method names following `PSR-1`_. For example,
both ``myProperty`` and ``my_property`` properties are readable if there's a
``getMyProperty()`` method and writable if there's a ``setMyProperty()`` method.
-.. versionadded:: 6.4
-
- In Symfony versions prior to 6.4, snake case properties (e.g. ``my_property``)
- were not writable by camel case methods (e.g. ``setMyProperty()``). You had
- to define method names with underscores (e.g. ``setMy_property()``).
-
.. _property-info-initializable:
Property Initializable Information
@@ -419,6 +433,12 @@ library is present::
// Description information.
$phpDocExtractor->getShortDescription($class, $property);
$phpDocExtractor->getLongDescription($class, $property);
+ $phpDocExtractor->getDocBlock($class, $property);
+
+.. versionadded:: 7.1
+
+ The :method:`Symfony\\Component\\PropertyInfo\\Extractor\\PhpDocExtractor::getDocBlock`
+ method was introduced in Symfony 7.1.
PhpStanExtractor
~~~~~~~~~~~~~~~~
@@ -449,7 +469,18 @@ information from annotations of properties and methods, such as ``@var``,
use App\Domain\Foo;
$phpStanExtractor = new PhpStanExtractor();
+
+ // Type information.
$phpStanExtractor->getTypesFromConstructor(Foo::class, 'bar');
+ // Description information.
+ $phpStanExtractor->getShortDescription($class, 'bar');
+ $phpStanExtractor->getLongDescription($class, 'bar');
+
+.. versionadded:: 7.3
+
+ The :method:`Symfony\\Component\\PropertyInfo\\Extractor\\PhpStanExtractor::getShortDescription`
+ and :method:`Symfony\\Component\\PropertyInfo\\Extractor\\PhpStanExtractor::getLongDescription`
+ methods were introduced in Symfony 7.3.
SerializerExtractor
~~~~~~~~~~~~~~~~~~~
@@ -474,20 +505,6 @@ with the ``property_info`` service in the Symfony Framework::
// the `serializer_groups` option must be configured (may be set to null)
$serializerExtractor->getProperties($class, ['serializer_groups' => ['mygroup']]);
-.. versionadded:: 6.4
-
- The
- :class:`Symfony\\Component\\Serializer\\Mapping\\Loader\\AttributeLoader`
- was introduced in Symfony 6.4. Prior to this, the
- :class:`Symfony\\Component\\Serializer\\Mapping\\Loader\\AnnotationLoader`
- must be used.
-
-.. deprecated:: 6.4
-
- The
- :class:`Symfony\\Component\\Serializer\\Mapping\\Loader\\AnnotationLoader`
- was deprecated in Symfony 6.4.
-
If ``serializer_groups`` is set to ``null``, serializer groups metadata won't be
checked but you will get only the properties considered by the Serializer
Component (notably the ``#[Ignore]`` attribute is taken into account).
@@ -521,6 +538,8 @@ with the ``property_info`` service in the Symfony Framework::
// Type information.
$doctrineExtractor->getTypes($class, $property);
+.. _components-property-information-constructor-extractor:
+
ConstructorExtractor
~~~~~~~~~~~~~~~~~~~~
@@ -553,6 +572,7 @@ Creating Your Own Extractors
You can create your own property information extractors by creating a
class that implements one or more of the following interfaces:
+:class:`Symfony\\Component\\PropertyInfo\\Extractor\\ConstructorArgumentTypeExtractorInterface`,
:class:`Symfony\\Component\\PropertyInfo\\PropertyAccessExtractorInterface`,
:class:`Symfony\\Component\\PropertyInfo\\PropertyDescriptionExtractorInterface`,
:class:`Symfony\\Component\\PropertyInfo\\PropertyListExtractorInterface`,
@@ -570,6 +590,11 @@ service by defining it as a service with one or more of the following
* ``property_info.access_extractor`` if it provides access information.
* ``property_info.initializable_extractor`` if it provides initializable information
(it checks if a property can be initialized through the constructor).
+* ``property_info.constructor_extractor`` if it provides type information from the constructor argument.
+
+ .. versionadded:: 7.3
+
+ The ``property_info.constructor_extractor`` tag was introduced in Symfony 7.3.
.. _`PSR-1`: https://www.php-fig.org/psr/psr-1/
.. _`phpDocumentor Reflection`: https://github.com/phpDocumentor/ReflectionDocBlock
diff --git a/components/type_info.rst b/components/type_info.rst
new file mode 100644
index 00000000000..817c7f1d61a
--- /dev/null
+++ b/components/type_info.rst
@@ -0,0 +1,202 @@
+The TypeInfo Component
+======================
+
+The TypeInfo component extracts type information from PHP elements like properties,
+arguments and return types.
+
+This component provides:
+
+* A powerful ``Type`` definition that can handle unions, intersections, and generics
+ (and can be extended to support more types in the future);
+* A way to get types from PHP elements such as properties, method arguments,
+ return types, and raw strings.
+
+Installation
+------------
+
+.. code-block:: terminal
+
+ $ composer require symfony/type-info
+
+.. include:: /components/require_autoload.rst.inc
+
+Usage
+-----
+
+This component gives you a :class:`Symfony\\Component\\TypeInfo\\Type` object that
+represents the PHP type of anything you built or asked to resolve.
+
+There are two ways to use this component. First one is to create a type manually thanks
+to the :class:`Symfony\\Component\\TypeInfo\\Type` static methods as following::
+
+ use Symfony\Component\TypeInfo\Type;
+
+ Type::int();
+ Type::nullable(Type::string());
+ Type::generic(Type::object(Collection::class), Type::int());
+ Type::list(Type::bool());
+ Type::intersection(Type::object(\Stringable::class), Type::object(\Iterator::class));
+
+Many others methods are available and can be found
+in :class:`Symfony\\Component\\TypeInfo\\TypeFactoryTrait`.
+
+You can also use a generic method that detects the type automatically::
+
+ Type::fromValue(1.1); // same as Type::float()
+ Type::fromValue('...'); // same as Type::string()
+ Type::fromValue(false); // same as Type::false()
+
+.. versionadded:: 7.3
+
+ The ``fromValue()`` method was introduced in Symfony 7.3.
+
+Resolvers
+~~~~~~~~~
+
+The second way to use the component is by using ``TypeInfo`` to resolve a type
+based on reflection or a simple string. This approach is designed for libraries
+that need a simple way to describe a class or anything with a type::
+
+ use Symfony\Component\TypeInfo\Type;
+ use Symfony\Component\TypeInfo\TypeResolver\TypeResolver;
+
+ class Dummy
+ {
+ public function __construct(
+ public int $id,
+ ) {
+ }
+ }
+
+ // Instantiate a new resolver
+ $typeResolver = TypeResolver::create();
+
+ // Then resolve types for any subject
+ $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns an "int" Type instance
+ $typeResolver->resolve('bool'); // returns a "bool" Type instance
+
+ // Types can be instantiated thanks to static factories
+ $type = Type::list(Type::nullable(Type::bool()));
+
+ // Type instances have several helper methods
+
+ // for collections, it returns the type of the item used as the key;
+ // in this example, the collection is a list, so it returns an "int" Type instance
+ $keyType = $type->getCollectionKeyType();
+
+ // you can chain the utility methods (e.g. to introspect the values of the collection)
+ // the following code will return true
+ $isValueNullable = $type->getCollectionValueType()->isNullable();
+
+Each of these calls will return you a ``Type`` instance that corresponds to the
+static method used. You can also resolve types from a string (as shown in the
+``bool`` parameter of the previous example)
+
+PHPDoc Parsing
+~~~~~~~~~~~~~~
+
+In many cases, you may not have cleanly typed properties or may need more precise
+type definitions provided by advanced PHPDoc. To achieve this, you can use a string
+resolver based on the PHPDoc annotations.
+
+First, run the command ``composer require phpstan/phpdoc-parser`` to install the
+PHP package required for string resolving. Then, follow these steps::
+
+ use Symfony\Component\TypeInfo\TypeResolver\TypeResolver;
+
+ class Dummy
+ {
+ public function __construct(
+ public int $id,
+ /** @var string[] $tags */
+ public array $tags,
+ ) {
+ }
+ }
+
+ $typeResolver = TypeResolver::create();
+ $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'id')); // returns an "int" Type
+ $typeResolver->resolve(new \ReflectionProperty(Dummy::class, 'tags')); // returns a collection with "int" as key and "string" as values Type
+
+Advanced Usages
+~~~~~~~~~~~~~~~
+
+The TypeInfo component provides various methods to manipulate and check types,
+depending on your needs.
+
+**Identify** a type::
+
+ // define a simple integer type
+ $type = Type::int();
+ // check if the type matches a specific identifier
+ $type->isIdentifiedBy(TypeIdentifier::INT); // true
+ $type->isIdentifiedBy(TypeIdentifier::STRING); // false
+
+ // define a union type (equivalent to PHP's int|string)
+ $type = Type::union(Type::string(), Type::int());
+ // now the second check is true because the union type contains the string type
+ $type->isIdentifiedBy(TypeIdentifier::INT); // true
+ $type->isIdentifiedBy(TypeIdentifier::STRING); // true
+
+ class DummyParent {}
+ class Dummy extends DummyParent implements DummyInterface {}
+
+ // define an object type
+ $type = Type::object(Dummy::class);
+
+ // check if the type is an object or matches a specific class
+ $type->isIdentifiedBy(TypeIdentifier::OBJECT); // true
+ $type->isIdentifiedBy(Dummy::class); // true
+ // check if it inherits/implements something
+ $type->isIdentifiedBy(DummyParent::class); // true
+ $type->isIdentifiedBy(DummyInterface::class); // true
+
+Checking if a type **accepts a value**::
+
+ $type = Type::int();
+ // check if the type accepts a given value
+ $type->accepts(123); // true
+ $type->accepts('z'); // false
+
+ $type = Type::union(Type::string(), Type::int());
+ // now the second check is true because the union type accepts either an int or a string value
+ $type->accepts(123); // true
+ $type->accepts('z'); // true
+
+.. versionadded:: 7.3
+
+ The :method:`Symfony\\Component\\TypeInfo\\Type::accepts`
+ method was introduced in Symfony 7.3.
+
+Using callables for **complex checks**::
+
+ class Foo
+ {
+ private int $integer;
+ private string $string;
+ private ?float $float;
+ }
+
+ $reflClass = new \ReflectionClass(Foo::class);
+
+ $resolver = TypeResolver::create();
+ $integerType = $resolver->resolve($reflClass->getProperty('integer'));
+ $stringType = $resolver->resolve($reflClass->getProperty('string'));
+ $floatType = $resolver->resolve($reflClass->getProperty('float'));
+
+ // define a callable to validate non-nullable number types
+ $isNonNullableNumber = function (Type $type): bool {
+ if ($type->isNullable()) {
+ return false;
+ }
+
+ if ($type->isIdentifiedBy(TypeIdentifier::INT) || $type->isIdentifiedBy(TypeIdentifier::FLOAT)) {
+ return true;
+ }
+
+ return false;
+ };
+
+ $integerType->isSatisfiedBy($isNonNullableNumber); // true
+ $stringType->isSatisfiedBy($isNonNullableNumber); // false
+ $floatType->isSatisfiedBy($isNonNullableNumber); // false
diff --git a/components/uid.rst b/components/uid.rst
index 4b6938e98d2..b4083765436 100644
--- a/components/uid.rst
+++ b/components/uid.rst
@@ -144,10 +144,6 @@ implementation-specific, and no particular format should be assumed::
$uuid = Uuid::v8('d9e7a184-5d5b-11ea-a62a-3499710062d0');
// $uuid is an instance of Symfony\Component\Uid\UuidV8
-.. versionadded:: 6.2
-
- UUID versions 7 and 8 were introduced in Symfony 6.2.
-
If your UUID value is already generated in another format, use any of the
following methods to create a ``Uuid`` object from it::
@@ -168,10 +164,10 @@ configure the behavior of the factory using configuration files::
# config/packages/uid.yaml
framework:
uid:
- default_uuid_version: 6
+ default_uuid_version: 7
name_based_uuid_version: 5
name_based_uuid_namespace: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
- time_based_uuid_version: 6
+ time_based_uuid_version: 7
time_based_uuid_node: 121212121212
.. code-block:: xml
@@ -187,10 +183,10 @@ configure the behavior of the factory using configuration files::
@@ -209,10 +205,10 @@ configure the behavior of the factory using configuration files::
$container->extension('framework', [
'uid' => [
- 'default_uuid_version' => 6,
+ 'default_uuid_version' => 7,
'name_based_uuid_version' => 5,
'name_based_uuid_namespace' => '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
- 'time_based_uuid_version' => 6,
+ 'time_based_uuid_version' => 7,
'time_based_uuid_node' => 121212121212,
],
]);
@@ -234,7 +230,7 @@ on the configuration you defined::
public function generate(): void
{
- // This creates a UUID of the version given in the configuration file (v6 by default)
+ // This creates a UUID of the version given in the configuration file (v7 by default)
$uuid = $this->uuidFactory->create();
$nameBasedUuid = $this->uuidFactory->nameBased(/** ... */);
@@ -257,10 +253,31 @@ Use these methods to transform the UUID object into different bases::
$uuid->toBase58(); // string(22) "TuetYWNHhmuSQ3xPoVLv9M"
$uuid->toRfc4122(); // string(36) "d9e7a184-5d5b-11ea-a62a-3499710062d0"
$uuid->toHex(); // string(34) "0xd9e7a1845d5b11eaa62a3499710062d0"
+ $uuid->toString(); // string(36) "d9e7a184-5d5b-11ea-a62a-3499710062d0"
-.. versionadded:: 6.2
+.. versionadded:: 7.1
- The ``toHex()`` method was introduced in Symfony 6.2.
+ The ``toString()`` method was introduced in Symfony 7.1.
+
+You can also convert some UUID versions to others::
+
+ // convert V1 to V6 or V7
+ $uuid = Uuid::v1();
+
+ $uuid->toV6(); // returns a Symfony\Component\Uid\UuidV6 instance
+ $uuid->toV7(); // returns a Symfony\Component\Uid\UuidV7 instance
+
+ // convert V6 to V7
+ $uuid = Uuid::v6();
+
+ $uuid->toV7(); // returns a Symfony\Component\Uid\UuidV7 instance
+
+.. versionadded:: 7.1
+
+ The :method:`Symfony\\Component\\Uid\\UuidV1::toV6`,
+ :method:`Symfony\\Component\\Uid\\UuidV1::toV7` and
+ :method:`Symfony\\Component\\Uid\\UuidV6::toV7`
+ methods were introduced in Symfony 7.1.
Working with UUIDs
~~~~~~~~~~~~~~~~~~
@@ -299,6 +316,31 @@ UUID objects created with the ``Uuid`` class can use the following methods
// * int < 0 if $uuid1 is less than $uuid4
$uuid1->compare($uuid4); // e.g. int(4)
+If you're working with different UUIDs format and want to validate them,
+you can use the ``$format`` parameter of the :method:`Symfony\\Component\\Uid\\Uuid::isValid`
+method to specify the UUID format you're expecting::
+
+ use Symfony\Component\Uid\Uuid;
+
+ $isValid = Uuid::isValid('90067ce4-f083-47d2-a0f4-c47359de0f97', Uuid::FORMAT_RFC_4122); // accept only RFC 4122 UUIDs
+ $isValid = Uuid::isValid('3aJ7CNpDMfXPZrCsn4Cgey', Uuid::FORMAT_BASE_32 | Uuid::FORMAT_BASE_58); // accept multiple formats
+
+The following constants are available:
+
+* ``Uuid::FORMAT_BINARY``
+* ``Uuid::FORMAT_BASE_32``
+* ``Uuid::FORMAT_BASE_58``
+* ``Uuid::FORMAT_RFC_4122``
+* ``Uuid::FORMAT_RFC_9562`` (equivalent to ``Uuid::FORMAT_RFC_4122``)
+
+You can also use the ``Uuid::FORMAT_ALL`` constant to accept any UUID format.
+By default, only the RFC 4122 format is accepted.
+
+.. versionadded:: 7.2
+
+ The ``$format`` parameter of the :method:`Symfony\\Component\\Uid\\Uuid::isValid`
+ method and the related constants were introduced in Symfony 7.2.
+
Storing UUIDs in Databases
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -321,10 +363,6 @@ type, which converts to/from UUID objects automatically::
// ...
}
-.. versionadded:: 6.2
-
- The ``UuidType::NAME`` constant was introduced in Symfony 6.2.
-
There's also a Doctrine generator to help auto-generate UUID values for the
entity primary keys::
@@ -471,10 +509,6 @@ Use these methods to transform the ULID object into different bases::
$ulid->toRfc4122(); // string(36) "0171069d-593d-97d3-8b3e-23d06de5b308"
$ulid->toHex(); // string(34) "0x0171069d593d97d38b3e23d06de5b308"
-.. versionadded:: 6.2
-
- The ``toHex()`` method was introduced in Symfony 6.2.
-
Working with ULIDs
~~~~~~~~~~~~~~~~~~
@@ -518,10 +552,6 @@ type, which converts to/from ULID objects automatically::
// ...
}
-.. versionadded:: 6.2
-
- The ``UlidType::NAME`` constant was introduced in Symfony 6.2.
-
There's also a Doctrine generator to help auto-generate ULID values for the
entity primary keys::
diff --git a/components/validator.rst b/components/validator.rst
index 085c77a7946..12c61507257 100644
--- a/components/validator.rst
+++ b/components/validator.rst
@@ -36,7 +36,7 @@ characters long::
$validator = Validation::createValidator();
$violations = $validator->validate('Bernhard', [
- new Length(['min' => 10]),
+ new Length(min: 10),
new NotBlank(),
]);
diff --git a/components/validator/metadata.rst b/components/validator/metadata.rst
index e7df42413bc..782e1ee216f 100755
--- a/components/validator/metadata.rst
+++ b/components/validator/metadata.rst
@@ -24,7 +24,7 @@ the ``Author`` class has at least 3 characters::
$metadata->addPropertyConstraint('firstName', new Assert\NotBlank());
$metadata->addPropertyConstraint(
'firstName',
- new Assert\Length(["min" => 3])
+ new Assert\Length(min: 3)
);
}
}
@@ -55,9 +55,9 @@ Then, add the Validator component configuration to the class::
{
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
- $metadata->addGetterConstraint('passwordSafe', new Assert\IsTrue([
- 'message' => 'The password cannot match your first name',
- ]));
+ $metadata->addGetterConstraint('passwordSafe', new Assert\IsTrue(
+ message: 'The password cannot match your first name',
+ ));
}
}
diff --git a/components/validator/resources.rst b/components/validator/resources.rst
index 32b8e26f8a8..7d6cd0e8e5d 100644
--- a/components/validator/resources.rst
+++ b/components/validator/resources.rst
@@ -42,10 +42,10 @@ In this example, the validation metadata is retrieved executing the
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
$metadata->addPropertyConstraint('name', new Assert\NotBlank());
- $metadata->addPropertyConstraint('name', new Assert\Length([
- 'min' => 5,
- 'max' => 20,
- ]));
+ $metadata->addPropertyConstraint('name', new Assert\Length(
+ min: 5,
+ max: 20,
+ ));
}
}
@@ -83,70 +83,9 @@ configure the locations of these files::
:method:`Symfony\\Component\\Validator\\ValidatorBuilder::addXmlMappings`
to configure an array of file paths.
-The AnnotationLoader
---------------------
-
-.. deprecated:: 6.4
-
- The :class:`Symfony\\Component\\Validator\\Mapping\\Loader\\AnnotationLoader`
- is deprecated since Symfony 6.4, use the
- :class:`Symfony\\Component\\Validator\\Mapping\\Loader\\AttributeLoader`
- instead.
-
-The component provides an
-:class:`Symfony\\Component\\Validator\\Mapping\\Loader\\AnnotationLoader` to get
-the metadata from the annotations of the class. Annotations are defined as ``@``
-prefixed classes included in doc block comments (``/** ... */``). For example::
-
- use Symfony\Component\Validator\Constraints as Assert;
- // ...
-
- class User
- {
- /**
- * @Assert\NotBlank
- */
- protected string $name;
- }
-
-To enable the annotation loader, call the
-:method:`Symfony\\Component\\Validator\\ValidatorBuilder::enableAnnotationMapping` method.
-If you use annotations instead of attributes, it's also required to call
-``addDefaultDoctrineAnnotationReader()`` to use Doctrine's annotation reader::
-
- use Symfony\Component\Validator\Validation;
-
- $validator = Validation::createValidatorBuilder()
- ->enableAnnotationMapping()
- ->addDefaultDoctrineAnnotationReader() // add this only when using annotations
- ->getValidator();
-
-.. deprecated:: 6.4
-
- Annotations are deprecated since Symfony 6.4, use attributes instead.
-
-To disable the annotation loader after it was enabled, call
-:method:`Symfony\\Component\\Validator\\ValidatorBuilder::disableAnnotationMapping`.
-
-.. deprecated:: 6.4
-
- The :method:`Symfony\\Component\\Validator\\ValidatorBuilder::enableAnnotationMapping`
- and :method:`Symfony\\Component\\Validator\\ValidatorBuilder::disableAnnotationMapping`
- methods are deprecated since Symfony 6.4, use the
- :method:`Symfony\\Component\\Validator\\ValidatorBuilder::enableAttributeMapping`
- and :method:`Symfony\\Component\\Validator\\ValidatorBuilder::disableAttributeMapping`
- methods instead.
-
-.. include:: /_includes/_annotation_loader_tip.rst.inc
-
The AttributeLoader
-------------------
-.. versionadded:: 6.4
-
- The :class:`Symfony\\Component\\Validator\\Mapping\\Loader\\AttributeLoader`
- was introduced in Symfony 6.4.
-
The component provides an
:class:`Symfony\\Component\\Validator\\Mapping\\Loader\\AttributeLoader` to get
the metadata from the attributes of the class. For example::
diff --git a/components/var_dumper.rst b/components/var_dumper.rst
index cfc57140b52..c6966a692af 100644
--- a/components/var_dumper.rst
+++ b/components/var_dumper.rst
@@ -485,10 +485,6 @@ then its dump representation::
.. image:: /_images/components/var_dumper/10-uninitialized.png
:alt: Dump output where the uninitialized property is represented by a question mark followed by the type definition.
-.. versionadded:: 6.4
-
- Displaying uninitialized variables information was introduced in Symfony 6.4.
-
.. _var-dumper-advanced:
Advanced Usage
diff --git a/components/var_exporter.rst b/components/var_exporter.rst
index cb935c22da4..c7ec9cd90d0 100644
--- a/components/var_exporter.rst
+++ b/components/var_exporter.rst
@@ -177,25 +177,53 @@ populated by using the special ``"\0"`` property name to define their internal v
"\0" => [$inputArray],
]);
-.. versionadded:: 6.2
-
- The :class:`Symfony\\Component\\VarExporter\\Hydrator` was introduced in Symfony 6.2.
-
Creating Lazy Objects
---------------------
-Lazy-objects are objects instantiated empty and populated on-demand. This is
-particularly useful when you have for example properties in your classes that
-requires some heavy computation to determine their value. In this case, you
-may want to trigger the property's value processing only when you actually need
-its value. Thanks to this, the heavy computation won't be done if you never use
-this property. The VarExporter component is bundled with two traits helping
-you implement such mechanism easily in your classes.
+Lazy objects are objects instantiated empty and populated on demand. This is
+particularly useful when, for example, a class has properties that require
+heavy computation to determine their values. In such cases, you may want to
+trigger the computation only when the property is actually accessed. This way,
+the expensive processing is avoided entirely if the property is never used.
+
+Since version 8.4, PHP provides support for lazy objects via the reflection API.
+This native API works with concrete classes, but not with abstract or internal ones.
+This component provides helpers to generate lazy objects using the decorator
+pattern, which also works with abstract classes, internal classes, and interfaces::
+
+ $proxyCode = ProxyHelper::generateLazyProxy(new \ReflectionClass(SomeInterface::class));
+ // $proxyCode should be dumped into a file in production environments
+ eval('class ProxyDecorator'.$proxyCode);
+
+ $proxy = ProxyDecorator::createLazyProxy(initializer: function (): SomeInterface {
+ // use whatever heavy logic you need here
+ // to compute the $dependencies of the proxied class
+ $instance = new SomeHeavyClass(...$dependencies);
+ // call setters, etc. if needed
+
+ return $instance;
+ });
+
+Use this mechanism only when native lazy objects cannot be leveraged
+(otherwise you'll get a deprecation notice).
+
+Legacy Creation of Lazy Objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When using a PHP version earlier than 8.4, native lazy objects are not available.
+In these cases, the VarExporter component provides two traits that help you
+implement lazy-loading mechanisms in your classes.
.. _var-exporter_ghost-objects:
LazyGhostTrait
-~~~~~~~~~~~~~~
+..............
+
+.. deprecated:: 7.3
+
+ ``LazyGhostTrait`` is deprecated since Symfony 7.3. Use PHP 8.4's native lazy
+ objects instead. Note that using the trait with PHP versions earlier than 8.4
+ does not trigger a deprecation, to ease the transition.
Ghost objects are empty objects, which see their properties populated the first
time any method is called. Thanks to :class:`Symfony\\Component\\VarExporter\\LazyGhostTrait`,
@@ -225,12 +253,6 @@ initialized::
}
}
-.. deprecated:: 6.4
-
- Using an array of closures for property-based initialization in the
- ``createLazyGhost()`` method is deprecated since Symfony 6.4. Pass
- a single closure that initializes the whole object instead.
-
:class:`Symfony\\Component\\VarExporter\\LazyGhostTrait` also allows to
convert non-lazy classes to lazy ones::
@@ -274,35 +296,20 @@ While you never query ``$processor->hash`` value, heavy methods will never be
triggered. But still, the ``$processor`` object exists and can be used in your
code, passed to methods, functions, etc.
-Additionally and by adding two arguments to the initializer function, it is
-possible to initialize properties one-by-one::
-
- $processor = LazyHashProcessor::createLazyGhost(initializer: function (HashProcessor $instance, string $propertyName, ?string $propertyScope): mixed {
- if (HashProcessor::class === $propertyScope && 'hash' === $propertyName) {
- // Return $hash value
- }
-
- // Then you can add more logic for the other properties
- });
-
-.. deprecated:: 6.4
-
- The use of ``propertyName`` and ``propertyScope`` in the initializer function
- is deprecated since Symfony 6.4 and will be removed in Symfony 7.0.
- The initializer should now handle the entire object initialization at once.
-
Ghost objects unfortunately can't work with abstract classes or internal PHP
classes. Nevertheless, the VarExporter component covers this need with the help
of :ref:`Virtual Proxies `.
-.. versionadded:: 6.2
-
- The :class:`Symfony\\Component\\VarExporter\\LazyGhostTrait` was introduced in Symfony 6.2.
-
.. _var-exporter_virtual-proxies:
LazyProxyTrait
-~~~~~~~~~~~~~~
+..............
+
+.. deprecated:: 7.3
+
+ ``LazyProxyTrait`` is deprecated since Symfony 7.3. Use PHP 8.4's native lazy
+ objects instead. Note that using the trait with PHP versions earlier than 8.4
+ does not trigger a deprecation, to ease the transition.
The purpose of virtual proxies in the same one as
:ref:`ghost objects `, but their internal behavior is
@@ -364,10 +371,5 @@ Just like ghost objects, while you never query ``$processor->hash``, its value
will not be computed. The main difference with ghost objects is that this time,
a proxy of an abstract class was created. This also works with internal PHP class.
-.. versionadded:: 6.2
-
- The :class:`Symfony\\Component\\VarExporter\\LazyProxyTrait` and
- :class:`Symfony\\Component\\VarExporter\\ProxyHelper` were introduced in Symfony 6.2.
-
.. _`OPcache`: https://www.php.net/opcache
.. _`PSR-2`: https://www.php-fig.org/psr/psr-2/
diff --git a/components/yaml.rst b/components/yaml.rst
index 700ebd22379..efaf84f04e6 100644
--- a/components/yaml.rst
+++ b/components/yaml.rst
@@ -214,6 +214,8 @@ During the parsing of the YAML contents, all the ``_`` characters are removed
from the numeric literal contents, so there is not a limit in the number of
underscores you can include or the way you group contents.
+.. _yaml-flags:
+
Advanced Usage: Flags
---------------------
@@ -355,9 +357,25 @@ and the special ``!php/enum`` syntax to parse them as proper PHP enums::
// the value of the 'foo' key is a string because it missed the `!php/enum` syntax
// $parameters = ['foo' => 'FooEnum::Foo', 'bar' => 'foo'];
-.. versionadded:: 6.2
+You can also use ``!php/enum`` to get all the enumeration cases by only
+giving the enumeration FQCN::
- The support for PHP enumerations was introduced in Symfony 6.2.
+ enum FooEnum: string
+ {
+ case Foo = 'foo';
+ case Bar = 'bar';
+ }
+
+ // ...
+
+ $yaml = '{ bar: !php/enum FooEnum }';
+ $parameters = Yaml::parse($yaml, Yaml::PARSE_CONSTANT);
+ // $parameters = ['bar' => ['foo', 'bar']];
+
+.. versionadded:: 7.1
+
+ The support for using the enum FQCN without specifying a case
+ was introduced in Symfony 7.1.
Parsing and Dumping of Binary Data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -410,6 +428,16 @@ you can dump them as ``~`` with the ``DUMP_NULL_AS_TILDE`` flag::
$dumped = Yaml::dump(['foo' => null], 2, 4, Yaml::DUMP_NULL_AS_TILDE);
// foo: ~
+Another valid representation of the ``null`` value is an empty string. You can
+use the ``DUMP_NULL_AS_EMPTY`` flag to dump null values as empty strings::
+
+ $dumped = Yaml::dump(['foo' => null], 2, 4, Yaml::DUMP_NULL_AS_EMPTY);
+ // foo:
+
+.. versionadded:: 7.3
+
+ The ``DUMP_NULL_AS_EMPTY`` flag was introduced in Symfony 7.3.
+
Dumping Numeric Keys as Strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -422,9 +450,57 @@ By default, digit-only array keys are dumped as integers. You can use the
$dumped = Yaml::dump([200 => 'foo'], 2, 4, Yaml::DUMP_NUMERIC_KEY_AS_STRING);
// '200': foo
-.. versionadded:: 6.3
+Dumping Double Quotes on Values
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default, only unsafe string values are enclosed in double quotes (for example,
+if they are reserved words or contain newlines and spaces). Use the
+``DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES`` flag to add double quotes to all string values::
+
+ $dumped = Yaml::dump([
+ 'foo' => 'bar', 'some foo' => 'some bar', 'x' => 3.14, 'y' => true, 'z' => null,
+ ]);
+ // foo: bar, 'some foo': 'some bar', x: 3.14, 'y': true, z: null
+
+ $dumped = Yaml::dump([
+ 'foo' => 'bar', 'some foo' => 'some bar', 'x' => 3.14, 'y' => true, 'z' => null,
+ ], 2, 4, Yaml::DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES);
+ // "foo": "bar", "some foo": "some bar", "x": 3.14, "y": true, "z": null
+
+.. versionadded:: 7.3
+
+ The ``Yaml::DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES`` flag was introduced in Symfony 7.3.
+
+Dumping Collection of Maps
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When the YAML component dumps collections of maps, it uses a hyphen on a separate
+line as a delimiter:
+
+.. code-block:: yaml
+
+ planets:
+ -
+ name: Mercury
+ distance: 57910000
+ -
+ name: Jupiter
+ distance: 778500000
+
+To produce a more compact output where the delimiter is included within the map,
+use the ``Yaml::DUMP_COMPACT_NESTED_MAPPING`` flag:
+
+.. code-block:: yaml
+
+ planets:
+ - name: Mercury
+ distance: 57910000
+ - name: Jupiter
+ distance: 778500000
+
+.. versionadded:: 7.3
- The ``DUMP_NUMERIC_KEY_AS_STRING`` flag was introduced in Symfony 6.3.
+ The ``Yaml::DUMP_COMPACT_NESTED_MAPPING`` flag was introduced in Symfony 7.3.
Syntax Validation
~~~~~~~~~~~~~~~~~
diff --git a/configuration.rst b/configuration.rst
index 625e93b12d0..4b1e75dcabe 100644
--- a/configuration.rst
+++ b/configuration.rst
@@ -81,10 +81,6 @@ readable. These are the main advantages and disadvantages of each format:
methods in the ``src/Kernel.php`` file to add support for the ``.xml`` file
extension.
- .. versionadded:: 6.1
-
- The automatic loading of PHP configuration files was introduced in Symfony 6.1.
-
Importing Configuration Files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -292,14 +288,6 @@ reusable configuration value. By convention, parameters are defined under the
something@example.com
-.. versionadded:: 6.2
-
- Passing an enum case as a service parameter was introduced in Symfony 6.2.
-
-.. versionadded:: 6.3
-
- The ``trim`` attribute was introduced in Symfony 6.3.
-
Once defined, you can reference this parameter value from any other
configuration file using a special syntax: wrap the parameter name in two ``%``
(e.g. ``%app.admin_email%``):
@@ -394,9 +382,19 @@ a new ``locale`` parameter is added to the ``config/services.yaml`` file).
They are useful when working with :doc:`Compiler Passes `
to declare some temporary parameters that won't be available later in the application.
-.. versionadded:: 6.3
+Configuration parameters are usually validation-free, but you can ensure that
+essential parameters for your application's functionality are not empty::
+
+ /** @var ContainerBuilder $container */
+ $container->parameterCannotBeEmpty('app.private_key', 'Did you forget to set a value for the "app.private_key" parameter?');
- Compile-time parameters were introduced in Symfony 6.3.
+If a non-empty parameter is ``null``, an empty string ``''``, or an empty array ``[]``,
+Symfony will throw an exception. This validation is **not** made at compile time
+but when attempting to retrieve the value of the parameter.
+
+.. versionadded:: 7.2
+
+ Validating non-empty parameters was introduced in Symfony 7.2.
.. seealso::
@@ -948,6 +946,49 @@ get the environment variables and will not spend time parsing the ``.env`` files
Update your deployment tools/workflow to run the ``dotenv:dump`` command after
each deploy to improve the application performance.
+Storing Environment Variables In Other Files
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default, the environment variables are stored in the ``.env`` file located
+at the root of your project. However, you can store them in other files in
+multiple ways.
+
+If you use the :doc:`Runtime component `, the dotenv
+path is part of the options you can set in your ``composer.json`` file:
+
+.. code-block:: json
+
+ {
+ // ...
+ "extra": {
+ // ...
+ "runtime": {
+ "dotenv_path": "my/custom/path/to/.env"
+ }
+ }
+ }
+
+As an alternate option, you can directly invoke the ``Dotenv`` class in your
+``bootstrap.php`` file or any other file of your application::
+
+ use Symfony\Component\Dotenv\Dotenv;
+
+ (new Dotenv())->bootEnv(dirname(__DIR__).'my/custom/path/to/.env');
+
+Symfony will then look for the environment variables in that file, but also in
+the local and environment-specific files (e.g. ``.*.local`` and
+``.*..local``). Read
+:ref:`how to override environment variables `
+to learn more about this.
+
+If you need to know the path to the ``.env`` file that Symfony is using, you can
+read the ``SYMFONY_DOTENV_PATH`` environment variable in your application.
+
+.. versionadded:: 7.1
+
+ The ``SYMFONY_DOTENV_PATH`` environment variable was introduced in Symfony
+ 7.1.
+
.. _configuration-secrets:
Encrypting Environment Variables (Secrets)
@@ -992,10 +1033,6 @@ Use the ``debug:dotenv`` command to understand how Symfony parses the different
# look for a specific variable passing its full or partial name as an argument
$ php bin/console debug:dotenv foo
-.. versionadded:: 6.2
-
- The option to pass variable names to ``debug:dotenv`` was introduced in Symfony 6.2.
-
Additionally, and regardless of how you set environment variables, you can see all
environment variables, with their values, referenced in Symfony's container configuration,
you can also see the number of occurrences of each environment variable in the container:
diff --git a/configuration/env_var_processors.rst b/configuration/env_var_processors.rst
index 1e57fd65387..2e82104db66 100644
--- a/configuration/env_var_processors.rst
+++ b/configuration/env_var_processors.rst
@@ -419,10 +419,6 @@ Symfony provides the following env var processors:
->set(\RedisCluster::class, \RedisCluster::class)->args([null, '%env(shuffle:csv:REDIS_NODES)%']);
};
- .. versionadded:: 6.2
-
- The ``env(shuffle:...)`` env var processor was introduced in Symfony 6.2.
-
``env(file:FOO)``
Returns the contents of a file whose path is the value of the ``FOO`` env var:
@@ -788,10 +784,6 @@ Symfony provides the following env var processors:
The value stored in the ``CARD_SUIT`` env var would be a string (e.g. ``'spades'``)
but the application will use the enum value (e.g. ``Suit::Spades``).
- .. versionadded:: 6.2
-
- The ``env(enum:...)`` env var processor was introduced in Symfony 6.2.
-
``env(defined:NO_FOO)``
Evaluates to ``true`` if the env var exists and its value is not ``''``
(an empty string) or ``null``; it returns ``false`` otherwise.
@@ -826,9 +818,56 @@ Symfony provides the following env var processors:
// config/services.php
$container->setParameter('typed_env', '%env(defined:FOO)%');
- .. versionadded:: 6.4
+.. _urlencode_environment_variable_processor:
+
+``env(urlencode:FOO)``
+ Encodes the content of the ``FOO`` env var using the :phpfunction:`urlencode`
+ PHP function. This is especially useful when ``FOO`` value is not compatible
+ with DSN syntax.
+
+ .. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ parameters:
+ env(DATABASE_URL): 'mysql://db_user:foo@b$r@127.0.0.1:3306/db_name'
+ encoded_database_url: '%env(urlencode:DATABASE_URL)%'
+
+ .. code-block:: xml
+
+
+
+
+
+
+ mysql://db_user:foo@b$r@127.0.0.1:3306/db_name
+ %env(urlencode:DATABASE_URL)%
+
+
+
+ .. code-block:: php
+
+ // config/packages/framework.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (ContainerBuilder $container): void {
+ $container->setParameter('env(DATABASE_URL)', 'mysql://db_user:foo@b$r@127.0.0.1:3306/db_name');
+ $container->setParameter('encoded_database_url', '%env(urlencode:DATABASE_URL)%');
+ };
+
+ .. versionadded:: 7.1
- The ``env(defined:...)`` env var processor was introduced in Symfony 6.4.
+ The ``env(urlencode:...)`` env var processor was introduced in Symfony 7.1.
It is also possible to combine any number of processors:
diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst
index 511a15a69fd..542532ee1af 100644
--- a/configuration/micro_kernel_trait.rst
+++ b/configuration/micro_kernel_trait.rst
@@ -16,9 +16,7 @@ via Composer:
.. code-block:: terminal
- $ composer require symfony/config symfony/http-kernel \
- symfony/http-foundation symfony/routing \
- symfony/dependency-injection symfony/framework-bundle
+ $ composer require symfony/framework-bundle symfony/runtime
Next, create an ``index.php`` file that defines the kernel class and runs it:
@@ -34,19 +32,12 @@ Next, create an ``index.php`` file that defines the kernel class and runs it:
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Attribute\Route;
- require __DIR__.'/vendor/autoload.php';
+ require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
class Kernel extends BaseKernel
{
use MicroKernelTrait;
- public function registerBundles(): array
- {
- return [
- new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
- ];
- }
-
protected function configureContainer(ContainerConfigurator $container): void
{
// PHP equivalent of config/packages/framework.yaml
@@ -64,11 +55,9 @@ Next, create an ``index.php`` file that defines the kernel class and runs it:
}
}
- $kernel = new Kernel('dev', true);
- $request = Request::createFromGlobals();
- $response = $kernel->handle($request);
- $response->send();
- $kernel->terminate($request, $response);
+ return static function (array $context) {
+ return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
+ };
.. code-block:: php
@@ -80,19 +69,12 @@ Next, create an ``index.php`` file that defines the kernel class and runs it:
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
- require __DIR__.'/vendor/autoload.php';
+ require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
class Kernel extends BaseKernel
{
use MicroKernelTrait;
- public function registerBundles(): array
- {
- return [
- new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
- ];
- }
-
protected function configureContainer(ContainerConfigurator $container): void
{
// PHP equivalent of config/packages/framework.yaml
@@ -114,21 +96,9 @@ Next, create an ``index.php`` file that defines the kernel class and runs it:
}
}
- $kernel = new Kernel('dev', true);
- $request = Request::createFromGlobals();
- $response = $kernel->handle($request);
- $response->send();
- $kernel->terminate($request, $response);
-
-.. versionadded:: 6.1
-
- The PHP attributes notation has been introduced in Symfony 6.1.
-
-.. note::
-
- In addition to the ``index.php`` file, you'll need to create a directory called
- ``config/`` in your project (even if it's empty because you define the configuration
- options inside the ``configureContainer()`` method).
+ return static function (array $context) {
+ return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
+ };
That's it! To test it, start the :ref:`Symfony local web server `:
@@ -138,6 +108,23 @@ That's it! To test it, start the :ref:`Symfony local web server getEnvironment()) {
- $bundles[] = new \Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
+ yield new WebProfilerBundle();
}
-
- return $bundles;
}
protected function build(ContainerBuilder $containerBuilder): void
@@ -287,8 +296,8 @@ Now it looks like this::
{
// import the WebProfilerRoutes, only if the bundle is enabled
if (isset($this->bundles['WebProfilerBundle'])) {
- $routes->import('@WebProfilerBundle/Resources/config/routing/wdt.xml')->prefix('/_wdt');
- $routes->import('@WebProfilerBundle/Resources/config/routing/profiler.xml')->prefix('/_profiler');
+ $routes->import('@WebProfilerBundle/Resources/config/routing/wdt.php', 'php')->prefix('/_wdt');
+ $routes->import('@WebProfilerBundle/Resources/config/routing/profiler.php', 'php')->prefix('/_profiler');
}
// load the routes defined as PHP attributes
@@ -296,19 +305,16 @@ Now it looks like this::
$routes->import(__DIR__.'/Controller/', 'attribute');
}
- // optional, to use the standard Symfony cache directory
- public function getCacheDir(): string
- {
- return __DIR__.'/../var/cache/'.$this->getEnvironment();
- }
-
- // optional, to use the standard Symfony logs directory
- public function getLogDir(): string
- {
- return __DIR__.'/../var/log';
- }
+ // optionally, you can define the getCacheDir() and getLogDir() methods
+ // to override the default locations for these directories
}
+
+.. versionadded:: 7.3
+
+ The ``wdt.php`` and ``profiler.php`` files were introduced in Symfony 7.3.
+ Previously, you had to import ``wdt.xml`` and ``profiler.xml``
+
Before continuing, run this command to add support for the new dependencies:
.. code-block:: terminal
@@ -344,10 +350,6 @@ add a service conditionally based on the ``foo`` value::
}
}
-.. versionadded:: 6.1
-
- The ``AbstractExtension`` class was introduced in Symfony 6.1.
-
Unlike the previous kernel, this loads an external ``config/framework.yaml`` file,
because the configuration started to get bigger:
diff --git a/configuration/multiple_kernels.rst b/configuration/multiple_kernels.rst
index dd857fff243..ec8742213b5 100644
--- a/configuration/multiple_kernels.rst
+++ b/configuration/multiple_kernels.rst
@@ -117,7 +117,9 @@ resources::
// src/Kernel.php
namespace Shared;
+ use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
+ use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
class Kernel extends BaseKernel
@@ -258,6 +260,7 @@ the application ID to run under CLI context::
// bin/console
use Shared\Kernel;
+ use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
diff --git a/configuration/secrets.rst b/configuration/secrets.rst
index 653bd92f611..285b89d521e 100644
--- a/configuration/secrets.rst
+++ b/configuration/secrets.rst
@@ -166,6 +166,22 @@ secrets' values by passing the ``--reveal`` option:
DATABASE_PASSWORD "my secret"
------------------- ------------ -------------
+Reveal Existing Secrets
+-----------------------
+
+If you have the **decryption key**, the ``secrets:reveal`` command allows
+you to reveal a single secret's value.
+
+.. code-block:: terminal
+
+ $ php bin/console secrets:reveal DATABASE_PASSWORD
+
+ my secret
+
+.. versionadded:: 7.1
+
+ The ``secrets:reveal`` command was introduced in Symfony 7.1.
+
Remove Secrets
--------------
@@ -295,7 +311,7 @@ The secrets system is enabled by default and some of its behavior can be configu
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/framework https://symfony.com/schema/dic/framework/framework-1.0.xsd"
>
-
+ ` tool, follow
:ref:`these instructions ` to enable autocompletion.
+.. _console_creating-command:
+
Creating a Command
------------------
-Commands are defined in classes extending
-:class:`Symfony\\Component\\Console\\Command\\Command`. For example, you may
-want a command to create a user::
+Commands are defined in classes and auto-registered using the ``#[AsCommand]``
+attribute. For example, you may want a command to create a user::
// src/Command/CreateUserCommand.php
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Output\OutputInterface;
// the name of the command is what users type after "php bin/console"
#[AsCommand(name: 'app:create-user')]
- class CreateUserCommand extends Command
+ class CreateUserCommand
{
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(): int
{
// ... put here the code to create the user
@@ -151,117 +142,54 @@ want a command to create a user::
}
}
-Configuring the Command
-~~~~~~~~~~~~~~~~~~~~~~~
-
-You can optionally define a description, help message and the
-:doc:`input options and arguments ` by overriding the
-``configure()`` method::
+If you can't use PHP attributes, register the command as a service and
+:doc:`tag it ` with the ``console.command`` tag. If you're using the
+:ref:`default services.yaml configuration `,
+this is already done for you, thanks to :ref:`autoconfiguration `.
- // src/Command/CreateUserCommand.php
+You can also use ``#[AsCommand]`` to add a description and longer help text for the command::
- // ...
- class CreateUserCommand extends Command
+ #[AsCommand(
+ name: 'app:create-user',
+ description: 'Creates a new user.', // the command description shown when running "php bin/console list"
+ help: 'This command allows you to create a user...', // the command help shown when running the command with the "--help" option
+ )]
+ class CreateUserCommand
{
- // the command description shown when running "php bin/console list"
- protected static $defaultDescription = 'Creates a new user.';
-
- // ...
- protected function configure(): void
+ public function __invoke(): int
{
- $this
- // the command help shown when running the command with the "--help" option
- ->setHelp('This command allows you to create a user...')
- ;
+ // ...
}
}
-.. tip::
-
- Defining the ``$defaultDescription`` static property instead of using the
- ``setDescription()`` method allows to get the command description without
- instantiating its class. This makes the ``php bin/console list`` command run
- much faster.
-
- If you want to always run the ``list`` command fast, add the ``--short`` option
- to it (``php bin/console list --short``). This will avoid instantiating command
- classes, but it won't show any description for commands that use the
- ``setDescription()`` method instead of the static property.
-
-.. deprecated:: 6.1
+Additionally, you can extend the :class:`Symfony\\Component\\Console\\Command\\Command` class to
+leverage advanced features like lifecycle hooks (e.g. :method:`Symfony\\Component\\Console\\Command\\Command::initialize` and
+and :method:`Symfony\\Component\\Console\\Command\\Command::interact`)::
- The static property ``$defaultDescription`` was deprecated in Symfony 6.1.
- Instead, use the ``#[AsCommand]`` attribute to define the optional command
- description.
-
-The ``configure()`` method is called automatically at the end of the command
-constructor. If your command defines its own constructor, set the properties
-first and then call to the parent constructor, to make those properties
-available in the ``configure()`` method::
-
- // ...
+ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Input\InputArgument;
+ use Symfony\Component\Console\Input\InputInterface;
+ use Symfony\Component\Console\Output\OutputInterface;
+ #[AsCommand(name: 'app:create-user')]
class CreateUserCommand extends Command
{
- // ...
-
- public function __construct(bool $requirePassword = false)
+ public function initialize(InputInterface $input, OutputInterface $output): void
{
- // best practices recommend to call the parent constructor first and
- // then set your own properties. That wouldn't work in this case
- // because configure() needs the properties set in this constructor
- $this->requirePassword = $requirePassword;
-
- parent::__construct();
+ // ...
}
- protected function configure(): void
+ public function interact(InputInterface $input, OutputInterface $output): void
{
- $this
- // ...
- ->addArgument('password', $this->requirePassword ? InputArgument::REQUIRED : InputArgument::OPTIONAL, 'User password')
- ;
+ // ...
}
- }
-
-.. _console_registering-the-command:
-
-Registering the Command
-~~~~~~~~~~~~~~~~~~~~~~~
-You can register the command by adding the ``AsCommand`` attribute to it::
-
- // src/Command/CreateUserCommand.php
- namespace App\Command;
-
- use Symfony\Component\Console\Attribute\AsCommand;
- use Symfony\Component\Console\Command\Command;
-
- // the "name" and "description" arguments of AsCommand replace the
- // static $defaultName and $defaultDescription properties
- #[AsCommand(
- name: 'app:create-user',
- description: 'Creates a new user.',
- hidden: false,
- aliases: ['app:add-user']
- )]
- class CreateUserCommand extends Command
- {
- // ...
+ public function __invoke(): int
+ {
+ // ...
+ }
}
-If you can't use PHP attributes, register the command as a service and
-:doc:`tag it ` with the ``console.command`` tag. If you're using the
-:ref:`default services.yaml configuration `,
-this is already done for you, thanks to :ref:`autoconfiguration `.
-
-.. deprecated:: 6.1
-
- The static property ``$defaultName`` was deprecated in Symfony 6.1.
- Define your command name with the ``#[AsCommand]`` attribute instead.
-
Running the Command
~~~~~~~~~~~~~~~~~~~
@@ -272,16 +200,16 @@ After configuring and registering the command, you can run it in the terminal:
$ php bin/console app:create-user
As you might expect, this command will do nothing as you didn't write any logic
-yet. Add your own logic inside the ``execute()`` method.
+yet. Add your own logic inside the ``__invoke()`` method.
Console Output
--------------
-The ``execute()`` method has access to the output stream to write messages to
+The ``__invoke()`` method has access to the output stream to write messages to
the console::
// ...
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(OutputInterface $output): int
{
// outputs multiple lines to the console (adding "\n" at the end of each line)
$output->writeln([
@@ -332,9 +260,10 @@ method, which returns an instance of
// ...
use Symfony\Component\Console\Output\ConsoleOutputInterface;
- class MyCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(OutputInterface $output): int
{
if (!$output instanceof ConsoleOutputInterface) {
throw new \LogicException('This command accepts only an instance of "ConsoleOutputInterface".');
@@ -378,10 +307,6 @@ method, which returns an instance of
A new line is appended automatically when displaying information in a section.
-.. versionadded:: 6.2
-
- The feature to limit the height of a console section was introduced in Symfony 6.2.
-
Output sections let you manipulate the Console output in advanced ways, such as
:ref:`displaying multiple progress bars ` which
are updated independently and :ref:`appending rows to tables `
@@ -397,20 +322,12 @@ Console Input
Use input options or arguments to pass information to the command::
- use Symfony\Component\Console\Input\InputArgument;
-
- // ...
- protected function configure(): void
- {
- $this
- // configure an argument
- ->addArgument('username', InputArgument::REQUIRED, 'The username of the user.')
- // ...
- ;
- }
+ use Symfony\Component\Console\Attribute\Argument;
- // ...
- public function execute(InputInterface $input, OutputInterface $output): int
+ // The #[Argument] attribute configures $username as a
+ // required input argument and its value is automatically
+ // passed to this parameter
+ public function __invoke(#[Argument('The username of the user.')] string $username, OutputInterface $output): int
{
$output->writeln([
'User Creator',
@@ -418,8 +335,7 @@ Use input options or arguments to pass information to the command::
'',
]);
- // retrieve the argument value using getArgument()
- $output->writeln('Username: '.$input->getArgument('username'));
+ $output->writeln('Username: '.$username);
return Command::SUCCESS;
}
@@ -449,23 +365,22 @@ as a service, you can use normal dependency injection. Imagine you have a
// ...
use App\Service\UserManager;
- use Symfony\Component\Console\Command\Command;
+ use Symfony\Component\Console\Attribute\Argument;
+ use Symfony\Component\Console\Attribute\AsCommand;
- class CreateUserCommand extends Command
+ #[AsCommand(name: 'app:create-user')]
+ class CreateUserCommand
{
public function __construct(
- private UserManager $userManager,
- ){
- parent::__construct();
+ private UserManager $userManager
+ ) {
}
- // ...
-
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(#[Argument] string $username, OutputInterface $output): int
{
// ...
- $this->userManager->create($input->getArgument('username'));
+ $this->userManager->create($username);
$output->writeln('User successfully generated!');
@@ -493,7 +408,7 @@ command:
Note that it will not be called when the command is run without interaction
(e.g. when passing the ``--no-interaction`` global option flag).
-:method:`Symfony\\Component\\Console\\Command\\Command::execute` *(required)*
+``__invoke()`` (or :method:`Symfony\\Component\\Console\\Command\\Command::execute`) *(required)*
This method is executed after ``interact()`` and ``initialize()``.
It contains the logic you want the command to execute and it must
return an integer which will be used as the command `exit status`_.
@@ -603,11 +518,6 @@ even the color mode being used. You have access to such information thanks to th
// changes the color mode
$colorMode = $terminal->setColorMode(AnsiColorMode::Ansi24);
-.. versionadded:: 6.2
-
- The support for setting and getting the current color mode was introduced
- in Symfony 6.2.
-
Logging Command Errors
----------------------
@@ -652,10 +562,6 @@ profile is accessible through the web page of the profiler.
profile. Moreover, consider using the ``--limit`` option to only process a few
messages to make the profile more readable in the profiler.
-.. versionadded:: 6.4
-
- The ``--profile`` option was introduced in Symfony 6.4.
-
Learn More
----------
diff --git a/console/calling_commands.rst b/console/calling_commands.rst
index dd1f0b12ff9..875ead15d2d 100644
--- a/console/calling_commands.rst
+++ b/console/calling_commands.rst
@@ -14,20 +14,18 @@ arguments and options you want to pass to the command. The command name must be
the first argument.
Eventually, calling the ``doRun()`` method actually runs the command and returns
-the returned code from the command (return value from command ``execute()``
+the returned code from the command (return value from command ``__invoke()``
method)::
// ...
- use Symfony\Component\Console\Command;
+ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\ArrayInput;
- use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
- class CreateUserCommand extends Command
+ #[AsCommand(name: 'app:create-user')]
+ class CreateUserCommand
{
- // ...
-
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(OutputInterface $output): int
{
$greetInput = new ArrayInput([
// the command name is passed as first argument
diff --git a/console/coloring.rst b/console/coloring.rst
index c54045250a8..8b6655d6b71 100644
--- a/console/coloring.rst
+++ b/console/coloring.rst
@@ -58,10 +58,6 @@ Any hex color is supported for foreground and background colors. Besides that, t
the nearest color depending on the terminal capabilities. E.g. ``#c0392b`` is
degraded to ``#d75f5f`` in 256-color terminals and to ``red`` in 8-color terminals.
- .. versionadded:: 6.2
-
- The support for 256-color terminals was introduced in Symfony 6.2.
-
And available options are: ``bold``, ``underscore``, ``blink``, ``reverse``
(enables the "reverse video" mode where the background and foreground colors
are swapped) and ``conceal`` (sets the foreground color to transparent, making
diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst
index 1393879a1df..ed5b99f9cb4 100644
--- a/console/commands_as_services.rst
+++ b/console/commands_as_services.rst
@@ -16,27 +16,16 @@ For example, suppose you want to log something from within your command::
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Attribute\AsCommand;
- use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Output\OutputInterface;
- #[AsCommand(name: 'app:sunshine')]
- class SunshineCommand extends Command
+ #[AsCommand(name: 'app:sunshine', description: 'Good morning!')]
+ class SunshineCommand
{
public function __construct(
private LoggerInterface $logger,
) {
- // you *must* call the parent constructor
- parent::__construct();
- }
-
- protected function configure(): void
- {
- $this
- ->setDescription('Good morning!');
}
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(): int
{
$this->logger->info('Waking up the sun');
// ...
@@ -70,7 +59,7 @@ To make your command lazily loaded, either define its name using the PHP
// ...
#[AsCommand(name: 'app:sunshine')]
- class SunshineCommand extends Command
+ class SunshineCommand
{
// ...
}
diff --git a/console/hide_commands.rst b/console/hide_commands.rst
index 44a69d09289..4ab9d3a6dad 100644
--- a/console/hide_commands.rst
+++ b/console/hide_commands.rst
@@ -15,10 +15,9 @@ the ``hidden`` property of the ``AsCommand`` attribute::
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
- use Symfony\Component\Console\Command\Command;
#[AsCommand(name: 'app:legacy', hidden: true)]
- class LegacyCommand extends Command
+ class LegacyCommand
{
// ...
}
diff --git a/console/input.rst b/console/input.rst
index 09cba08a41a..d5b6e4881bb 100644
--- a/console/input.rst
+++ b/console/input.rst
@@ -311,6 +311,42 @@ The above code can be simplified as follows because ``false !== null``::
$yell = ($optionValue !== false);
$yellLouder = ($optionValue === 'louder');
+Fetching The Raw Command Input
+------------------------------
+
+Symfony provides a :method:`Symfony\\Component\\Console\\Input\\ArgvInput::getRawTokens`
+method to fetch the raw input that was passed to the command. This is useful if
+you want to parse the input yourself or when you need to pass the input to another
+command without having to worry about the number of arguments or options::
+
+ // ...
+ use Symfony\Component\Process\Process;
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ // if this command was run as:
+ // php bin/console app:my-command foo --bar --baz=3 --qux=value1 --qux=value2
+
+ $tokens = $input->getRawTokens();
+ // $tokens = ['app:my-command', 'foo', '--bar', '--baz=3', '--qux=value1', '--qux=value2'];
+
+ // pass true as argument to not include the original command name
+ $tokens = $input->getRawTokens(true);
+ // $tokens = ['foo', '--bar', '--baz=3', '--qux=value1', '--qux=value2'];
+
+ // pass the raw input to any other command (from Symfony or the operating system)
+ $process = new Process(['app:other-command', ...$input->getRawTokens(true)]);
+ $process->setTty(true);
+ $process->mustRun();
+
+ // ...
+ }
+
+.. versionadded:: 7.1
+
+ The :method:`Symfony\\Component\\Console\\Input\\ArgvInput::getRawTokens`
+ method was introduced in Symfony 7.1.
+
Adding Argument/Option Value Completion
---------------------------------------
@@ -354,12 +390,6 @@ To achieve this, use the 5th argument of ``addArgument()`` or the 6th argument o
}
}
-.. versionadded:: 6.1
-
- The argument to ``addOption()``/``addArgument()`` was introduced in
- Symfony 6.1. Prior to this version, you had to override the
- ``complete()`` method of the command.
-
That's all you need! Assuming users "Fabien" and "Fabrice" exist, pressing
tab after typing ``app:greet Fa`` will give you these names as a suggestion.
@@ -416,13 +446,18 @@ The Console component adds some predefined options to all commands:
* ``--verbose``: sets the verbosity level (e.g. ``1`` the default, ``2`` and
``3``, or you can use respective shortcuts ``-v``, ``-vv`` and ``-vvv``)
-* ``--quiet|-q``: disables output and interaction
+* ``--silent``: disables all output and interaction, including errors
+* ``--quiet|-q``: disables output and interaction, but errors are still displayed
* ``--no-interaction|-n``: disables interaction
* ``--version|-V``: outputs the version number of the console application
* ``--help|-h``: displays the command help
* ``--ansi|--no-ansi``: whether to force of disable coloring the output
* ``--profile``: enables the Symfony profiler
+.. versionadded:: 7.2
+
+ The ``--silent`` option was introduced in Symfony 7.2.
+
When using the ``FrameworkBundle``, two more options are predefined:
* ``--env|-e``: sets the Kernel configuration environment (defaults to ``APP_ENV``)
diff --git a/console/lockable_trait.rst b/console/lockable_trait.rst
index 02f635f5788..2a4fd64ffaf 100644
--- a/console/lockable_trait.rst
+++ b/console/lockable_trait.rst
@@ -13,19 +13,17 @@ that adds two convenient methods to lock and release commands::
// ...
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\LockableTrait;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Output\OutputInterface;
+ use Symfony\Component\Console\Style\SymfonyStyle;
- class UpdateContentsCommand extends Command
+ #[AsCommand(name: 'contents:update')]
+ class UpdateContentsCommand
{
use LockableTrait;
- // ...
-
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(SymfonyStyle $io): int
{
if (!$this->lock()) {
- $output->writeln('The command is already running in another process.');
+ $io->writeln('The command is already running in another process.');
return Command::SUCCESS;
}
@@ -43,4 +41,29 @@ that adds two convenient methods to lock and release commands::
}
}
+The LockableTrait will use the ``SemaphoreStore`` if available and will default
+to ``FlockStore`` otherwise. You can override this behavior by setting
+a ``$lockFactory`` property with your own lock factory::
+
+ // ...
+ use Symfony\Component\Console\Command\Command;
+ use Symfony\Component\Console\Command\LockableTrait;
+ use Symfony\Component\Lock\LockFactory;
+
+ #[AsCommand(name: 'contents:update')]
+ class UpdateContentsCommand
+ {
+ use LockableTrait;
+
+ public function __construct(private LockFactory $lockFactory)
+ {
+ }
+
+ // ...
+ }
+
+.. versionadded:: 7.1
+
+ The ``$lockFactory`` property was introduced in Symfony 7.1.
+
.. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science)
diff --git a/console/style.rst b/console/style.rst
index 8540ce493f2..5357b9e6172 100644
--- a/console/style.rst
+++ b/console/style.rst
@@ -7,18 +7,18 @@ questions to the user involves a lot of repetitive code.
Consider for example the code used to display the title of the following command::
- // src/Command/GreetCommand.php
+ // src/Command/MyCommand.php
namespace App\Command;
+ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
- class GreetCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- // ...
-
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
$output->writeln([
'Lorem Ipsum Dolor Sit Amet>',
@@ -42,26 +42,22 @@ which allow to create *semantic* commands and forget about their styling.
Basic Usage
-----------
-In your command, instantiate the :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle`
-class and pass the ``$input`` and ``$output`` variables as its arguments. Then,
-you can start using any of its helpers, such as ``title()``, which displays the
-title of the command::
+In your ``__invoke()`` method, add an argument of type :class:`Symfony\\Component\\Console\\Style\\SymfonyStyle`.
+Then, you can start using any of its helpers, such as ``title()``, which
+displays the title of the command::
- // src/Command/GreetCommand.php
+ // src/Command/MyCommand.php
namespace App\Command;
+ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
- class GreetCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- // ...
-
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(SymfonyStyle $io): int
{
- $io = new SymfonyStyle($input, $output);
$io->title('Lorem Ipsum Dolor Sit Amet');
// ...
@@ -169,6 +165,32 @@ Content Methods
styled according to the Symfony Style Guide, which allows you to use
features such as dynamically appending rows.
+:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::tree`
+ It displays the given nested array as a formatted directory/file tree
+ structure in the console output::
+
+ $io->tree([
+ 'src' => [
+ 'Controller' => [
+ 'DefaultController.php',
+ ],
+ 'Kernel.php',
+ ],
+ 'templates' => [
+ 'base.html.twig',
+ ],
+ ]);
+
+.. versionadded:: 7.3
+
+ The ``SymfonyStyle::tree()`` and the ``SymfonyStyle::createTree()`` methods
+ were introduced in Symfony 7.3.
+
+:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::createTree`
+ Creates an instance of :class:`Symfony\\Component\\Console\\Helper\\TreeHelper`
+ styled according to the Symfony Style Guide, which allows you to use
+ features such as dynamically nesting nodes.
+
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::newLine`
It displays a blank line in the command output. Although it may seem useful,
most of the times you won't need it at all. The reason is that every helper
@@ -339,10 +361,6 @@ User Input Methods
$io->choice('Select the queue to analyze', ['queue1', 'queue2', 'queue3'], multiSelect: true);
-.. versionadded:: 6.2
-
- The ``multiSelect`` option of ``choice()`` was introduced in Symfony 6.2.
-
.. _symfony-style-blocks:
Result Methods
@@ -432,29 +450,23 @@ long they are. This is done to enable clickable URLs in terminals that support t
If you prefer to wrap all contents, including URLs, use this method::
- // src/Command/GreetCommand.php
+ // src/Command/MyCommand.php
namespace App\Command;
// ...
use Symfony\Component\Console\Style\SymfonyStyle;
- class GreetCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- // ...
-
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(SymfonyStyle $io): int
{
- $io = new SymfonyStyle($input, $output);
$io->getOutputWrapper()->setAllowCutUrls(true);
// ...
}
}
-.. versionadded:: 6.2
-
- The ``setAllowCutUrls()`` method was introduced in Symfony 6.2.
-
Defining your Own Styles
------------------------
@@ -475,7 +487,7 @@ Then, instantiate this custom class instead of the default ``SymfonyStyle`` in
your commands. Thanks to the ``StyleInterface`` you won't need to change the code
of your commands to change their appearance::
- // src/Command/GreetCommand.php
+ // src/Command/MyCommand.php
namespace App\Console;
use App\Console\CustomStyle;
@@ -483,16 +495,11 @@ of your commands to change their appearance::
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
- class GreetCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
- // ...
-
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(InputInterface $input, OutputInterface $output): int
{
- // Before
- $io = new SymfonyStyle($input, $output);
-
- // After
$io = new CustomStyle($input, $output);
// ...
diff --git a/console/verbosity.rst b/console/verbosity.rst
index b822e3d7534..3afd085d773 100644
--- a/console/verbosity.rst
+++ b/console/verbosity.rst
@@ -7,7 +7,10 @@ messages, but you can control their verbosity with the ``-q`` and ``-v`` options
.. code-block:: terminal
- # do not output any message (not even the command result messages)
+ # suppress all output, including errors
+ $ php bin/console some-command --silent
+
+ # suppress all output (even the command result messages) but display errors
$ php bin/console some-command -q
$ php bin/console some-command --quiet
@@ -23,6 +26,10 @@ messages, but you can control their verbosity with the ``-q`` and ``-v`` options
# display all messages (useful to debug errors)
$ php bin/console some-command -vvv
+.. versionadded:: 7.2
+
+ The ``--silent`` option was introduced in Symfony 7.2.
+
The verbosity level can also be controlled globally for all commands with the
``SHELL_VERBOSITY`` environment variable (the ``-q`` and ``-v`` options still
have more precedence over the value of ``SHELL_VERBOSITY``):
@@ -30,6 +37,7 @@ have more precedence over the value of ``SHELL_VERBOSITY``):
===================== ========================= ===========================================
Console option ``SHELL_VERBOSITY`` value Equivalent PHP constant
===================== ========================= ===========================================
+``--silent`` ``-2`` ``OutputInterface::VERBOSITY_SILENT``
``-q`` or ``--quiet`` ``-1`` ``OutputInterface::VERBOSITY_QUIET``
(none) ``0`` ``OutputInterface::VERBOSITY_NORMAL``
``-v`` ``1`` ``OutputInterface::VERBOSITY_VERBOSE``
@@ -41,24 +49,25 @@ It is possible to print a message in a command for only a specific verbosity
level. For example::
// ...
+ use Symfony\Component\Console\Attribute\Argument;
+ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
- class CreateUserCommand extends Command
+ #[AsCommand(name: 'app:create-user')]
+ class CreateUserCommand
{
- // ...
-
- public function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(OutputInterface $output, #[Argument] string $username, #[Argument] string $password): int
{
$user = new User(...);
$output->writeln([
- 'Username: '.$input->getArgument('username'),
- 'Password: '.$input->getArgument('password'),
+ 'Username: '.$username,
+ 'Password: '.$password,
]);
- // available methods: ->isQuiet(), ->isVerbose(), ->isVeryVerbose(), ->isDebug()
+ // available methods: ->isSilent(), ->isQuiet(), ->isVerbose(), ->isVeryVerbose(), ->isDebug()
if ($output->isVerbose()) {
$output->writeln('User class: '.$user::class);
}
@@ -73,10 +82,19 @@ level. For example::
}
}
-When the quiet level is used, all output is suppressed as the default
+.. versionadded:: 7.2
+
+ The ``isSilent()`` method was introduced in Symfony 7.2.
+
+When the silent or quiet level are used, all output is suppressed as the default
:method:`Symfony\\Component\\Console\\Output\\Output::write` method returns
without actually printing.
+.. tip::
+
+ When using the ``silent`` verbosity, errors won't be displayed in the console
+ but they will still be logged through the :doc:`Symfony logger ` integration.
+
.. tip::
The MonologBridge provides a :class:`Symfony\\Bridge\\Monolog\\Handler\\ConsoleHandler`
diff --git a/contributing/code/pull_requests.rst b/contributing/code/pull_requests.rst
index 548fc6c190c..6b40e940dfb 100644
--- a/contributing/code/pull_requests.rst
+++ b/contributing/code/pull_requests.rst
@@ -31,7 +31,7 @@ Before working on Symfony, setup a friendly environment with the following
software:
* Git;
-* PHP version 8.1 or above.
+* PHP version 8.2 or above.
Configure Git
~~~~~~~~~~~~~
@@ -132,7 +132,7 @@ work:
branch (you can find them on the `Symfony releases page`_). E.g. if you
found a bug introduced in ``v5.1.10``, you need to work on ``5.4``.
-* ``7.1``, if you are adding a new feature.
+* ``7.2``, if you are adding a new feature.
The only exception is when a new :doc:`major Symfony version `
(5.0, 6.0, etc.) comes out every two years. Because of the
diff --git a/contributing/code/tests.rst b/contributing/code/tests.rst
index e6af1170e3d..060e3eda02b 100644
--- a/contributing/code/tests.rst
+++ b/contributing/code/tests.rst
@@ -32,7 +32,7 @@ tests, such as Doctrine, Twig and Monolog. To do so,
.. code-block:: terminal
- $ COMPOSER_ROOT_VERSION=6.4.x-dev composer update
+ $ COMPOSER_ROOT_VERSION=7.2.x-dev composer update
.. _running:
diff --git a/contributing/documentation/format.rst b/contributing/documentation/format.rst
index ef17b55011e..3318df50841 100644
--- a/contributing/documentation/format.rst
+++ b/contributing/documentation/format.rst
@@ -243,39 +243,39 @@ If you are documenting a brand new feature, a change or a deprecation that's
been made in Symfony, you should precede your description of the change with
the corresponding directive and a short description:
-For a new feature or a behavior change use the ``.. versionadded:: 6.x``
+For a new feature or a behavior change use the ``.. versionadded:: 7.x``
directive:
.. code-block:: rst
- .. versionadded:: 6.2
+ .. versionadded:: 7.2
- ... ... ... was introduced in Symfony 6.2.
+ ... ... ... was introduced in Symfony 7.2.
If you are documenting a behavior change, it may be helpful to *briefly*
describe how the behavior has changed:
.. code-block:: rst
- .. versionadded:: 6.2
+ .. versionadded:: 7.2
- ... ... ... was introduced in Symfony 6.2. Prior to this,
+ ... ... ... was introduced in Symfony 7.2. Prior to this,
... ... ... ... ... ... ... ... .
-For a deprecation use the ``.. deprecated:: 6.x`` directive:
+For a deprecation use the ``.. deprecated:: 7.x`` directive:
.. code-block:: rst
- .. deprecated:: 6.2
+ .. deprecated:: 7.2
- ... ... ... was deprecated in Symfony 6.2.
+ ... ... ... was deprecated in Symfony 7.2.
-Whenever a new major version of Symfony is released (e.g. 6.0, 7.0, etc), a new
+Whenever a new major version of Symfony is released (e.g. 8.0, 9.0, etc), a new
branch of the documentation is created from the ``x.4`` branch of the previous
major version. At this point, all the ``versionadded`` and ``deprecated`` tags
for Symfony versions that have a lower major version will be removed. For
-example, if Symfony 6.0 were released today, 5.0 to 5.4 ``versionadded`` and
-``deprecated`` tags would be removed from the new ``6.0`` branch.
+example, if Symfony 8.0 were released today, 7.0 to 7.4 ``versionadded`` and
+``deprecated`` tags would be removed from the new ``8.0`` branch.
.. _`reStructuredText`: https://docutils.sourceforge.io/rst.html
.. _`Docs Builder`: https://github.com/symfony-tools/docs-builder
diff --git a/controller.rst b/controller.rst
index 43785329efb..5b0b77b35b9 100644
--- a/controller.rst
+++ b/controller.rst
@@ -230,10 +230,6 @@ command:
You can read more about this attribute in :ref:`autowire-attribute`.
- .. versionadded:: 6.1
-
- The ``#[Autowire]`` attribute was introduced in Symfony 6.1.
-
Like with all services, you can also use regular
:ref:`constructor injection ` in your
controllers.
@@ -375,6 +371,11 @@ The ``MapQueryParameter`` attribute supports the following argument types:
* ``float``
* ``int``
* ``string``
+* Objects that extend :class:`Symfony\\Component\\Uid\\AbstractUid`
+
+.. versionadded:: 7.3
+
+ Support for ``AbstractUid`` objects was introduced in Symfony 7.3.
``#[MapQueryParameter]`` can take an optional argument called ``filter``. You can use the
`Validate Filters`_ constants defined in PHP::
@@ -393,11 +394,6 @@ The ``MapQueryParameter`` attribute supports the following argument types:
// ...
}
-.. versionadded:: 6.3
-
- The :class:`Symfony\\Component\\HttpKernel\\Attribute\\MapQueryParameter` attribute
- was introduced in Symfony 6.3.
-
.. _controller-mapping-query-string:
Mapping The Whole Query String
@@ -461,30 +457,41 @@ HTTP status to return if the validation fails::
The default status code returned if the validation fails is 404.
-If you need a valid DTO even when the request query string is empty, set a
-default value for your controller arguments::
+If you want to map your object to a nested array in your query using a specific key,
+set the ``key`` option in the ``#[MapQueryString]`` attribute::
- use App\Model\UserDto;
+ use App\Model\SearchDto;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Attribute\MapQueryString;
// ...
public function dashboard(
- #[MapQueryString] UserDto $userDto = new UserDto()
+ #[MapQueryString(key: 'search')] SearchDto $searchDto
): Response
{
// ...
}
-.. versionadded:: 6.3
+.. versionadded:: 7.3
- The :class:`Symfony\\Component\\HttpKernel\\Attribute\\MapQueryString` attribute
- was introduced in Symfony 6.3.
+ The ``key`` option of ``#[MapQueryString]`` was introduced in Symfony 7.3.
-.. versionadded:: 6.4
+If you need a valid DTO even when the request query string is empty, set a
+default value for your controller arguments::
+
+ use App\Model\UserDto;
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\HttpKernel\Attribute\MapQueryString;
- The ``validationFailedStatusCode`` parameter was introduced in Symfony 6.4.
+ // ...
+
+ public function dashboard(
+ #[MapQueryString] UserDto $userDto = new UserDto()
+ ): Response
+ {
+ // ...
+ }
.. _controller-mapping-request-payload:
@@ -583,14 +590,137 @@ if you want to map a nested array of specific DTOs::
) {}
}
-.. versionadded:: 6.3
+Instead of returning an array of DTO objects, you can tell Symfony to transform
+each DTO object into an array and return something like this:
+
+.. code-block:: json
+
+ [
+ {
+ "firstName": "John",
+ "lastName": "Smith",
+ "age": 28
+ },
+ {
+ "firstName": "Jane",
+ "lastName": "Doe",
+ "age": 30
+ }
+ ]
+
+To do so, map the parameter as an array and configure the type of each element
+using the ``type`` option of the attribute::
+
+ public function dashboard(
+ #[MapRequestPayload(type: UserDto::class)] array $users
+ ): Response
+ {
+ // ...
+ }
+
+.. versionadded:: 7.1
+
+ The ``type`` option of ``#[MapRequestPayload]`` was introduced in Symfony 7.1.
- The :class:`Symfony\\Component\\HttpKernel\\Attribute\\MapRequestPayload` attribute
- was introduced in Symfony 6.3.
+.. _controller_map-uploaded-file:
-.. versionadded:: 6.4
+Mapping Uploaded Files
+~~~~~~~~~~~~~~~~~~~~~~
- The ``validationFailedStatusCode`` parameter was introduced in Symfony 6.4.
+Symfony provides an attribute called ``#[MapUploadedFile]`` to map one or more
+``UploadedFile`` objects to controller arguments::
+
+ namespace App\Controller;
+
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+ use Symfony\Component\HttpFoundation\File\UploadedFile;
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\HttpKernel\Attribute\MapUploadedFile;
+ use Symfony\Component\Routing\Attribute\Route;
+
+ class UserController extends AbstractController
+ {
+ #[Route('/user/picture', methods: ['PUT'])]
+ public function changePicture(
+ #[MapUploadedFile] UploadedFile $picture,
+ ): Response {
+ // ...
+ }
+ }
+
+In this example, the associated :doc:`argument resolver `
+fetches the ``UploadedFile`` based on the argument name (``$picture``). If no file
+is submitted, an ``HttpException`` is thrown. You can change this by making the
+controller argument nullable:
+
+.. code-block:: php-attributes
+
+ #[MapUploadedFile]
+ ?UploadedFile $document
+
+The ``#[MapUploadedFile]`` attribute also allows to pass a list of constraints
+to apply to the uploaded file::
+
+ namespace App\Controller;
+
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+ use Symfony\Component\HttpFoundation\File\UploadedFile;
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\HttpKernel\Attribute\MapUploadedFile;
+ use Symfony\Component\Routing\Attribute\Route;
+ use Symfony\Component\Validator\Constraints as Assert;
+
+ class UserController extends AbstractController
+ {
+ #[Route('/user/picture', methods: ['PUT'])]
+ public function changePicture(
+ #[MapUploadedFile([
+ new Assert\File(mimeTypes: ['image/png', 'image/jpeg']),
+ new Assert\Image(maxWidth: 3840, maxHeight: 2160),
+ ])]
+ UploadedFile $picture,
+ ): Response {
+ // ...
+ }
+ }
+
+The validation constraints are checked before injecting the ``UploadedFile`` into
+the controller argument. If there's a constraint violation, an ``HttpException``
+is thrown and the controller's action is not executed.
+
+If you need to upload a collection of files, map them to an array or a variadic
+argument. The given constraint will be applied to all files and if any of them
+fails, an ``HttpException`` is thrown:
+
+.. code-block:: php-attributes
+
+ #[MapUploadedFile(new Assert\File(mimeTypes: ['application/pdf']))]
+ array $documents
+
+ #[MapUploadedFile(new Assert\File(mimeTypes: ['application/pdf']))]
+ UploadedFile ...$documents
+
+Use the ``name`` option to rename the uploaded file to a custom value:
+
+.. code-block:: php-attributes
+
+ #[MapUploadedFile(name: 'something-else')]
+ UploadedFile $document
+
+In addition, you can change the status code of the HTTP exception thrown when
+there are constraint violations:
+
+.. code-block:: php-attributes
+
+ #[MapUploadedFile(
+ constraints: new Assert\File(maxSize: '2M'),
+ validationFailedStatusCode: Response::HTTP_REQUEST_ENTITY_TOO_LARGE
+ )]
+ UploadedFile $document
+
+.. versionadded:: 7.1
+
+ The ``#[MapUploadedFile]`` attribute was introduced in Symfony 7.1.
Managing the Session
--------------------
@@ -773,11 +903,6 @@ The ``file()`` helper provides some arguments to configure its behavior::
Sending Early Hints
~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 6.3
-
- The Early Hints helper of the ``AbstractController`` was introduced
- in Symfony 6.3.
-
`Early hints`_ tell the browser to start downloading some assets even before the
application sends the response content. This improves perceived performance
because the browser can prefetch resources that will be needed once the full
diff --git a/controller/error_pages.rst b/controller/error_pages.rst
index fc36b88779a..06087837437 100644
--- a/controller/error_pages.rst
+++ b/controller/error_pages.rst
@@ -154,7 +154,8 @@ automatically when installing ``symfony/framework-bundle``):
# config/routes/framework.yaml
when@dev:
_errors:
- resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
+ resource: '@FrameworkBundle/Resources/config/routing/errors.php'
+ type: php
prefix: /_error
.. code-block:: xml
@@ -167,7 +168,7 @@ automatically when installing ``symfony/framework-bundle``):
https://symfony.com/schema/routing/routing-1.0.xsd">
-
+
@@ -178,7 +179,7 @@ automatically when installing ``symfony/framework-bundle``):
return function (RoutingConfigurator $routes): void {
if ('dev' === $routes->env()) {
- $routes->import('@FrameworkBundle/Resources/config/routing/errors.xml')
+ $routes->import('@FrameworkBundle/Resources/config/routing/errors.php', 'php')
->prefix('/_error')
;
}
@@ -191,6 +192,11 @@ need to replace ``http://localhost/`` by the host used in your local setup):
* ``http://localhost/_error/{statusCode}`` for HTML
* ``http://localhost/_error/{statusCode}.{format}`` for any other format
+.. versionadded:: 7.3
+
+ The ``errors.php`` file was introduced in Symfony 7.3.
+ Previously, you had to import ``errors.xml``
+
.. _overriding-non-html-error-output:
Overriding Error output for non-HTML formats
@@ -336,3 +342,50 @@ time and again, you can have just one (or several) listeners deal with them.
your application (like :class:`Symfony\\Component\\Security\\Core\\Exception\\AccessDeniedException`)
and takes measures like redirecting the user to the login page, logging them
out and other things.
+
+Dumping Error Pages as Static HTML Files
+----------------------------------------
+
+.. versionadded:: 7.3
+
+ The feature to dump error pages into static HTML files was introduced in Symfony 7.3.
+
+If an error occurs before reaching your Symfony application, web servers display
+their own default error pages instead of your custom ones. Dumping your application's
+error pages to static HTML ensures users always see your defined pages and improves
+performance by allowing the server to deliver errors instantly without calling
+your application.
+
+Symfony provides the following command to turn your error pages into static HTML files:
+
+.. code-block:: terminal
+
+ # the first argument is the path where the HTML files are stored
+ $ APP_ENV=prod php bin/console error:dump var/cache/prod/error_pages/
+
+ # by default, it generates the pages of all 4xx and 5xx errors, but you can
+ # pass a list of HTTP status codes to only generate those
+ $ APP_ENV=prod php bin/console error:dump var/cache/prod/error_pages/ 401 403 404 500
+
+You must also configure your web server to use these generated pages. For example,
+if you use Nginx:
+
+.. code-block:: nginx
+
+ # /etc/nginx/conf.d/example.com.conf
+ server {
+ # Existing server configuration
+ # ...
+
+ # Serve static error pages
+ error_page 400 /error_pages/400.html;
+ error_page 401 /error_pages/401.html;
+ # ...
+ error_page 510 /error_pages/510.html;
+ error_page 511 /error_pages/511.html;
+
+ location ^~ /error_pages/ {
+ root /path/to/your/symfony/var/cache/error_pages;
+ internal; # prevent direct URL access
+ }
+ }
diff --git a/controller/service.rst b/controller/service.rst
index 88af093ff29..cf83e066a19 100644
--- a/controller/service.rst
+++ b/controller/service.rst
@@ -7,11 +7,65 @@ and your controllers extend the `AbstractController`_ class, they *are* automati
registered as services. This means you can use dependency injection like any
other normal service.
-If your controllers don't extend the `AbstractController`_ class, you must
-explicitly mark your controller services as ``public``. Alternatively, you can
-apply the ``controller.service_arguments`` tag to your controller services. This
-will make the tagged services ``public`` and will allow you to inject services
-in method parameters:
+If you prefer to not extend the ``AbstractController`` class, you can register
+your controllers as services in several ways:
+
+#. Using the ``#[Route]`` attribute;
+#. Using the ``#[AsController]`` attribute;
+#. Using the ``controller.service_arguments`` service tag.
+
+Using the ``#[Route]`` Attribute
+--------------------------------
+
+When using :ref:`the #[Route] attribute ` to define
+routes on any PHP class, Symfony treats that class as a controller. It registers
+it as a public, non-lazy service and enables service argument injection in all
+its methods.
+
+This is the simplest and recommended way to register controllers as services
+when not extending the base controller class.
+
+.. versionadded:: 7.3
+
+ The feature to register controllers as services when using the ``#[Route]``
+ attribute was introduced in Symfony 7.3.
+
+Using the ``#[AsController]`` Attribute
+---------------------------------------
+
+If you prefer, you can use the ``#[AsController]`` PHP attribute to automatically
+apply the ``controller.service_arguments`` tag to your controller services::
+
+ // src/Controller/HelloController.php
+ namespace App\Controller;
+
+ use Symfony\Component\HttpFoundation\Response;
+ use Symfony\Component\HttpKernel\Attribute\AsController;
+ use Symfony\Component\Routing\Attribute\Route;
+
+ #[AsController]
+ class HelloController
+ {
+ #[Route('/hello', name: 'hello', methods: ['GET'])]
+ public function index(): Response
+ {
+ // ...
+ }
+ }
+
+.. tip::
+
+ When using the ``#[Route]`` attribute, Symfony already registers the controller
+ class as a service, so using the ``#[AsController]`` attribute is redundant.
+
+Using the ``controller.service_arguments`` Service Tag
+------------------------------------------------------
+
+If your controllers don't extend the `AbstractController`_ class and you don't
+use the ``#[AsController]`` or ``#[Route]`` attributes, you must register the
+controllers as public services manually and apply the ``controller.service_arguments``
+:doc:`service tag ` to enable service injection in
+controller actions:
.. configuration-block::
@@ -58,26 +112,6 @@ in method parameters:
calls:
- [setContainer, ['@abstract_controller.locator']]
-If you prefer, you can use the ``#[AsController]`` PHP attribute to automatically
-apply the ``controller.service_arguments`` tag to your controller services::
-
- // src/Controller/HelloController.php
- namespace App\Controller;
-
- use Symfony\Component\HttpFoundation\Response;
- use Symfony\Component\HttpKernel\Attribute\AsController;
- use Symfony\Component\Routing\Attribute\Route;
-
- #[AsController]
- class HelloController
- {
- #[Route('/hello', name: 'hello', methods: ['GET'])]
- public function index(): Response
- {
- // ...
- }
- }
-
Registering your controller as a service is the first step, but you also need to
update your routing config to reference the service properly, so that Symfony
knows to use it.
diff --git a/controller/upload_file.rst b/controller/upload_file.rst
index 941bd911a78..793cd26dd65 100644
--- a/controller/upload_file.rst
+++ b/controller/upload_file.rst
@@ -75,11 +75,11 @@ so Symfony doesn't try to get/set its value from the related entity::
// unmapped fields can't define their validation using attributes
// in the associated entity, so you can use the PHP constraint classes
'constraints' => [
- new File([
- 'maxSize' => '1024k',
- 'extensions' => ['pdf'],
- 'extensionsMessage' => 'Please upload a valid PDF document',
- ])
+ new File(
+ maxSize: '1024k',
+ extensions: ['pdf'],
+ extensionsMessage: 'Please upload a valid PDF document',
+ )
],
])
// ...
@@ -182,13 +182,24 @@ There are some important things to consider in the code of the above controller:
users. This also applies to the files uploaded by your visitors. The ``UploadedFile``
class provides methods to get the original file extension
(:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientOriginalExtension`),
- the original file size (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getSize`)
- and the original file name (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientOriginalName`).
+ the original file size (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getSize`),
+ the original file name (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientOriginalName`)
+ and the original file path (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientOriginalPath`).
However, they are considered *not safe* because a malicious user could tamper
that information. That's why it's always better to generate a unique name and
use the :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::guessExtension`
method to let Symfony guess the right extension according to the file MIME type;
+.. note::
+
+ If a directory was uploaded, ``getClientOriginalPath()`` will contain
+ the **webkitRelativePath** as provided by the browser. Otherwise this
+ value will be identical to ``getClientOriginalName()``.
+
+.. versionadded:: 7.1
+
+ The ``getClientOriginalPath()`` method was introduced in Symfony 7.1.
+
You can use the following code to link to the PDF brochure of a product:
.. code-block:: html+twig
diff --git a/controller/value_resolver.rst b/controller/value_resolver.rst
index f0f0db9aff1..835edcfbff9 100644
--- a/controller/value_resolver.rst
+++ b/controller/value_resolver.rst
@@ -75,10 +75,6 @@ Symfony ships with the following value resolvers in the
The example above allows requesting only ``/cards/D`` and ``/cards/S``
URLs and leads to 404 Not Found response in two other cases.
- .. versionadded:: 6.1
-
- The ``BackedEnumValueResolver`` and ``EnumRequirement`` were introduced in Symfony 6.1.
-
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestPayloadValueResolver`
Maps the request payload or the query string into the type-hinted object.
@@ -87,10 +83,6 @@ Symfony ships with the following value resolvers in the
or the :ref:`MapQueryString ` attribute
in order to use this resolver.
- .. versionadded:: 6.3
-
- The ``RequestPayloadValueResolver`` was introduced in Symfony 6.3.
-
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestAttributeValueResolver`
Attempts to find a request attribute that matches the name of the argument.
@@ -110,16 +102,6 @@ Symfony ships with the following value resolvers in the
receives when testing your application and using the
:class:`Symfony\\Component\\Clock\\MockClock` implementation.
- .. versionadded:: 6.1
-
- The ``DateTimeValueResolver`` and the ``MapDateTime`` attribute were
- introduced in Symfony 6.1.
-
- .. versionadded:: 6.3
-
- The use of the :doc:`Clock component ` to generate the
- ``DateTimeInterface`` object was introduced in Symfony 6.3.
-
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\RequestValueResolver`
Injects the current ``Request`` if type-hinted with ``Request`` or a class
extending ``Request``.
@@ -159,10 +141,6 @@ Symfony ships with the following value resolvers in the
}
}
- .. versionadded:: 6.1
-
- The ``UidValueResolver`` was introduced in Symfony 6.1.
-
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver\\VariadicValueResolver`
Verifies if the request data is an array and will add all of them to the
argument list. When the action is called, the last (variadic) argument will
@@ -188,10 +166,6 @@ In addition, some components, bridges and official bundles provide other value r
If the argument is not nullable and there is no logged in token, an ``HttpException``
with status code 401 is thrown by the resolver to prevent access to the controller.
- .. versionadded:: 6.3
-
- The ``SecurityTokenValueResolver`` was introduced in Symfony 6.3.
-
:class:`Symfony\\Bridge\\Doctrine\\ArgumentResolver\\EntityValueResolver`
Automatically query for an entity and pass it as an argument to your controller.
@@ -215,10 +189,6 @@ In addition, some components, bridges and official bundles provide other value r
To learn more about the use of the ``EntityValueResolver``, see the dedicated
section :ref:`Automatically Fetching Objects `.
- .. versionadded:: 6.2
-
- The ``EntityValueResolver`` was introduced in Symfony 6.2.
-
PSR-7 Objects Resolver:
Injects a Symfony HttpFoundation ``Request`` object created from a PSR-7 object
of type ``Psr\Http\Message\ServerRequestInterface``,
@@ -264,10 +234,6 @@ lets you do this by "targeting" the resolver you want::
}
}
-.. versionadded:: 6.3
-
- The ``ValueResolver`` attribute was introduced in Symfony 6.3.
-
In the example above, the ``SessionValueResolver`` will be called first because
it is targeted. The ``DefaultValueResolver`` will be called next if no value has
been provided; that's why you can assign ``null`` as ``$session``'s default value.
@@ -301,13 +267,6 @@ object whenever a controller argument has a type implementing
}
}
-.. versionadded:: 6.2
-
- The ``ValueResolverInterface`` was introduced in Symfony 6.2. Prior to
- 6.2, you had to use the
- :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface`,
- which defines different methods.
-
Adding a new value resolver requires creating a class that implements
:class:`Symfony\\Component\\HttpKernel\\Controller\\ValueResolverInterface`
and defining a service for it.
@@ -451,7 +410,7 @@ command to see which argument resolvers are present and in which order they run:
.. code-block:: terminal
- $ php bin/console debug:container debug.argument_resolver.inner --show-arguments
+ $ php bin/console debug:container debug.argument_resolver.inner
You can also configure the name passed to the ``ValueResolver`` attribute to target
your resolver. Otherwise it will default to the service's id.
@@ -497,8 +456,3 @@ You can then pass this name as ``ValueResolver``'s first argument to target your
// ... do something with $id
}
}
-
-.. versionadded:: 6.3
-
- The ``controller.targeted_value_resolver`` tag and ``AsTargetedValueResolver``
- attribute were introduced in Symfony 6.3.
diff --git a/create_framework/http_kernel_httpkernel_class.rst b/create_framework/http_kernel_httpkernel_class.rst
index 05d9ad6d93e..158de638f8a 100644
--- a/create_framework/http_kernel_httpkernel_class.rst
+++ b/create_framework/http_kernel_httpkernel_class.rst
@@ -113,11 +113,6 @@ client; that's what the ``ResponseListener`` does::
$dispatcher->addSubscriber(new HttpKernel\EventListener\ResponseListener('UTF-8'));
-If you want out of the box support for streamed responses, subscribe
-to ``StreamedResponseListener``::
-
- $dispatcher->addSubscriber(new HttpKernel\EventListener\StreamedResponseListener());
-
And in your controller, return a ``StreamedResponse`` instance instead of a
``Response`` instance.
diff --git a/create_framework/unit_testing.rst b/create_framework/unit_testing.rst
index f935a024b20..9c179cd3152 100644
--- a/create_framework/unit_testing.rst
+++ b/create_framework/unit_testing.rst
@@ -12,7 +12,7 @@ using `PHPUnit`_. At first, install PHPUnit as a development dependency:
.. code-block:: terminal
- $ composer require --dev phpunit/phpunit:^10.0
+ $ composer require --dev phpunit/phpunit:^11.0
Then, create a PHPUnit configuration file in ``example.com/phpunit.xml.dist``:
@@ -21,7 +21,7 @@ Then, create a PHPUnit configuration file in ``example.com/phpunit.xml.dist``:
192.0.0.1,10.0.0.0/8
+
+ private_rangesx-forwarded-for
@@ -75,6 +83,8 @@ and what headers your reverse proxy uses to send information:
$framework
// the IP address (or range) of your proxy
->trustedProxies('192.0.0.1,10.0.0.0/8')
+ // shortcut for private IP address ranges of your proxy
+ ->trustedProxies('private_ranges')
// trust *all* "X-Forwarded-*" headers (the ! prefix means to not trust those headers)
->trustedHeaders(['x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix'])
// or, if your proxy instead uses the "Forwarded" header
@@ -82,6 +92,16 @@ and what headers your reverse proxy uses to send information:
;
};
+.. versionadded:: 7.1
+
+ ``private_ranges`` as a shortcut for private IP address ranges for the
+ ``trusted_proxies`` option was introduced in Symfony 7.1.
+
+.. versionadded:: 7.2
+
+ Support for the ``SYMFONY_TRUSTED_PROXIES`` and ``SYMFONY_TRUSTED_HEADERS``
+ environment variables was introduced in Symfony 7.2.
+
.. danger::
Enabling the ``Request::HEADER_X_FORWARDED_HOST`` option exposes the
@@ -132,9 +152,17 @@ In this case, you'll need to - *very carefully* - trust *all* proxies.
framework:
# ...
# trust *all* requests (the 'REMOTE_ADDR' string is replaced at
- # run time by $_SERVER['REMOTE_ADDR'])
+ # runtime by $_SERVER['REMOTE_ADDR'])
trusted_proxies: '127.0.0.1,REMOTE_ADDR'
+ # you can also use the 'PRIVATE_SUBNETS' string, which is replaced at
+ # runtime by the IpUtils::PRIVATE_SUBNETS constant
+ # trusted_proxies: '127.0.0.1,PRIVATE_SUBNETS'
+
+.. versionadded:: 7.2
+
+ The support for the ``'PRIVATE_SUBNETS'`` string was introduced in Symfony 7.2.
+
That's it! It's critical that you prevent traffic from all non-trusted sources.
If you allow outside traffic, they could "spoof" their true IP address and
other information.
diff --git a/doctrine.rst b/doctrine.rst
index ae7cb259628..6a1438322fa 100644
--- a/doctrine.rst
+++ b/doctrine.rst
@@ -62,10 +62,11 @@ The database connection information is stored as an environment variable called
If the username, password, host or database name contain any character considered
special in a URI (such as ``: / ? # [ ] @ ! $ & ' ( ) * + , ; =``),
- you must encode them. See `RFC 3986`_ for the full list of reserved characters or
- use the :phpfunction:`urlencode` function to encode them. In this case you need to
- remove the ``resolve:`` prefix in ``config/packages/doctrine.yaml`` to avoid errors:
- ``url: '%env(DATABASE_URL)%'``
+ you must encode them. See `RFC 3986`_ for the full list of reserved characters.
+ You can use the :phpfunction:`urlencode` function to encode them or
+ the :ref:`urlencode environment variable processor `.
+ In this case you need to remove the ``resolve:`` prefix in ``config/packages/doctrine.yaml``
+ to avoid errors: ``url: '%env(DATABASE_URL)%'``
Now that your connection parameters are setup, Doctrine can create the ``db_name``
database for you:
@@ -83,6 +84,8 @@ affect how Doctrine functions.
There are many other Doctrine commands. Run ``php bin/console list doctrine``
to see a full list.
+.. _doctrine-adding-mapping:
+
Creating an Entity Class
------------------------
@@ -90,8 +93,6 @@ Suppose you're building an application where products need to be displayed.
Without even thinking about Doctrine or databases, you already know that
you need a ``Product`` object to represent those products.
-.. _doctrine-adding-mapping:
-
You can use the ``make:entity`` command to create this class and any fields you
need. The command will ask you some questions - answer them like done below:
@@ -173,13 +174,6 @@ Whoa! You now have a new ``src/Entity/Product.php`` file::
Confused why the price is an integer? Don't worry: this is just an example.
But, storing prices as integers (e.g. 100 = $1 USD) can avoid rounding issues.
-.. note::
-
- If you are using an SQLite database, you'll see the following error:
- *PDOException: SQLSTATE[HY000]: General error: 1 Cannot add a NOT NULL
- column with default value NULL*. Add a ``nullable=true`` option to the
- ``description`` property to fix the problem.
-
.. warning::
There is a `limit of 767 bytes for the index key prefix`_ when using
@@ -326,6 +320,13 @@ before, execute your migrations:
$ php bin/console doctrine:migrations:migrate
+.. warning::
+
+ If you are using an SQLite database, you'll see the following error:
+ *PDOException: SQLSTATE[HY000]: General error: 1 Cannot add a NOT NULL
+ column with default value NULL*. Add a ``nullable=true`` option to the
+ ``description`` property to fix the problem.
+
This will only execute the *one* new migration file, because DoctrineMigrationsBundle
knows that the first migration was already executed earlier. Behind the scenes, it
manages a ``migration_versions`` table to track this.
@@ -619,10 +620,6 @@ the :ref:`doctrine-queries` section.
Automatically Fetching Objects (EntityValueResolver)
----------------------------------------------------
-.. versionadded:: 6.2
-
- Entity Value Resolver was introduced in Symfony 6.2.
-
.. versionadded:: 2.7.1
Autowiring of the ``EntityValueResolver`` was introduced in DoctrineBundle 2.7.1.
@@ -697,7 +694,7 @@ will automatically fetch them::
/**
* Perform a findOneBy() where the slug property matches {slug}.
*/
- #[Route('/product/{slug}')]
+ #[Route('/product/{slug:product}')]
public function showBySlug(Product $product): Response
{
}
@@ -711,14 +708,17 @@ Automatic fetching works in these situations:
*all* of the wildcards in your route that are actually properties
on your entity (non-properties are ignored).
-This behavior is enabled by default on all controllers. If you prefer, you can
-restrict this feature to only work on route wildcards called ``id`` to look for
-entities by primary key. To do so, set the option
-``doctrine.orm.controller_resolver.auto_mapping`` to ``false``.
+The ``{slug:product}`` syntax maps the route parameter named ``slug`` to the
+controller argument named ``$product``. It also hints the resolver to look up
+the corresponding ``Product`` object from the database using the slug.
-When ``auto_mapping`` is disabled, you can configure the mapping explicitly for
-any controller argument with the ``MapEntity`` attribute. You can even control
-the ``EntityValueResolver`` behavior by using the `MapEntity options`_ ::
+.. versionadded:: 7.1
+
+ Route parameter mapping was introduced in Symfony 7.1.
+
+You can also configure the mapping explicitly for any controller argument
+using the ``MapEntity`` attribute. You can even control the behavior of the
+``EntityValueResolver`` by using the `MapEntity options`_ ::
// src/Controller/ProductController.php
namespace App\Controller;
@@ -758,6 +758,20 @@ In the expression, the ``repository`` variable will be your entity's
Repository class and any route wildcards - like ``{product_id}`` are
available as variables.
+The repository method called in the expression can also return a list of entities.
+In that case, update the type of your controller argument::
+
+ #[Route('/posts_by/{author_id}')]
+ public function authorPosts(
+ #[MapEntity(class: Post::class, expr: 'repository.findBy({"author": author_id}, {}, 10)')]
+ iterable $posts
+ ): Response {
+ }
+
+.. versionadded:: 7.1
+
+ The mapping of the lists of entities was introduced in Symfony 7.1.
+
This can also be used to help resolve multiple arguments::
#[Route('/product/{id}/comments/{comment_id}')]
@@ -784,10 +798,31 @@ variable. Let's say you want the first or the last comment of a product dependin
): Response {
}
-.. versionadded:: 6.4
+.. _doctrine-entity-value-resolver-resolve-target-entities:
+
+Fetch via Interfaces
+~~~~~~~~~~~~~~~~~~~~
+
+Suppose your ``Product`` class implements an interface called ``ProductInterface``.
+If you want to decouple your controllers from the concrete entity implementation,
+you can reference the entity by its interface instead.
- The support for the ``request`` variable in expressions was introduced
- in Symfony 6.4.
+To enable this, first configure the
+:doc:`resolve_target_entities option `.
+Then, your controller can type-hint the interface, and the entity will be
+resolved automatically::
+
+ public function show(
+ #[MapEntity]
+ ProductInterface $product
+ ): Response {
+ // ...
+ }
+
+.. versionadded:: 7.3
+
+ Support for target entity resolution in the ``EntityValueResolver`` was
+ introduced Symfony 7.3
MapEntity Options
~~~~~~~~~~~~~~~~~
@@ -820,18 +855,6 @@ control behavior:
): Response {
}
-``exclude``
- Configures the properties that should be used in the ``findOneBy()``
- method by *excluding* one or more properties so that not *all* are used::
-
- #[Route('/product/{slug}/{date}')]
- public function show(
- #[MapEntity(exclude: ['date'])]
- Product $product,
- \DateTime $date
- ): Response {
- }
-
``stripNull``
If true, then when ``findOneBy()`` is used, any values that are
``null`` will not be used for the query.
@@ -854,6 +877,21 @@ control behavior:
``disabled``
If true, the ``EntityValueResolver`` will not try to replace the argument.
+``message``
+ An optional custom message displayed when there's a :class:`Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException`,
+ but **only in the development environment** (you won't see this message in production)::
+
+ #[Route('/product/{product_id}')]
+ public function show(
+ #[MapEntity(id: 'product_id', message: 'The product does not exist')]
+ Product $product
+ ): Response {
+ }
+
+.. versionadded:: 7.1
+
+ The ``message`` option was introduced in Symfony 7.1.
+
Updating an Object
------------------
@@ -1096,12 +1134,10 @@ Learn more
doctrine/associations
doctrine/events
- doctrine/registration_form
doctrine/custom_dql_functions
doctrine/dbal
doctrine/multiple_entity_managers
doctrine/resolve_target_entity
- doctrine/reverse_engineering
testing/database
.. _`Doctrine`: https://www.doctrine-project.org/
diff --git a/doctrine/events.rst b/doctrine/events.rst
index 3f8f93bc2ee..accf424083a 100644
--- a/doctrine/events.rst
+++ b/doctrine/events.rst
@@ -34,7 +34,7 @@ to learn everything about them.
.. seealso::
- This article covers listeners and subscribers for Doctrine ORM. If you are
+ This article covers listeners for Doctrine ORM. If you are
using ODM for MongoDB, read the `DoctrineMongoDBBundle documentation`_.
Doctrine Lifecycle Callbacks
@@ -317,7 +317,7 @@ listener in the Symfony application by creating a new service for it and
# this is the only required option for the lifecycle listener tag
event: 'postPersist'
- # listeners can define their priority in case multiple subscribers or listeners are associated
+ # listeners can define their priority in case listeners are associated
# to the same event (default priority = 0; higher numbers = listener is run earlier)
priority: 500
@@ -335,7 +335,7 @@ listener in the Symfony application by creating a new service for it and
@@ -364,7 +364,7 @@ listener in the Symfony application by creating a new service for it and
// this is the only required option for the lifecycle listener tag
'event' => 'postPersist',
- // listeners can define their priority in case multiple subscribers or listeners are associated
+ // listeners can define their priority in case multiple listeners are associated
// to the same event (default priority = 0; higher numbers = listener is run earlier)
'priority' => 500,
@@ -378,28 +378,11 @@ listener in the Symfony application by creating a new service for it and
The `AsDoctrineListener`_ attribute was introduced in DoctrineBundle 2.8.0.
-.. tip::
-
- Symfony loads (and instantiates) Doctrine listeners only when the related
- Doctrine event is actually fired; whereas Doctrine subscribers are always
- loaded (and instantiated) by Symfony, making them less performant.
-
.. tip::
The value of the ``connection`` option can also be a
:ref:`configuration parameter `.
-Doctrine Lifecycle Subscribers
-------------------------------
-
-.. deprecated:: 6.3
-
- Lifecycle subscribers are deprecated starting from Symfony 6.3.
-
-This was another way of listening to events provided by Doctrine. However, they
-were deprecated in Symfony 6.3 and it's no longer recommended to use them.
-Instead, use any of the other alternatives shown above.
-
.. _`Doctrine`: https://www.doctrine-project.org/
.. _`lifecycle events`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/events.html#lifecycle-events
.. _`official docs about Doctrine events`: https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/events.html
diff --git a/doctrine/registration_form.rst b/doctrine/registration_form.rst
deleted file mode 100644
index 7063b7157a4..00000000000
--- a/doctrine/registration_form.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-How to Implement a Registration Form
-====================================
-
-This article has been removed because it only explained things that are
-already explained in other articles. Specifically, to implement a registration
-form you must:
-
-#. :ref:`Define a class to represent users `;
-#. :doc:`Create a form ` to ask for the registration information (you can
- generate this with the ``make:registration-form`` command provided by the `MakerBundle`_);
-#. Create :doc:`a controller ` to :ref:`process the form `;
-#. :ref:`Protect some parts of your application ` so that
- only registered users can access to them.
-
-.. _`MakerBundle`: https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html
diff --git a/doctrine/resolve_target_entity.rst b/doctrine/resolve_target_entity.rst
index 5ae6475a957..1495f475628 100644
--- a/doctrine/resolve_target_entity.rst
+++ b/doctrine/resolve_target_entity.rst
@@ -1,39 +1,45 @@
-How to Define Relationships with Abstract Classes and Interfaces
-================================================================
+Referencing Entities with Abstract Classes and Interfaces
+=========================================================
-One of the goals of bundles is to create discrete bundles of functionality
-that do not have many (if any) dependencies, allowing you to use that
-functionality in other applications without including unnecessary items.
+In applications where functionality is organized in layers or modules with
+minimal concrete dependencies, such as monoliths split into multiple modules,
+it can be challenging to avoid tight coupling between entities.
-Doctrine 2.2 includes a new utility called the ``ResolveTargetEntityListener``,
-that functions by intercepting certain calls inside Doctrine and rewriting
-``targetEntity`` parameters in your metadata mapping at runtime. It means that
-in your bundle you are able to use an interface or abstract class in your
-mappings and expect correct mapping to a concrete entity at runtime.
+Doctrine provides a utility called the ``ResolveTargetEntityListener`` to solve
+this issue. It works by intercepting certain calls within Doctrine and rewriting
+``targetEntity`` parameters in your metadata mapping at runtime. This allows you
+to reference an interface or abstract class in your mappings and have it resolved
+to a concrete entity at runtime.
-This functionality allows you to define relationships between different entities
-without making them hard dependencies.
+This makes it possible to define relationships between entities without
+creating hard dependencies. This feature also works with the ``EntityValueResolver``
+:ref:`as explained in the main Doctrine article `.
+
+.. versionadded:: 7.3
+
+ Support for target entity resolution in the ``EntityValueResolver`` was
+ introduced Symfony 7.3
Background
----------
-Suppose you have an InvoiceBundle which provides invoicing functionality
-and a CustomerBundle that contains customer management tools. You want
-to keep these separated, because they can be used in other systems without
-each other, but for your application you want to use them together.
+Suppose you have an application with two modules: an Invoice module that
+provides invoicing functionality, and a Customer module that handles customer
+management. You want to keep these modules decoupled, so that neither is aware
+of the other's implementation details.
-In this case, you have an ``Invoice`` entity with a relationship to a
-non-existent object, an ``InvoiceSubjectInterface``. The goal is to get
-the ``ResolveTargetEntityListener`` to replace any mention of the interface
-with a real object that implements that interface.
+In this case, your ``Invoice`` entity has a relationship to the interface
+``InvoiceSubjectInterface``. Since interfaces are not valid Doctrine entities,
+the goal is to use the ``ResolveTargetEntityListener`` to replace all
+references to this interface with a concrete class that implements it.
Set up
------
-This article uses the following two basic entities (which are incomplete for
-brevity) to explain how to set up and use the ``ResolveTargetEntityListener``.
+This article uses two basic (incomplete) entities to demonstrate how to set up
+and use the ``ResolveTargetEntityListener``.
-A Customer entity::
+A ``Customer`` entity::
// src/Entity/Customer.php
namespace App\Entity;
@@ -50,7 +56,7 @@ A Customer entity::
// are already implemented in the BaseCustomer
}
-An Invoice entity::
+An ``Invoice`` entity::
// src/Entity/Invoice.php
namespace App\Entity;
@@ -58,9 +64,6 @@ An Invoice entity::
use App\Model\InvoiceSubjectInterface;
use Doctrine\ORM\Mapping as ORM;
- /**
- * Represents an Invoice.
- */
#[ORM\Entity]
#[ORM\Table(name: 'invoice')]
class Invoice
@@ -69,7 +72,7 @@ An Invoice entity::
protected InvoiceSubjectInterface $subject;
}
-An InvoiceSubjectInterface::
+The interface representing the subject used in the invoice::
// src/Model/InvoiceSubjectInterface.php
namespace App\Model;
@@ -89,8 +92,8 @@ An InvoiceSubjectInterface::
public function getName(): string;
}
-Next, you need to configure the listener, which tells the DoctrineBundle
-about the replacement:
+Now configure the ``resolve_target_entities`` option to tell Doctrine
+how to replace the interface with the concrete class:
.. configuration-block::
@@ -140,7 +143,6 @@ about the replacement:
Final Thoughts
--------------
-With the ``ResolveTargetEntityListener``, you are able to decouple your
-bundles, keeping them usable by themselves, but still being able to
-define relationships between different objects. By using this method,
-your bundles will end up being easier to maintain independently.
+Using ``ResolveTargetEntityListener`` allows you to decouple your modules
+while still defining relationships between their entities. This makes your
+codebase more modular and easier to maintain over time.
diff --git a/doctrine/reverse_engineering.rst b/doctrine/reverse_engineering.rst
deleted file mode 100644
index 7f1ea793958..00000000000
--- a/doctrine/reverse_engineering.rst
+++ /dev/null
@@ -1,15 +0,0 @@
-How to Generate Entities from an Existing Database
-==================================================
-
-.. warning::
-
- The ``doctrine:mapping:import`` command used to generate Doctrine entities
- from existing databases was deprecated by Doctrine in 2019 and there's no
- replacement for it.
-
- Instead, you can use the ``make:entity`` command from `Symfony Maker Bundle`_
- to help you generate the code of your Doctrine entities. This command
- requires manual supervision because it doesn't generate entities from
- existing databases.
-
-.. _`Symfony Maker Bundle`: https://symfony.com/bundles/SymfonyMakerBundle/current/index.html
diff --git a/emoji.rst b/emoji.rst
new file mode 100644
index 00000000000..551497f0c76
--- /dev/null
+++ b/emoji.rst
@@ -0,0 +1,173 @@
+Working with Emojis
+===================
+
+.. versionadded:: 7.1
+
+ The emoji component was introduced in Symfony 7.1.
+
+Symfony provides several utilities to work with emoji characters and sequences
+from the `Unicode CLDR dataset`_. They are available via the Emoji component,
+which you must first install in your application:
+
+.. _installation:
+
+.. code-block:: terminal
+
+ $ composer require symfony/emoji
+
+.. include:: /components/require_autoload.rst.inc
+
+The data needed to store the transliteration of all emojis (~5,000) into all
+languages take a considerable disk space.
+
+If you need to save disk space (e.g. because you deploy to some service with tight
+size constraints), run this command (e.g. as an automated script after ``composer install``)
+to compress the internal Symfony emoji data files using the PHP ``zlib`` extension:
+
+.. code-block:: terminal
+
+ # adjust the path to the 'compress' binary based on your application installation
+ $ php ./vendor/symfony/emoji/Resources/bin/compress
+
+.. _emoji-transliteration:
+
+Emoji Transliteration
+---------------------
+
+The ``EmojiTransliterator`` class offers a way to translate emojis into their
+textual representation in all languages based on the `Unicode CLDR dataset`_::
+
+ use Symfony\Component\Emoji\EmojiTransliterator;
+
+ // Describe emojis in English
+ $transliterator = EmojiTransliterator::create('en');
+ $transliterator->transliterate('Menus with 🍕 or 🍝');
+ // => 'Menus with pizza or spaghetti'
+
+ // Describe emojis in Ukrainian
+ $transliterator = EmojiTransliterator::create('uk');
+ $transliterator->transliterate('Menus with 🍕 or 🍝');
+ // => 'Menus with піца or спагеті'
+
+.. tip::
+
+ When using the :ref:`slugger ` from the String component,
+ you can combine it with the ``EmojiTransliterator`` to :ref:`slugify emojis `.
+
+Transliterating Emoji Text Short Codes
+--------------------------------------
+
+Services like GitHub and Slack allows to include emojis in your messages using
+text short codes (e.g. you can add the ``:+1:`` code to render the 👍 emoji).
+
+Symfony also provides a feature to transliterate emojis into short codes and vice
+versa. The short codes are slightly different on each service, so you must pass
+the name of the service as an argument when creating the transliterator.
+
+GitHub Emoji Short Codes Transliteration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Convert emojis to GitHub short codes with the ``emoji-github`` locale::
+
+ $transliterator = EmojiTransliterator::create('emoji-github');
+ $transliterator->transliterate('Teenage 🐢 really love 🍕');
+ // => 'Teenage :turtle: really love :pizza:'
+
+Convert GitHub short codes to emojis with the ``github-emoji`` locale::
+
+ $transliterator = EmojiTransliterator::create('github-emoji');
+ $transliterator->transliterate('Teenage :turtle: really love :pizza:');
+ // => 'Teenage 🐢 really love 🍕'
+
+Gitlab Emoji Short Codes Transliteration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Convert emojis to Gitlab short codes with the ``emoji-gitlab`` locale::
+
+ $transliterator = EmojiTransliterator::create('emoji-gitlab');
+ $transliterator->transliterate('Breakfast with 🥝 or 🥛');
+ // => 'Breakfast with :kiwi: or :milk:'
+
+Convert Gitlab short codes to emojis with the ``gitlab-emoji`` locale::
+
+ $transliterator = EmojiTransliterator::create('gitlab-emoji');
+ $transliterator->transliterate('Breakfast with :kiwi: or :milk:');
+ // => 'Breakfast with 🥝 or 🥛'
+
+Slack Emoji Short Codes Transliteration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Convert emojis to Slack short codes with the ``emoji-slack`` locale::
+
+ $transliterator = EmojiTransliterator::create('emoji-slack');
+ $transliterator->transliterate('Menus with 🥗 or 🧆');
+ // => 'Menus with :green_salad: or :falafel:'
+
+Convert Slack short codes to emojis with the ``slack-emoji`` locale::
+
+ $transliterator = EmojiTransliterator::create('slack-emoji');
+ $transliterator->transliterate('Menus with :green_salad: or :falafel:');
+ // => 'Menus with 🥗 or 🧆'
+
+.. _text-emoji:
+
+Universal Emoji Short Codes Transliteration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you don't know which service was used to generate the short codes, you can use
+the ``text-emoji`` locale, which combines all codes from all services::
+
+ $transliterator = EmojiTransliterator::create('text-emoji');
+
+ // Github short codes
+ $transliterator->transliterate('Breakfast with :kiwi-fruit: or :milk-glass:');
+ // Gitlab short codes
+ $transliterator->transliterate('Breakfast with :kiwi: or :milk:');
+ // Slack short codes
+ $transliterator->transliterate('Breakfast with :kiwifruit: or :glass-of-milk:');
+
+ // all the above examples produce the same result:
+ // => 'Breakfast with 🥝 or 🥛'
+
+You can convert emojis to short codes with the ``emoji-text`` locale::
+
+ $transliterator = EmojiTransliterator::create('emoji-text');
+ $transliterator->transliterate('Breakfast with 🥝 or 🥛');
+ // => 'Breakfast with :kiwifruit: or :milk-glass:
+
+Inverse Emoji Transliteration
+-----------------------------
+
+Given the textual representation of an emoji, you can reverse it back to get the
+actual emoji thanks to the :ref:`emojify filter `:
+
+.. code-block:: twig
+
+ {{ 'I like :kiwi-fruit:'|emojify }} {# renders: I like 🥝 #}
+ {{ 'I like :kiwi:'|emojify }} {# renders: I like 🥝 #}
+ {{ 'I like :kiwifruit:'|emojify }} {# renders: I like 🥝 #}
+
+By default, ``emojify`` uses the :ref:`text catalog `, which
+merges the emoji text codes of all services. If you prefer, you can select a
+specific catalog to use:
+
+.. code-block:: twig
+
+ {{ 'I :green-heart: this'|emojify }} {# renders: I 💚 this #}
+ {{ ':green_salad: is nice'|emojify('slack') }} {# renders: 🥗 is nice #}
+ {{ 'My :turtle: has no name yet'|emojify('github') }} {# renders: My 🐢 has no name yet #}
+ {{ ':kiwi: is a great fruit'|emojify('gitlab') }} {# renders: 🥝 is a great fruit #}
+
+Removing Emojis
+---------------
+
+The ``EmojiTransliterator`` can also be used to remove all emojis from a string,
+via the special ``strip`` locale::
+
+ use Symfony\Component\Emoji\EmojiTransliterator;
+
+ $transliterator = EmojiTransliterator::create('strip');
+ $transliterator->transliterate('🎉Hey!🥳 🎁Happy Birthday!🎁');
+ // => 'Hey! Happy Birthday!'
+
+.. _`Unicode CLDR dataset`: https://github.com/unicode-org/cldr
diff --git a/form/form_customization.rst b/form/form_customization.rst
index 1c23601a883..dc09aefe77d 100644
--- a/form/form_customization.rst
+++ b/form/form_customization.rst
@@ -103,6 +103,7 @@ That's why Symfony provides other Twig form helpers that render the value of
each form field part without adding any HTML around it:
* ``field_name()``
+* ``field_id()``
* ``field_value()``
* ``field_label()``
* ``field_help()``
@@ -116,6 +117,7 @@ fields, so you no longer have to deal with form themes:
+.. versionadded:: 7.3
+
+ The ``field_id()`` helper was introduced in Symfony 7.3.
+
Form Rendering Variables
------------------------
diff --git a/form/form_dependencies.rst b/form/form_dependencies.rst
deleted file mode 100644
index 96b067362ff..00000000000
--- a/form/form_dependencies.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-How to Access Services or Config from Inside a Form
-===================================================
-
-The content of this article is no longer relevant because in current Symfony
-versions, form classes are services by default and you can inject services in
-them using the :doc:`service autowiring ` feature.
-
-Read the article about :doc:`creating custom form types `
-to see an example of how to inject the database service into a form type. In the
-same article you can also read about
-:ref:`configuration options for form types `, which is
-another way of passing services to forms.
diff --git a/form/unit_testing.rst b/form/unit_testing.rst
index 2a53d43dd33..9603c5bc0d2 100644
--- a/form/unit_testing.rst
+++ b/form/unit_testing.rst
@@ -249,9 +249,4 @@ All you need to do is to implement the
:method:`Symfony\\Bridge\\Twig\\Test\\FormLayoutTestCase::getTwigExtensions` and
the :method:`Symfony\\Bridge\\Twig\\Test\\FormLayoutTestCase::getThemes` methods.
-.. versionadded:: 6.4
-
- The :class:`Symfony\\Bridge\\Twig\\Test\\FormLayoutTestCase` class was
- introduced in Symfony 6.4.
-
.. _`PHPUnit data providers`: https://docs.phpunit.de/en/9.6/writing-tests-for-phpunit.html#data-providers
diff --git a/form/without_class.rst b/form/without_class.rst
index c0da3c9db5a..c31ff346170 100644
--- a/form/without_class.rst
+++ b/form/without_class.rst
@@ -96,12 +96,12 @@ but here's a short example::
{
$builder
->add('firstName', TextType::class, [
- 'constraints' => new Length(['min' => 3]),
+ 'constraints' => new Length(min: 3),
])
->add('lastName', TextType::class, [
'constraints' => [
new NotBlank(),
- new Length(['min' => 3]),
+ new Length(min: 3),
],
])
;
@@ -153,10 +153,10 @@ This can be done by setting the ``constraints`` option in the
$resolver->setDefaults([
'data_class' => null,
'constraints' => new Collection([
- 'firstName' => new Length(['min' => 3]),
+ 'firstName' => new Length(min: 3),
'lastName' => [
new NotBlank(),
- new Length(['min' => 3]),
+ new Length(min: 3),
],
]),
]);
diff --git a/forms.rst b/forms.rst
index bea93edb25d..83065d7524b 100644
--- a/forms.rst
+++ b/forms.rst
@@ -298,13 +298,6 @@ Now that the form has been created, the next step is to render it::
Internally, the ``render()`` method calls ``$form->createView()`` to
transform the form into a *form view* instance.
-.. deprecated:: 6.2
-
- Prior to Symfony 6.2, you had to use ``$this->render(..., ['form' => $form->createView()])``
- or the ``renderForm()`` method to render the form. The ``renderForm()``
- method is deprecated in favor of directly passing the ``FormInterface``
- instance to ``render()``.
-
Then, use some :ref:`form helper functions ` to
render the form contents:
@@ -971,7 +964,6 @@ Advanced Features:
/controller/upload_file
/security/csrf
- /form/form_dependencies
/form/create_custom_field_type
/form/data_transformers
/form/data_mappers
diff --git a/frontend/asset_mapper.rst b/frontend/asset_mapper.rst
index 6f045938339..912f645bf6a 100644
--- a/frontend/asset_mapper.rst
+++ b/frontend/asset_mapper.rst
@@ -1,10 +1,6 @@
AssetMapper: Simple, Modern CSS & JS Management
===============================================
-.. versionadded:: 6.3
-
- The AssetMapper component was introduced in Symfony 6.3.
-
The AssetMapper component lets you write modern JavaScript and CSS without the complexity
of using a bundler. Browsers *already* support many modern JavaScript features
like the ``import`` statement and ES6 classes. And the HTTP/2 protocol means that
@@ -16,7 +12,7 @@ The component has two main features:
* :ref:`Mapping & Versioning Assets `: All files inside of ``assets/``
are made available publicly and **versioned**. You can reference the file
``assets/images/product.jpg`` in a Twig template with ``{{ asset('images/product.jpg') }}``.
- The final URL will include a version hash, like ``/assets/images/product-3c16d9220694c0e56d8648f25e6035e9.jpg``.
+ The final URL will include a version hash, like ``/assets/images/product-3c16d92m.jpg``.
* :ref:`Importmaps `: A native browser feature that makes it easier
to use the JavaScript ``import`` statement (e.g. ``import { Modal } from 'bootstrap'``)
@@ -74,7 +70,7 @@ The path - ``images/duck.png`` - is relative to your mapped directory (``assets/
This is known as the **logical path** to your asset.
If you look at the HTML in your page, the URL will be something
-like: ``/assets/images/duck-3c16d9220694c0e56d8648f25e6035e9.png``. If you change
+like: ``/assets/images/duck-3c16d92m.png``. If you change
the file, the version part of the URL will also change automatically.
.. _asset-mapper-compile-assets:
@@ -82,7 +78,7 @@ the file, the version part of the URL will also change automatically.
Serving Assets in dev vs prod
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-In the ``dev`` environment, the URL ``/assets/images/duck-3c16d9220694c0e56d8648f25e6035e9.png``
+In the ``dev`` environment, the URL ``/assets/images/duck-3c16d92m.png``
is handled and returned by your Symfony app.
For the ``prod`` environment, before deploy, you should run:
@@ -143,6 +139,28 @@ This will show you all the mapped paths and the assets inside of each:
The "Logical Path" is the path to use when referencing the asset, like
from a template.
+The ``debug:asset-map`` command provides several options to filter results:
+
+.. code-block:: terminal
+
+ # provide an asset name or dir to only show results that match it
+ $ php bin/console debug:asset-map bootstrap.js
+ $ php bin/console debug:asset-map style/
+
+ # provide an extension to only show that file type
+ $ php bin/console debug:asset-map --ext=css
+
+ # you can also only show assets in vendor/ dir or exclude any results from it
+ $ php bin/console debug:asset-map --vendor
+ $ php bin/console debug:asset-map --no-vendor
+
+ # you can also combine all filters (e.g. find bold web fonts in your own asset dirs)
+ $ php bin/console debug:asset-map bold --no-vendor --ext=woff2
+
+.. versionadded:: 7.2
+
+ The options to filter ``debug:asset-map`` results were introduced in Symfony 7.2.
+
.. _importmaps-javascript:
Importmaps & Writing JavaScript
@@ -197,6 +215,15 @@ to add any `npm package`_:
$ php bin/console importmap:require bootstrap
+.. tip::
+
+ Add the ``--dry-run`` option to simulate package installation without actually
+ making any changes (e.g. ``php bin/console importmap:require bootstrap --dry-run``)
+
+ .. versionadded:: 7.3
+
+ The ``--dry-run`` option was introduced in Symfony 7.3.
+
This adds the ``bootstrap`` package to your ``importmap.php`` file::
// importmap.php
@@ -274,11 +301,6 @@ You can update your third-party packages to their current versions by running:
$ php bin/console importmap:update bootstrap lodash
$ php bin/console importmap:outdated bootstrap lodash
-.. versionadded:: 6.4
-
- The ``importmap:install`` and ``importmap:outdated`` commands were introduced
- in Symfony 6.4.
-
Removing JavaScript Packages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -317,9 +339,9 @@ outputs an `importmap`_:
@@ -376,8 +398,8 @@ The ``importmap()`` function also outputs a set of "preloads":
.. code-block:: html
-
-
+
+
This is a performance optimization and you can learn more about below
in :ref:`Performance: Add Preloading `.
@@ -454,10 +476,6 @@ from inside ``app.js``:
Handling CSS
------------
-.. versionadded:: 6.4
-
- The ability to import CSS files was introduced in Symfony 6.4.
-
CSS can be added to your page by importing it from a JavaScript file. The default
``assets/app.js`` already imports ``assets/styles/app.css``:
@@ -532,9 +550,9 @@ for ``duck.png``:
.. code-block:: css
- /* public/assets/styles/app-3c16d9220694c0e56d8648f25e6035e9.css */
+ /* public/assets/styles/app-3c16d92m.css */
.quack {
- background-image: url('../images/duck-3c16d9220694c0e56d8648f25e6035e9.png');
+ background-image: url('../images/duck-3c16d92m.png');
}
.. _asset-mapper-tailwind:
@@ -611,7 +629,7 @@ Sometimes a JavaScript file you're importing (e.g. ``import './duck.js'``),
or a CSS/image file you're referencing won't be found, and you'll see a 404
error in your browser's console. You'll also notice that the 404 URL is missing
the version hash in the filename (e.g. a 404 to ``/assets/duck.js`` instead of
-a path like ``/assets/duck.1b7a64b3b3d31219c262cf72521a5267.js``).
+a path like ``/assets/duck-1b7a64b3.js``).
This is usually because the path is wrong. If you're referencing the file
directly in a Twig template:
@@ -694,7 +712,9 @@ which will automatically do most of these things for you:
- **Compress your assets**: Your web server should compress (e.g. using gzip)
your assets (JavaScript, CSS, images) before sending them to the browser. This
is automatically enabled in Caddy and can be activated in Nginx and Apache.
- In Cloudflare, assets are compressed by default.
+ In Cloudflare, assets are compressed by default. AssetMapper also supports
+ :ref:`precompressing your web assets ` to further
+ improve performance.
- **Set long-lived cache expiry**: Your web server should set a long-lived
``Cache-Control`` HTTP header on your assets. Because the AssetMapper component includes a version
@@ -710,10 +730,6 @@ check the performance of your site.
Performance: Understanding Preloading
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 6.4
-
- Automatic preloading of JavaScript files was introduced in Symfony 6.4.
-
One issue that Lighthouse may report is:
Avoid Chaining Critical Requests
@@ -746,10 +762,84 @@ even though it hasn't yet seen the ``import`` statement for them.
Additionally, if the :doc:`WebLink Component ` is available in your application,
Symfony will add a ``Link`` header in the response to preload the CSS files.
-.. versionadded:: 6.4
+.. _performance-precompressing:
+
+Pre-Compressing Assets
+----------------------
+
+.. versionadded:: 7.3
+
+ Support for pre-compressing assets was introduced in Symfony 7.3.
+
+Although most web servers (Caddy, Nginx, Apache, FrankenPHP) and services like Cloudflare
+provide asset compression features, AssetMapper also allows you to compress all
+your assets before serving them.
+
+This improves performance because you can compress assets using the highest (and
+slowest) compression ratios beforehand and provide those compressed assets to the
+server, which then returns them to the client without wasting CPU resources on
+compression.
+
+AssetMapper supports `Brotli`_, `Zstandard`_ and `gzip`_ compression formats.
+Before using any of them, the machine that pre-compresses assets must have
+installed the following PHP extensions or CLI commands:
+
+* Brotli: ``brotli`` CLI command; `brotli PHP extension`_;
+* Zstandard: ``zstd`` CLI command; `zstd PHP extension`_;
+* gzip: ``zopfli`` (better) or ``gzip`` CLI command; `zlib PHP extension`_.
- Automatic preloading of CSS files when WebLink is available was
- introduced in Symfony 6.4.
+Then, update your AssetMapper configuration to define which compression to use
+and which file extensions should be compressed:
+
+.. code-block:: yaml
+
+ # config/packages/asset_mapper.yaml
+ framework:
+ asset_mapper:
+ # ...
+
+ precompress:
+ # possible values: 'brotli', 'zstandard', 'gzip'
+ format: 'zstandard'
+
+ # you can also pass multiple values to generate files in several formats
+ # format: ['brotli', 'zstandard']
+
+ # if you don't define the following option, AssetMapper will compress all
+ # the extensions considered safe (css, js, json, svg, xml, ttf, otf, wasm, etc.)
+ extensions: ['css', 'js', 'json', 'svg', 'xml']
+
+Now, when running the ``asset-map:compile`` command, all matching files will be
+compressed in the configured format and at the highest compression level. The
+compressed files are created with the same name as the original but with the
+``.br``, ``.zst``, or ``.gz`` extension appended.
+
+Then, you need to configure your web server to serve the precompressed assets
+instead of the original ones:
+
+.. configuration-block::
+
+ .. code-block:: caddy
+
+ file_server {
+ precompressed br zstd gzip
+ }
+
+ .. code-block:: nginx
+
+ gzip_static on;
+
+ # Requires https://github.com/google/ngx_brotli
+ brotli_static on;
+
+ # Requires https://github.com/tokers/zstd-nginx-module
+ zstd_static on;
+
+.. tip::
+
+ AssetMapper provides an ``assets:compress`` CLI command and a service called
+ ``asset_mapper.compressor`` that you can use anywhere in your application to
+ compress any kind of files (e.g. files uploaded by users to your application).
Frequently Asked Questions
--------------------------
@@ -895,7 +985,7 @@ be versioned! It will output something like:
.. code-block:: html+twig
-
+
Overriding 3rd-Party Assets
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1015,10 +1105,6 @@ is useful if you want to avoid leaking sensitive files like ``.env`` or
This option is enabled by default.
-.. versionadded:: 6.4
-
- The ``exclude_dotfiles`` option was introduced in Symfony 6.4.
-
.. _config-importmap-polyfill:
``framework.asset_mapper.importmap_polyfill``
@@ -1048,12 +1134,6 @@ via a CDN (i.e. the default value for this setting is ``es-module-shims``):
$ php bin/console importmap:require es-module-shims
-.. versionadded:: 6.4
-
- Passing an importmap name in ``importmap_polyfill`` was
- introduced in Symfony 6.4. Prior to this, you could pass ``false``
- or a custom URL to load the polyfill.
-
``framework.asset_mapper.importmap_script_attributes``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1230,10 +1310,6 @@ command as part of your CI to be warned anytime a new vulnerability is found.
The command takes a ``--format`` option to choose the output format between
``txt`` and ``json``.
-.. versionadded:: 6.4
-
- The ``importmap:audit`` command was introduced in Symfony 6.4.
-
.. _latest asset-mapper recipe: https://github.com/symfony/recipes/tree/main/symfony/asset-mapper
.. _import statement: https://caniuse.com/es6-module-dynamic-import
.. _ES6: https://caniuse.com/es6
@@ -1261,3 +1337,9 @@ command as part of your CI to be warned anytime a new vulnerability is found.
.. _strict-dynamic: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#strict-dynamic
.. _kocal/biome-js-bundle: https://github.com/Kocal/BiomeJsBundle
.. _`SensioLabs Minify Bundle`: https://github.com/sensiolabs/minify-bundle
+.. _`Brotli`: https://en.wikipedia.org/wiki/Brotli
+.. _`Zstandard`: https://en.wikipedia.org/wiki/Zstd
+.. _`gzip`: https://en.wikipedia.org/wiki/Gzip
+.. _`brotli PHP extension`: https://pecl.php.net/package/brotli
+.. _`zstd PHP extension`: https://pecl.php.net/package/zstd
+.. _`zlib PHP extension`: https://www.php.net/manual/en/book.zlib.php
diff --git a/html_sanitizer.rst b/html_sanitizer.rst
index 17096db913f..27aa98f6a89 100644
--- a/html_sanitizer.rst
+++ b/html_sanitizer.rst
@@ -1,10 +1,6 @@
HTML Sanitizer
==============
-.. versionadded:: 6.1
-
- The HTML Sanitizer component was introduced in Symfony 6.1.
-
The HTML Sanitizer component aims at sanitizing/cleaning untrusted HTML
code (e.g. created by a WYSIWYG editor in the browser) into HTML that can
be trusted. It is based on the `HTML Sanitizer W3C Standard Proposal`_.
@@ -793,17 +789,17 @@ URLs of ```` elements:
->sanitizer('app.post_sanitizer')
// if `true`, all URLs using the `http://` scheme will be converted to
// use the `https://` scheme instead. `http` still needs to be
- // allowed in `allowedLinkSchemes`
+ // allowed in `allowLinkSchemes`
->forceHttpsUrls(true)
// specifies the allowed URL schemes. If the URL has a different scheme, the
// attribute will be dropped
- ->allowedLinkSchemes(['http', 'https', 'mailto'])
+ ->allowLinkSchemes(['http', 'https', 'mailto'])
// specifies the allowed hosts, the attribute will be dropped if the
// URL contains a different host. Subdomains are allowed: e.g. the following
// config would also allow 'www.symfony.com', 'live.symfony.com', etc.
- ->allowedLinkHosts(['symfony.com'])
+ ->allowLinkHosts(['symfony.com'])
// whether to allow relative links (i.e. URLs without scheme and host)
->allowRelativeLinks(true)
@@ -819,16 +815,16 @@ URLs of ```` elements:
(new HtmlSanitizerConfig())
// if `true`, all URLs using the `http://` scheme will be converted to
// use the `https://` scheme instead. `http` still needs to be
- // allowed in `allowedLinkSchemes`
+ // allowed in `allowLinkSchemes`
->forceHttpsUrls()
// specifies the allowed URL schemes. If the URL has a different scheme, the
// attribute will be dropped
- ->allowedLinkSchemes(['http', 'https', 'mailto'])
+ ->allowLinkSchemes(['http', 'https', 'mailto'])
// specifies the allowed hosts, the attribute will be dropped if the
// URL contains a different host which is not a subdomain of the allowed host
- ->allowedLinkHosts(['symfony.com']) // Also allows any subdomain (i.e. www.symfony.com)
+ ->allowLinkHosts(['symfony.com']) // Also allows any subdomain (i.e. www.symfony.com)
// whether to allow relative links (i.e. URLs without scheme and host)
->allowRelativeLinks()
@@ -913,16 +909,16 @@ the HTML sanitizer: ``src``, ``href``, ``lowsrc``, ``background`` and ``ping``.
->sanitizer('app.post_sanitizer')
// if `true`, all URLs using the `http://` scheme will be converted to
// use the `https://` scheme instead. `http` still needs to be
- // allowed in `allowedMediaSchemes`
+ // allowed in `allowMediaSchemes`
->forceHttpsUrls(true)
// specifies the allowed URL schemes. If the URL has a different scheme, the
// attribute will be dropped
- ->allowedMediaSchemes(['http', 'https', 'mailto'])
+ ->allowMediaSchemes(['http', 'https', 'mailto'])
// specifies the allowed hosts, the attribute will be dropped if the URL
// contains a different host which is not a subdomain of the allowed host
- ->allowedMediaHosts(['symfony.com']) // Also allows any subdomain (i.e. www.symfony.com)
+ ->allowMediaHosts(['symfony.com']) // Also allows any subdomain (i.e. www.symfony.com)
// whether to allow relative URLs (i.e. URLs without scheme and host)
->allowRelativeMedias(true)
@@ -938,16 +934,16 @@ the HTML sanitizer: ``src``, ``href``, ``lowsrc``, ``background`` and ``ping``.
(new HtmlSanitizerConfig())
// if `true`, all URLs using the `http://` scheme will be converted to
// use the `https://` scheme instead. `http` still needs to be
- // allowed in `allowedMediaSchemes`
+ // allowed in `allowMediaSchemes`
->forceHttpsUrls()
// specifies the allowed URL schemes. If the URL has a different scheme, the
// attribute will be dropped
- ->allowedMediaSchemes(['http', 'https', 'mailto'])
+ ->allowMediaSchemes(['http', 'https', 'mailto'])
// specifies the allowed hosts, the attribute will be dropped if the URL
// contains a different host which is not a subdomain of the allowed host
- ->allowedMediaHosts(['symfony.com']) // Also allows any subdomain (i.e. www.symfony.com)
+ ->allowMediaHosts(['symfony.com']) // Also allows any subdomain (i.e. www.symfony.com)
// whether to allow relative URLs (i.e. URLs without scheme and host)
->allowRelativeMedias()
@@ -1023,11 +1019,6 @@ increase or decrease this limit:
It is possible to disable this length limit by setting the max input length to
``-1``. Beware that it may expose your application to `DoS attacks`_.
-.. versionadded:: 6.4
-
- The support for disabling the length limit of the HTML sanitizer was
- introduced in Symfony 6.4.
-
Custom Attribute Sanitizers
~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/http_cache.rst b/http_cache.rst
index 7e886b022d0..1797219c649 100644
--- a/http_cache.rst
+++ b/http_cache.rst
@@ -229,10 +229,6 @@ The *easiest* way to cache a response is by caching it for a specific amount of
return $response;
}
-.. versionadded:: 6.2
-
- The ``#[Cache]`` attribute was introduced in Symfony 6.2.
-
Thanks to this new code, your HTTP response will have the following header:
.. code-block:: text
diff --git a/http_cache/esi.rst b/http_cache/esi.rst
index 044430edcc3..588cad424cd 100644
--- a/http_cache/esi.rst
+++ b/http_cache/esi.rst
@@ -279,8 +279,4 @@ The ``render_esi`` helper supports three other useful options:
``absolute_uri``
If set to true, an absolute URI will be generated. **default**: ``false``
-.. versionadded:: 6.2
-
- The ``absolute_uri`` option was introduced in Symfony 6.2.
-
.. _`ESI`: https://www.w3.org/TR/esi-lang/
diff --git a/http_cache/varnish.rst b/http_cache/varnish.rst
index a9bb668c100..6fcb7fd766b 100644
--- a/http_cache/varnish.rst
+++ b/http_cache/varnish.rst
@@ -62,7 +62,7 @@ If you know for sure that the backend never uses sessions or basic
authentication, have Varnish remove the corresponding header from requests to
prevent clients from bypassing the cache. In practice, you will need sessions
at least for some parts of the site, e.g. when using forms with
-:doc:`CSRF Protection `. In this situation, make sure to
+:doc:`stateful CSRF Protection `. In this situation, make sure to
:ref:`only start a session when actually needed `
and clear the session when it is no longer needed. Alternatively, you can look
into :ref:`caching pages that contain CSRF protected forms `.
diff --git a/http_client.rst b/http_client.rst
index 36ed66a1506..bdb51681dc3 100644
--- a/http_client.rst
+++ b/http_client.rst
@@ -150,10 +150,18 @@ brings most of the available options with type-hinted getters and setters::
$this->client = $client->withOptions(
(new HttpOptions())
->setBaseUri('https://...')
+ // replaces *all* headers at once, and deletes the headers you do not provide
->setHeaders(['header-name' => 'header-value'])
+ // set or replace a single header using setHeader()
+ ->setHeader('another-header-name', 'another-header-value')
->toArray()
);
+.. versionadded:: 7.1
+
+ The :method:`Symfony\\Component\\HttpClient\\HttpOptions::setHeader`
+ method was introduced in Symfony 7.1.
+
Some options are described in this guide:
* `Authentication`_
@@ -640,13 +648,6 @@ of the opened file, but you can configure both with the PHP streaming configurat
stream_context_set_option($fileHandle, 'http', 'filename', 'the-name.txt');
stream_context_set_option($fileHandle, 'http', 'content_type', 'my/content-type');
-.. versionadded:: 6.3
-
- The feature to upload files using handles was introduced in Symfony 6.3.
- In previous Symfony versions you had to encode the body contents according
- to the ``multipart/form-data`` content-type using the :doc:`Symfony Mime `
- component.
-
.. tip::
When using multidimensional arrays the :class:`Symfony\\Component\\Mime\\Part\\Multipart\\FormDataPart`
@@ -744,10 +745,6 @@ when using any HTTP method and ``500``, ``504``, ``507`` and ``510`` when using
an HTTP `idempotent method`_. Use the ``max_retries`` setting to configure the
amount of times a request is retried.
-.. versionadded:: 6.4
-
- The ``max_retries`` options was introduced in Symfony 6.4.
-
Check out the full list of configurable :ref:`retry_failed options `
to learn how to tweak each of them to fit your application needs.
@@ -767,10 +764,6 @@ each retry.
Retry Over Several Base URIs
............................
-.. versionadded:: 6.3
-
- The multiple ``base_uri`` feature was added in Symfony 6.3.
-
The ``RetryableHttpClient`` can be configured to use multiple base URIs. This
feature provides increased flexibility and reliability for making HTTP
requests. Pass an array of base URIs as option ``base_uri`` when making a
@@ -989,11 +982,6 @@ If you want to define your own logic to handle variables of URI templates, you
can do so by redefining the ``http_client.uri_template_expander`` alias. Your
service must be invokable.
-.. versionadded:: 6.3
-
- The :class:`Symfony\\Component\\HttpClient\\UriTemplateHttpClient` was
- introduced in Symfony 6.3.
-
Performance
-----------
@@ -1513,6 +1501,114 @@ installed in your application::
:class:`Symfony\\Component\\HttpClient\\CachingHttpClient` accepts a third argument
to set the options of the :class:`Symfony\\Component\\HttpKernel\\HttpCache\\HttpCache`.
+Limit the Number of Requests
+----------------------------
+
+This component provides a :class:`Symfony\\Component\\HttpClient\\ThrottlingHttpClient`
+decorator that allows to limit the number of requests within a certain period,
+potentially delaying calls based on the rate limiting policy.
+
+The implementation leverages the
+:class:`Symfony\\Component\\RateLimiter\\LimiterInterface` class under the hood
+so the :doc:`Rate Limiter component ` needs to be
+installed in your application::
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ http_client:
+ scoped_clients:
+ example.client:
+ base_uri: 'https://example.com'
+ rate_limiter: 'http_example_limiter'
+
+ rate_limiter:
+ # Don't send more than 10 requests in 5 seconds
+ http_example_limiter:
+ policy: 'token_bucket'
+ limit: 10
+ rate: { interval: '5 seconds', amount: 10 }
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/framework.php
+ use Symfony\Config\FrameworkConfig;
+
+ return static function (FrameworkConfig $framework): void {
+ $framework->httpClient()->scopedClient('example.client')
+ ->baseUri('https://example.com')
+ ->rateLimiter('http_example_limiter');
+ // ...
+ ;
+
+ $framework->rateLimiter()
+ // Don't send more than 10 requests in 5 seconds
+ ->limiter('http_example_limiter')
+ ->policy('token_bucket')
+ ->limit(10)
+ ->rate()
+ ->interval('5 seconds')
+ ->amount(10)
+ ;
+ };
+
+ .. code-block:: php-standalone
+
+ use Symfony\Component\HttpClient\HttpClient;
+ use Symfony\Component\HttpClient\ThrottlingHttpClient;
+ use Symfony\Component\RateLimiter\RateLimiterFactory;
+ use Symfony\Component\RateLimiter\Storage\InMemoryStorage;
+
+ $factory = new RateLimiterFactory([
+ 'id' => 'http_example_limiter',
+ 'policy' => 'token_bucket',
+ 'limit' => 10,
+ 'rate' => ['interval' => '5 seconds', 'amount' => 10],
+ ], new InMemoryStorage());
+ $limiter = $factory->create();
+
+ $client = HttpClient::createForBaseUri('https://example.com');
+ $throttlingClient = new ThrottlingHttpClient($client, $limiter);
+
+.. versionadded:: 7.1
+
+ The :class:`Symfony\\Component\\HttpClient\\ThrottlingHttpClient` was
+ introduced in Symfony 7.1.
+
Consuming Server-Sent Events
----------------------------
@@ -1567,10 +1663,6 @@ to wrap your HTTP client, open a connection to a server that responds with a
use the :method:`Symfony\\Component\\HttpClient\\Chunk\\ServerSentEvent::getArrayData`
method to directly get the decoded JSON as array.
-.. versionadded:: 6.3
-
- The ``ServerSentEvent::getArrayData()`` method was introduced in Symfony 6.3.
-
Interoperability
----------------
@@ -1688,10 +1780,6 @@ You can also pass a set of default options to your client thanks to the
// ...
-.. versionadded:: 6.2
-
- The ``Psr18Client::withOptions()`` method was introduced in Symfony 6.2.
-
HTTPlug
~~~~~~~
@@ -1793,10 +1881,6 @@ You can also pass a set of default options to your client thanks to the
// ...
-.. versionadded:: 6.2
-
- The ``HttplugClient::withOptions()`` method was introduced in Symfony 6.2.
-
Native PHP Streams
~~~~~~~~~~~~~~~~~~
@@ -1964,6 +2048,20 @@ in order when requests are made::
$response1 = $client->request('...'); // returns $responses[0]
$response2 = $client->request('...'); // returns $responses[1]
+It is also possible to create a
+:class:`Symfony\\Component\\HttpClient\\Response\\MockResponse` directly
+from a file, which is particularly useful when storing your response
+snapshots in files::
+
+ use Symfony\Component\HttpClient\Response\MockResponse;
+
+ $response = MockResponse::fromFile('tests/fixtures/response.xml');
+
+.. versionadded:: 7.1
+
+ The :method:`Symfony\\Component\\HttpClient\\Response\\MockResponse::fromFile`
+ method was introduced in Symfony 7.1.
+
Another way of using :class:`Symfony\\Component\\HttpClient\\MockHttpClient` is to
pass a callback that generates the responses dynamically when it's called::
@@ -2138,9 +2236,18 @@ You can use :class:`Symfony\\Component\\HttpClient\\Response\\JsonMockResponse`
'foo' => 'bar',
]);
-.. versionadded:: 6.3
+Just like :class:`Symfony\\Component\\HttpClient\\Response\\MockResponse`, you can
+also create a :class:`Symfony\\Component\\HttpClient\\Response\\JsonMockResponse`
+directly from a file::
+
+ use Symfony\Component\HttpClient\Response\JsonMockResponse;
+
+ $response = JsonMockResponse::fromFile('tests/fixtures/response.json');
- The ``JsonMockResponse`` was introduced in Symfony 6.3.
+.. versionadded:: 7.1
+
+ The :method:`Symfony\\Component\\HttpClient\\Response\\JsonMockResponse::fromFile`
+ method was introduced in Symfony 7.1.
Testing Request Data
~~~~~~~~~~~~~~~~~~~~
@@ -2293,10 +2400,6 @@ will find the associated response based on the request method, URL and body (if
Note that **this won't work** if the request body or URI is random / always
changing (e.g. if it contains current date or random UUIDs).
-.. versionadded:: 6.4
-
- The ``HarFileResponseFactory`` was introduced in Symfony 6.4.
-
Testing Network Transport Exceptions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2368,12 +2471,6 @@ body::
}
}
-.. versionadded:: 6.1
-
- Being allowed to pass an exception directly to the body of a
- :class:`Symfony\\Component\\HttpClient\\Response\\MockResponse` was
- introduced in Symfony 6.1.
-
.. _`cURL PHP extension`: https://www.php.net/curl
.. _`Zlib PHP extension`: https://www.php.net/zlib
.. _`PSR-17`: https://www.php-fig.org/psr/psr-17/
diff --git a/introduction/from_flat_php_to_symfony.rst b/introduction/from_flat_php_to_symfony.rst
index 7386f629546..3ae6a16d8a3 100644
--- a/introduction/from_flat_php_to_symfony.rst
+++ b/introduction/from_flat_php_to_symfony.rst
@@ -240,7 +240,7 @@ the ``templates/layout.php``:
You now have a setup that will allow you to reuse the layout.
Unfortunately, to accomplish this, you're forced to use a few ugly
PHP functions (``ob_start()``, ``ob_get_clean()``) in the template. Symfony
-solves this using a `Templating`_ component. You'll see it in action shortly.
+solves this using `Twig`_. You'll see it in action shortly.
Adding a Blog "show" Page
-------------------------
@@ -568,9 +568,8 @@ nice way to group related pages. The controller functions are also sometimes cal
The two controllers (or actions) are still lightweight. Each uses the
:doc:`Doctrine ORM library ` to retrieve objects from the
-database and the Templating component to render a template and return a
-``Response`` object. The ``list.html.twig`` template is now quite a bit simpler,
-and uses Twig:
+database and Twig to render a template and return a ``Response`` object.
+The ``list.html.twig`` template is now quite a bit simpler, and uses Twig:
.. code-block:: html+twig
@@ -677,7 +676,7 @@ migrating the blog from flat PHP to Symfony has improved your life:
:doc:`routing `, or rendering :doc:`controllers `;
* Symfony gives you **access to open source tools** such as `Doctrine`_ and the
- `Templating`_, :doc:`Security `, :doc:`Form `,
+ `Twig`_, :doc:`Security `, :doc:`Form `,
`Validator`_ and `Translation`_ components (to name a few);
* The application now enjoys **fully-flexible URLs** thanks to the Routing
@@ -694,7 +693,7 @@ A good selection of `Symfony community tools`_ can be found on GitHub.
.. _`Model-View-Controller`: https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
.. _`Doctrine`: https://www.doctrine-project.org/
-.. _Templating: https://github.com/symfony/templating
+.. _Twig: https://github.com/twigphp/twig
.. _Translation: https://github.com/symfony/translation
.. _`Composer`: https://getcomposer.org
.. _`download Composer`: https://getcomposer.org/download/
diff --git a/lock.rst b/lock.rst
index 400a918f7f3..3b9f1692df4 100644
--- a/lock.rst
+++ b/lock.rst
@@ -62,6 +62,8 @@ this behavior by using the ``lock`` key like:
lock: 'oci:host=127.0.0.1;dbname=app'
lock: 'mongodb://127.0.0.1/app?collection=lock'
lock: '%env(LOCK_DSN)%'
+ # using an existing service
+ lock: 'snc_redis.default'
# named locks
lock:
@@ -119,6 +121,9 @@ this behavior by using the ``lock`` key like:
%env(LOCK_DSN)%
+
+ snc_redis.default
+
semaphoreredis://r2.docker
@@ -131,6 +136,7 @@ this behavior by using the ``lock`` key like:
// config/packages/lock.php
use Symfony\Config\FrameworkConfig;
+ use function Symfony\Component\DependencyInjection\Loader\Configurator\env;
return static function (FrameworkConfig $framework): void {
$framework->lock()
@@ -152,6 +158,8 @@ this behavior by using the ``lock`` key like:
->resource('default', ['oci:host=127.0.0.1;dbname=app'])
->resource('default', ['mongodb://127.0.0.1/app?collection=lock'])
->resource('default', [env('LOCK_DSN')])
+ // using an existing service
+ ->resource('default', ['snc_redis.default'])
// named locks
->resource('invoice', ['semaphore', 'redis://r2.docker'])
@@ -159,10 +167,9 @@ this behavior by using the ``lock`` key like:
;
};
-.. versionadded:: 6.1
+.. versionadded:: 7.2
- The CSV support (e.g. ``zookeeper://localhost01,localhost02:2181``) in
- ZookeeperStore DSN was introduced in Symfony 6.1.
+ The option to use an existing service as the lock/semaphore was introduced in Symfony 7.2.
Locking a Resource
------------------
diff --git a/logging/handlers.rst b/logging/handlers.rst
index c28bcfcd2cb..eb2b1ac2c9c 100644
--- a/logging/handlers.rst
+++ b/logging/handlers.rst
@@ -24,7 +24,7 @@ To use it, declare it as a service:
$endpoint: "http://127.0.0.1:9200"
$index: "monolog"
$client: null
- $level: !php/const Monolog\Logger::DEBUG
+ $level: !php/enum Monolog\Level::Debug
$bubble: true
$elasticsearchVersion: '1.0.0'
@@ -48,7 +48,7 @@ To use it, declare it as a service:
http://127.0.0.1:9200monolog
- Monolog\Logger::DEBUG
+ Monolog\Level::Debugtrue1.0.0
@@ -58,7 +58,7 @@ To use it, declare it as a service:
.. code-block:: php
// config/services.php
- use Monolog\Logger;
+ use Monolog\Level;
use Symfony\Bridge\Monolog\Handler\ElasticsearchLogstashHandler;
$container->register(ElasticsearchLogstashHandler::class);
@@ -69,7 +69,7 @@ To use it, declare it as a service:
'$endpoint' => "http://127.0.0.1:9200",
'$index' => "monolog",
'$client' => null,
- '$level' => Logger::DEBUG,
+ '$level' => Level::Debug,
'$bubble' => true,
'$elasticsearchVersion' => '1.0.0',
])
diff --git a/logging/monolog_console.rst b/logging/monolog_console.rst
index 0502c87dacf..4d007abe854 100644
--- a/logging/monolog_console.rst
+++ b/logging/monolog_console.rst
@@ -10,10 +10,9 @@ When a lot of logging has to happen, it's cumbersome to print information
depending on the verbosity settings (``-v``, ``-vv``, ``-vvv``) because the
calls need to be wrapped in conditions. For example::
- use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(OutputInterface $output): int
{
if ($output->isDebug()) {
$output->writeln('Some info');
@@ -34,28 +33,27 @@ the current log level and the console verbosity.
The example above could then be rewritten as::
- // src/Command/YourCommand.php
+ // src/Command/MyCommand.php
namespace App\Command;
use Psr\Log\LoggerInterface;
+ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
- use Symfony\Component\Console\Input\InputInterface;
- use Symfony\Component\Console\Output\OutputInterface;
- class YourCommand extends Command
+ #[AsCommand(name: 'app:my-command')]
+ class MyCommand
{
public function __construct(
private LoggerInterface $logger,
) {
- parent::__construct();
}
- protected function execute(InputInterface $input, OutputInterface $output): int
+ public function __invoke(): int
{
$this->logger->debug('Some info');
$this->logger->notice('Some more info');
- // ...
+ return Command::SUCCESS;
}
}
diff --git a/mailer.rst b/mailer.rst
index 303c7db2061..bdc50757cdd 100644
--- a/mailer.rst
+++ b/mailer.rst
@@ -100,33 +100,37 @@ via a third-party provider:
===================== =============================================== ===============
Service Install with Webhook support
===================== =============================================== ===============
+`AhaSend`_ ``composer require symfony/aha-send-mailer`` yes
`Amazon SES`_ ``composer require symfony/amazon-mailer``
+`Azure`_ ``composer require symfony/azure-mailer``
`Brevo`_ ``composer require symfony/brevo-mailer`` yes
`Infobip`_ ``composer require symfony/infobip-mailer``
`Mailgun`_ ``composer require symfony/mailgun-mailer`` yes
`Mailjet`_ ``composer require symfony/mailjet-mailer`` yes
+`Mailomat`_ ``composer require symfony/mailomat-mailer`` yes
`MailPace`_ ``composer require symfony/mail-pace-mailer``
-`MailerSend`_ ``composer require symfony/mailer-send-mailer``
-`Mandrill`_ ``composer require symfony/mailchimp-mailer``
+`MailerSend`_ ``composer require symfony/mailer-send-mailer`` yes
+`Mailtrap`_ ``composer require symfony/mailtrap-mailer`` yes
+`Mandrill`_ ``composer require symfony/mailchimp-mailer`` yes
+`Postal`_ ``composer require symfony/postal-mailer``
`Postmark`_ ``composer require symfony/postmark-mailer`` yes
+`Resend`_ ``composer require symfony/resend-mailer`` yes
`Scaleway`_ ``composer require symfony/scaleway-mailer``
`SendGrid`_ ``composer require symfony/sendgrid-mailer`` yes
+`Sweego`_ ``composer require symfony/sweego-mailer`` yes
===================== =============================================== ===============
-.. versionadded:: 6.2
+.. versionadded:: 7.1
- The Infobip integration was introduced in Symfony 6.2 and the ``MailPace``
- integration was renamed in Symfony 6.2 (in previous Symfony versions it was
- called ``OhMySMTP``).
+ The Azure and Resend integrations were introduced in Symfony 7.1.
-.. versionadded:: 6.3
+.. versionadded:: 7.2
- The MailerSend integration was introduced in Symfony 6.3.
+ The Mailomat, Mailtrap, Postal and Sweego integrations were introduced in Symfony 7.2.
-.. versionadded:: 6.4
+.. versionadded:: 7.3
- The ``Brevo`` (in previous Symfony versions it was called ``Sendinblue``)
- and ``Scaleway`` integrations were introduced in Symfony 6.4.
+ The AhaSend integration was introduced in Symfony 7.3.
.. note::
@@ -176,10 +180,16 @@ party provider:
+------------------------+---------------------------------------------------------+
| Provider | Formats |
+========================+=========================================================+
+| `AhaSend`_ | - API ``ahasend+api://KEY@default`` |
+| | - HTTP n/a |
+| | - SMTP ``ahasend+smtp://USERNAME:PASSWORD@default`` |
++------------------------+---------------------------------------------------------+
| `Amazon SES`_ | - SMTP ``ses+smtp://USERNAME:PASSWORD@default`` |
| | - HTTP ``ses+https://ACCESS_KEY:SECRET_KEY@default`` |
| | - API ``ses+api://ACCESS_KEY:SECRET_KEY@default`` |
+------------------------+---------------------------------------------------------+
+| `Azure`_ | - API ``azure+api://ACS_RESOURCE_NAME:KEY@default`` |
++------------------------+---------------------------------------------------------+
| `Brevo`_ | - SMTP ``brevo+smtp://USERNAME:PASSWORD@default`` |
| | - HTTP n/a |
| | - API ``brevo+api://KEY@default`` |
@@ -208,14 +218,30 @@ party provider:
| | - HTTP n/a |
| | - API ``mailjet+api://ACCESS_KEY:SECRET_KEY@default`` |
+------------------------+---------------------------------------------------------+
+| `Mailomat`_ | - SMTP ``mailomat+smtp://USERNAME:PASSWORD@default`` |
+| | - HTTP n/a |
+| | - API ``mailomat+api://KEY@default`` |
++------------------------+---------------------------------------------------------+
| `MailPace`_ | - SMTP ``mailpace+api://API_TOKEN@default`` |
| | - HTTP n/a |
| | - API ``mailpace+api://API_TOKEN@default`` |
+------------------------+---------------------------------------------------------+
+| `Mailtrap`_ | - SMTP ``mailtrap+smtp://PASSWORD@default`` |
+| | - HTTP n/a |
+| | - API ``mailtrap+api://API_TOKEN@default`` |
++------------------------+---------------------------------------------------------+
+| `Postal`_ | - SMTP n/a |
+| | - HTTP n/a |
+| | - API ``postal+api://API_KEY@BASE_URL`` |
++------------------------+---------------------------------------------------------+
| `Postmark`_ | - SMTP ``postmark+smtp://ID@default`` |
| | - HTTP n/a |
| | - API ``postmark+api://KEY@default`` |
+------------------------+---------------------------------------------------------+
+| `Resend`_ | - SMTP ``resend+smtp://resend:API_KEY@default`` |
+| | - HTTP n/a |
+| | - API ``resend+api://API_KEY@default`` |
++------------------------+---------------------------------------------------------+
| `Scaleway`_ | - SMTP ``scaleway+smtp://PROJECT_ID:API_KEY@default`` |
| | - HTTP n/a |
| | - API ``scaleway+api://PROJECT_ID:API_KEY@default`` |
@@ -224,10 +250,10 @@ party provider:
| | - HTTP n/a |
| | - API ``sendgrid+api://KEY@default`` |
+------------------------+---------------------------------------------------------+
-
-.. versionadded:: 6.3
-
- The ``sandbox`` option in ``Mailjet`` API was introduced in Symfony 6.3.
+| `Sweego`_ | - SMTP ``sweego+smtp://LOGIN:PASSWORD@HOST:PORT`` |
+| | - HTTP n/a |
+| | - API ``sweego+api://API_KEY@default`` |
++------------------------+---------------------------------------------------------+
.. warning::
@@ -242,12 +268,6 @@ party provider:
you need to add the ``ping_threshold`` parameter to your ``MAILER_DSN`` with
a value lower than ``10``: ``ses+smtp://USERNAME:PASSWORD@default?ping_threshold=9``
-.. warning::
-
- If you send custom headers when using the `Amazon SES`_ transport (to receive
- them later via a webhook), make sure to use the ``ses+https`` provider because
- it's the only one that supports them.
-
.. note::
When using SMTP, the default timeout for sending a message before throwing an
@@ -314,6 +334,17 @@ The failover-transport starts using the first transport and if it fails, it
will retry the same delivery with the next transports until one of them succeeds
(or until all of them fail).
+By default, delivery is retried 60 seconds after a failed attempt. You can adjust
+the retry period by setting the ``retry_period`` option in the DSN:
+
+.. code-block:: env
+
+ MAILER_DSN="failover(postmark+api://ID@default sendgrid+smtp://KEY@default)?retry_period=15"
+
+.. versionadded:: 7.3
+
+ The ``retry_period`` option was introduced in Symfony 7.3.
+
Load Balancing
~~~~~~~~~~~~~~
@@ -334,6 +365,17 @@ As with the failover transport, round-robin retries deliveries until
a transport succeeds (or all fail). In contrast to the failover transport,
it *spreads* the load across all its transports.
+By default, delivery is retried 60 seconds after a failed attempt. You can adjust
+the retry period by setting the ``retry_period`` option in the DSN:
+
+.. code-block:: env
+
+ MAILER_DSN="roundrobin(postmark+api://ID@default sendgrid+smtp://KEY@default)?retry_period=15"
+
+.. versionadded:: 7.3
+
+ The ``retry_period`` option was introduced in Symfony 7.3.
+
TLS Peer Verification
~~~~~~~~~~~~~~~~~~~~~
@@ -354,9 +396,72 @@ may be specified as SHA1 or MD5 hash::
$dsn = 'smtp://user:pass@smtp.example.com?peer_fingerprint=6A1CF3B08D175A284C30BC10DE19162307C7286E';
-.. versionadded:: 6.4
+Disabling Automatic TLS
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 7.1
+
+ The option to disable automatic TLS was introduced in Symfony 7.1.
+
+By default, the Mailer component will use encryption when the OpenSSL extension
+is enabled and the SMTP server supports ``STARTTLS``. This behavior can be turned
+off by calling ``setAutoTls(false)`` on the ``EsmtpTransport`` instance, or by
+setting the ``auto_tls`` option to ``false`` in the DSN::
+
+ $dsn = 'smtp://user:pass@10.0.0.25?auto_tls=false';
+
+.. warning::
+
+ It's not recommended to disable TLS while connecting to an SMTP server over
+ the Internet, but it can be useful when both the application and the SMTP
+ server are in a secured network, where there is no need for additional encryption.
+
+.. note::
+
+ This setting only works when the ``smtp://`` protocol is used.
+
+Ensure TLS
+~~~~~~~~~~
+
+You may want to ensure that TLS is used (either directly or via ``STARTTLS``)
+when sending mail over SMTP, regardless of other options or SMTP server support.
+To require TLS, call ``setRequireTls(true)`` on the ``EsmtpTransport`` instance,
+or set the ``require_tls`` option to ``true`` in the DSN::
+
+ $dsn = 'smtp://user:pass@10.0.0.25?require_tls=true';
+
+When TLS is required, a :class:`Symfony\\Component\\Mailer\\Exception\\TransportException`
+is thrown if a TLS connection cannot be established during the initial communication
+with the SMTP server.
+
+.. note::
+
+ This setting only applies when using the ``smtp://`` protocol.
+
+.. versionadded:: 7.3
+
+ The ``require_tls`` option was introduced in Symfony 7.3.
+
+Binding to IPv4 or IPv6
+~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 7.3
+
+ The option to bind to IPv4, or IPv6, or a specific IP address was introduced in Symfony 7.3.
+
+By default, the underlying ``SocketStream`` will bind to IPv4 or IPv6 based on the
+available interfaces. You can enforce binding to a specific protocol or IP address
+by using the ``source_ip`` option. To bind to IPv4, use::
+
+ $dsn = 'smtp://smtp.example.com?source_ip=0.0.0.0';
+
+As per RFC2732, IPv6 addresses must be enclosed in square brackets. To bind to IPv6, use::
+
+ $dsn = 'smtp://smtp.example.com?source_ip=[::]';
+
+.. note::
- The ``peer_fingerprint`` option was introduced in Symfony 6.4.
+ This option only works when using the ``smtp://`` protocol.
Overriding default SMTP authenticators
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -383,11 +488,6 @@ This can be done from ``EsmtpTransport`` constructor or using the
// Option 2: call a method to redefine the authenticators
$transport->setAuthenticators([new XOAuth2Authenticator()]);
-.. versionadded:: 6.3
-
- The ``$authenticators`` constructor parameter and the ``setAuthenticators()``
- method were introduced in Symfony 6.3.
-
Other Options
~~~~~~~~~~~~~
@@ -423,10 +523,6 @@ Other Options
$dsn = 'smtps://smtp.example.com?max_per_second=2'
- .. versionadded:: 6.2
-
- The ``max_per_second`` option was introduced in Symfony 6.2.
-
Custom Transport Factories
~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -514,6 +610,10 @@ both strings or address objects::
// email address as a simple string
->from('fabien@example.com')
+ // non-ASCII characters are supported both in the local part and the domain;
+ // if the SMTP server doesn't support this feature, you'll see an exception
+ ->from('jânë.dœ@ëxãmplę.com')
+
// email address as an object
->from(new Address('fabien@example.com'))
@@ -534,6 +634,11 @@ both strings or address objects::
:ref:`configure emails globally ` to set the
same ``From`` email to all messages.
+.. versionadded:: 7.2
+
+ Support for non-ASCII email addresses (e.g. ``jânë.dœ@ëxãmplę.com``)
+ was introduced in Symfony 7.2.
+
.. note::
The local part of the address (what goes before the ``@``) can include UTF-8
@@ -639,12 +744,6 @@ the ``DataPart``::
->addPart(new DataPart(fopen('/path/to/documents/contract.doc', 'r')))
;
-.. deprecated:: 6.2
-
- In Symfony versions previous to 6.2, the method ``attachPart()`` could be
- used to add attachments. This method has been deprecated and replaced
- with ``addPart()``.
-
Embedding Images
~~~~~~~~~~~~~~~~
@@ -696,15 +795,6 @@ method to define a custom Content-ID for the image and use it as its ``cid`` ref
->html('... ...')
;
-.. versionadded:: 6.1
-
- The support of embedded images as HTML backgrounds was introduced in Symfony
- 6.1.
-
-.. versionadded:: 6.3
-
- The support of custom ``cid`` for embedded images was introduced in Symfony 6.3.
-
.. _mailer-configure-email-globally:
Configuring Emails Globally
@@ -904,11 +994,6 @@ for Twig templates::
])
;
-.. versionadded:: 6.4
-
- The :method:`Symfony\\Bridge\\Twig\\Mime\\TemplatedEmail::locale` method
- was introduced in Symfony 6.4.
-
Then, create the template:
.. code-block:: html+twig
@@ -1030,6 +1115,18 @@ the email contents: