diff --git a/.doctor-rst.yaml b/.doctor-rst.yaml
index e71ca735f09..a568b014eab 100644
--- a/.doctor-rst.yaml
+++ b/.doctor-rst.yaml
@@ -50,18 +50,18 @@ rules:
yarn_dev_option_at_the_end: ~
# no_app_bundle: ~
- # 4.x
+ # master
versionadded_directive_major_version:
- major_version: 4
+ major_version: 5
versionadded_directive_min_version:
- min_version: '4.0'
+ min_version: '5.0'
deprecated_directive_major_version:
- major_version: 4
+ major_version: 5
deprecated_directive_min_version:
- min_version: '4.0'
+ min_version: '5.0'
# do not report as violation
whitelist:
@@ -90,8 +90,13 @@ whitelist:
- '.. versionadded:: 1.6' # Flex in setup/upgrade_minor.rst
- '0 => 123' # assertion for var_dumper - components/var_dumper.rst
- '1 => "foo"' # assertion for var_dumper - components/var_dumper.rst
+ - '123,' # assertion for var_dumper - components/var_dumper.rst
+ - '"foo",' # assertion for var_dumper - components/var_dumper.rst
- '$var .= "Because of this `\xE9` octet (\\xE9),\n";'
- "`Deploying Symfony 4 Apps on Heroku`_."
- ".. _`Deploying Symfony 4 Apps on Heroku`: https://devcenter.heroku.com/articles/deploying-symfony4"
+ - "// 224, 165, 141, 224, 164, 164, 224, 165, 135])"
- '.. versionadded:: 0.2' # MercureBundle
+ - 'provides a ``loginUser()`` method to simulate logging in in your functional'
- '.. code-block:: twig'
+ - '.. versionadded:: 3.6' # MonologBundle
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 0586345396f..ddeb73add51 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,7 +1,7 @@
+
+
+
+
+
+
+
+
+ env(base64:CACHE_DECRYPTION_KEY)
+
+
+
+
+
+
+
+
+ .. code-block:: php
+
+ // config/packages/cache.php
+ use Symfony\Component\Cache\Marshaller\SodiumMarshaller;
+ use Symfony\Component\DependencyInjection\ChildDefinition;
+ use Symfony\Component\DependencyInjection\Reference;
+
+ // ...
+ $container->setDefinition(SodiumMarshaller::class, new ChildDefinition('cache.default_marshaller'))
+ ->addArgument(['env(base64:CACHE_DECRYPTION_KEY)'])
+ // use multiple keys in order to rotate them
+ //->addArgument(['env(base64:CACHE_DECRYPTION_KEY)', 'env(base64:OLD_CACHE_DECRYPTION_KEY)'])
+ ->addArgument(new Reference(SodiumMarshaller::class.'.inner'));
+
+.. caution::
+
+ This will encrypt the values of the cache items, but not the cache keys. Be
+ careful not the leak sensitive data in the keys.
+
+When configuring multiple keys, the first key will be used for reading and
+writing, and the additional key(s) will only be used for reading. Once all
+cache items encrypted with the old key have expired, you can remove
+``OLD_CACHE_DECRYPTION_KEY`` completely.
diff --git a/components/asset.rst b/components/asset.rst
index 6aab732333f..48e51754449 100644
--- a/components/asset.rst
+++ b/components/asset.rst
@@ -165,6 +165,22 @@ In those cases, use the
echo $package->getUrl('css/app.css');
// result: build/css/app.b916426ea1d10021f3f17ce8031f93c2.css
+If your JSON file is not on your local filesystem but is accessible over HTTP,
+use the :class:`Symfony\\Component\\Asset\\VersionStrategy\\RemoteJsonManifestVersionStrategy`
+with the :doc:`HttpClient component `::
+
+ use Symfony\Component\Asset\Package;
+ use Symfony\Component\Asset\VersionStrategy\RemoteJsonManifestVersionStrategy;
+ use Symfony\Component\HttpClient\HttpClient;
+
+ $httpClient = HttpClient::create();
+ $manifestUrl = 'https://cdn.example.com/rev-manifest.json';
+ $package = new Package(new RemoteJsonManifestVersionStrategy($manifestUrl, $httpClient));
+
+.. versionadded:: 5.1
+
+ The ``RemoteJsonManifestVersionStrategy`` was introduced in Symfony 5.1.
+
Custom Version Strategies
.........................
@@ -184,12 +200,12 @@ every day::
$this->version = date('Ymd');
}
- public function getVersion($path)
+ public function getVersion(string $path)
{
return $this->version;
}
- public function applyVersion($path)
+ public function applyVersion(string $path)
{
return sprintf('%s?v=%s', $path, $this->getVersion($path));
}
diff --git a/components/browser_kit.rst b/components/browser_kit.rst
index 76c0e33d5e1..475c84b9365 100644
--- a/components/browser_kit.rst
+++ b/components/browser_kit.rst
@@ -40,13 +40,7 @@ The component only provides an abstract client and does not provide any backend
ready to use for the HTTP layer. To create your own client, you must extend the
``AbstractBrowser`` class and implement the
:method:`Symfony\\Component\\BrowserKit\\AbstractBrowser::doRequest` method.
-
-.. deprecated:: 4.3
-
- In Symfony 4.3 and earlier versions, the ``AbstractBrowser`` class was called
- ``Client`` (which is now deprecated).
-
-The ``doRequest()`` method accepts a request and should return a response::
+This method accepts a request and should return a response::
namespace Acme;
@@ -86,6 +80,20 @@ The value returned by the ``request()`` method is an instance of the
:doc:`DomCrawler component `, which allows accessing
and traversing HTML elements programmatically.
+The :method:`Symfony\\Component\\BrowserKit\\AbstractBrowser::jsonRequest` method,
+which defines the same arguments as the ``request()`` method, is a shortcut to
+convert the request parameters into a JSON string and set the needed HTTP headers::
+
+ use Acme\Client;
+
+ $client = new Client();
+ // this encodes parameters as JSON and sets the required CONTENT_TYPE and HTTP_ACCEPT headers
+ $crawler = $client->jsonRequest('GET', '/', ['some_parameter' => 'some_value']);
+
+.. versionadded:: 5.3
+
+ The ``jsonRequest()`` method was introduced in Symfony 5.3.
+
The :method:`Symfony\\Component\\BrowserKit\\AbstractBrowser::xmlHttpRequest` method,
which defines the same arguments as the ``request()`` method, is a shortcut to
make AJAX requests::
@@ -167,6 +175,32 @@ provides access to the form properties (e.g. ``$form->getUri()``,
// submit that form
$crawler = $client->submit($form);
+Custom Header Handling
+~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 5.2
+
+ The ``getHeaders()`` method was introduced in Symfony 5.2.
+
+The optional HTTP headers passed to the ``request()`` method follows the FastCGI
+request format (uppercase, underscores instead of dashes and prefixed with ``HTTP_``).
+Before saving those headers to the request, they are lower-cased, with ``HTTP_``
+stripped, and underscores turned to dashes.
+
+If you're making a request to an application that has special rules about header
+capitalization or punctuation, override the ``getHeaders()`` method, which must
+return an associative array of headers::
+
+ protected function getHeaders(Request $request): array
+ {
+ $headers = parent::getHeaders($request);
+ if (isset($request->getServer()['api_key'])) {
+ $headers['api_key'] = $request->getServer()['api_key'];
+ }
+
+ return $headers;
+ }
+
Cookies
-------
@@ -317,10 +351,6 @@ dedicated web crawler or scraper such as `Goutte`_::
'.table-list-header-toggle a:nth-child(1)'
)->text());
-.. versionadded:: 4.3
-
- The feature to make external HTTP requests was introduced in Symfony 4.3.
-
Learn more
----------
diff --git a/components/cache/adapters/array_cache_adapter.rst b/components/cache/adapters/array_cache_adapter.rst
index ffdc1d60dd0..c7b06f40753 100644
--- a/components/cache/adapters/array_cache_adapter.rst
+++ b/components/cache/adapters/array_cache_adapter.rst
@@ -8,10 +8,7 @@ Array Cache Adapter
Generally, this adapter is useful for testing purposes, as its contents are stored in memory
and not persisted outside the running PHP process in any way. It can also be useful while
warming up caches, due to the :method:`Symfony\\Component\\Cache\\Adapter\\ArrayAdapter::getValues`
-method.
-
-This adapter can be passed a default cache lifetime as its first parameter, and a boolean that
-toggles serialization as its second parameter::
+method::
use Symfony\Component\Cache\Adapter\ArrayAdapter;
@@ -23,5 +20,17 @@ toggles serialization as its second parameter::
$defaultLifetime = 0,
// if ``true``, the values saved in the cache are serialized before storing them
- $storeSerialized = true
+ $storeSerialized = true,
+
+ // the maximum lifetime (in seconds) of the entire cache (after this time, the
+ // entire cache is deleted to avoid stale data from consuming memory)
+ $maxLifetime = 0,
+
+ // 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
);
+
+.. versionadded:: 5.1
+
+ The ``maxLifetime`` and ``maxItems`` options were introduced in Symfony 5.1.
diff --git a/components/cache/adapters/couchbasebucket_adapter.rst b/components/cache/adapters/couchbasebucket_adapter.rst
new file mode 100644
index 00000000000..7043a7c3e95
--- /dev/null
+++ b/components/cache/adapters/couchbasebucket_adapter.rst
@@ -0,0 +1,150 @@
+.. index::
+ single: Cache Pool
+ single: Couchabase Cache
+
+.. _couchbase-adapter:
+
+Couchbase Cache Adapter
+=======================
+
+.. versionadded:: 5.1
+
+ The CouchbaseBucketAdapter was introduced in Symfony 5.1.
+
+This adapter stores the values in-memory using one (or more) `Couchbase server`_
+instances. Unlike the :ref:`APCu adapter `, and similarly to the
+:ref:`Memcached adapter `, it is not limited to the current server's
+shared memory; you can store contents independent of your PHP environment.
+The ability to utilize a cluster of servers to provide redundancy and/or fail-over
+is also available.
+
+.. caution::
+
+ **Requirements:** The `Couchbase PHP extension`_ as well as a `Couchbase server`_
+ must be installed, active, and running to use this adapter. Version ``2.6`` or
+ greater of the `Couchbase PHP extension`_ is required for this adapter.
+
+This adapter expects a `Couchbase Bucket`_ instance to be passed as the first
+parameter. A namespace and default cache lifetime can optionally be passed as
+the second and third parameters::
+
+ use Symfony\Component\Cache\Adapter\CouchbaseBucketAdapter;
+
+ $cache = new CouchbaseBucketAdapter(
+ // the client object that sets options and adds the server instance(s)
+ \CouchbaseBucket $client,
+
+ // the name of bucket
+ string $bucket,
+
+ // a string prefixed to the keys of the items stored in this cache
+ $namespace = '',
+
+ // the default lifetime (in seconds) for cache items that do not define their
+ // own lifetime, with a value 0 causing items to be stored indefinitely
+ $defaultLifetime = 0,
+ );
+
+
+Configure the Connection
+------------------------
+
+The :method:`Symfony\\Component\\Cache\\Adapter\\CouchbaseBucketAdapter::createConnection`
+helper method allows creating and configuring a `Couchbase Bucket`_ class instance using a
+`Data Source Name (DSN)`_ or an array of DSNs::
+
+ use Symfony\Component\Cache\Adapter\CouchbaseBucketAdapter;
+
+ // pass a single DSN string to register a single server with the client
+ $client = CouchbaseBucketAdapter::createConnection(
+ 'couchbase://localhost'
+ // the DSN can include config options (pass them as a query string):
+ // 'couchbase://localhost:11210?operationTimeout=10'
+ // 'couchbase://localhost:11210?operationTimeout=10&configTimout=20'
+ );
+
+ // pass an array of DSN strings to register multiple servers with the client
+ $client = CouchbaseBucketAdapter::createConnection([
+ 'couchbase://10.0.0.100',
+ 'couchbase://10.0.0.101',
+ 'couchbase://10.0.0.102',
+ // etc...
+ ]);
+
+ // a single DSN can define multiple servers using the following syntax:
+ // host[hostname-or-IP:port] (where port is optional). Sockets must include a trailing ':'
+ $client = CouchbaseBucketAdapter::createConnection(
+ 'couchbase:?host[localhost]&host[localhost:12345]'
+ );
+
+
+Configure the Options
+---------------------
+
+The :method:`Symfony\\Component\\Cache\\Adapter\\CouchbaseBucketAdapter::createConnection`
+helper method also accepts an array of options as its second argument. The
+expected format is an associative array of ``key => value`` pairs representing
+option names and their respective values::
+
+ use Symfony\Component\Cache\Adapter\CouchbaseBucketAdapter;
+
+ $client = CouchbaseBucketAdapter::createConnection(
+ // a DSN string or an array of DSN strings
+ [],
+
+ // associative array of configuration options
+ [
+ 'username' => 'xxxxxx',
+ 'password' => 'yyyyyy',
+ 'configTimeout' => '100',
+ ]
+ );
+
+Available Options
+~~~~~~~~~~~~~~~~~
+
+``username`` (type: ``string``)
+ Username for connection ``CouchbaseCluster``.
+
+``password`` (type: ``string``)
+ Password of connection ``CouchbaseCluster``.
+
+``operationTimeout`` (type: ``int``, default: ``2500000``)
+ The operation timeout (in microseconds) is the maximum amount of time the library will
+ wait for an operation to receive a response before invoking its callback with a failure status.
+
+``configTimeout`` (type: ``int``, default: ``5000000``)
+ How long (in microseconds) the client will wait to obtain the initial configuration.
+
+``configNodeTimeout`` (type: ``int``, default: ``2000000``)
+ Per-node configuration timeout (in microseconds).
+
+``viewTimeout`` (type: ``int``, default: ``75000000``)
+ The I/O timeout (in microseconds) for HTTP requests to Couchbase Views API.
+
+``httpTimeout`` (type: ``int``, default: ``75000000``)
+ The I/O timeout (in microseconds) for HTTP queries (management API).
+
+``configDelay`` (type: ``int``, default: ``10000``)
+ Config refresh throttling
+ Modify the amount of time (in microseconds) before the configuration error threshold will forcefully be set to its maximum number forcing a configuration refresh.
+
+``htconfigIdleTimeout`` (type: ``int``, default: ``4294967295``)
+ Idling/Persistence for HTTP bootstrap (in microseconds).
+
+``durabilityInterval`` (type: ``int``, default: ``100000``)
+ The time (in microseconds) the client will wait between repeated probes to a given server.
+
+``durabilityTimeout`` (type: ``int``, default: ``5000000``)
+ The time (in microseconds) the client will spend sending repeated probes to a given key's vBucket masters and replicas before they are deemed not to have satisfied the durability requirements.
+
+.. tip::
+
+ Reference the `Couchbase Bucket`_ extension's `predefined constants`_ documentation
+ for additional information about the available options.
+
+.. _`Couchbase PHP extension`: https://docs.couchbase.com/sdk-api/couchbase-php-client-2.6.0/files/couchbase.html
+.. _`predefined constants`: https://docs.couchbase.com/sdk-api/couchbase-php-client-2.6.0/classes/Couchbase.Bucket.html
+.. _`Couchbase server`: https://couchbase.com/
+.. _`Couchbase Bucket`: https://docs.couchbase.com/sdk-api/couchbase-php-client-2.6.0/classes/Couchbase.Bucket.html
+.. _`Data Source Name (DSN)`: https://en.wikipedia.org/wiki/Data_source_name
diff --git a/components/cache/adapters/memcached_adapter.rst b/components/cache/adapters/memcached_adapter.rst
index 9cd8c8cdd25..95161637b25 100644
--- a/components/cache/adapters/memcached_adapter.rst
+++ b/components/cache/adapters/memcached_adapter.rst
@@ -70,10 +70,6 @@ helper method allows creating and configuring a `Memcached`_ class instance usin
'memcached:?host[localhost]&host[localhost:12345]&host[/some/memcached.sock:]=3'
);
-.. versionadded:: 4.2
-
- The option to define multiple servers in a single DSN was introduced in Symfony 4.2.
-
The `Data Source Name (DSN)`_ for this adapter must use the following format:
.. code-block:: text
diff --git a/components/cache/adapters/redis_adapter.rst b/components/cache/adapters/redis_adapter.rst
index 935afcd9f92..a81455d8e28 100644
--- a/components/cache/adapters/redis_adapter.rst
+++ b/components/cache/adapters/redis_adapter.rst
@@ -95,21 +95,13 @@ Below are common examples of valid DSNs showing a combination of available value
);
`Redis Sentinel`_, which provides high availability for Redis, is also supported
-when using the Predis library. Use the ``redis_sentinel`` parameter to set the
-name of your service group::
+when using the PHP Redis Extension v5.2+ or the Predis library. Use the ``redis_sentinel``
+parameter to set the name of your service group::
RedisAdapter::createConnection(
'redis:?host[redis1:26379]&host[redis2:26379]&host[redis3:26379]&redis_sentinel=mymaster'
);
-.. versionadded:: 4.2
-
- The option to define multiple servers in a single DSN was introduced in Symfony 4.2.
-
-.. versionadded:: 4.4
-
- Redis Sentinel support was introduced in Symfony 4.4.
-
.. note::
See the :class:`Symfony\\Component\\Cache\\Traits\\RedisTrait` for more options
diff --git a/components/cache/psr6_psr16_adapters.rst b/components/cache/psr6_psr16_adapters.rst
index 28a41fca0e7..6b98d26744b 100644
--- a/components/cache/psr6_psr16_adapters.rst
+++ b/components/cache/psr6_psr16_adapters.rst
@@ -46,10 +46,6 @@ this use-case::
// now use this wherever you want
$githubApiClient = new GitHubApiClient($psr6Cache);
-.. versionadded:: 4.3
-
- The ``Psr16Adapter`` class was introduced in Symfony 4.3.
-
Using a PSR-6 Cache Object as a PSR-16 Cache
--------------------------------------------
@@ -87,8 +83,4 @@ this use-case::
// now use this wherever you want
$githubApiClient = new GitHubApiClient($psr16Cache);
-.. versionadded:: 4.3
-
- The ``Psr16Cache`` class was introduced in Symfony 4.3.
-
.. _`PSR-16`: https://www.php-fig.org/psr/psr-16/
diff --git a/components/config/definition.rst b/components/config/definition.rst
index 67c7392eb33..dfe9f8e166a 100644
--- a/components/config/definition.rst
+++ b/components/config/definition.rst
@@ -68,10 +68,6 @@ implements the :class:`Symfony\\Component\\Config\\Definition\\ConfigurationInte
}
}
-.. deprecated:: 4.2
-
- Not passing the root node name to ``TreeBuilder`` was deprecated in Symfony 4.2.
-
Adding Node Definitions to the Tree
-----------------------------------
@@ -439,6 +435,13 @@ The following example shows these methods in practice::
Deprecating the Option
----------------------
+.. versionadded:: 5.1
+
+ The signature of the ``setDeprecated()`` method changed from
+ ``setDeprecated(?string $message)`` to
+ ``setDeprecated(string $package, string $version, ?string $message)``
+ in Symfony 5.1.
+
You can deprecate options using the
:method:`Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition::setDeprecated`
method::
@@ -447,11 +450,15 @@ method::
->children()
->integerNode('old_option')
// this outputs the following generic deprecation message:
- // The child node "old_option" at path "..." is deprecated.
- ->setDeprecated()
+ // Since acme/package 1.2: The child node "old_option" at path "..." is deprecated.
+ ->setDeprecated('acme/package', '1.2')
// you can also pass a custom deprecation message (%node% and %path% placeholders are available):
- ->setDeprecated('The "%node%" option is deprecated. Use "new_config_option" instead.')
+ ->setDeprecated(
+ 'acme/package',
+ '1.2',
+ 'The "%node%" option is deprecated. Use "new_config_option" instead.'
+ )
->end()
->end()
;
diff --git a/components/console/helpers/progressbar.rst b/components/console/helpers/progressbar.rst
index c8ef2f25a1f..ebdcaba1d64 100644
--- a/components/console/helpers/progressbar.rst
+++ b/components/console/helpers/progressbar.rst
@@ -56,11 +56,6 @@ you can also set the current progress by calling the
to redraw every N iterations. By default, redraw frequency is
**100ms** or **10%** of your ``max``.
- .. versionadded:: 4.4
-
- The ``minSecondsBetweenRedraws()`` and ``maxSecondsBetweenRedraws()``
- methods were introduced in Symfony 4.4.
-
If you don't know the exact number of steps in advance, set it to a reasonable
value and then call the ``setMaxSteps()`` method to update it as needed::
@@ -130,10 +125,6 @@ The previous code will output:
1/2 [==============>-------------] 50%
2/2 [============================] 100%
-.. versionadded:: 4.3
-
- The ``iterate()`` method was introduced in Symfony 4.3.
-
Customizing the Progress Bar
----------------------------
@@ -322,11 +313,6 @@ to display it can be customized::
$progressBar->advance();
}
- .. versionadded:: 4.4
-
- The ``minSecondsBetweenRedraws`` and ``maxSecondsBetweenRedraws()`` methods
- were introduced in Symfony 4.4.
-
Custom Placeholders
~~~~~~~~~~~~~~~~~~~
diff --git a/components/console/helpers/questionhelper.rst b/components/console/helpers/questionhelper.rst
index 2174550a0cd..57239a8d6e4 100644
--- a/components/console/helpers/questionhelper.rst
+++ b/components/console/helpers/questionhelper.rst
@@ -40,7 +40,7 @@ the following to your command::
$question = new ConfirmationQuestion('Continue with this action?', false);
if (!$helper->ask($input, $output, $question)) {
- return 0;
+ return Command::SUCCESS;
}
}
}
@@ -105,6 +105,7 @@ from a predefined list::
$helper = $this->getHelper('question');
$question = new ChoiceQuestion(
'Please select your favorite color (defaults to red)',
+ // choices can also be PHP objects that implement __toString() method
['red', 'blue', 'yellow'],
0
);
@@ -116,6 +117,10 @@ from a predefined list::
// ... do something with the color
}
+.. versionadded:: 5.2
+
+ Support for using PHP objects as choice values was introduced in Symfony 5.2.
+
The option which should be selected by default is provided with the third
argument of the constructor. The default is ``null``, which means that no
option is the default one.
@@ -214,10 +219,6 @@ provide a callback function to dynamically generate suggestions::
$filePath = $helper->ask($input, $output, $question);
}
-.. versionadded:: 4.3
-
- The ``setAutocompleterCallback()`` method was introduced in Symfony 4.3.
-
Do not Trim the Answer
~~~~~~~~~~~~~~~~~~~~~~
@@ -238,9 +239,35 @@ You can also specify if you want to not trim the answer by setting it directly w
$name = $helper->ask($input, $output, $question);
}
-.. versionadded:: 4.4
+Accept Multiline Answers
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 5.2
+
+ The ``setMultiline()`` and ``isMultiline()`` methods were introduced in
+ Symfony 5.2.
+
+By default, the question helper stops reading user input when it receives a newline
+character (i.e., when the user hits ``ENTER`` once). However, you may specify that
+the response to a question should allow multiline answers by passing ``true`` to
+:method:`Symfony\\Component\\Console\\Question\\Question::setMultiline`::
+
+ use Symfony\Component\Console\Question\Question;
+
+ // ...
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ // ...
+ $helper = $this->getHelper('question');
+
+ $question = new Question('How do you solve world peace?');
+ $question->setMultiline(true);
+
+ $answer = $helper->ask($input, $output, $question);
+ }
- The ``setTrimmable()`` method was introduced in Symfony 4.4.
+Multiline questions stop reading user input after receiving an end-of-transmission
+control character (``Ctrl-D`` on Unix systems or ``Ctrl-Z`` on Windows).
Hiding the User's Response
~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/components/console/helpers/table.rst b/components/console/helpers/table.rst
index 0e8df0d1031..7944c795684 100644
--- a/components/console/helpers/table.rst
+++ b/components/console/helpers/table.rst
@@ -265,6 +265,39 @@ Here is a full list of things you can customize:
This method can also be used to override a built-in style.
+.. versionadded:: 5.2
+
+ The option to style table cells was introduced in Symfony 5.2.
+
+In addition to the built-in table styles, you can also apply different styles
+to each table cell via :class:`Symfony\\Component\\Console\\Helper\\TableCellStyle`::
+
+ use Symfony\Component\Console\Helper\Table;
+ use Symfony\Component\Console\Helper\TableCellStyle;
+
+ $table = new Table($output);
+
+ $table->setRows([
+ [
+ '978-0804169127',
+ new TableCell(
+ 'Divine Comedy',
+ [
+ 'style' => new TableCellStyle([
+ 'align' => 'center',
+ 'fg' => 'red',
+ 'bg' => 'green',
+
+ // or
+ 'cellFormat' => '%s',
+ ])
+ ]
+ )
+ ],
+ ]);
+
+ $table->render();
+
Spanning Multiple Columns and Rows
----------------------------------
diff --git a/components/console/single_command_tool.rst b/components/console/single_command_tool.rst
index 457efb48dae..500d679d1e1 100644
--- a/components/console/single_command_tool.rst
+++ b/components/console/single_command_tool.rst
@@ -5,34 +5,36 @@ Building a single Command Application
=====================================
When building a command line tool, you may not need to provide several commands.
-In such case, having to pass the command name each time is tedious. Fortunately,
-it is possible to remove this need by declaring a single command application::
+In such case, having to pass the command name each time is tedious.
+
+.. versionadded:: 5.1
+
+ The :class:`Symfony\\Component\\Console\\SingleCommandApplication` class was
+ introduced in Symfony 5.1.
+
+Fortunately, it is possible to remove this need by declaring a single command
+application::
#!/usr/bin/env php
register('echo')
- ->addArgument('foo', InputArgument::OPTIONAL, 'The directory')
- ->addOption('bar', null, InputOption::VALUE_REQUIRED)
- ->setCode(function(InputInterface $input, OutputInterface $output) {
- // output arguments and options
- })
- ->getApplication()
- ->setDefaultCommand('echo', true) // Single command application
+ use Symfony\Component\Console\SingleCommandApplication;
+
+ (new SingleCommandApplication())
+ ->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) {
+ // output arguments and options
+ })
->run();
-The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` method
-accepts a boolean as second parameter. If true, the command ``echo`` will then
-always be used, without having to pass its name.
-
You can still register a command as usual::
#!/usr/bin/env php
@@ -49,3 +51,7 @@ You can still register a command as usual::
$application->setDefaultCommand($command->getName(), true);
$application->run();
+
+The :method:`Symfony\\Component\\Console\\Application::setDefaultCommand` method
+accepts a boolean as second parameter. If true, the command ``echo`` will then
+always be used, without having to pass its name.
diff --git a/components/css_selector.rst b/components/css_selector.rst
index c8100793ab4..df9ddd84487 100644
--- a/components/css_selector.rst
+++ b/components/css_selector.rst
@@ -98,10 +98,6 @@ Pseudo-classes are partially supported:
``li:first-of-type``) but not with the ``*`` selector).
* Supported: ``*:only-of-type``.
-.. versionadded:: 4.4
-
- The support for ``*:only-of-type`` was introduced in Symfony 4.4.
-
Learn more
----------
diff --git a/components/dependency_injection.rst b/components/dependency_injection.rst
index 486f89e599d..b303e96d484 100644
--- a/components/dependency_injection.rst
+++ b/components/dependency_injection.rst
@@ -299,15 +299,16 @@ config files:
$services = $configurator->services();
$services->set('mailer', 'Mailer')
- ->args(['%mailer.transport%'])
+ // the param() method was introduced in Symfony 5.2.
+ ->args([param('mailer.transport')])
;
$services->set('newsletter_manager', 'NewsletterManager')
- ->call('setMailer', [ref('mailer')])
+ // In versions earlier to Symfony 5.1 the service() function was called ref()
+ ->call('setMailer', [service('mailer')])
;
};
-
Learn More
----------
diff --git a/components/dom_crawler.rst b/components/dom_crawler.rst
index 1363d977a3a..7f2b28ad582 100644
--- a/components/dom_crawler.rst
+++ b/components/dom_crawler.rst
@@ -77,10 +77,6 @@ tree.
The DomCrawler component will use it automatically when the content has
an HTML5 doctype.
- .. versionadded:: 4.3
-
- The automatic support of the html5-php library was introduced in Symfony 4.3.
-
Node Filtering
~~~~~~~~~~~~~~
@@ -170,10 +166,6 @@ Verify if the current node matches a selector::
$crawler->matches('p.lorem');
-.. versionadded:: 4.4
-
- The ``matches()`` method was introduced in Symfony 4.4.
-
Node Traversing
~~~~~~~~~~~~~~~
@@ -195,10 +187,14 @@ Get the same level nodes after or before the current selection::
$crawler->filter('body > p')->nextAll();
$crawler->filter('body > p')->previousAll();
-Get all the child or parent nodes::
+Get all the child or ancestor nodes::
$crawler->filter('body')->children();
- $crawler->filter('body > p')->parents();
+ $crawler->filter('body > p')->ancestors();
+
+.. versionadded:: 5.3
+
+ The ``ancestors()`` method was introduced in Symfony 5.3.
Get all the direct child nodes matching a CSS selector::
@@ -208,10 +204,6 @@ Get the first parent (heading toward the document root) of the element that matc
$crawler->closest('p.lorem');
-.. versionadded:: 4.4
-
- The ``closest()`` method was introduced in Symfony 4.4.
-
.. note::
All the traversal methods return a new :class:`Symfony\\Component\\DomCrawler\\Crawler`
@@ -233,17 +225,10 @@ Access the value of the first node of the current selection::
// avoid the exception passing an argument that text() returns when node does not exist
$message = $crawler->filterXPath('//body/p')->text('Default text content');
- // pass TRUE as the second argument of text() to remove all extra white spaces, including
- // the internal ones (e.g. " foo\n bar baz \n " is returned as "foo bar baz")
- $crawler->filterXPath('//body/p')->text('Default text content', true);
-
-.. versionadded:: 4.3
-
- The default argument of ``text()`` was introduced in Symfony 4.3.
-
-.. versionadded:: 4.4
-
- The option to trim white spaces in ``text()`` was introduced in Symfony 4.4.
+ // by default, text() trims white spaces, including the internal ones
+ // (e.g. " foo\n bar baz \n " is returned as "foo bar baz")
+ // pass FALSE as the second argument to return the original text unchanged
+ $crawler->filterXPath('//body/p')->text('Default text content', false);
Access the attribute value of the first node of the current selection::
@@ -261,10 +246,6 @@ Extract attribute and/or node values from the list of nodes::
Special attribute ``_text`` represents a node value, while ``_name``
represents the element name (the HTML tag name).
- .. versionadded:: 4.3
-
- The special attribute ``_name`` was introduced in Symfony 4.3.
-
Call an anonymous function on each node of the list::
use Symfony\Component\DomCrawler\Crawler;
@@ -358,19 +339,11 @@ and :phpclass:`DOMNode` objects::
// avoid the exception passing an argument that html() returns when node does not exist
$html = $crawler->html('Default HTML content');
- .. versionadded:: 4.3
-
- The default argument of ``html()`` was introduced in Symfony 4.3.
-
Or you can get the outer HTML of the first node using
:method:`Symfony\\Component\\DomCrawler\\Crawler::outerHtml`::
$html = $crawler->outerHtml();
- .. versionadded:: 4.4
-
- The ``outerHtml()`` method was introduced in Symfony 4.4.
-
Expression Evaluation
~~~~~~~~~~~~~~~~~~~~~
@@ -522,10 +495,6 @@ useful methods for working with forms::
$method = $form->getMethod();
$name = $form->getName();
-.. versionadded:: 4.4
-
- The ``getName()`` method was introduced in Symfony 4.4.
-
The :method:`Symfony\\Component\\DomCrawler\\Form::getUri` method does more
than just return the ``action`` attribute of the form. If the form method
is GET, then it mimics the browser's behavior and returns the ``action``
@@ -657,6 +626,23 @@ the whole form or specific field(s)::
$form->disableValidation();
$form['country']->select('Invalid value');
+Resolving a URI
+~~~~~~~~~~~~~~~
+
+.. versionadded:: 5.1
+
+ The :class:`Symfony\\Component\\DomCrawler\\UriResolver` helper class was added in Symfony 5.1.
+
+The :class:`Symfony\\Component\\DomCrawler\\UriResolver` class takes an URI
+(relative, absolute, fragment, etc.) and turns it into an absolute URI against
+another given base URI::
+
+ use Symfony\Component\DomCrawler\UriResolver;
+
+ UriResolver::resolve('/foo', 'http://localhost/bar/foo/'); // http://localhost/foo
+ UriResolver::resolve('?a=b', 'http://localhost/bar#foo'); // http://localhost/bar?a=b
+ UriResolver::resolve('../../', 'http://localhost/'); // http://localhost/
+
Learn more
----------
diff --git a/components/event_dispatcher.rst b/components/event_dispatcher.rst
index ef0392e6dfc..abbaa3a4ad9 100644
--- a/components/event_dispatcher.rst
+++ b/components/event_dispatcher.rst
@@ -262,14 +262,6 @@ determine which instance is passed.
default values by passing custom values to the constructors of
``RegisterListenersPass`` and ``AddEventAliasesPass``.
-.. versionadded:: 4.3
-
- Aliasing event names is possible since Symfony 4.3.
-
-.. versionadded:: 4.4
-
- The ``AddEventAliasesPass`` class was introduced in Symfony 4.4.
-
.. _event_dispatcher-closures-as-listeners:
.. index::
diff --git a/components/event_dispatcher/traceable_dispatcher.rst b/components/event_dispatcher/traceable_dispatcher.rst
index 87d58023445..33a98a2336b 100644
--- a/components/event_dispatcher/traceable_dispatcher.rst
+++ b/components/event_dispatcher/traceable_dispatcher.rst
@@ -41,10 +41,10 @@ to register event listeners and dispatch events::
$traceableEventDispatcher->dispatch($event, 'event.the_name');
After your application has been processed, you can use the
-:method:`Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface::getCalledListeners`
+:method:`Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher::getCalledListeners`
method to retrieve an array of event listeners that have been called in
your application. Similarly, the
-:method:`Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcherInterface::getNotCalledListeners`
+:method:`Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher::getNotCalledListeners`
method returns an array of event listeners that have not been called::
// ...
diff --git a/components/expression_language/syntax.rst b/components/expression_language/syntax.rst
index 5b7ee72ec7a..045451491f5 100644
--- a/components/expression_language/syntax.rst
+++ b/components/expression_language/syntax.rst
@@ -21,10 +21,6 @@ The component supports:
* **null** - ``null``
* **exponential** - also known as scientific (e.g. ``1.99E+3`` or ``1e-2``)
- .. versionadded:: 4.4
-
- The ``exponential`` literal was introduced in Symfony 4.4.
-
.. caution::
A backslash (``\``) must be escaped by 4 backslashes (``\\\\``) in a string
diff --git a/components/filesystem.rst b/components/filesystem.rst
index c0cad3dc9b9..6a9282bfe23 100644
--- a/components/filesystem.rst
+++ b/components/filesystem.rst
@@ -288,6 +288,12 @@ exception on failure::
// returns a path like : /tmp/prefix_wyjgtF
$filesystem->tempnam('/tmp', 'prefix_');
+ // returns a path like : /tmp/prefix_wyjgtF.png
+ $filesystem->tempnam('/tmp', 'prefix_', '.png');
+
+.. versionadded:: 5.1
+
+ The option to set a suffix in ``tempnam()`` was introduced in Symfony 5.1.
``dumpFile``
~~~~~~~~~~~~
diff --git a/components/finder.rst b/components/finder.rst
index f799208558e..c0c5682d19a 100644
--- a/components/finder.rst
+++ b/components/finder.rst
@@ -148,10 +148,6 @@ rules to exclude files and directories from the results with the
// excludes files/directories matching the .gitignore patterns
$finder->ignoreVCSIgnored(true);
-.. versionadded:: 4.3
-
- The ``ignoreVCSIgnored()`` method was introduced in Symfony 4.3.
-
File Name
~~~~~~~~~
@@ -248,11 +244,6 @@ Multiple paths can be excluded by chaining calls or passing an array::
// same as above
$finder->notPath(['first/dir', 'other/dir']);
-.. versionadded:: 4.2
-
- Support for passing arrays to ``notPath()`` was introduced in Symfony
- 4.2
-
File Size
~~~~~~~~~
diff --git a/components/http_foundation.rst b/components/http_foundation.rst
index 980eb597c3b..e00f9dabb7b 100644
--- a/components/http_foundation.rst
+++ b/components/http_foundation.rst
@@ -83,17 +83,19 @@ can be accessed via several public properties:
Each property is a :class:`Symfony\\Component\\HttpFoundation\\ParameterBag`
instance (or a sub-class of), which is a data holder class:
-* ``request``: :class:`Symfony\\Component\\HttpFoundation\\ParameterBag`;
+* ``request``: :class:`Symfony\\Component\\HttpFoundation\\ParameterBag` or
+ :class:`Symfony\\Component\\HttpFoundation\\InputBag` if the data is
+ coming from ``$_POST`` parameters;
-* ``query``: :class:`Symfony\\Component\\HttpFoundation\\ParameterBag`;
+* ``query``: :class:`Symfony\\Component\\HttpFoundation\\InputBag`;
-* ``cookies``: :class:`Symfony\\Component\\HttpFoundation\\ParameterBag`;
+* ``cookies``: :class:`Symfony\\Component\\HttpFoundation\\InputBag`;
* ``attributes``: :class:`Symfony\\Component\\HttpFoundation\\ParameterBag`;
-* ``files``: :class:`Symfony\\Component\\HttpFoundation\\FileBag`;
+* ``files``: :class:`Symfony\\Component\\HttpFoundation\\FileBag`;
-* ``server``: :class:`Symfony\\Component\\HttpFoundation\\ServerBag`;
+* ``server``: :class:`Symfony\\Component\\HttpFoundation\\ServerBag`;
* ``headers``: :class:`Symfony\\Component\\HttpFoundation\\HeaderBag`.
@@ -188,9 +190,18 @@ Finally, the raw data sent with the request body can be accessed using
$content = $request->getContent();
-For instance, this may be useful to process a JSON string sent to the
+For instance, this may be useful to process a XML string sent to the
application by a remote service using the HTTP POST method.
+If the request body is a JSON string, it can be accessed using
+:method:`Symfony\\Component\\HttpFoundation\\Request::toArray`::
+
+ $data = $request->toArray();
+
+.. versionadded:: 5.2
+
+ The ``toArray()`` method was introduced in Symfony 5.2.
+
Identifying a Request
~~~~~~~~~~~~~~~~~~~~~
@@ -236,7 +247,8 @@ Accessing the Session
~~~~~~~~~~~~~~~~~~~~~
If you have a session attached to the request, you can access it via the
-:method:`Symfony\\Component\\HttpFoundation\\Request::getSession` method;
+:method:`Symfony\\Component\\HttpFoundation\\Request::getSession` method or the
+:method:`Symfony\\Component\\HttpFoundation\\RequestStack::getSession` method;
the
:method:`Symfony\\Component\\HttpFoundation\\Request::hasPreviousSession`
method tells you if the request contains a session which was started in one of
@@ -272,6 +284,14 @@ this complexity and defines some methods for the most common tasks::
HeaderUtils::unquote('"foo \"bar\""');
// => 'foo "bar"'
+ // Parses a query string but maintains dots (PHP parse_str() replaces '.' by '_')
+ HeaderUtils::parseQuery('foo[bar.baz]=qux');
+ // => ['foo' => ['bar.baz' => 'qux']]
+
+.. versionadded:: 5.2
+
+ The ``parseQuery()`` method was introduced in Symfony 5.2.
+
Accessing ``Accept-*`` Headers Data
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -319,10 +339,6 @@ are also supported::
Anonymizing IP Addresses
~~~~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 4.4
-
- The ``anonymize()`` method was introduced in Symfony 4.4.
-
An increasingly common need for applications to comply with user protection
regulations is to anonymize IP addresses before logging and storing them for
analysis purposes. Use the ``anonymize()`` method from the
@@ -447,9 +463,21 @@ method takes an instance of
You can clear a cookie via the
:method:`Symfony\\Component\\HttpFoundation\\ResponseHeaderBag::clearCookie` method.
-Note you can create a
-:class:`Symfony\\Component\\HttpFoundation\\Cookie` object from a raw header
-value using :method:`Symfony\\Component\\HttpFoundation\\Cookie::fromString`.
+In addition to the ``Cookie::create()`` method, you can create a ``Cookie``
+object from a raw header value using :method:`Symfony\\Component\\HttpFoundation\\Cookie::fromString`
+method. You can also use the ``with*()`` methods to change some Cookie property (or
+to build the entire Cookie using a fluent interface). Each ``with*()`` method returns
+a new object with the modified property::
+
+ $cookie = Cookie::create('foo')
+ ->withValue('bar')
+ ->withExpires(strtotime('Fri, 20-May-2011 15:25:52 GMT'))
+ ->withDomain('.example.com')
+ ->withSecure(true);
+
+.. versionadded:: 5.1
+
+ The ``with*()`` methods were introduced in Symfony 5.1.
Managing the HTTP Cache
~~~~~~~~~~~~~~~~~~~~~~~
@@ -481,15 +509,25 @@ can be used to set the most commonly used cache information in one method
call::
$response->setCache([
- 'etag' => 'abcdef',
- 'last_modified' => new \DateTime(),
- 'max_age' => 600,
- 's_maxage' => 600,
- 'private' => false,
- 'public' => true,
- 'immutable' => true,
+ 'must_revalidate' => false,
+ 'no_cache' => false,
+ 'no_store' => false,
+ 'no_transform' => false,
+ 'public' => true,
+ 'private' => false,
+ 'proxy_revalidate' => false,
+ 'max_age' => 600,
+ 's_maxage' => 600,
+ 'immutable' => true,
+ 'last_modified' => new \DateTime(),
+ 'etag' => 'abcdef'
]);
+.. versionadded:: 5.1
+
+ The ``must_revalidate``, ``no_cache``, ``no_store``, ``no_transform`` and
+ ``proxy_revalidate`` directives were introduced in Symfony 5.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`
@@ -710,6 +748,37 @@ Session
The session information is in its own document: :doc:`/components/http_foundation/sessions`.
+Safe Content Preference
+-----------------------
+
+Some web sites have a "safe" mode to assist those who don't want to be exposed
+to content to which they might object. The `RFC 8674`_ specification defines a
+way for user agents to ask for safe content to a server.
+
+The specification does not define what content might be considered objectionable,
+so the concept of "safe" is not precisely defined. Rather, the term is interpreted
+by the server and within the scope of each web site that chooses to act upon this information.
+
+Symfony offers two methods to interact with this preference:
+
+* :method:`Symfony\\Component\\HttpFoundation\\Request::preferSafeContent`;
+* :method:`Symfony\\Component\\HttpFoundation\\Response::setContentSafe`;
+
+.. versionadded:: 5.1
+
+ The ``preferSafeContent()`` and ``setContentSafe()`` methods were introduced
+ in Symfony 5.1.
+
+The following example shows how to detect if the user agent prefers "safe" content::
+
+ if ($request->preferSafeContent()) {
+ $response = new Response($alternativeContent);
+ // this informs the user we respected their preferences
+ $response->setContentSafe();
+
+ return $response;
+ }
+
Learn More
----------
@@ -727,3 +796,4 @@ Learn More
.. _Apache: https://tn123.org/mod_xsendfile/
.. _`JSON Hijacking`: https://haacked.com/archive/2009/06/25/json-hijacking.aspx/
.. _OWASP guidelines: https://cheatsheetseries.owasp.org/cheatsheets/AJAX_Security_Cheat_Sheet.html#always-return-json-with-an-object-on-the-outside
+.. _RFC 8674: https://tools.ietf.org/html/rfc8674
diff --git a/components/http_foundation/sessions.rst b/components/http_foundation/sessions.rst
index 9c9479e3e5e..5756a38fc58 100644
--- a/components/http_foundation/sessions.rst
+++ b/components/http_foundation/sessions.rst
@@ -169,6 +169,11 @@ and "Remember Me" login settings or other user based state information.
:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\NamespacedAttributeBag`
This implementation allows for attributes to be stored in a structured namespace.
+ .. deprecated:: 5.3
+
+ The ``NamespacedAttributeBag`` class is deprecated since Symfony 5.3.
+ If you need this feature, you will have to implement the class yourself.
+
:class:`Symfony\\Component\\HttpFoundation\\Session\\Attribute\\AttributeBagInterface`
has the API
@@ -237,6 +242,11 @@ So any processing of this might quickly get ugly, even adding a token to the arr
$tokens['c'] = $value;
$session->set('tokens', $tokens);
+.. deprecated:: 5.3
+
+ The ``NamespacedAttributeBag`` class is deprecated since Symfony 5.3.
+ If you need this feature, you will have to implement the class yourself.
+
With structured namespacing, the key can be translated to the array
structure like this using a namespace character (which defaults to ``/``)::
diff --git a/components/http_kernel.rst b/components/http_kernel.rst
index 9a1d5a31049..c0da0fd6cfa 100644
--- a/components/http_kernel.rst
+++ b/components/http_kernel.rst
@@ -65,8 +65,8 @@ that system::
*/
public function handle(
Request $request,
- $type = self::MASTER_REQUEST,
- $catch = true
+ int $type = self::MASTER_REQUEST,
+ bool $catch = true
);
}
@@ -530,22 +530,10 @@ object, which you can use to access the original exception via the
method. A typical listener on this event will check for a certain type of
exception and create an appropriate error ``Response``.
-.. versionadded:: 4.4
-
- The :method:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent::getThrowable` and
- :method:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent::setThrowable` methods
- were introduced in Symfony 4.4.
-
-.. deprecated:: 4.4
-
- The :method:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent::getException` and
- :method:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent::setException` methods
- are deprecated since Symfony 4.4.
-
For example, to generate a 404 page, you might throw a special type of exception
and then add a listener on this event that looks for this exception and
creates and returns a 404 ``Response``. In fact, the HttpKernel component
-comes with an :class:`Symfony\\Component\\HttpKernel\\EventListener\\ExceptionListener`,
+comes with an :class:`Symfony\\Component\\HttpKernel\\EventListener\\ErrorListener`,
which if you choose to use, will do this and more by default (see the sidebar
below for more details).
@@ -559,10 +547,10 @@ below for more details).
There are two main listeners to ``kernel.exception`` when using the
Symfony Framework.
- **ExceptionListener in the HttpKernel Component**
+ **ErrorListener in the HttpKernel Component**
The first comes core to the HttpKernel component
- and is called :class:`Symfony\\Component\\HttpKernel\\EventListener\\ExceptionListener`.
+ and is called :class:`Symfony\\Component\\HttpKernel\\EventListener\\ErrorListener`.
The listener has several goals:
1) The thrown exception is converted into a
@@ -627,19 +615,6 @@ kernel.terminate ``KernelEvents::TERMINATE`` :class:`Sym
kernel.exception ``KernelEvents::EXCEPTION`` :class:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent`
=========================== ====================================== ========================================================================
-.. deprecated:: 4.3
-
- Since Symfony 4.3, most of the event classes were renamed.
- The following old classes were deprecated:
-
- * `GetResponseEvent` renamed to :class:`Symfony\\Component\\HttpKernel\\Event\\RequestEvent`
- * `FilterControllerEvent` renamed to :class:`Symfony\\Component\\HttpKernel\\Event\\ControllerEvent`
- * `FilterControllerArgumentsEvent` renamed to :class:`Symfony\\Component\\HttpKernel\\Event\\ControllerArgumentsEvent`
- * `GetResponseForControllerResultEvent` renamed to :class:`Symfony\\Component\\HttpKernel\\Event\\ViewEvent`
- * `FilterResponseEvent` renamed to :class:`Symfony\\Component\\HttpKernel\\Event\\ResponseEvent`
- * `PostResponseEvent` renamed to :class:`Symfony\\Component\\HttpKernel\\Event\\TerminateEvent`
- * `GetResponseForExceptionEvent` renamed to :class:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent`
-
.. _http-kernel-working-example:
A full Working Example
diff --git a/components/inflector.rst b/components/inflector.rst
index 5e9f4325884..c42d6ebaeaa 100644
--- a/components/inflector.rst
+++ b/components/inflector.rst
@@ -5,60 +5,8 @@
The Inflector Component
=======================
- The Inflector component converts English words between their singular and
- plural forms.
+.. deprecated:: 5.1
-Installation
-------------
-
-.. code-block:: terminal
-
- $ composer require symfony/inflector
-
-.. include:: /components/require_autoload.rst.inc
-
-When you May Need an Inflector
-------------------------------
-
-In some scenarios such as code generation and code introspection, it's usually
-required to convert words from/to singular/plural. For example, if you need to
-know which property is associated with an *adder* method, you must convert from
-plural to singular (``addStories()`` method -> ``$story`` property).
-
-Although most human languages define simple pluralization rules, they also
-define lots of exceptions. For example, the general rule in English is to add an
-``s`` at the end of the word (``book`` -> ``books``) but there are lots of
-exceptions even for common words (``woman`` -> ``women``, ``life`` -> ``lives``,
-``news`` -> ``news``, ``radius`` -> ``radii``, etc.)
-
-This component abstracts all those pluralization rules so you can convert
-from/to singular/plural with confidence. However, due to the complexity of the
-human languages, this component only provides support for the English language.
-
-Usage
------
-
-The Inflector component provides two static methods to convert from/to
-singular/plural::
-
- use Symfony\Component\Inflector\Inflector;
-
- Inflector::singularize('alumni'); // 'alumnus'
- Inflector::singularize('knives'); // 'knife'
- Inflector::singularize('mice'); // 'mouse'
-
- Inflector::pluralize('grandchild'); // 'grandchildren'
- Inflector::pluralize('news'); // 'news'
- Inflector::pluralize('bacterium'); // 'bacteria'
-
-Sometimes it's not possible to determine a unique singular/plural form for the
-given word. In those cases, the methods return an array with all the possible
-forms::
-
- use Symfony\Component\Inflector\Inflector;
-
- Inflector::singularize('indices'); // ['index', 'indix', 'indice']
- Inflector::singularize('leaves'); // ['leaf', 'leave', 'leaff']
-
- Inflector::pluralize('matrix'); // ['matricies', 'matrixes']
- Inflector::pluralize('person'); // ['persons', 'people']
+ The Inflector component was deprecated in Symfony 5.1 and its code was moved
+ into the :doc:`String ` component.
+ :ref:`Read the new Inflector docs `.
diff --git a/components/intl.rst b/components/intl.rst
index 6417489b14e..cb120034615 100644
--- a/components/intl.rst
+++ b/components/intl.rst
@@ -120,14 +120,6 @@ You may convert codes between two-letter alpha2 and three-letter alpha3 codes::
$alpha2Code = Languages::getAlpha2Code($alpha3Code);
-.. versionadded:: 4.3
-
- The ``Languages`` class was introduced in Symfony 4.3.
-
-.. versionadded:: 4.4
-
- The full support for alpha3 codes was introduced in Symfony 4.4.
-
The ``Scripts`` class provides access to the optional four-letter script code
that can follow the language code according to the `Unicode ISO 15924 Registry`_
(e.g. ``HANS`` in ``zh_HANS`` for simplified Chinese and ``HANT`` in ``zh_HANT``
@@ -159,10 +151,6 @@ to catching the exception, you can also check if a given script code is valid::
$isValidScript = Scripts::exists($scriptCode);
-.. versionadded:: 4.3
-
- The ``Scripts`` class was introduced in Symfony 4.3.
-
Country Names
~~~~~~~~~~~~~
@@ -219,14 +207,6 @@ You may convert codes between two-letter alpha2 and three-letter alpha3 codes::
$alpha2Code = Countries::getAlpha2Code($alpha3Code);
-.. versionadded:: 4.3
-
- The ``Countries`` class was introduced in Symfony 4.3.
-
-.. versionadded:: 4.4
-
- The support for alpha3 codes was introduced in Symfony 4.4.
-
Locales
~~~~~~~
@@ -262,10 +242,6 @@ to catching the exception, you can also check if a given locale code is valid::
$isValidLocale = Locales::exists($localeCode);
-.. versionadded:: 4.3
-
- The ``Locales`` class was introduced in Symfony 4.3.
-
Currencies
~~~~~~~~~~
@@ -308,10 +284,6 @@ to catching the exception, you can also check if a given currency code is valid:
$isValidCurrency = Currencies::exists($currencyCode);
-.. versionadded:: 4.3
-
- The ``Currencies`` class was introduced in Symfony 4.3.
-
.. _component-intl-timezones:
Timezones
@@ -390,10 +362,6 @@ to catching the exception, you can also check if a given timezone ID is valid::
$isValidTimezone = Timezones::exists($timezoneId);
-.. versionadded:: 4.3
-
- The ``Timezones`` class was introduced in Symfony 4.3.
-
Learn more
----------
diff --git a/components/ldap.rst b/components/ldap.rst
index d7cb6ed17cd..89fb39cb8e8 100644
--- a/components/ldap.rst
+++ b/components/ldap.rst
@@ -142,6 +142,9 @@ delete existing ones::
$phoneNumber = $entry->getAttribute('phoneNumber');
$isContractor = $entry->hasAttribute('contractorCompany');
+ // attribute names in getAttribute() and hasAttribute() methods are case-sensitive
+ // pass FALSE as the second method argument to make them case-insensitive
+ $isContractor = $entry->hasAttribute('contractorCompany', false);
$entry->setAttribute('email', ['fabpot@symfony.com']);
$entryManager->update($entry);
@@ -153,6 +156,11 @@ delete existing ones::
// Removing an existing entry
$entryManager->remove(new Entry('cn=Test User,dc=symfony,dc=com'));
+.. versionadded:: 5.3
+
+ The option to make attribute names case-insensitive in ``getAttribute()``
+ and ``hasAttribute()`` was introduce in Symfony 5.3.
+
Batch Updating
______________
diff --git a/components/lock.rst b/components/lock.rst
index 16049101e10..f5087d12e85 100644
--- a/components/lock.rst
+++ b/components/lock.rst
@@ -36,11 +36,6 @@ which in turn requires another class to manage the storage of locks::
$store = new SemaphoreStore();
$factory = new LockFactory($store);
-.. versionadded:: 4.4
-
- The ``Symfony\Component\Lock\LockFactory`` class was introduced in Symfony
- 4.4. In previous versions it was called ``Symfony\Component\Lock\Factory``.
-
The lock is created by calling the :method:`Symfony\\Component\\Lock\\LockFactory::createLock`
method. Its first argument is an arbitrary string that represents the locked
resource. Then, a call to the :method:`Symfony\\Component\\Lock\\LockInterface::acquire`
@@ -124,6 +119,18 @@ they can be decorated with the ``RetryTillSaveStore`` class::
$lock = $factory->createLock('notification-flush');
$lock->acquire(true);
+When the provided store does not implement the
+:class:`Symfony\\Component\\Lock\\BlockingStoreInterface` interface, the
+``Lock`` class will retry to acquire the lock in a non-blocking way until the
+lock is acquired.
+
+.. deprecated:: 5.2
+
+ As of Symfony 5.2, you don't need to use the ``RetryTillSaveStore`` class
+ anymore. The ``Lock`` class now provides the default logic to acquire locks
+ in blocking mode when the store does not implement the
+ ``BlockingStoreInterface`` interface.
+
Expiring Locks
--------------
@@ -199,6 +206,63 @@ This component also provides two useful methods related to expiring locks:
``getRemainingLifetime()`` (which returns ``null`` or a ``float``
as seconds) and ``isExpired()`` (which returns a boolean).
+Shared Locks
+------------
+
+.. versionadded:: 5.2
+
+ Shared locks (and the associated ``acquireRead()`` method and
+ ``SharedLockStoreInterface``) were introduced in Symfony 5.2.
+
+A shared or `readers–writer lock`_ is a synchronization primitive that allows
+concurrent access for read-only operations, while write operations require
+exclusive access. This means that multiple threads can read the data in parallel
+but an exclusive lock is needed for writing or modifying data. They are used for
+example for data structures that cannot be updated atomically and are invalid
+until the update is complete.
+
+Use the :method:`Symfony\\Component\\Lock\\SharedLockInterface::acquireRead` method
+to acquire a read-only lock, and the existing
+:method:`Symfony\\Component\\Lock\\LockInterface::acquire` method to acquire a
+write lock::
+
+ $lock = $factory->createLock('user'.$user->id);
+ if (!$lock->acquireRead()) {
+ return;
+ }
+
+Similar to the ``acquire()`` method, pass ``true`` as the argument of ``acquireRead()``
+to acquire the lock in a blocking mode::
+
+ $lock = $factory->createLock('user'.$user->id);
+ $lock->acquireRead(true);
+
+.. note::
+
+ The `priority policy`_ of Symfony's shared locks depends on the underlying
+ store (e.g. Redis store prioritizes readers vs writers).
+
+When a read-only lock is acquired with the method ``acquireRead()``, it's
+possible to **promote** the lock, and change it to write lock, by calling the
+``acquire()`` method::
+
+ $lock = $factory->createLock('user'.$userId);
+ $lock->acquireRead(true);
+
+ if (!$this->shouldUpdate($userId)) {
+ return;
+ }
+
+ $lock->acquire(true); // Promote the lock to write lock
+ $this->update($userId);
+
+In the same way, it's possible to **demote** a write lock, and change it to a
+read-only lock by calling the ``acquireRead()`` method.
+
+When the provided store does not implement the
+:class:`Symfony\\Component\\Lock\\SharedLockStoreInterface` interface, the
+``Lock`` class will fallback to a write lock by calling the ``acquire()`` method.
+
The Owner of The Lock
---------------------
@@ -253,22 +317,18 @@ Locks are created and managed in ``Stores``, which are classes that implement
The component includes the following built-in store types:
-============================================ ====== ======== ========
-Store Scope Blocking Expiring
-============================================ ====== ======== ========
-:ref:`FlockStore ` local yes no
-:ref:`MemcachedStore ` remote no yes
-:ref:`PdoStore ` remote no yes
-:ref:`RedisStore ` remote no yes
-:ref:`SemaphoreStore ` local yes no
-:ref:`ZookeeperStore ` remote no no
-============================================ ====== ======== ========
-
-.. versionadded:: 4.4
-
- The ``PersistingStoreInterface`` and ``BlockingStoreInterface`` interfaces were
- introduced in Symfony 4.4. In previous versions there was only one interface
- called ``Symfony\Component\Lock\StoreInterface``.
+============================================ ====== ======== ======== =======
+Store Scope Blocking Expiring Sharing
+============================================ ====== ======== ======== =======
+:ref:`FlockStore ` local yes no yes
+:ref:`MemcachedStore ` remote no yes no
+:ref:`MongoDbStore ` remote no yes no
+:ref:`PdoStore ` remote no yes no
+:ref:`PostgreSqlStore ` remote yes yes yes
+:ref:`RedisStore ` remote no yes yes
+:ref:`SemaphoreStore ` local yes no no
+:ref:`ZookeeperStore ` remote no no no
+============================================ ====== ======== ======== =======
.. _lock-store-flock:
@@ -312,6 +372,67 @@ support blocking, and expects a TTL to avoid stalled locks::
Memcached does not support TTL lower than 1 second.
+.. _lock-store-mongodb:
+
+MongoDbStore
+~~~~~~~~~~~~
+
+.. versionadded:: 5.1
+
+ The ``MongoDbStore`` was introduced in Symfony 5.1.
+
+The MongoDbStore saves locks on a MongoDB server ``>=2.2``, it requires a
+``\MongoDB\Collection`` or ``\MongoDB\Client`` from `mongodb/mongodb`_ or a
+`MongoDB Connection String`_.
+This store does not support blocking and expects a TTL to
+avoid stalled locks::
+
+ use Symfony\Component\Lock\Store\MongoDbStore;
+
+ $mongo = 'mongodb://localhost/database?collection=lock';
+ $options = [
+ 'gcProbablity' => 0.001,
+ 'database' => 'myapp',
+ 'collection' => 'lock',
+ 'uriOptions' => [],
+ 'driverOptions' => [],
+ ];
+ $store = new MongoDbStore($mongo, $options);
+
+The ``MongoDbStore`` takes the following ``$options`` (depending on the first parameter type):
+
+============= ================================================================================================
+Option Description
+============= ================================================================================================
+gcProbablity 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:
+
+``MongoDB\Collection``:
+
+- ``$options['database']`` is ignored
+- ``$options['collection']`` is ignored
+
+``MongoDB\Client``:
+
+- ``$options['database']`` is mandatory
+- ``$options['collection']`` is mandatory
+
+MongoDB Connection String:
+
+- ``$options['database']`` is used otherwise ``/path`` from the DSN, at least one is mandatory
+- ``$options['collection']`` is used otherwise ``?collection=`` from the DSN, at least one is mandatory
+
+.. note::
+
+ The ``collection`` querystring parameter is not part of the `MongoDB Connection String`_ definition.
+ It is used to allow constructing a ``MongoDbStore`` using a `Data Source Name (DSN)`_ without ``$options``.
+
.. _lock-store-pdo:
PdoStore
@@ -324,32 +445,41 @@ support blocking, and expects a TTL to avoid stalled locks::
use Symfony\Component\Lock\Store\PdoStore;
// a PDO, a Doctrine DBAL connection or DSN for lazy connecting through PDO
- $databaseConnectionOrDSN = 'mysql:host=127.0.0.1;dbname=lock';
+ $databaseConnectionOrDSN = 'mysql:host=127.0.0.1;dbname=app';
$store = new PdoStore($databaseConnectionOrDSN, ['db_username' => 'myuser', 'db_password' => 'mypassword']);
.. note::
This store does not support TTL lower than 1 second.
-Before storing locks in the database, you must create the table that stores
-the information. The store provides a method called
-:method:`Symfony\\Component\\Lock\\Store\\PdoStore::createTable`
-to set up this table for you according to the database engine used::
+The table where values are stored is created automatically on the first call to
+the :method:`Symfony\\Component\\Lock\\Store\\PdoStore::save` method.
+You can also create this table explicitly by calling the
+:method:`Symfony\\Component\\Lock\\Store\\PdoStore::createTable` method in
+your code.
- try {
- $store->createTable();
- } catch (\PDOException $exception) {
- // the table could not be created for some reason
- }
+.. _lock-store-pgsql:
-A great way to set up the table in production is to call the ``createTable()``
-method in your local computer and then generate a
-:ref:`database migration `:
+PostgreSqlStore
+~~~~~~~~~~~~~~~
-.. code-block:: terminal
+The PostgreSqlStore uses `Advisory Locks`_ provided by PostgreSQL. It requires a
+`PDO`_ connection, a `Doctrine DBAL Connection`_, or a
+`Data Source Name (DSN)`_. It supports native blocking, as well as sharing
+locks::
- $ php bin/console doctrine:migrations:diff
- $ php bin/console doctrine:migrations:migrate
+ use Symfony\Component\Lock\Store\PostgreSqlStore;
+
+ // a PDO, a Doctrine DBAL connection or DSN for lazy connecting through PDO
+ $databaseConnectionOrDSN = 'postgresql://myuser:mypassword@localhost:5634/lock';
+ $store = new PostgreSqlStore($databaseConnectionOrDSN);
+
+In opposite to the ``PdoStore``, the ``PostgreSqlStore`` does not need a table to
+store locks and does not expire.
+
+.. versionadded:: 5.2
+
+ The ``PostgreSqlStore`` was introduced in Symfony 5.2.
.. _lock-store-redis:
@@ -448,7 +578,9 @@ Remote Stores
~~~~~~~~~~~~~
Remote stores (:ref:`MemcachedStore `,
+:ref:`MongoDbStore `,
:ref:`PdoStore `,
+:ref:`PostgreSqlStore `,
:ref:`RedisStore ` and
:ref:`ZookeeperStore `) use a unique token to recognize
the true owner of the lock. This token is stored in the
@@ -473,6 +605,7 @@ Expiring Stores
~~~~~~~~~~~~~~~
Expiring stores (:ref:`MemcachedStore `,
+:ref:`MongoDbStore `,
:ref:`PdoStore ` and
:ref:`RedisStore `)
guarantee that the lock is acquired only for the defined duration of time. If
@@ -593,6 +726,47 @@ method uses the Memcached's ``flush()`` method which purges and removes everythi
The method ``flush()`` must not be called, or locks should be stored in a
dedicated Memcached service away from Cache.
+MongoDbStore
+~~~~~~~~~~~~
+
+.. caution::
+
+ The locked resource name is indexed in the ``_id`` field of the lock
+ collection. Beware that in MongoDB an indexed field's value can be
+ `a maximum of 1024 bytes in length`_ inclusive of structural overhead.
+
+A TTL index must be used to automatically clean up expired locks.
+Such an index can be created manually:
+
+.. code-block:: javascript
+
+ db.lock.ensureIndex(
+ { "expires_at": 1 },
+ { "expireAfterSeconds": 0 }
+ )
+
+Alternatively, the method ``MongoDbStore::createTtlIndex(int $expireAfterSeconds = 0)``
+can be called once to create the TTL index during database setup. Read more
+about `Expire Data from Collections by Setting TTL`_ in MongoDB.
+
+.. tip::
+
+ ``MongoDbStore`` will attempt to automatically create a TTL index.
+ It's recommended to set constructor option ``gcProbablity = 0.0`` to
+ disable this behavior if you have manually dealt with TTL index creation.
+
+.. caution::
+
+ This store relies on all PHP application and database nodes to have
+ synchronized clocks for lock expiry to occur at the correct time. To ensure
+ locks don't expire prematurely; the lock TTL should be set with enough extra
+ time in ``expireAfterSeconds`` to account for any clock drift between nodes.
+
+``writeConcern`` and ``readConcern`` are not specified by MongoDbStore meaning
+the collection's settings will take effect.
+``readPreference`` is ``primary`` for all queries.
+Read more about `Replica Set Read and Write Semantics`_ in MongoDB.
+
PdoStore
~~~~~~~~~~
@@ -617,6 +791,20 @@ have synchronized clocks.
To ensure locks don't expire prematurely; the TTLs should be set with
enough extra time to account for any clock drift between nodes.
+PostgreSqlStore
+~~~~~~~~~~~~~~~
+
+The PdoStore relies on the `Advisory Locks`_ properties of the PostgreSQL
+database. That means that by using :ref:`PostgreSqlStore `
+the locks will be automatically released at the end of the session in case the
+client cannot unlock for any reason.
+
+If the PostgreSQL service or the machine hosting it restarts, every lock would
+be lost without notifying the running processes.
+
+If the TCP connection is lost, the PostgreSQL may release locks without
+notifying the application.
+
RedisStore
~~~~~~~~~~
@@ -719,10 +907,19 @@ instance, during the deployment of a new version. Processes with new
configuration must not be started while old processes with old configuration
are still running.
+.. _`a maximum of 1024 bytes in length`: https://docs.mongodb.com/manual/reference/limits/#Index-Key-Limit
.. _`ACID`: https://en.wikipedia.org/wiki/ACID
+.. _`Advisory Locks`: https://www.postgresql.org/docs/current/explicit-locking.html
+.. _`Data Source Name (DSN)`: https://en.wikipedia.org/wiki/Data_source_name
+.. _`Doctrine DBAL Connection`: https://github.com/doctrine/dbal/blob/master/src/Connection.php
+.. _`Expire Data from Collections by Setting TTL`: https://docs.mongodb.com/manual/tutorial/expire-data/
.. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science)
-.. _`PHP semaphore functions`: https://www.php.net/manual/en/book.sem.php
+.. _`MongoDB Connection String`: https://docs.mongodb.com/manual/reference/connection-string/
+.. _`mongodb/mongodb`: https://packagist.org/packages/mongodb/mongodb
+.. _`MongoDBClient::__construct`: https://docs.mongodb.com/php-library/current/reference/method/MongoDBClient__construct/
.. _`PDO`: https://www.php.net/pdo
-.. _`Doctrine DBAL Connection`: https://github.com/doctrine/dbal/blob/master/lib/Doctrine/DBAL/Connection.php
-.. _`Data Source Name (DSN)`: https://en.wikipedia.org/wiki/Data_source_name
+.. _`PHP semaphore functions`: https://www.php.net/manual/en/book.sem.php
+.. _`Replica Set Read and Write Semantics`: https://docs.mongodb.com/manual/applications/replication/
.. _`ZooKeeper`: https://zookeeper.apache.org/
+.. _`readers–writer lock`: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
+.. _`priority policy`: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock#Priority_policies
diff --git a/components/messenger.rst b/components/messenger.rst
index 0772293eab1..7e1af990db1 100644
--- a/components/messenger.rst
+++ b/components/messenger.rst
@@ -77,15 +77,9 @@ middleware stack. The component comes with a set of middleware that you can use.
When using the message bus with Symfony's FrameworkBundle, the following middleware
are configured for you:
-#. :class:`Symfony\\Component\\Messenger\\Middleware\\LoggingMiddleware` (logs the processing of your messages)
-#. :class:`Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware` (enables asynchronous processing)
+#. :class:`Symfony\\Component\\Messenger\\Middleware\\SendMessageMiddleware` (enables asynchronous processing, logs the processing of your messages if you pass a logger)
#. :class:`Symfony\\Component\\Messenger\\Middleware\\HandleMessageMiddleware` (calls the registered handler(s))
-.. deprecated:: 4.3
-
- The ``LoggingMiddleware`` is deprecated since Symfony 4.3 and will be
- removed in 5.0. Pass a logger to ``SendMessageMiddleware`` instead.
-
Example::
use App\Message\MyMessage;
@@ -332,12 +326,6 @@ do is to write your own CSV receiver::
}
}
-.. versionadded:: 4.3
-
- In Symfony 4.3, the ``ReceiverInterface`` has changed its methods as shown
- in the example above. You may need to update your code if you used this
- interface in previous Symfony versions.
-
Receiver and Sender on the same Bus
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/components/mime.rst b/components/mime.rst
index 95206fa22e3..0ceb3007bc5 100644
--- a/components/mime.rst
+++ b/components/mime.rst
@@ -9,10 +9,6 @@ The Mime Component
The Mime component allows manipulating the MIME messages used to send emails
and provides utilities related to MIME types.
-.. versionadded:: 4.3
-
- The Mime component was introduced in Symfony 4.3.
-
Installation
------------
diff --git a/components/options_resolver.rst b/components/options_resolver.rst
index a88c9f95d31..941d61de6c7 100644
--- a/components/options_resolver.rst
+++ b/components/options_resolver.rst
@@ -440,10 +440,6 @@ This way, the ``$value`` argument will receive the previously normalized
value, otherwise you can prepend the new normalizer by passing ``true`` as
third argument.
-.. versionadded:: 4.3
-
- The ``addNormalizer()`` method was introduced in Symfony 4.3.
-
Default Values that Depend on another Option
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -724,18 +720,31 @@ In same way, parent options can access to the nested options as normal arrays::
Deprecating the Option
~~~~~~~~~~~~~~~~~~~~~~
+.. versionadded:: 5.1
+
+ The signature of the ``setDeprecated()`` method changed from
+ ``setDeprecated(string $option, ?string $message)`` to
+ ``setDeprecated(string $option, string $package, string $version, $message)``
+ in Symfony 5.1.
+
Once an option is outdated or you decided not to maintain it anymore, you can
deprecate it using the :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::setDeprecated`
method::
$resolver
->setDefined(['hostname', 'host'])
- // this outputs the following generic deprecation message:
- // The option "hostname" is deprecated.
- ->setDeprecated('hostname')
- // you can also pass a custom deprecation message
- ->setDeprecated('hostname', 'The option "hostname" is deprecated, use "host" instead.')
+ // this outputs the following generic deprecation message:
+ // Since acme/package 1.2: The option "hostname" is deprecated.
+ ->setDeprecated('hostname', 'acme/package', '1.2')
+
+ // you can also pass a custom deprecation message (%name% placeholder is available)
+ ->setDeprecated(
+ 'hostname',
+ 'acme/package',
+ '1.2',
+ 'The option "hostname" is deprecated, use "host" instead.'
+ )
;
.. note::
@@ -760,7 +769,7 @@ the option::
->setDefault('encryption', null)
->setDefault('port', null)
->setAllowedTypes('port', ['null', 'int'])
- ->setDeprecated('port', function (Options $options, $value) {
+ ->setDeprecated('port', 'acme/package', '1.2', function (Options $options, $value) {
if (null === $value) {
return 'Passing "null" to option "port" is deprecated, pass an integer instead.';
}
@@ -782,6 +791,40 @@ the option::
This closure receives as argument the value of the option after validating it
and before normalizing it when the option is being resolved.
+Chaining Option Configurations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In many cases you may need to define multiple configurations for each option.
+For example, suppose the ``InvoiceMailer`` class has an ``host`` option that is required
+and a ``transport`` option which can be one of ``sendmail``, ``mail`` and ``smtp``.
+You can improve the readability of the code avoiding to duplicate option name for
+each configuration using the :method:`Symfony\\Component\\OptionsResolver\\OptionsResolver::define`
+method::
+
+ // ...
+ class InvoiceMailer
+ {
+ // ...
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ // ...
+ $resolver->define('host')
+ ->required()
+ ->default('smtp.example.org')
+ ->allowedTypes('string')
+ ->info('The IP address or hostname');
+
+ $resolver->define('transport')
+ ->required()
+ ->default('transport')
+ ->allowedValues(['sendmail', 'mail', 'smtp']);
+ }
+ }
+
+.. versionadded:: 5.1
+
+ The ``define()`` and ``info()`` methods were introduced in Symfony 5.1.
+
Performance Tweaks
~~~~~~~~~~~~~~~~~~
diff --git a/components/phpunit_bridge.rst b/components/phpunit_bridge.rst
index 284dae4d816..dedb70c20d3 100644
--- a/components/phpunit_bridge.rst
+++ b/components/phpunit_bridge.rst
@@ -161,18 +161,14 @@ each test suite's results in their own section.
Trigger Deprecation Notices
---------------------------
-Deprecation notices can be triggered by using::
+Deprecation notices can be triggered by using ``trigger_deprecation`` from
+the ``symfony/deprecation-contracts`` package::
- @trigger_error('Your deprecation message', E_USER_DEPRECATED);
+ // indicates something is deprecated since version 1.3 of vendor-name/packagename
+ trigger_deprecation('vendor-name/package-name', '1.3', 'Your deprecation message');
-You can also require the ``symfony/deprecation-contracts`` package that provides
-a global ``trigger_deprecation()`` function for this usage.
-
-Without the `@-silencing operator`_, users would need to opt-out from deprecation
-notices. Silencing by default swaps this behavior and allows users to opt-in
-when they are ready to cope with them (by adding a custom error handler like the
-one provided by this bridge). When not silenced, deprecation notices will appear
-in the **Unsilenced** section of the deprecation report.
+ // you can also use printf format (all arguments after the message will be used)
+ trigger_deprecation('...', '1.3', 'Value "%s" is deprecated, use ... instead.', $value);
Mark Tests as Legacy
--------------------
@@ -293,6 +289,35 @@ Here is a summary that should help you pick the right configuration:
| | cannot afford to use one of the modes above. |
+------------------------+-----------------------------------------------------+
+Baseline Deprecations
+.....................
+
+If your application has some deprecations that you can't fix for some reasons,
+you can tell Symfony to ignore them. The trick is to create a file with the
+allowed deprecations and define it as the "deprecation baseline". Deprecations
+inside that file are ignore but the rest of deprecations are still reported.
+
+First, generate the file with the allowed deprecations (run the same command
+whenever you want to update the existing file):
+
+.. code-block:: terminal
+
+ $ SYMFONY_DEPRECATIONS_HELPER='generateBaseline=true&baselineFile=./tests/allowed.json' ./vendor/bin/simple-phpunit
+
+This command stores all the deprecations reported while running tests in the
+given file path and encoded in JSON.
+
+Then, you can run the following command to use that file and ignore those deprecations:
+
+.. code-block:: terminal
+
+ $ SYMFONY_DEPRECATIONS_HELPER='baselineFile=./tests/allowed.json' ./vendor/bin/simple-phpunit
+
+.. versionadded:: 5.2
+
+ The ``baselineFile`` and ``generateBaseline`` options were introduced in
+ Symfony 5.2.
+
Disabling the Verbose Output
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -300,6 +325,14 @@ By default, the bridge will display a detailed output with the number of
deprecations and where they arise. If this is too much for you, you can use
``SYMFONY_DEPRECATIONS_HELPER=verbose=0`` to turn the verbose output off.
+It's also possible to change verbosity per deprecation type. For example, using
+``quiet[]=indirect&quiet[]=other`` will hide details for deprecations of types
+"indirect" and "other".
+
+.. versionadded:: 5.1
+
+ The ``quiet`` option was introduced in Symfony 5.1.
+
Disabling the Deprecation Helper
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -332,32 +365,70 @@ time. This can be disabled with the ``debug-class-loader`` option.
-.. versionadded:: 4.2
+Compile-time Deprecations
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Use the ``debug:container`` command to list the deprecations generated during
+the compiling and warming up of the container:
+
+.. code-block:: terminal
+
+ $ php bin/console debug:container --deprecations
+
+.. versionadded:: 5.1
+
+ The ``--deprecations`` option was introduced in Symfony 5.1.
+
+Log Deprecations
+~~~~~~~~~~~~~~~~
+
+For turning the verbose output off and write it to a log file instead you can use
+``SYMFONY_DEPRECATIONS_HELPER='logFile=/path/deprecations.log'``.
- The ``DebugClassLoader`` integration was introduced in Symfony 4.2.
+.. versionadded:: 5.3
+
+ The ``logFile`` option was introduced in Symfony 5.3.
Write Assertions about Deprecations
-----------------------------------
When adding deprecations to your code, you might like writing tests that verify
that they are triggered as required. To do so, the bridge provides the
-``@expectedDeprecation`` annotation that you can use on your test methods.
+``expectDeprecation()`` method that you can use on your test methods.
It requires you to pass the expected message, given in the same format as for
the `PHPUnit's assertStringMatchesFormat()`_ method. If you expect more than one
-deprecation message for a given test method, you can use the annotation several
+deprecation message for a given test method, you can use the method several
times (order matters)::
- /**
- * @group legacy
- * @expectedDeprecation This "%s" method is deprecated.
- * @expectedDeprecation The second argument of the "%s" method is deprecated.
- */
- public function testDeprecatedCode()
+ use PHPUnit\Framework\TestCase;
+ use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
+
+ class MyTest extends TestCase
{
- @trigger_error('This "Foo" method is deprecated.', E_USER_DEPRECATED);
- @trigger_error('The second argument of the "Bar" method is deprecated.', E_USER_DEPRECATED);
+ use ExpectDeprecationTrait;
+
+ /**
+ * @group legacy
+ */
+ public function testDeprecatedCode()
+ {
+ // test some code that triggers the following deprecation:
+ // trigger_deprecation('vendor-name/package-name', '5.1', 'This "Foo" method is deprecated.');
+ $this->expectDeprecation('Since vendor-name/package-name 5.1: This "%s" method is deprecated');
+
+ // ...
+
+ // test some code that triggers the following deprecation:
+ // trigger_deprecation('vendor-name/package-name', '4.4', 'The second argument of the "Bar" method is deprecated.');
+ $this->expectDeprecation('Since vendor-name/package-name 4.4: The second argument of the "%s" method is deprecated.');
+ }
}
+.. deprecated:: 5.1
+
+ Symfony versions previous to 5.1 also included a ``@expectedDeprecation``
+ annotation to test deprecations, but it was deprecated in favor of the method.
+
Display the Full Stack Trace
----------------------------
@@ -394,10 +465,6 @@ the test suite cannot use the latest versions of PHPUnit because:
Polyfills for the Unavailable Methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 4.4
-
- This feature was introduced in Symfony 4.4.
-
When using the ``simple-phpunit`` script, PHPUnit Bridge injects polyfills for
most methods of the ``TestCase`` and ``Assert`` classes (e.g. ``expectException()``,
``expectExceptionMessage()``, ``assertContainsEquals()``, etc.). This allows writing
@@ -407,10 +474,6 @@ older PHPUnit versions.
Removing the Void Return Type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 4.4
-
- This feature was introduced in Symfony 4.4.
-
When running the ``simple-phpunit`` script with the ``SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT``
environment variable set to ``1``, the PHPUnit bridge will alter the code of
PHPUnit to remove the return type (introduced in PHPUnit 8) from ``setUp()``,
@@ -446,10 +509,6 @@ call to the ``doSetUp()``, ``doTearDown()``, ``doSetUpBeforeClass()`` and
Using Namespaced PHPUnit Classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 4.4
-
- This feature was introduced in Symfony 4.4.
-
The PHPUnit bridge adds namespaced class aliases for most of the PHPUnit classes
declared without namespaces (e.g. ``PHPUnit_Framework_Assert``), allowing you to
always use the namespaced class declaration even when the test is executed with
@@ -619,20 +678,19 @@ functions:
Use Case
~~~~~~~~
-Consider the following example that uses the ``checkMX`` option of the ``Email``
-constraint to test the validity of the email domain::
+Consider the following example that tests a custom class called ``DomainValidator``
+which defines a ``checkDnsRecord`` option to also validate that a domain is
+associated to a valid host::
+ use App\Validator\DomainValidator;
use PHPUnit\Framework\TestCase;
- use Symfony\Component\Validator\Constraints\Email;
class MyTest extends TestCase
{
public function testEmail()
{
- $validator = ...
- $constraint = new Email(['checkMX' => true]);
-
- $result = $validator->validate('foo@example.com', $constraint);
+ $validator = new DomainValidator(['checkDnsRecord' => true]);
+ $isValid = $validator->validate('example.com');
// ...
}
@@ -642,22 +700,23 @@ In order to avoid making a real network connection, add the ``@dns-sensitive``
annotation to the class and use the ``DnsMock::withMockedHosts()`` to configure
the data you expect to get for the given hosts::
+ use App\Validator\DomainValidator;
use PHPUnit\Framework\TestCase;
- use Symfony\Component\Validator\Constraints\Email;
+ use Symfony\Bridge\PhpUnit\DnsMock;
/**
* @group dns-sensitive
*/
- class MyTest extends TestCase
+ class DomainValidatorTest extends TestCase
{
public function testEmails()
{
- DnsMock::withMockedHosts(['example.com' => [['type' => 'MX']]]);
-
- $validator = ...
- $constraint = new Email(['checkMX' => true]);
+ DnsMock::withMockedHosts([
+ 'example.com' => [['type' => 'A', 'ip' => '1.2.3.4']],
+ ]);
- $result = $validator->validate('foo@example.com', $constraint);
+ $validator = new DomainValidator(['checkDnsRecord' => true]);
+ $isValid = $validator->validate('example.com');
// ...
}
@@ -856,6 +915,15 @@ If you have installed the bridge through Composer, you can run it by calling e.g
It's also possible to set ``SYMFONY_PHPUNIT_VERSION`` as a real env var
(not defined in a :ref:`dotenv file `).
+ In the same way, ``SYMFONY_MAX_PHPUNIT_VERSION`` will set the maximum version
+ of PHPUnit to be considered. This is useful when testing a framework that does
+ not support the latest version(s) of PHPUnit.
+
+.. versionadded:: 5.2
+
+ The ``SYMFONY_MAX_PHPUNIT_VERSION`` env variable was introduced in
+ Symfony 5.2.
+
.. tip::
If you still need to use ``prophecy`` (but not ``symfony/yaml``),
diff --git a/components/process.rst b/components/process.rst
index b7845145ca3..2cd131be46d 100644
--- a/components/process.rst
+++ b/components/process.rst
@@ -102,10 +102,20 @@ with a non-zero code)::
:method:`Symfony\\Component\\Process\\Process::getLastOutputTime` method.
This method returns ``null`` if the process wasn't started!
- .. versionadded:: 4.4
+Configuring Process Options
+---------------------------
- The :method:`Symfony\\Component\\Process\\Process::getLastOutputTime`
- method was introduced in Symfony 4.4.
+.. versionadded:: 5.2
+
+ The feature to configure process options was introduced in Symfony 5.2.
+
+Symfony uses the PHP :phpfunction:`proc_open` function to run the processes.
+You can configure the options passed to the ``other_options`` argument of
+``proc_open()`` using the ``setOptions()`` method::
+
+ $process = new Process(['...', '...', '...']);
+ // this option allows a subprocess to continue running after the main script exited
+ $process->setOptions(['create_new_console' => true]);
Using Features From the OS Shell
--------------------------------
@@ -150,10 +160,6 @@ enclosing a variable name into ``"${:`` and ``}"`` exactly, the process object
will replace it with its escaped value, or will fail if the variable is not
found in the list of environment variables attached to the command.
-.. versionadded:: 4.4
-
- Portable command lines were introduced in Symfony 4.4.
-
Setting Environment Variables for Processes
-------------------------------------------
@@ -383,6 +389,22 @@ instead::
);
$process->run();
+Using a Prepared Command Line
+-----------------------------
+
+You can run a process by using a prepared command line with double quote
+variable notation. This allows you to use placeholders so that only the
+parameterized values can be changed, but not the rest of the script::
+
+ use Symfony\Component\Process\Process;
+
+ $process = Process::fromShellCommandline('echo "$name"');
+ $process->run(null, ['name' => 'Elsa']);
+
+.. caution::
+
+ A prepared command line will not be escaped automatically!
+
Process Timeout
---------------
@@ -413,6 +435,14 @@ check regularly::
usleep(200000);
}
+.. tip::
+
+ You can get the process start time using the ``getStartTime()`` method.
+
+ .. versionadded:: 5.1
+
+ The ``getStartTime()`` method was introduced in Symfony 5.1.
+
.. _reference-process-signal:
Process Idle Timeout
diff --git a/components/property_access.rst b/components/property_access.rst
index 7b9053b835f..3495cef07c5 100644
--- a/components/property_access.rst
+++ b/components/property_access.rst
@@ -169,11 +169,6 @@ This will produce: ``This person is an author``
Accessing a non Existing Property Path
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 4.3
-
- The ``disableExceptionOnInvalidPropertyPath()`` method was introduced in
- Symfony 4.3.
-
By default a :class:`Symfony\\Component\\PropertyAccess\\Exception\\NoSuchPropertyException`
is thrown if the property path passed to :method:`Symfony\\Component\\PropertyAccess\\PropertyAccessor::getValue`
does not exist. You can change this behavior using the
@@ -196,6 +191,8 @@ method::
$value = $propertyAccessor->getValue($person, 'birthday');
+.. _components-property-access-magic-get:
+
Magic ``__get()`` Method
~~~~~~~~~~~~~~~~~~~~~~~~
@@ -218,6 +215,11 @@ The ``getValue()`` method can also use the magic ``__get()`` method::
var_dump($propertyAccessor->getValue($person, 'Wouter')); // [...]
+.. versionadded:: 5.2
+
+ The magic ``__get()`` method can be disabled since in Symfony 5.2.
+ see `Enable other Features`_.
+
.. _components-property-access-magic-call:
Magic ``__call()`` Method
@@ -278,6 +280,8 @@ also write to an array. This can be achieved using the
// or
// var_dump($person['first_name']); // 'Wouter'
+.. _components-property-access-writing-to-objects:
+
Writing to Objects
------------------
@@ -356,6 +360,11 @@ see `Enable other Features`_::
var_dump($person->getWouter()); // [...]
+.. versionadded:: 5.2
+
+ The magic ``__set()`` method can be disabled since in Symfony 5.2.
+ see `Enable other Features`_.
+
Writing to Array Properties
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -399,6 +408,45 @@ and ``removeChild()`` methods to access to the ``children`` property.
If available, *adder* and *remover* methods have priority over a *setter* method.
+Using non-standard adder/remover methods
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes, adder and remover methods don't use the standard ``add`` or ``remove`` prefix, like in this example::
+
+ // ...
+ class PeopleList
+ {
+ // ...
+
+ public function joinPeople(string $people): void
+ {
+ $this->peoples[] = $people;
+ }
+
+ public function leavePeople(string $people): void
+ {
+ foreach ($this->peoples as $id => $item) {
+ if ($people === $item) {
+ unset($this->peoples[$id]);
+ break;
+ }
+ }
+ }
+ }
+
+ use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
+ use Symfony\Component\PropertyAccess\PropertyAccessor;
+
+ $list = new PeopleList();
+ $reflectionExtractor = new ReflectionExtractor(null, null, ['join', 'leave']);
+ $propertyAccessor = new PropertyAccessor(false, false, null, true, $reflectionExtractor, $reflectionExtractor);
+ $propertyAccessor->setValue($person, 'peoples', ['kevin', 'wouter']);
+
+ var_dump($person->getPeoples()); // ['kevin', 'wouter']
+
+Instead of calling ``add()`` and ``remove()``, the PropertyAccess
+component will call ``join()`` and ``leave()`` methods.
+
Checking Property Paths
-----------------------
@@ -466,14 +514,20 @@ configured to enable extra features. To do that you could use the
// ...
$propertyAccessorBuilder = PropertyAccess::createPropertyAccessorBuilder();
- // enables magic __call
- $propertyAccessorBuilder->enableMagicCall();
+ $propertyAccessorBuilder->enableMagicCall(); // enables magic __call
+ $propertyAccessorBuilder->enableMagicGet(); // enables magic __get
+ $propertyAccessorBuilder->enableMagicSet(); // enables magic __set
+ $propertyAccessorBuilder->enableMagicMethods(); // enables magic __get, __set and __call
- // disables magic __call
- $propertyAccessorBuilder->disableMagicCall();
+ $propertyAccessorBuilder->disableMagicCall(); // disables magic __call
+ $propertyAccessorBuilder->disableMagicGet(); // disables magic __get
+ $propertyAccessorBuilder->disableMagicSet(); // disables magic __set
+ $propertyAccessorBuilder->disableMagicMethods(); // disables magic __get, __set and __call
- // checks if magic __call handling is enabled
+ // checks if magic __call, __get or __set handling are enabled
$propertyAccessorBuilder->isMagicCallEnabled(); // true or false
+ $propertyAccessorBuilder->isMagicGetEnabled(); // true or false
+ $propertyAccessorBuilder->isMagicSetEnabled(); // true or false
// At the end get the configured property accessor
$propertyAccessor = $propertyAccessorBuilder->getPropertyAccessor();
@@ -485,7 +539,7 @@ configured to enable extra features. To do that you could use the
Or you can pass parameters directly to the constructor (not the recommended way)::
- // ...
- $propertyAccessor = new PropertyAccessor(true); // this enables handling of magic __call
+ // enable handling of magic __call, __set but not __get:
+ $propertyAccessor = new PropertyAccessor(PropertyAccessor::MAGIC_CALL | PropertyAccessor::MAGIC_SET);
.. _The Inflector component: https://github.com/symfony/inflector
diff --git a/components/property_info.rst b/components/property_info.rst
index d8e3a693b12..b6684d948d8 100644
--- a/components/property_info.rst
+++ b/components/property_info.rst
@@ -293,10 +293,6 @@ string values: ``array``, ``bool``, ``callable``, ``float``, ``int``,
Constants inside the :class:`Symfony\\Component\\PropertyInfo\\Type`
class, in the form ``Type::BUILTIN_TYPE_*``, are provided for convenience.
-.. versionadded:: 4.4
-
- Support for typed properties (added in PHP 7.4) was introduced in Symfony 4.4.
-
``Type::isNullable()``
~~~~~~~~~~~~~~~~~~~~~~
@@ -327,10 +323,6 @@ this returns ``true`` if:
``@var SomeClass``, ``@var SomeClass``,
``@var Doctrine\Common\Collections\Collection``, etc.)
-.. versionadded:: 4.2
-
- The support of phpDocumentor collection types was introduced in Symfony 4.2.
-
``Type::getCollectionKeyType()`` & ``Type::getCollectionValueType()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -384,11 +376,6 @@ return and scalar types for PHP 7::
// Initializable information
$reflectionExtractor->isInitializable($class, $property);
-.. versionadded:: 4.1
-
- The feature to extract the property types from constructor arguments was
- introduced in Symfony 4.1.
-
.. note::
When using the Symfony framework, this service is automatically registered
@@ -447,8 +434,16 @@ with the ``property_info`` service in the Symfony Framework::
);
$serializerExtractor = new SerializerExtractor($serializerClassMetadataFactory);
- // List information.
- $serializerExtractor->getProperties($class);
+ // the `serializer_groups` option must be configured (may be set to null)
+ $serializerExtractor->getProperties($class, ['serializer_groups' => ['mygroup']]);
+
+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`` annotation is taken into account).
+
+.. versionadded:: 5.2
+
+ Support for the ``null`` value in ``serializer_groups`` was introduced in Symfony 5.2.
DoctrineExtractor
~~~~~~~~~~~~~~~~~
@@ -504,10 +499,6 @@ service by defining it as a service with one or more of the following
* ``property_info.initializable_extractor`` if it provides initializable information
(it checks if a property can be initialized through the constructor).
-.. versionadded:: 4.2
-
- The ``property_info.initializable_extractor`` was introduced in Symfony 4.2.
-
.. _`phpDocumentor Reflection`: https://github.com/phpDocumentor/ReflectionDocBlock
.. _`phpdocumentor/reflection-docblock`: https://packagist.org/packages/phpdocumentor/reflection-docblock
.. _`Doctrine ORM`: https://www.doctrine-project.org/projects/orm.html
diff --git a/components/security.rst b/components/security.rst
index ee6c1580472..9985b611c63 100644
--- a/components/security.rst
+++ b/components/security.rst
@@ -14,12 +14,6 @@ The Security Component
Installation
------------
-.. code-block:: terminal
-
- $ composer require symfony/security
-
-.. include:: /components/require_autoload.rst.inc
-
The Security component is divided into several smaller sub-components which can
be used separately:
@@ -38,6 +32,17 @@ be used separately:
It brings many layers of authentication together, allowing the creation
of complex authentication systems.
+You can install each of them separately in your project:
+
+.. code-block:: terminal
+
+ $ composer require symfony/security-core
+ $ composer require symfony/security-http
+ $ composer require symfony/security-csrf
+ $ composer require symfony/security-guard
+
+.. include:: /components/require_autoload.rst.inc
+
.. seealso::
This article explains how to use the Security features as an independent
diff --git a/components/security/authentication.rst b/components/security/authentication.rst
index 1f75b433c6c..8761e87915a 100644
--- a/components/security/authentication.rst
+++ b/components/security/authentication.rst
@@ -319,10 +319,6 @@ the ``switch_user`` firewall listener.
The ``Symfony\Component\Security\Http\Event\DeauthenticatedEvent`` event is triggered when a token has been deauthenticated
because of a user change, it can help you doing some clean-up task.
-.. versionadded:: 4.3
-
- The ``Symfony\Component\Security\Http\Event\DeauthenticatedEvent`` event was introduced in Symfony 4.3.
-
.. seealso::
For more information on switching users, see
diff --git a/components/security/authorization.rst b/components/security/authorization.rst
index f6a8776aa7b..b884ce97ac0 100644
--- a/components/security/authorization.rst
+++ b/components/security/authorization.rst
@@ -19,7 +19,7 @@ by an instance of :class:`Symfony\\Component\\Security\\Core\\Authorization\\Acc
An authorization decision will always be based on a few things:
* The current token
- For instance, the token's :method:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface::getRoles`
+ For instance, the token's :method:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface::getRoleNames`
method may be used to retrieve the roles of the current user (e.g.
``ROLE_SUPER_ADMIN``), or a decision may be based on the class of the token.
* A set of attributes
@@ -51,6 +51,13 @@ recognizes several strategies:
abstained from voting, the decision is based on the ``allow_if_all_abstain``
config option (which defaults to ``false``).
+``priority``
+ grants or denies access by the first voter that does not abstain;
+
+ .. versionadded:: 5.1
+
+ The ``priority`` version strategy was introduced in Symfony 5.1.
+
Usage of the available options in detail::
use Symfony\Component\Security\Core\Authorization\AccessDecisionManager;
@@ -58,7 +65,7 @@ Usage of the available options in detail::
// instances of Symfony\Component\Security\Core\Authorization\Voter\VoterInterface
$voters = [...];
- // one of "affirmative", "consensus", "unanimous"
+ // one of "affirmative", "consensus", "unanimous", "priority"
$strategy = ...;
// whether or not to grant access when all voters abstain
@@ -100,10 +107,22 @@ AuthenticatedVoter
~~~~~~~~~~~~~~~~~~
The :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\AuthenticatedVoter`
-voter supports the attributes ``IS_AUTHENTICATED_FULLY``, ``IS_AUTHENTICATED_REMEMBERED``,
-and ``IS_AUTHENTICATED_ANONYMOUSLY`` and grants access based on the current
-level of authentication, i.e. is the user fully authenticated, or only based
-on a "remember-me" cookie, or even authenticated anonymously?::
+voter supports the attributes ``IS_AUTHENTICATED_FULLY``,
+``IS_AUTHENTICATED_REMEMBERED``, ``IS_AUTHENTICATED_ANONYMOUSLY``,
+to grant access based on the current level of authentication, i.e. is the
+user fully authenticated, or only based on a "remember-me" cookie, or even
+authenticated anonymously?
+
+It also supports the attributes ``IS_ANONYMOUS``, ``IS_REMEMBERED``,
+``IS_IMPERSONATOR`` to grant access based on a specific state of
+authentication.
+
+.. versionadded:: 5.1
+
+ The ``IS_ANONYMOUS``, ``IS_REMEMBERED`` and ``IS_IMPERSONATOR``
+ attributes were introduced in Symfony 5.1.
+
+::
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver;
@@ -125,7 +144,7 @@ RoleVoter
The :class:`Symfony\\Component\\Security\\Core\\Authorization\\Voter\\RoleVoter`
supports attributes starting with ``ROLE_`` and grants access to the user
when at least one required ``ROLE_*`` attribute can be found in the array of
-roles returned by the token's :method:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface::getRoles`
+roles returned by the token's :method:`Symfony\\Component\\Security\\Core\\Authentication\\Token\\TokenInterface::getRoleNames`
method::
use Symfony\Component\Security\Core\Authorization\Voter\RoleVoter;
@@ -187,7 +206,7 @@ expressions have access to a number of
$object = ...;
$expression = new Expression(
- '"ROLE_ADMIN" in roles or (not is_anonymous() and user.isSuperAdmin())'
+ '"ROLE_ADMIN" in role_names or (not is_anonymous() and user.isSuperAdmin())'
)
$vote = $expressionVoter->vote($token, $object, [$expression]);
@@ -260,4 +279,3 @@ decision manager::
if (!$authorizationChecker->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
-
diff --git a/components/semaphore.rst b/components/semaphore.rst
new file mode 100644
index 00000000000..ebae3df89e8
--- /dev/null
+++ b/components/semaphore.rst
@@ -0,0 +1,81 @@
+.. index::
+ single: Semaphore
+ single: Components; Semaphore
+
+The Semaphore Component
+=======================
+
+ The Semaphore Component manages `semaphores`_, a mechanism to provide
+ exclusive access to a shared resource.
+
+.. versionadded:: 5.2
+
+ The Semaphore Component was introduced in Symfony 5.2.
+
+Installation
+------------
+
+.. code-block:: terminal
+
+ $ composer require symfony/semaphore
+
+.. include:: /components/require_autoload.rst.inc
+
+Usage
+-----
+
+In computer science, a semaphore is a variable or abstract data type used to
+control access to a common resource by multiple processes in a concurrent
+system such as a multitasking operating system. The main difference
+with :doc:`locks ` is that semaphores allow more than one process to
+access a resource, whereas locks only allow one process.
+
+Create semaphores with the :class:`Symfony\\Component\\Semaphore\\SemaphoreFactory`
+class, which in turn requires another class to manage the storage::
+
+ use Symfony\Component\Semaphore\SemaphoreFactory;
+ use Symfony\Component\Semaphore\Store\RedisStore;
+
+ $redis = new Redis();
+ $redis->connect('172.17.0.2');
+
+ $store = new RedisStore($redis);
+ $factory = new SemaphoreFactory($store);
+
+The semaphore is created by calling the
+:method:`Symfony\\Component\\Semaphore\\SemaphoreFactory::createSemaphore`
+method. Its first argument is an arbitrary string that represents the locked
+resource. Its second argument is the maximum number of process allowed. Then, a
+call to the :method:`Symfony\\Component\\Semaphore\\SemaphoreInterface::acquire`
+method will try to acquire the semaphore::
+
+ // ...
+ $semaphore = $factory->createSemaphore('pdf-invoice-generation', 2);
+
+ if ($semaphore->acquire()) {
+ // The resource "pdf-invoice-generation" is locked.
+ // You can compute and generate invoice safely here.
+
+ $semaphore->release();
+ }
+
+If the semaphore can not be acquired, the method returns ``false``. The
+``acquire()`` method can be safely called repeatedly, even if the semaphore is
+already acquired.
+
+.. note::
+
+ Unlike other implementations, the Semaphore component distinguishes
+ semaphores instances even when they are created for the same resource. If a
+ semaphore has to be used by several services, they should share the same
+ ``Semaphore`` instance returned by the ``SemaphoreFactory::createSemaphore``
+ method.
+
+.. tip::
+
+ If you don't release the semaphore explicitly, it will be released
+ automatically on instance destruction. In some cases, it can be useful to
+ lock a resource across several requests. To disable the automatic release
+ behavior, set the fifth argument of the ``createSemaphore()`` method to ``false``.
+
+.. _`semaphores`: https://en.wikipedia.org/wiki/Semaphore_(programming)
diff --git a/components/serializer.rst b/components/serializer.rst
index f554ed7cf76..400aebee771 100644
--- a/components/serializer.rst
+++ b/components/serializer.rst
@@ -227,11 +227,6 @@ normalized data, instead of the denormalizer re-creating them. Note that
arrays of objects. Those will still be replaced when present in the normalized
data.
-.. versionadded:: 4.3
-
- The ``AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE`` option was
- introduced in Symfony 4.3.
-
.. _component-serializer-attributes-groups:
Attributes Groups
@@ -418,9 +413,74 @@ As for groups, attributes can be selected during both the serialization and dese
Ignoring Attributes
-------------------
-As an option, there's a way to ignore attributes from the origin object.
-To remove those attributes provide an array via the ``AbstractNormalizer::IGNORED_ATTRIBUTES``
-key in the ``context`` parameter of the desired serializer method::
+All attributes are included by default when serializing objects. There are two
+options to ignore some of those attributes.
+
+Option 1: Using ``@Ignore`` Annotation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. configuration-block::
+
+ .. code-block:: php-annotations
+
+ namespace App\Model;
+
+ use Symfony\Component\Serializer\Annotation\Ignore;
+
+ class MyClass
+ {
+ public $foo;
+
+ /**
+ * @Ignore()
+ */
+ public $bar;
+ }
+
+ .. code-block:: yaml
+
+ App\Model\MyClass:
+ attributes:
+ bar:
+ ignore: true
+
+ .. code-block:: xml
+
+
+
+
+
+ true
+
+
+
+
+You can now ignore specific attributes during serialization::
+
+ use App\Model\MyClass;
+ use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
+ use Symfony\Component\Serializer\Serializer;
+
+ $obj = new MyClass();
+ $obj->foo = 'foo';
+ $obj->bar = 'bar';
+
+ $normalizer = new ObjectNormalizer($classMetadataFactory);
+ $serializer = new Serializer([$normalizer]);
+
+ $data = $serializer->normalize($obj);
+ // $data = ['foo' => 'foo'];
+
+Option 2: Using the Context
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Pass an array with the names of the attributes to ignore using the
+``AbstractNormalizer::IGNORED_ATTRIBUTES`` key in the ``context`` of the
+serializer method::
use Acme\Person;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
@@ -438,12 +498,6 @@ key in the ``context`` parameter of the desired serializer method::
$serializer = new Serializer([$normalizer], [$encoder]);
$serializer->serialize($person, 'json', [AbstractNormalizer::IGNORED_ATTRIBUTES => ['age']]); // Output: {"name":"foo"}
-.. deprecated:: 4.2
-
- The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setIgnoredAttributes`
- method that was used as an alternative to the ``AbstractNormalizer::IGNORED_ATTRIBUTES`` option
- was deprecated in Symfony 4.2.
-
.. _component-serializer-converting-property-names-when-serializing-and-deserializing:
Converting Property Names when Serializing and Deserializing
@@ -474,12 +528,12 @@ A custom name converter can handle such cases::
class OrgPrefixNameConverter implements NameConverterInterface
{
- public function normalize($propertyName)
+ public function normalize(string $propertyName)
{
return 'org_'.$propertyName;
}
- public function denormalize($propertyName)
+ public function denormalize(string $propertyName)
{
// removes 'org_' prefix
return 'org_' === substr($propertyName, 0, 4) ? substr($propertyName, 4) : $propertyName;
@@ -643,12 +697,6 @@ and ``remove``.
Using Callbacks to Serialize Properties with Object Instances
-------------------------------------------------------------
-.. deprecated:: 4.2
-
- The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setCallbacks`
- method is deprecated since Symfony 4.2. Use the ``callbacks``
- key of the context instead.
-
When serializing, you can set a callback to format a specific object property::
use App\Model\Person;
@@ -681,11 +729,6 @@ When serializing, you can set a callback to format a specific object property::
$serializer->serialize($person, 'json');
// Output: {"name":"cordoval", "age": 34, "createdAt": "2014-03-22T09:43:12-0500"}
-.. deprecated:: 4.2
-
- The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setCallbacks` is deprecated since
- Symfony 4.2, use the "callbacks" key of the context instead.
-
.. _component-serializer-normalizers:
Normalizers
@@ -762,10 +805,6 @@ The Serializer component provides several built-in normalizers:
This normalizer converts :phpclass:`DateTimeZone` objects into strings that
represent the name of the timezone according to the `list of PHP timezones`_.
- .. versionadded:: 4.3
-
- The ``DateTimeZoneNormalizer`` was introduced in Symfony 4.3.
-
:class:`Symfony\\Component\\Serializer\\Normalizer\\DataUriNormalizer`
This normalizer converts :phpclass:`SplFileInfo` objects into a data URI
string (``data:...``) such that files can be embedded into serialized data.
@@ -774,6 +813,12 @@ The Serializer component provides several built-in normalizers:
This normalizer converts :phpclass:`DateInterval` objects into strings.
By default, it uses the ``P%yY%mM%dDT%hH%iM%sS`` format.
+:class:`Symfony\\Component\\Serializer\\Normalizer\\FormErrorNormalizer`
+ This normalizer works with classes that implement
+ :class:`Symfony\\Component\\Form\\FormInterface`.
+
+ It will get errors from the form and normalize them into an normalized array.
+
:class:`Symfony\\Component\\Serializer\\Normalizer\\ConstraintViolationListNormalizer`
This normalizer converts objects that implement
:class:`Symfony\\Component\\Validator\\ConstraintViolationListInterface`
@@ -782,13 +827,27 @@ The Serializer component provides several built-in normalizers:
:class:`Symfony\\Component\\Serializer\\Normalizer\\ProblemNormalizer`
Normalizes errors according to the API Problem spec `RFC 7807`_.
-.. note::
+:class:`Symfony\\Component\\Serializer\\Normalizer\\UidNormalizer`
+ This normalizer converts objects that implement
+ :class:`Symfony\\Component\\Uid\\AbstractUid` into strings.
+ The default normalization format for objects that implement :class:`Symfony\\Component\\Uid\\Uuid`
+ is the `RFC 4122`_ format (example: ``d9e7a184-5d5b-11ea-a62a-3499710062d0``).
+ The default normalization format for objects that implement :class:`Symfony\\Component\\Uid\\Ulid`
+ is the Base 32 format (example: ``01E439TP9XJZ9RPFH3T1PYBCR8``).
+ You can change the string format by setting the serializer context option
+ ``UidNormalizer::NORMALIZATION_FORMAT_KEY`` to ``UidNormalizer::NORMALIZATION_FORMAT_BASE_58``,
+ ``UidNormalizer::NORMALIZATION_FORMAT_BASE_32`` or ``UidNormalizer::NORMALIZATION_FORMAT_RFC_4122``.
- You can also create your own Normalizer to use another structure. Read more at
- :doc:`/serializer/custom_normalizer`.
+ Also it can denormalize ``uuid`` or ``ulid`` strings to :class:`Symfony\\Component\\Uid\\Uuid`
+ or :class:`Symfony\\Component\\Uid\\Ulid`. The format does not matter.
-All these normalizers are enabled by default when using the Serializer component
-in a Symfony application.
+.. versionadded:: 5.2
+
+ The ``UidNormalizer`` was introduced in Symfony 5.2.
+
+.. versionadded:: 5.3
+
+ The ``UidNormalizer`` normalization formats were introduced in Symfony 5.3.
.. _component-serializer-encoders:
@@ -885,10 +944,6 @@ Option Description D
``output_utf8_bom`` Outputs special `UTF-8 BOM`_ along with encoded data ``false``
======================= ===================================================== ==========================
-.. versionadded:: 4.4
-
- The ``output_utf8_bom`` option was introduced in Symfony 4.4.
-
The ``XmlEncoder``
~~~~~~~~~~~~~~~~~~
@@ -940,8 +995,7 @@ always as a collection.
.. tip::
XML comments are ignored by default when decoding contents, but this
- behavior can be changed with the optional ``$decoderIgnoredNodeTypes`` argument of
- the ``XmlEncoder`` class constructor.
+ behavior can be changed with the optional context key ``XmlEncoder::DECODER_IGNORED_NODE_TYPES``.
Data with ``#comment`` keys are encoded to XML comments by default. This can be
changed with the optional ``$encoderIgnoredNodeTypes`` argument of the
@@ -977,11 +1031,6 @@ Option Description
generated XML
============================== ================================================= ==========================
-.. versionadded:: 4.2
-
- The ``decoder_ignored_node_types`` and ``encoder_ignored_node_types``
- options were introduced in Symfony 4.2.
-
The ``YamlEncoder``
~~~~~~~~~~~~~~~~~~~
@@ -1118,12 +1167,6 @@ having unique identifiers::
var_dump($serializer->serialize($org, 'json'));
// {"name":"Les-Tilleuls.coop","members":[{"name":"K\u00e9vin", organization: "Les-Tilleuls.coop"}]}
-.. deprecated:: 4.2
-
- The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setCircularReferenceHandler`
- method is deprecated since Symfony 4.2. Use the ``AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER``
- key of the context instead.
-
Handling Serialization Depth
----------------------------
@@ -1276,12 +1319,6 @@ having unique identifiers::
];
*/
-.. deprecated:: 4.2
-
- The :method:`Symfony\\Component\\Serializer\\Normalizer\\AbstractNormalizer::setMaxDepthHandler`
- method is deprecated since Symfony 4.2. Use the ``max_depth_handler``
- key of the context instead.
-
Handling Arrays
---------------
@@ -1557,3 +1594,4 @@ Learn more
.. _`Value Objects`: https://en.wikipedia.org/wiki/Value_object
.. _`API Platform`: https://api-platform.com
.. _`list of PHP timezones`: https://www.php.net/manual/en/timezones.php
+.. _`RFC 4122`: https://tools.ietf.org/html/rfc4122
diff --git a/components/string.rst b/components/string.rst
new file mode 100644
index 00000000000..48f17f0b3e9
--- /dev/null
+++ b/components/string.rst
@@ -0,0 +1,584 @@
+.. index::
+ single: String
+ single: Components; String
+
+The String Component
+====================
+
+ The String component provides a single object-oriented API to work with
+ three "unit systems" of strings: bytes, code points and grapheme clusters.
+
+.. versionadded:: 5.0
+
+ The String component was introduced in Symfony 5.0.
+
+Installation
+------------
+
+.. code-block:: terminal
+
+ $ composer require symfony/string
+
+.. include:: /components/require_autoload.rst.inc
+
+What is a String?
+-----------------
+
+You can skip this section if you already know what a *"code point"* or a
+*"grapheme cluster"* are in the context of handling strings. Otherwise, read
+this section to learn about the terminology used by this component.
+
+Languages like English require a very limited set of characters and symbols to
+display any content. Each string is a series of characters (letters or symbols)
+and they can be encoded even with the most limited standards (e.g. `ASCII`_).
+
+However, other languages require thousands of symbols to display their contents.
+They need complex encoding standards such as `Unicode`_ and concepts like
+"character" no longer make sense. Instead, you have to deal with these terms:
+
+* `Code points`_: they are the atomic unit of information. A string is a series
+ of code points. Each code point is a number whose meaning is given by the
+ `Unicode`_ standard. For example, the English letter ``A`` is the ``U+0041``
+ code point and the Japanese *kana* ``の`` is the ``U+306E`` code point.
+* `Grapheme clusters`_: they are a sequence of one or more code points which are
+ displayed as a single graphical unit. For example, the Spanish letter ``ñ`` is
+ a grapheme cluster that contains two code points: ``U+006E`` = ``n`` (*"latin
+ small letter N"*) + ``U+0303`` = ``◌̃`` (*"combining tilde"*).
+* Bytes: they are the actual information stored for the string contents. Each
+ code point can require one or more bytes of storage depending on the standard
+ being used (UTF-8, UTF-16, etc.).
+
+The following image displays the bytes, code points and grapheme clusters for
+the same word written in English (``hello``) and Hindi (``नमस्ते``):
+
+.. image:: /_images/components/string/bytes-points-graphemes.png
+ :align: center
+
+Usage
+-----
+
+Create a new object of type :class:`Symfony\\Component\\String\\ByteString`,
+:class:`Symfony\\Component\\String\\CodePointString` or
+:class:`Symfony\\Component\\String\\UnicodeString`, pass the string contents as
+their arguments and then use the object-oriented API to work with those strings::
+
+ use Symfony\Component\String\UnicodeString;
+
+ $text = (new UnicodeString('This is a déjà-vu situation.'))
+ ->trimEnd('.')
+ ->replace('déjà-vu', 'jamais-vu')
+ ->append('!');
+ // $text = 'This is a jamais-vu situation!'
+
+ $content = new UnicodeString('नमस्ते दुनिया');
+ if ($content->ignoreCase()->startsWith('नमस्ते')) {
+ // ...
+ }
+
+Method Reference
+----------------
+
+Methods to Create String Objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+First, you can create objects prepared to store strings as bytes, code points
+and grapheme clusters with the following classes::
+
+ use Symfony\Component\String\ByteString;
+ use Symfony\Component\String\CodePointString;
+ use Symfony\Component\String\UnicodeString;
+
+ $foo = new ByteString('hello');
+ $bar = new CodePointString('hello');
+ // UnicodeString is the most commonly used class
+ $baz = new UnicodeString('hello');
+
+Use the ``wrap()`` static method to instantiate more than one string object::
+
+ $contents = ByteString::wrap(['hello', 'world']); // $contents = ByteString[]
+ $contents = UnicodeString::wrap(['I', '❤️', 'Symfony']); // $contents = UnicodeString[]
+
+ // use the unwrap method to make the inverse conversion
+ $contents = UnicodeString::unwrap([
+ new UnicodeString('hello'), new UnicodeString('world'),
+ ]); // $contents = ['hello', 'world']
+
+If you work with lots of String objects, consider using the shortcut functions
+to make your code more concise::
+
+ // the b() function creates byte strings
+ use function Symfony\Component\String\b;
+
+ // both lines are equivalent
+ $foo = new ByteString('hello');
+ $foo = b('hello');
+
+ // the u() function creates Unicode strings
+ use function Symfony\Component\String\u;
+
+ // both lines are equivalent
+ $foo = new UnicodeString('hello');
+ $foo = u('hello');
+
+ // the s() function creates a byte string or Unicode string
+ // depending on the given contents
+ use function Symfony\Component\String\s;
+
+ // creates a ByteString object
+ $foo = s("\xfe\xff");
+ // creates a UnicodeString object
+ $foo = s('अनुच्छेद');
+
+.. versionadded:: 5.1
+
+ The ``s()`` function was introduced in Symfony 5.1.
+
+There are also some specialized constructors::
+
+ // ByteString can create a random string of the given length
+ $foo = ByteString::fromRandom(12);
+ // by default, random strings use A-Za-z0-9 characters; you can restrict
+ // the characters to use with the second optional argument
+ $foo = ByteString::fromRandom(6, 'AEIOU0123456789');
+ $foo = ByteString::fromRandom(10, 'qwertyuiop');
+
+ // CodePointString and UnicodeString can create a string from code points
+ $foo = UnicodeString::fromCodePoints(0x928, 0x92E, 0x938, 0x94D, 0x924, 0x947);
+ // equivalent to: $foo = new UnicodeString('नमस्ते');
+
+.. versionadded:: 5.1
+
+ The second argument of ``ByteString::fromRandom()`` was introduced in Symfony 5.1.
+
+Methods to Transform String Objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Each string object can be transformed into the other two types of objects::
+
+ $foo = ByteString::fromRandom(12)->toCodePointString();
+ $foo = (new CodePointString('hello'))->toUnicodeString();
+ $foo = UnicodeString::fromCodePoints(0x68, 0x65, 0x6C, 0x6C, 0x6F)->toByteString();
+
+ // the optional $toEncoding argument defines the encoding of the target string
+ $foo = (new CodePointString('hello'))->toByteString('Windows-1252');
+ // the optional $fromEncoding argument defines the encoding of the original string
+ $foo = (new ByteString('さよなら'))->toCodePointString('ISO-2022-JP');
+
+If the conversion is not possible for any reason, you'll get an
+:class:`Symfony\\Component\\String\\Exception\\InvalidArgumentException`.
+
+There is also a method to get the bytes stored at some position::
+
+ // ('नमस्ते' bytes = [224, 164, 168, 224, 164, 174, 224, 164, 184,
+ // 224, 165, 141, 224, 164, 164, 224, 165, 135])
+ b('नमस्ते')->bytesAt(0); // [224]
+ u('नमस्ते')->bytesAt(0); // [224, 164, 168]
+
+ b('नमस्ते')->bytesAt(1); // [164]
+ u('नमस्ते')->bytesAt(1); // [224, 164, 174]
+
+Methods Related to Length and White Spaces
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ // returns the number of graphemes, code points or bytes of the given string
+ $word = 'नमस्ते';
+ (new ByteString($word))->length(); // 18 (bytes)
+ (new CodePointString($word))->length(); // 6 (code points)
+ (new UnicodeString($word))->length(); // 4 (graphemes)
+
+ // some symbols require double the width of others to represent them when using
+ // a monospaced font (e.g. in a console). This method returns the total width
+ // needed to represent the entire word
+ $word = 'नमस्ते';
+ (new ByteString($word))->width(); // 18
+ (new CodePointString($word))->width(); // 4
+ (new UnicodeString($word))->width(); // 4
+ // if the text contains multiple lines, it returns the max width of all lines
+ $text = "<<width(); // 14
+
+ // only returns TRUE if the string is exactly an empty string (not even white spaces)
+ u('hello world')->isEmpty(); // false
+ u(' ')->isEmpty(); // false
+ u('')->isEmpty(); // true
+
+ // removes all white spaces from the start and end of the string and replaces two
+ // or more consecutive white spaces inside contents by a single white space
+ u(" \n\n hello world \n \n")->collapseWhitespace(); // 'hello world'
+
+Methods to Change Case
+~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ // changes all graphemes/code points to lower case
+ u('FOO Bar')->lower(); // 'foo bar'
+
+ // when dealing with different languages, uppercase/lowercase is not enough
+ // there are three cases (lower, upper, title), some characters have no case,
+ // case is context-sensitive and locale-sensitive, etc.
+ // this method returns a string that you can use in case-insensitive comparisons
+ u('FOO Bar')->folded(); // 'foo bar'
+ u('Die O\'Brian Straße')->folded(); // "die o'brian strasse"
+
+ // changes all graphemes/code points to upper case
+ u('foo BAR')->upper(); // 'FOO BAR'
+
+ // changes all graphemes/code points to "title case"
+ u('foo bar')->title(); // 'Foo bar'
+ u('foo bar')->title(true); // 'Foo Bar'
+
+ // changes all graphemes/code points to camelCase
+ u('Foo: Bar-baz.')->camel(); // 'fooBarBaz'
+ // changes all graphemes/code points to snake_case
+ u('Foo: Bar-baz.')->snake(); // 'foo_bar_baz'
+ // other cases can be achieved by chaining methods. E.g. PascalCase:
+ u('Foo: Bar-baz.')->camel()->title(); // 'FooBarBaz'
+
+The methods of all string classes are case-sensitive by default. You can perform
+case-insensitive operations with the ``ignoreCase()`` method::
+
+ u('abc')->indexOf('B'); // null
+ u('abc')->ignoreCase()->indexOf('B'); // 1
+
+Methods to Append and Prepend
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ // adds the given content (one or more strings) at the beginning/end of the string
+ u('world')->prepend('hello'); // 'helloworld'
+ u('world')->prepend('hello', ' '); // 'hello world'
+
+ u('hello')->append('world'); // 'helloworld'
+ u('hello')->append(' ', 'world'); // 'hello world'
+
+ // adds the given content at the beginning of the string (or removes it) to
+ // make sure that the content starts exactly with that content
+ u('Name')->ensureStart('get'); // 'getName'
+ u('getName')->ensureStart('get'); // 'getName'
+ u('getgetName')->ensureStart('get'); // 'getName'
+ // this method is similar, but works on the end of the content instead of on the beginning
+ u('User')->ensureEnd('Controller'); // 'UserController'
+ u('UserController')->ensureEnd('Controller'); // 'UserController'
+ u('UserControllerController')->ensureEnd('Controller'); // 'UserController'
+
+ // returns the contents found before/after the first occurrence of the given string
+ u('hello world')->before('world'); // 'hello '
+ u('hello world')->before('o'); // 'hell'
+ u('hello world')->before('o', true); // 'hello'
+
+ u('hello world')->after('hello'); // ' world'
+ u('hello world')->after('o'); // ' world'
+ u('hello world')->after('o', true); // 'o world'
+
+ // returns the contents found before/after the last occurrence of the given string
+ u('hello world')->beforeLast('o'); // 'hello w'
+ u('hello world')->beforeLast('o', true); // 'hello wo'
+
+ u('hello world')->afterLast('o'); // 'rld'
+ u('hello world')->afterLast('o', true); // 'orld'
+
+Methods to Pad and Trim
+~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ // makes a string as long as the first argument by adding the given
+ // string at the beginning, end or both sides of the string
+ u(' Lorem Ipsum ')->padBoth(20, '-'); // '--- Lorem Ipsum ----'
+ u(' Lorem Ipsum')->padStart(20, '-'); // '-------- Lorem Ipsum'
+ u('Lorem Ipsum ')->padEnd(20, '-'); // 'Lorem Ipsum --------'
+
+ // repeats the given string the number of times passed as argument
+ u('_.')->repeat(10); // '_._._._._._._._._._.'
+
+ // removes the given characters (by default, white spaces) from the string
+ u(' Lorem Ipsum ')->trim(); // 'Lorem Ipsum'
+ u('Lorem Ipsum ')->trim('m'); // 'Lorem Ipsum '
+ u('Lorem Ipsum')->trim('m'); // 'Lorem Ipsu'
+
+ u(' Lorem Ipsum ')->trimStart(); // 'Lorem Ipsum '
+ u(' Lorem Ipsum ')->trimEnd(); // ' Lorem Ipsum'
+
+Methods to Search and Replace
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ // checks if the string starts/ends with the given string
+ u('https://symfony.com')->startsWith('https'); // true
+ u('report-1234.pdf')->endsWith('.pdf'); // true
+
+ // checks if the string contents are exactly the same as the given contents
+ u('foo')->equalsTo('foo'); // true
+
+ // checks if the string content match the given regular expression
+ u('avatar-73647.png')->match('/avatar-(\d+)\.png/');
+ // result = ['avatar-73647.png', '73647']
+
+ // checks if the string contains any of the other given strings
+ u('aeiou')->containsAny('a'); // true
+ u('aeiou')->containsAny(['ab', 'efg']); // false
+ u('aeiou')->containsAny(['eio', 'foo', 'z']); // true
+
+ // finds the position of the first occurrence of the given string
+ // (the second argument is the position where the search starts and negative
+ // values have the same meaning as in PHP functions)
+ u('abcdeabcde')->indexOf('c'); // 2
+ u('abcdeabcde')->indexOf('c', 2); // 2
+ u('abcdeabcde')->indexOf('c', -4); // 7
+ u('abcdeabcde')->indexOf('eab'); // 4
+ u('abcdeabcde')->indexOf('k'); // null
+
+ // finds the position of the last occurrence of the given string
+ // (the second argument is the position where the search starts and negative
+ // values have the same meaning as in PHP functions)
+ u('abcdeabcde')->indexOfLast('c'); // 7
+ u('abcdeabcde')->indexOfLast('c', 2); // 7
+ u('abcdeabcde')->indexOfLast('c', -4); // 2
+ u('abcdeabcde')->indexOfLast('eab'); // 4
+ u('abcdeabcde')->indexOfLast('k'); // null
+
+ // replaces all occurrences of the given string
+ u('http://symfony.com')->replace('http://', 'https://'); // 'https://symfony.com'
+ // replaces all occurrences of the given regular expression
+ u('(+1) 206-555-0100')->replaceMatches('/[^A-Za-z0-9]++/', ''); // '12065550100'
+ // you can pass a callable as the second argument to perform advanced replacements
+ u('123')->replaceMatches('/\d/', function ($match) {
+ return '['.$match[0].']';
+ }); // result = '[1][2][3]'
+
+.. versionadded:: 5.1
+
+ The ``containsAny()`` method was introduced in Symfony 5.1.
+
+Methods to Join, Split, Truncate and Reverse
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ // uses the string as the "glue" to merge all the given strings
+ u(', ')->join(['foo', 'bar']); // 'foo, bar'
+
+ // breaks the string into pieces using the given delimiter
+ u('template_name.html.twig')->split('.'); // ['template_name', 'html', 'twig']
+ // you can set the maximum number of pieces as the second argument
+ u('template_name.html.twig')->split('.', 2); // ['template_name', 'html.twig']
+
+ // returns a substring which starts at the first argument and has the length of the
+ // second optional argument (negative values have the same meaning as in PHP functions)
+ u('Symfony is great')->slice(0, 7); // 'Symfony'
+ u('Symfony is great')->slice(0, -6); // 'Symfony is'
+ u('Symfony is great')->slice(11); // 'great'
+ u('Symfony is great')->slice(-5); // 'great'
+
+ // reduces the string to the length given as argument (if it's longer)
+ u('Lorem Ipsum')->truncate(3); // 'Lor'
+ u('Lorem Ipsum')->truncate(80); // 'Lorem Ipsum'
+ // the second argument is the character(s) added when a string is cut
+ // (the total length includes the length of this character(s))
+ u('Lorem Ipsum')->truncate(8, '…'); // 'Lorem I…'
+ // if the third argument is false, the last word before the cut is kept
+ // even if that generates a string longer than the desired length
+ u('Lorem Ipsum')->truncate(8, '…', false); // 'Lorem Ipsum'
+
+.. versionadded:: 5.1
+
+ The third argument of ``truncate()`` was introduced in Symfony 5.1.
+
+::
+
+ // breaks the string into lines of the given length
+ u('Lorem Ipsum')->wordwrap(4); // 'Lorem\nIpsum'
+ // by default it breaks by white space; pass TRUE to break unconditionally
+ u('Lorem Ipsum')->wordwrap(4, "\n", true); // 'Lore\nm\nIpsu\nm'
+
+ // replaces a portion of the string with the given contents:
+ // the second argument is the position where the replacement starts;
+ // the third argument is the number of graphemes/code points removed from the string
+ u('0123456789')->splice('xxx'); // 'xxx'
+ u('0123456789')->splice('xxx', 0, 2); // 'xxx23456789'
+ u('0123456789')->splice('xxx', 0, 6); // 'xxx6789'
+ u('0123456789')->splice('xxx', 6); // '012345xxx'
+
+ // breaks the string into pieces of the length given as argument
+ u('0123456789')->chunk(3); // ['012', '345', '678', '9']
+
+ // reverses the order of the string contents
+ u('foo bar')->reverse(); // 'rab oof'
+ u('さよなら')->reverse(); // 'らなよさ'
+
+.. versionadded:: 5.1
+
+ The ``reverse()`` method was introduced in Symfony 5.1.
+
+Methods Added by ByteString
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These methods are only available for ``ByteString`` objects::
+
+ // returns TRUE if the string contents are valid UTF-8 contents
+ b('Lorem Ipsum')->isUtf8(); // true
+ b("\xc3\x28")->isUtf8(); // false
+
+Methods Added by CodePointString and UnicodeString
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+These methods are only available for ``CodePointString`` and ``UnicodeString``
+objects::
+
+ // transliterates any string into the latin alphabet defined by the ASCII encoding
+ // (don't use this method to build a slugger because this component already provides
+ // a slugger, as explained later in this article)
+ u('नमस्ते')->ascii(); // 'namaste'
+ u('さよなら')->ascii(); // 'sayonara'
+ u('спасибо')->ascii(); // 'spasibo'
+
+ // returns an array with the code point or points stored at the given position
+ // (code points of 'नमस्ते' graphemes = [2344, 2350, 2360, 2340]
+ u('नमस्ते')->codePointsAt(0); // [2344]
+ u('नमस्ते')->codePointsAt(2); // [2360]
+
+`Unicode equivalence`_ is the specification by the Unicode standard that
+different sequences of code points represent the same character. For example,
+the Swedish letter ``å`` can be a single code point (``U+00E5`` = *"latin small
+letter A with ring above"*) or a sequence of two code points (``U+0061`` =
+*"latin small letter A"* + ``U+030A`` = *"combining ring above"*). The
+``normalize()`` method allows to pick the normalization mode::
+
+ // these encode the letter as a single code point: U+00E5
+ u('å')->normalize(UnicodeString::NFC);
+ u('å')->normalize(UnicodeString::NFKC);
+ // these encode the letter as two code points: U+0061 + U+030A
+ u('å')->normalize(UnicodeString::NFD);
+ u('å')->normalize(UnicodeString::NFKD);
+
+Slugger
+-------
+
+In some contexts, such as URLs and file/directory names, it's not safe to use
+any Unicode character. A *slugger* transforms a given string into another string
+that only includes safe ASCII characters::
+
+ use Symfony\Component\String\Slugger\AsciiSlugger;
+
+ $slugger = new AsciiSlugger();
+ $slug = $slugger->slug('Wôrķšƥáçè ~~sèťtïñğš~~');
+ // $slug = 'Workspace-settings'
+
+ // you can also pass an array with additional character substitutions
+ $slugger = new AsciiSlugger('en', ['en' => ['%' => 'percent', '€' => 'euro']]);
+ $slug = $slugger->slug('10% or 5€');
+ // $slug = '10-percent-or-5-euro'
+
+ // if there is no symbols map for your locale (e.g. 'en_GB') then the parent locale's symbols map
+ // will be used instead (i.e. 'en')
+ $slugger = new AsciiSlugger('en_GB', ['en' => ['%' => 'percent', '€' => 'euro']]);
+ $slug = $slugger->slug('10% or 5€');
+ // $slug = '10-percent-or-5-euro'
+
+ // for more dynamic substitutions, pass a PHP closure instead of an array
+ $slugger = new AsciiSlugger('en', function ($string, $locale) {
+ return str_replace('❤️', 'love', $string);
+ });
+
+.. versionadded:: 5.1
+
+ The feature to define additional substitutions was introduced in Symfony 5.1.
+
+.. versionadded:: 5.2
+
+ The feature to use a PHP closure to define substitutions was introduced in Symfony 5.2.
+
+.. versionadded:: 5.3
+
+ The feature to fallback to the parent locale's symbols map was introduced in Symfony 5.3.
+
+The separator between words is a dash (``-``) by default, but you can define
+another separator as the second argument::
+
+ $slug = $slugger->slug('Wôrķšƥáçè ~~sèťtïñğš~~', '/');
+ // $slug = 'Workspace/settings'
+
+The slugger transliterates the original string into the Latin script before
+applying the other transformations. The locale of the original string is
+detected automatically, but you can define it explicitly::
+
+ // this tells the slugger to transliterate from Korean language
+ $slugger = new AsciiSlugger('ko');
+
+ // you can override the locale as the third optional parameter of slug()
+ $slug = $slugger->slug('...', '-', 'fa');
+
+In a Symfony application, you don't need to create the slugger yourself. Thanks
+to :doc:`service autowiring `, you can inject a
+slugger by type-hinting a service constructor argument with the
+:class:`Symfony\\Component\\String\\Slugger\\SluggerInterface`. The locale of
+the injected slugger is the same as the request locale::
+
+ use Symfony\Component\String\Slugger\SluggerInterface;
+
+ class MyService
+ {
+ private $slugger;
+
+ public function __construct(SluggerInterface $slugger)
+ {
+ $this->slugger = $slugger;
+ }
+
+ public function someMethod()
+ {
+ $slug = $this->slugger->slug('...');
+ }
+ }
+
+.. _string-inflector:
+
+Inflector
+---------
+
+.. versionadded:: 5.1
+
+ The inflector feature was introduced in Symfony 5.1.
+
+In some scenarios such as code generation and code introspection, you need to
+convert words from/to singular/plural. For example, to know the property
+associated with an *adder* method, you must convert from plural
+(``addStories()`` method) to singular (``$story`` property).
+
+Most human languages have simple pluralization rules, but at the same time they
+define lots of exceptions. For example, the general rule in English is to add an
+``s`` at the end of the word (``book`` -> ``books``) but there are lots of
+exceptions even for common words (``woman`` -> ``women``, ``life`` -> ``lives``,
+``news`` -> ``news``, ``radius`` -> ``radii``, etc.)
+
+This component provides an :class:`Symfony\\Component\\String\\Inflector\\EnglishInflector`
+class to convert English words from/to singular/plural with confidence::
+
+ use Symfony\Component\String\Inflector\EnglishInflector;
+
+ $inflector = new EnglishInflector();
+
+ $result = $inflector->singularize('teeth'); // ['tooth']
+ $result = $inflector->singularize('radii'); // ['radius']
+ $result = $inflector->singularize('leaves'); // ['leaf', 'leave', 'leaff']
+
+ $result = $inflector->pluralize('bacterium'); // ['bacteria']
+ $result = $inflector->pluralize('news'); // ['news']
+ $result = $inflector->pluralize('person'); // ['persons', 'people']
+
+The value returned by both methods is always an array because sometimes it's not
+possible to determine a unique singular/plural form for the given word.
+
+.. _`ASCII`: https://en.wikipedia.org/wiki/ASCII
+.. _`Unicode`: https://en.wikipedia.org/wiki/Unicode
+.. _`Code points`: https://en.wikipedia.org/wiki/Code_point
+.. _`Grapheme clusters`: https://en.wikipedia.org/wiki/Grapheme
+.. _`Unicode equivalence`: https://en.wikipedia.org/wiki/Unicode_equivalence
diff --git a/components/uid.rst b/components/uid.rst
new file mode 100644
index 00000000000..e84b7296fad
--- /dev/null
+++ b/components/uid.rst
@@ -0,0 +1,371 @@
+.. index::
+ single: UID
+ single: Components; UID
+
+The UID Component
+=================
+
+ The UID component provides utilities to work with `unique identifiers`_ (UIDs)
+ such as UUIDs and ULIDs.
+
+.. versionadded:: 5.1
+
+ The UID component was introduced in Symfony 5.1.
+
+Installation
+------------
+
+.. code-block:: terminal
+
+ $ composer require symfony/uid
+
+.. include:: /components/require_autoload.rst.inc
+
+UUIDs
+-----
+
+`UUIDs`_ (*universally unique identifiers*) are one of the most popular UIDs in
+the software industry. UUIDs are 128-bit numbers usually represented as five
+groups of hexadecimal characters: ``xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx``
+(the ``M`` digit is the UUID version and the ``N`` digit is the UUID variant).
+
+Generating UUIDs
+~~~~~~~~~~~~~~~~
+
+Use the named constructors of the ``Uuid`` class or any of the specific classes
+to create each type of UUID::
+
+ use Symfony\Component\Uid\Uuid;
+
+ // UUID type 1 generates the UUID using the MAC address of your device and a timestamp.
+ // Both are obtained automatically, so you don't have to pass any constructor argument.
+ $uuid = Uuid::v1(); // $uuid is an instance of Symfony\Component\Uid\UuidV1
+
+ // UUID type 4 generates a random UUID, so you don't have to pass any constructor argument.
+ $uuid = Uuid::v4(); // $uuid is an instance of Symfony\Component\Uid\UuidV4
+
+ // UUID type 3 and 5 generate a UUID hashing the given namespace and name. Type 3 uses
+ // MD5 hashes and Type 5 uses SHA-1. The namespace is another UUID (e.g. a Type 4 UUID)
+ // and the name is an arbitrary string (e.g. a product name; if it's unique).
+ $namespace = Uuid::v4();
+ $name = $product->getUniqueName();
+
+ $uuid = Uuid::v3($namespace, $name); // $uuid is an instance of Symfony\Component\Uid\UuidV3
+ $uuid = Uuid::v5($namespace, $name); // $uuid is an instance of Symfony\Component\Uid\UuidV5
+
+ // the namespaces defined by RFC 4122 are available as constants
+ // (see https://tools.ietf.org/html/rfc4122#appendix-C)
+ $uuid = Uuid::v3(Uuid::NAMESPACE_DNS, $name);
+ $uuid = Uuid::v3(Uuid::NAMESPACE_URL, $name);
+ $uuid = Uuid::v3(Uuid::NAMESPACE_OID, $name);
+ $uuid = Uuid::v3(Uuid::NAMESPACE_X500, $name);
+
+ // UUID type 6 is not part of the UUID standard. It's lexicographically sortable
+ // (like ULIDs) and contains a 60-bit timestamp and 63 extra unique bits.
+ // It's defined in http://gh.peabody.io/uuidv6/
+ $uuid = Uuid::v6(); // $uuid is an instance of Symfony\Component\Uid\UuidV6
+
+.. versionadded:: 5.3
+
+ The ``Uuid::NAMESPACE_*`` constants were introduced in Symfony 5.3.
+
+If your UUID is generated by another system, use the ``fromString()`` method to
+create an object and make use of the utilities available for Symfony UUIDs::
+
+ // this value is generated somewhere else (can also be in binary format)
+ $uuidValue = 'd9e7a184-5d5b-11ea-a62a-3499710062d0';
+ $uuid = Uuid::fromString($uuidValue);
+
+Converting UUIDs
+~~~~~~~~~~~~~~~~
+
+Use these methods to transform the UUID object into different bases::
+
+ $uuid = Uuid::fromString('d9e7a184-5d5b-11ea-a62a-3499710062d0');
+
+ $uuid->toBinary(); // string(16) "..." (binary contents can't be printed)
+ $uuid->toBase32(); // string(26) "6SWYGR8QAV27NACAHMK5RG0RPG"
+ $uuid->toBase58(); // string(22) "TuetYWNHhmuSQ3xPoVLv9M"
+ $uuid->toRfc4122(); // string(36) "d9e7a184-5d5b-11ea-a62a-3499710062d0"
+
+Working with UUIDs
+~~~~~~~~~~~~~~~~~~
+
+UUID objects created with the ``Uuid`` class can use the following methods
+(which are equivalent to the ``uuid_*()`` method of the PHP extension)::
+
+ use Symfony\Component\Uid\NilUuid;
+ use Symfony\Component\Uid\Uuid;
+
+ // checking if the UUID is null (note that the class is called
+ // NilUuid instead of NullUuid to follow the UUID standard notation)
+ $uuid = Uuid::v4();
+ $uuid instanceof NilUuid; // false
+
+ // checking the type of UUID
+ use Symfony\Component\Uid\UuidV4;
+ $uuid = Uuid::v4();
+ $uuid instanceof UuidV4; // true
+
+ // getting the UUID datetime (it's only available in certain UUID types)
+ $uuid = Uuid::v1();
+ $uuid->getDateTime(); // returns a \DateTimeImmutable instance
+
+ // comparing UUIDs and checking for equality
+ $uuid1 = Uuid::v1();
+ $uuid4 = Uuid::v4();
+ $uuid1->equals($uuid4); // false
+
+ // this method returns:
+ // * int(0) if $uuid1 and $uuid4 are equal
+ // * int > 0 if $uuid1 is greater than $uuid4
+ // * int < 0 if $uuid1 is less than $uuid4
+ $uuid1->compare($uuid4); // e.g. int(4)
+
+.. versionadded:: 5.3
+
+ The ``getDateTime()`` method was introduced in Symfony 5.3. In previous
+ versions it was called ``getTime()``.
+
+Storing UUIDs in Databases
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you :doc:`use Doctrine `, consider using the ``uuid`` Doctrine
+type, which converts to/from UUID objects automatically::
+
+ // src/Entity/Product.php
+ namespace App\Entity;
+
+ use Doctrine\ORM\Mapping as ORM;
+
+ /**
+ * @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
+ */
+ class Product
+ {
+ /**
+ * @ORM\Column(type="uuid")
+ */
+ private $someProperty;
+
+ // ...
+ }
+
+There's also a Doctrine generator to help autogenerate UUID values for the
+entity primary keys::
+
+ // there are generators for UUID V1 and V6 too
+ use Symfony\Bridge\Doctrine\IdGenerator\UuidV4Generator;
+ use Symfony\Component\Uid\Uuid;
+
+ /**
+ * @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
+ */
+ class Product
+ {
+ /**
+ * @ORM\Id
+ * @ORM\Column(type="uuid", unique=true)
+ * @ORM\GeneratedValue(strategy="CUSTOM")
+ * @ORM\CustomIdGenerator(class=UuidV4Generator::class)
+ */
+ private $id;
+
+ // ...
+
+ public function getId(): ?Uuid
+ {
+ return $this->id;
+ }
+
+ // ...
+ }
+
+.. versionadded:: 5.2
+
+ The UUID type and generators were introduced in Symfony 5.2.
+
+When using built-in Doctrine repository methods (e.g. ``findOneBy()``), Doctrine
+knows how to convert these UUID types to build the SQL query
+(e.g. ``->findOneBy(['user' => $user->getUuid()])``). However, when using DQL
+queries or building the query yourself, you'll need to set ``uuid`` as the type
+of the UUID parameters::
+
+ // src/Repository/ProductRepository.php
+
+ // ...
+ class ProductRepository extends ServiceEntityRepository
+ {
+ // ...
+
+ public function findUserProducts(User $user): array
+ {
+ $qb = $this->createQueryBuilder('p')
+ // ...
+ // add 'uuid' as the third argument to tell Doctrine that this is an UUID
+ ->setParameter('user', $user->getUuid(), 'uuid')
+
+ // alternatively, you can convert it to a value compatible with
+ // the type inferred by Doctrine
+ ->setParameter('user', $user->getUuid()->toBinary())
+ ;
+
+ // ...
+ }
+ }
+
+ULIDs
+-----
+
+`ULIDs`_ (*Universally Unique Lexicographically Sortable Identifier*) are 128-bit
+numbers usually represented as a 26-character string: ``TTTTTTTTTTRRRRRRRRRRRRRRRR``
+(where ``T`` represents a timestamp and ``R`` represents the random bits).
+
+ULIDs are an alternative to UUIDs when using those is impractical. They provide
+128-bit compatibility with UUID, they are lexicographically sortable and they
+are encoded as 26-character strings (vs 36-character UUIDs).
+
+Generating ULIDs
+~~~~~~~~~~~~~~~~
+
+Instantiate the ``Ulid`` class to generate a random ULID value::
+
+ use Symfony\Component\Uid\Ulid;
+
+ $ulid = new Ulid(); // e.g. 01AN4Z07BY79KA1307SR9X4MV3
+
+If your ULID is generated by another system, use the ``fromString()`` method to
+create an object and make use of the utilities available for Symfony ULIDs::
+
+ // this value is generated somewhere else (can also be in binary format)
+ $ulidValue = '01E439TP9XJZ9RPFH3T1PYBCR8';
+ $ulid = Ulid::fromString($ulidValue);
+
+Converting ULIDs
+~~~~~~~~~~~~~~~~
+
+Use these methods to transform the ULID object into different bases::
+
+ $ulid = Ulid::fromString('01E439TP9XJZ9RPFH3T1PYBCR8');
+
+ $ulid->toBinary(); // string(16) "..." (binary contents can't be printed)
+ $ulid->toBase32(); // string(26) "01E439TP9XJZ9RPFH3T1PYBCR8"
+ $ulid->toBase58(); // string(22) "1BKocMc5BnrVcuq2ti4Eqm"
+ $ulid->toRfc4122(); // string(36) "0171069d-593d-97d3-8b3e-23d06de5b308"
+
+Working with ULIDs
+~~~~~~~~~~~~~~~~~~
+
+ULID objects created with the ``Ulid`` class can use the following methods::
+
+ use Symfony\Component\Uid\Ulid;
+
+ $ulid1 = new Ulid();
+ $ulid2 = new Ulid();
+
+ // checking if a given value is valid as ULID
+ $isValid = Ulid::isValid($ulidValue); // true or false
+
+ // getting the ULID datetime
+ $ulid1->getDateTime(); // returns a \DateTimeImmutable instance
+
+ // comparing ULIDs and checking for equality
+ $ulid1->equals($ulid2); // false
+ // this method returns $ulid1 <=> $ulid2
+ $ulid1->compare($ulid2); // e.g. int(-1)
+
+.. versionadded:: 5.3
+
+ The ``getDateTime()`` method was introduced in Symfony 5.3. In previous
+ versions it was called ``getTime()``.
+
+Storing ULIDs in Databases
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you :doc:`use Doctrine `, consider using the ``ulid`` Doctrine
+type, which converts to/from ULID objects automatically::
+
+ // src/Entity/Product.php
+ namespace App\Entity;
+
+ use Doctrine\ORM\Mapping as ORM;
+
+ /**
+ * @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
+ */
+ class Product
+ {
+ /**
+ * @ORM\Column(type="ulid")
+ */
+ private $someProperty;
+
+ // ...
+ }
+
+There's also a Doctrine generator to help autogenerate ULID values for the
+entity primary keys::
+
+ use Symfony\Bridge\Doctrine\IdGenerator\UlidGenerator;
+ use Symfony\Component\Uid\Ulid;
+
+ /**
+ * @ORM\Entity(repositoryClass="App\Repository\ProductRepository")
+ */
+ class Product
+ {
+ /**
+ * @ORM\Id
+ * @ORM\Column(type="ulid", unique=true)
+ * @ORM\GeneratedValue(strategy="CUSTOM")
+ * @ORM\CustomIdGenerator(class=UlidGenerator::class)
+ */
+ private $id;
+
+ // ...
+
+ public function getId(): ?Ulid
+ {
+ return $this->id;
+ }
+
+ // ...
+
+ }
+
+.. versionadded:: 5.2
+
+ The ULID type and generator were introduced in Symfony 5.2.
+
+When using built-in Doctrine repository methods (e.g. ``findOneBy()``), Doctrine
+knows how to convert these ULID types to build the SQL query
+(e.g. ``->findOneBy(['user' => $user->getUlid()])``). However, when using DQL
+queries or building the query yourself, you'll need to set ``ulid`` as the type
+of the ULID parameters::
+
+ // src/Repository/ProductRepository.php
+
+ // ...
+ class ProductRepository extends ServiceEntityRepository
+ {
+ // ...
+
+ public function findUserProducts(User $user): array
+ {
+ $qb = $this->createQueryBuilder('p')
+ // ...
+ // add 'ulid' as the third argument to tell Doctrine that this is an ULID
+ ->setParameter('user', $user->getUlid(), 'ulid')
+
+ // alternatively, you can convert it to a value compatible with
+ // the type inferred by Doctrine
+ ->setParameter('user', $user->getUlid()->toBinary())
+ ;
+
+ // ...
+ }
+ }
+
+.. _`unique identifiers`: https://en.wikipedia.org/wiki/UID
+.. _`UUIDs`: https://en.wikipedia.org/wiki/Universally_unique_identifier
+.. _`ULIDs`: https://github.com/ulid/spec
diff --git a/components/validator/resources.rst b/components/validator/resources.rst
index 1455c2518bc..7f9b02fb544 100644
--- a/components/validator/resources.rst
+++ b/components/validator/resources.rst
@@ -158,10 +158,6 @@ implement the PSR-6 interface :class:`Psr\\Cache\\CacheItemPoolInterface`)::
->setMappingCache(new SomePsr6Cache());
->getValidator();
-.. versionadded:: 4.4
-
- Support for PSR-6 compatible mapping caches was introduced in Symfony 4.4.
-
.. note::
The loaders already use a singleton load mechanism. That means that the
diff --git a/components/var_dumper.rst b/components/var_dumper.rst
index a607ddeb59b..b661bd7a44a 100644
--- a/components/var_dumper.rst
+++ b/components/var_dumper.rst
@@ -66,7 +66,7 @@ current PHP SAPI:
You can also select the output format explicitly defining the
``VAR_DUMPER_FORMAT`` environment variable and setting its value to either
- ``html`` or ``cli``.
+ ``html``, ``cli`` or :ref:`server `.
.. note::
@@ -186,6 +186,39 @@ Then you can use the following command to start a server out-of-the-box:
$ ./vendor/bin/var-dump-server
[OK] Server listening on tcp://127.0.0.1:9912
+.. _var-dumper-dump-server-format:
+
+Configuring the Dump Server with Environment Variables
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. versionadded:: 5.2
+
+ The ``VAR_DUMPER_FORMAT=server`` feature was introduced in Symfony 5.2.
+
+If you prefer to not modify the application configuration (e.g. to quickly debug
+a project given to you) use the ``VAR_DUMPER_FORMAT`` env var.
+
+First, start the server as usual:
+
+.. code-block:: terminal
+
+ $ ./vendor/bin/var-dump-server
+
+Then, run your code with the ``VAR_DUMPER_FORMAT=server`` env var by configuring
+this value in the :ref:`.env file of your application `. For
+console commands, you can also define this env var as follows:
+
+.. code-block:: terminal
+
+ $ VAR_DUMPER_FORMAT=server [your-cli-command]
+
+.. note::
+
+ The host used by the ``server`` format is the one configured in the
+ ``VAR_DUMPER_SERVER`` env var or ``127.0.0.1:9912`` if none is defined.
+ If you prefer, you can also configure the host in the ``VAR_DUMPER_FORMAT``
+ env var like this: ``VAR_DUMPER_FORMAT=tcp://127.0.0.1:1234``.
+
DebugBundle and Twig Integration
--------------------------------
@@ -227,10 +260,6 @@ option. Read more about this and other options in
If you want to use your browser search input, press ``Ctrl. + F`` or
``Cmd. + F`` again while having focus on VarDumper's search input.
- .. versionadded:: 4.4
-
- The feature to use the browser search input was introduced in Symfony 4.4.
-
Using the VarDumper Component in your PHPUnit Test Suite
--------------------------------------------------------
@@ -258,11 +287,6 @@ The ``VarDumperTestTrait`` also includes these other methods:
is called automatically after each case to reset the custom configuration
made in ``setUpVarDumper()``.
-.. versionadded:: 4.4
-
- The ``setUpVarDumper()`` and ``tearDownVarDumper()`` methods were introduced
- in Symfony 4.4.
-
Example::
use PHPUnit\Framework\TestCase;
diff --git a/components/yaml.rst b/components/yaml.rst
index 763051ad6d1..29b8114ff53 100644
--- a/components/yaml.rst
+++ b/components/yaml.rst
@@ -390,10 +390,6 @@ 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: ~
-.. versionadded:: 4.4
-
- The flag to dump ``null`` as ``~`` was introduced in Symfony 4.4.
-
Syntax Validation
~~~~~~~~~~~~~~~~~
diff --git a/components/yaml/yaml_format.rst b/components/yaml/yaml_format.rst
index 46d43a797be..0cca9901836 100644
--- a/components/yaml/yaml_format.rst
+++ b/components/yaml/yaml_format.rst
@@ -122,7 +122,13 @@ Numbers
.. code-block:: yaml
# an octal
- 014
+ 0o14
+
+.. deprecated:: 5.1
+
+ In YAML 1.1, octal numbers use the notation ``0...``, whereas in YAML 1.2
+ the notation changes to ``0o...``. Symfony 5.1 added support for YAML 1.2
+ notation and deprecated support for YAML 1.1 notation.
.. code-block:: yaml
diff --git a/configuration.rst b/configuration.rst
index ecc34b99ddc..1b028499eb1 100644
--- a/configuration.rst
+++ b/configuration.rst
@@ -144,10 +144,6 @@ configuration files, even if they use a different format:
// ...
-.. versionadded:: 4.4
-
- The ``not_found`` option value for ``ignore_errors`` was introduced in Symfony 4.4.
-
.. _config-parameter-intro:
.. _config-parameters-yml:
.. _configuration-parameters:
@@ -616,10 +612,6 @@ Define a default value in case the environment variable is not set:
DB_USER=
DB_PASS=${DB_USER:-root}pass # results in DB_PASS=rootpass
-.. versionadded:: 4.4
-
- The support for default values has been introduced in Symfony 4.4.
-
Embed commands via ``$()`` (not supported on Windows):
.. code-block:: bash
@@ -740,10 +732,6 @@ their values by running:
# run this command to show all the details for a specific env var:
$ php bin/console debug:container --env-var=FOO
-.. versionadded:: 4.3
-
- The option to debug environment variables was introduced in Symfony 4.3.
-
.. _configuration-accessing-parameters:
Accessing Configuration Parameters
diff --git a/configuration/dot-env-changes.rst b/configuration/dot-env-changes.rst
index df418e6ea75..89844d991b1 100644
--- a/configuration/dot-env-changes.rst
+++ b/configuration/dot-env-changes.rst
@@ -45,16 +45,12 @@ If you created your application after November 15th 2018, you don't need to make
any changes! Otherwise, here is the list of changes you'll need to make - these
changes can be made to any Symfony 3.4 or higher app:
-#. Create a new `config/bootstrap.php`_ file in your project. This file loads Composer's
- autoloader and loads all the ``.env`` files as needed (note: in an earlier recipe,
- this file was called ``src/.bootstrap.php``; if you are upgrading from Symfony 3.3
- or 4.1, use the `3.3/config/bootstrap.php`_ file instead).
+#. Update your ``public/index.php`` file to add the code of the `public/index.php`_
+ file provided by Symfony. If you've customized this file, make sure to keep
+ those changes (but add the rest of the changes made by Symfony).
-#. Update your `public/index.php`_ (`index.php diff`_) file to load the new ``config/bootstrap.php``
- file. If you've customized this file, make sure to keep those changes (but use
- the rest of the changes).
-
-#. Update your `bin/console`_ file to load the new ``config/bootstrap.php`` file.
+#. Update your ``bin/console`` file to add the code of the `bin/console`_ file
+ provided by Symfony.
#. Update ``.gitignore``:
@@ -86,14 +82,11 @@ changes can be made to any Symfony 3.4 or higher app:
You can also update the `comment on the top of .env`_ to reflect the new changes.
#. If you're using PHPUnit, you will also need to `create a new .env.test`_ file
- and update your `phpunit.xml.dist file`_ so it loads the ``config/bootstrap.php``
+ and update your `phpunit.xml.dist file`_ so it loads the ``tests/bootstrap.php``
file.
-.. _`config/bootstrap.php`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/4.2/config/bootstrap.php
-.. _`3.3/config/bootstrap.php`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/3.3/config/bootstrap.php
-.. _`public/index.php`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/4.2/public/index.php
-.. _`index.php diff`: https://github.com/symfony/recipes/compare/8a4e5555e30d5dff64275e2788a901f31a214e79...86e2b6795c455f026e5ab0cba2aff2c7a18511f7#diff-7d73eabd1e5eb7d969ddf9a7ce94f954
-.. _`bin/console`: https://github.com/symfony/recipes/blob/master/symfony/console/3.3/bin/console
+.. _`public/index.php`: https://github.com/symfony/recipes/blob/master/symfony/framework-bundle/5.1/public/index.php
+.. _`bin/console`: https://github.com/symfony/recipes/blob/master/symfony/console/5.1/bin/console
.. _`comment on the top of .env`: https://github.com/symfony/recipes/blob/master/symfony/flex/1.0/.env
.. _`create a new .env.test`: https://github.com/symfony/recipes/blob/master/symfony/phpunit-bridge/3.3/.env.test
.. _`phpunit.xml.dist file`: https://github.com/symfony/recipes/blob/master/symfony/phpunit-bridge/3.3/phpunit.xml.dist
diff --git a/configuration/env_var_processors.rst b/configuration/env_var_processors.rst
index 7b134067bef..407b5137fbc 100644
--- a/configuration/env_var_processors.rst
+++ b/configuration/env_var_processors.rst
@@ -164,7 +164,9 @@ Symfony provides the following env var processors:
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://symfony.com/schema/dic/security"
xsi:schemaLocation="http://symfony.com/schema/dic/services
- https://symfony.com/schema/dic/services/services-1.0.xsd">
+ https://symfony.com/schema/dic/services/services-1.0.xsd
+ http://symfony.com/schema/dic/security
+ https://symfony.com/schema/dic/security/security-1.0.xsd">
Symfony\Component\HttpFoundation\Request::METHOD_HEAD
@@ -397,10 +399,6 @@ Symfony provides the following env var processors:
'auth' => '%env(require:PHP_FILE)%',
]);
- .. versionadded:: 4.3
-
- The ``require`` processor was introduced in Symfony 4.3.
-
``env(trim:FOO)``
Trims the content of ``FOO`` env var, removing whitespaces from the beginning
and end of the string. This is especially useful in combination with the
@@ -443,10 +441,6 @@ Symfony provides the following env var processors:
'auth' => '%env(trim:file:AUTH_FILE)%',
]);
- .. versionadded:: 4.3
-
- The ``trim`` processor was introduced in Symfony 4.3.
-
``env(key:FOO:BAR)``
Retrieves the value associated with the key ``FOO`` from the array whose
contents are stored in the ``BAR`` env var:
@@ -528,10 +522,6 @@ Symfony provides the following env var processors:
When the fallback parameter is omitted (e.g. ``env(default::API_KEY)``), the
value returned is ``null``.
- .. versionadded:: 4.3
-
- The ``default`` processor was introduced in Symfony 4.3.
-
``env(url:FOO)``
Parses an absolute URL and returns its components as an associative array.
@@ -601,10 +591,6 @@ Symfony provides the following env var processors:
In order to ease extraction of the resource from the URL, the leading
``/`` is trimmed from the ``path`` component.
- .. versionadded:: 4.3
-
- The ``url`` processor was introduced in Symfony 4.3.
-
``env(query_string:FOO)``
Parses the query string part of the given URL and returns its components as
an associative array.
@@ -651,10 +637,6 @@ Symfony provides the following env var processors:
],
]);
- .. versionadded:: 4.3
-
- The ``query_string`` processor was introduced in Symfony 4.3.
-
It is also possible to combine any number of processors:
.. configuration-block::
@@ -717,7 +699,7 @@ create a class that implements
class LowercasingEnvVarProcessor implements EnvVarProcessorInterface
{
- public function getEnv($prefix, $name, \Closure $getEnv)
+ public function getEnv(string $prefix, string $name, \Closure $getEnv)
{
$env = $getEnv($name);
diff --git a/configuration/micro_kernel_trait.rst b/configuration/micro_kernel_trait.rst
index 16402bd5a54..890f60d1ca8 100644
--- a/configuration/micro_kernel_trait.rst
+++ b/configuration/micro_kernel_trait.rst
@@ -24,12 +24,11 @@ Next, create an ``index.php`` file that defines the kernel class and runs it::
// index.php
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
- use Symfony\Component\Config\Loader\LoaderInterface;
- use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
- use Symfony\Component\Routing\RouteCollectionBuilder;
+ use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
require __DIR__.'/vendor/autoload.php';
@@ -37,29 +36,27 @@ Next, create an ``index.php`` file that defines the kernel class and runs it::
{
use MicroKernelTrait;
- public function registerBundles()
+ public function registerBundles(): array
{
return [
- new Symfony\Bundle\FrameworkBundle\FrameworkBundle()
+ new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
];
}
- protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
+ protected function configureContainer(ContainerConfigurator $c): void
{
// PHP equivalent of config/packages/framework.yaml
- $c->loadFromExtension('framework', [
+ $c->extension('framework', [
'secret' => 'S0ME_SECRET'
]);
}
- protected function configureRoutes(RouteCollectionBuilder $routes)
+ protected function configureRoutes(RoutingConfigurator $routes): void
{
- // kernel is a service that points to this class
- // optional 3rd argument is the route name
- $routes->add('/random/{limit}', 'kernel::randomNumber');
+ $routes->add('random_number', '/random/{limit}')->controller([$this, 'randomNumber']);
}
- public function randomNumber($limit)
+ public function randomNumber(int $limit): JsonResponse
{
return new JsonResponse([
'number' => random_int(0, $limit),
@@ -91,15 +88,15 @@ that define your bundles, your services and your routes:
**registerBundles()**
This is the same ``registerBundles()`` that you see in a normal kernel.
-**configureContainer(ContainerBuilder $c, LoaderInterface $loader)**
+**configureContainer(ContainerConfigurator $c)**
This method builds and configures the container. In practice, you will use
- ``loadFromExtension`` to configure different bundles (this is the equivalent
+ ``extension()`` to configure different bundles (this is the equivalent
of what you see in a normal ``config/packages/*`` file). You can also register
services directly in PHP or load external configuration files (shown below).
-**configureRoutes(RouteCollectionBuilder $routes)**
+**configureRoutes(RoutingConfigurator $routes)**
Your job in this method is to add routes to the application. The
- ``RouteCollectionBuilder`` has methods that make adding routes in PHP more
+ ``RoutingConfigurator`` has methods that make adding routes in PHP more
fun. You can also load external routing files (shown below).
Advanced Example: Twig, Annotations and the Web Debug Toolbar
@@ -134,16 +131,15 @@ hold the kernel. Now it looks like this::
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
- use Symfony\Component\Config\Loader\LoaderInterface;
- use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
- use Symfony\Component\Routing\RouteCollectionBuilder;
+ use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
- public function registerBundles()
+ public function registerBundles(): array
{
$bundles = [
new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
@@ -151,45 +147,52 @@ hold the kernel. Now it looks like this::
];
if ($this->getEnvironment() == 'dev') {
- $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
+ $bundles[] = new \Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
}
return $bundles;
}
- protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
+ protected function configureContainer(ContainerConfigurator $c): void
{
- $loader->load(__DIR__.'/../config/framework.yaml');
+ $c->import(__DIR__.'/../config/framework.yaml');
+
+ // register all classes in /src/ as service
+ $c->services()
+ ->load('App\\', __DIR__.'/*')
+ ->autowire()
+ ->autoconfigure()
+ ;
// configure WebProfilerBundle only if the bundle is enabled
if (isset($this->bundles['WebProfilerBundle'])) {
- $c->loadFromExtension('web_profiler', [
+ $c->extension('web_profiler', [
'toolbar' => true,
'intercept_redirects' => false,
]);
}
}
- protected function configureRoutes(RouteCollectionBuilder $routes)
+ protected function configureRoutes(RoutingConfigurator $routes): void
{
// import the WebProfilerRoutes, only if the bundle is enabled
if (isset($this->bundles['WebProfilerBundle'])) {
- $routes->import('@WebProfilerBundle/Resources/config/routing/wdt.xml', '/_wdt');
- $routes->import('@WebProfilerBundle/Resources/config/routing/profiler.xml', '/_profiler');
+ $routes->import('@WebProfilerBundle/Resources/config/routing/wdt.xml')->prefix('/_wdt');
+ $routes->import('@WebProfilerBundle/Resources/config/routing/profiler.xml')->prefix('/_profiler');
}
// load the annotation routes
- $routes->import(__DIR__.'/../src/Controller/', '/', 'annotation');
+ $routes->import(__DIR__.'/Controller/', 'annotation');
}
// optional, to use the standard Symfony cache directory
- public function getCacheDir()
+ public function getCacheDir(): string
{
return __DIR__.'/../var/cache/'.$this->getEnvironment();
}
// optional, to use the standard Symfony logs directory
- public function getLogDir()
+ public function getLogDir(): string
{
return __DIR__.'/../var/log';
}
@@ -245,6 +248,7 @@ has one file in it::
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+ use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class MicroController extends AbstractController
@@ -252,7 +256,7 @@ has one file in it::
/**
* @Route("/random/{limit}")
*/
- public function randomNumber($limit)
+ public function randomNumber(int $limit): Response
{
$number = random_int(0, $limit);
@@ -327,7 +331,6 @@ As before you can use the :doc:`Symfony Local Web Server
.. code-block:: terminal
- cd public/
$ symfony server:start
Then visit the page in your browser: http://localhost:8000/random/10
diff --git a/configuration/override_dir_structure.rst b/configuration/override_dir_structure.rst
index e0bd417886d..46a17e71351 100644
--- a/configuration/override_dir_structure.rst
+++ b/configuration/override_dir_structure.rst
@@ -41,8 +41,8 @@ at your project root directory.
Override the Cache Directory
----------------------------
-You can change the default cache directory by overriding the ``getCacheDir()``
-method in the ``Kernel`` class of your application::
+Changing the cache directory can be achieved by overriding the
+``getCacheDir()`` method in the ``Kernel`` class of your application::
// src/Kernel.php
@@ -61,6 +61,9 @@ In this code, ``$this->environment`` is the current environment (i.e. ``dev``).
In this case you have changed the location of the cache directory to
``var/{environment}/cache/``.
+You can also change the cache directory defining an environment variable named
+``APP_CACHE_DIR`` whose value is the full path of the cache folder.
+
.. caution::
You should keep the cache directory different for each environment,
@@ -73,9 +76,11 @@ In this case you have changed the location of the cache directory to
Override the Log Directory
--------------------------
-Overriding the ``var/log/`` directory is the same as overriding the ``var/cache/``
-directory. The only difference is that you need to override the ``getLogDir()``
-method::
+Overriding the ``var/log/`` directory is almost the same as overriding the
+``var/cache/`` directory.
+
+You can do it overriding the ``getLogDir()`` method in the ``Kernel`` class of
+your application::
// src/Kernel.php
@@ -92,6 +97,9 @@ method::
Here you have changed the location of the directory to ``var/{environment}/log/``.
+You can also change the log directory defining an environment variable named
+``APP_LOG_DIR`` whose value is the full path of the log folder.
+
.. _override-templates-dir:
Override the Templates Directory
diff --git a/configuration/secrets.rst b/configuration/secrets.rst
index bb89c67258f..696ce519682 100644
--- a/configuration/secrets.rst
+++ b/configuration/secrets.rst
@@ -4,10 +4,6 @@
How to Keep Sensitive Information Secret
========================================
-.. versionadded:: 4.4
-
- The Secrets management was introduced in Symfony 4.4.
-
:ref:`Environment variables ` are the best way to store configuration
that depends on where the application is run - for example, some API key that
might be set to one value while developing locally and another value on production.
diff --git a/console.rst b/console.rst
index e980cea046a..63287faac82 100644
--- a/console.rst
+++ b/console.rst
@@ -48,16 +48,31 @@ want a command to create a user::
// ... put here the code to create the user
// this method must return an integer number with the "exit status code"
- // of the command.
+ // of the command. You can also use these constants to make code more readable
// return this if there was no problem running the command
- return 0;
+ // (it's equivalent to returning int(0))
+ return Command::SUCCESS;
// or return this if some error happened during the execution
- // return 1;
+ // (it's equivalent to returning int(1))
+ // return Command::FAILURE;
+
+ // or return this to indicate incorrect command usage; e.g. invalid options
+ // or missing arguments (it's equivalent to returning int(2))
+ // return Command::INVALID
}
}
+.. versionadded:: 5.1
+
+ The ``Command::SUCCESS`` and ``Command::FAILURE`` constants were introduced
+ in Symfony 5.1.
+
+.. versionadded:: 5.3
+
+ The ``Command::INVALID`` constant was introduced in Symfony 5.3
+
Configuring the Command
-----------------------
@@ -156,7 +171,7 @@ the console::
$output->write('You are about to ');
$output->write('create a user.');
- return 0;
+ return Command::SUCCESS;
}
Now, try executing the command:
@@ -215,7 +230,7 @@ method, which returns an instance of
$section1->clear(2);
// Output is now completely empty!
- return 0;
+ return Command::SUCCESS;
}
}
@@ -257,7 +272,7 @@ Use input options or arguments to pass information to the command::
// retrieve the argument value using getArgument()
$output->writeln('Username: '.$input->getArgument('username'));
- return 0;
+ return Command::SUCCESS;
}
Now, you can pass the username to the command:
@@ -308,7 +323,7 @@ as a service, you can use normal dependency injection. Imagine you have a
$output->writeln('User successfully generated!');
- return 0;
+ return Command::SUCCESS;
}
}
@@ -332,14 +347,9 @@ command:
: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 should
+ 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`_.
- .. deprecated:: 4.4
-
- Not returning an integer with the exit status as the result of
- ``execute()`` is deprecated since Symfony 4.4.
-
.. _console-testing-commands:
Testing Commands
@@ -376,12 +386,20 @@ console::
// the output of the command in the console
$output = $commandTester->getDisplay();
- $this->assertContains('Username: Wouter', $output);
+ $this->assertStringContainsString('Username: Wouter', $output);
// ...
}
}
+If you are using a :doc:`single-command application `,
+call ``setAutoExit(false)`` on it to get the command result in ``CommandTester``.
+
+.. versionadded:: 5.2
+
+ The ``setAutoExit()`` method for single-command applications was introduced
+ in Symfony 5.2.
+
.. tip::
You can also test a whole console application by using
diff --git a/console/coloring.rst b/console/coloring.rst
index 774a2ab96fa..7e77a090b25 100644
--- a/console/coloring.rst
+++ b/console/coloring.rst
@@ -40,13 +40,28 @@ It is possible to define your own styles using the
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
// ...
- $outputStyle = new OutputFormatterStyle('red', 'yellow', ['bold', 'blink']);
+ $outputStyle = new OutputFormatterStyle('red', '#ff0', ['bold', 'blink']);
$output->getFormatter()->setStyle('fire', $outputStyle);
$output->writeln('foo>');
-Available foreground and background colors are: ``black``, ``red``, ``green``,
-``yellow``, ``blue``, ``magenta``, ``cyan`` and ``white``.
+Any hex color is supported for foreground and background colors. Besides that, these named colors are supported:
+``black``, ``red``, ``green``, ``yellow``, ``blue``, ``magenta``, ``cyan``, ``white``,
+``gray``, ``bright-red``, ``bright-green``, ``bright-yellow``, ``bright-blue``,
+``bright-magenta``, ``bright-cyan`` and ``bright-white``.
+
+.. versionadded:: 5.2
+
+ True (hex) color support was introduced in Symfony 5.2
+
+.. versionadded:: 5.3
+
+ Support for bright colors was introduced in Symfony 5.3.
+
+.. note::
+
+ If the terminal doesn't support true colors, the nearest named color is used.
+ E.g. ``#c0392b`` is degraded to ``red`` or ``#f1c40f`` is degraded to ``yellow``.
And available options are: ``bold``, ``underscore``, ``blink``, ``reverse``
(enables the "reverse video" mode where the background and foreground colors
@@ -59,6 +74,9 @@ You can also set these colors and options directly inside the tag name::
// green text
$output->writeln('foo>');
+ // red text
+ $output->writeln('foo>');
+
// black text on a cyan background
$output->writeln('foo>');
@@ -77,10 +95,6 @@ You can also set these colors and options directly inside the tag name::
Displaying Clickable Links
~~~~~~~~~~~~~~~~~~~~~~~~~~
-.. versionadded:: 4.3
-
- The feature to display clickable links was introduced in Symfony 4.3.
-
Commands can use the special ```` tag to display links similar to the
```` elements of web pages::
diff --git a/console/commands_as_services.rst b/console/commands_as_services.rst
index fb5e7ff70eb..af4f275e221 100644
--- a/console/commands_as_services.rst
+++ b/console/commands_as_services.rst
@@ -45,8 +45,8 @@ For example, suppose you want to log something from within your command::
{
$this->logger->info('Waking up the sun');
// ...
-
- return 0;
+
+ return Command::SUCCESS;
}
}
@@ -63,13 +63,6 @@ command and start logging.
work (e.g. making database queries), as that code will be run, even if you're using
the console to execute a different command.
-.. note::
-
- In previous Symfony versions, you could make the command class extend from
- :class:`Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand` to
- get services via ``$this->getContainer()->get('SERVICE_ID')``. This is
- deprecated in Symfony 4.2 and it won't work in future Symfony versions.
-
.. _console-command-service-lazy-loading:
Lazy Loading
diff --git a/console/input.rst b/console/input.rst
index 0813bad58c5..8bd42ae6c85 100644
--- a/console/input.rst
+++ b/console/input.rst
@@ -52,6 +52,8 @@ You now have access to a ``last_name`` argument in your command::
}
$output->writeln($text.'!');
+
+ return Command::SUCCESS;
}
}
@@ -197,7 +199,7 @@ separation at all (e.g. ``-i 5`` or ``-i5``).
this situation, always place options after the command name, or avoid using
a space to separate the option name from its value.
-There are four option variants you can use:
+There are five option variants you can use:
``InputOption::VALUE_IS_ARRAY``
This option accepts multiple values (e.g. ``--dir=/foo --dir=/bar``);
@@ -214,6 +216,14 @@ There are four option variants you can use:
This option may or may not have a value (e.g. ``--yell`` or
``--yell=loud``).
+``InputOption::VALUE_NEGATABLE``
+ Accept either the flag (e.g. ``--yell``) or its negation (e.g.
+ ``--no-yell``).
+
+.. versionadded:: 5.3
+
+ The ``InputOption::VALUE_NEGATABLE`` constant was introduced in Symfony 5.3.
+
You can combine ``VALUE_IS_ARRAY`` with ``VALUE_REQUIRED`` or
``VALUE_OPTIONAL`` like this::
diff --git a/console/lockable_trait.rst b/console/lockable_trait.rst
index 36cd393907c..54f6e4b051d 100644
--- a/console/lockable_trait.rst
+++ b/console/lockable_trait.rst
@@ -27,7 +27,7 @@ that adds two convenient methods to lock and release commands::
if (!$this->lock()) {
$output->writeln('The command is already running in another process.');
- return 0;
+ return Command::SUCCESS;
}
// If you prefer to wait until the lock is released, use this:
@@ -39,8 +39,12 @@ that adds two convenient methods to lock and release commands::
// automatically when the execution of the command ends
$this->release();
- return 0;
+ return Command::SUCCESS;
}
}
+.. versionadded:: 5.1
+
+ The ``Command::SUCCESS`` constant was introduced in Symfony 5.1.
+
.. _`locks`: https://en.wikipedia.org/wiki/Lock_(computer_science)
diff --git a/console/style.rst b/console/style.rst
index dd981436e50..66db35011b1 100644
--- a/console/style.rst
+++ b/console/style.rst
@@ -152,10 +152,6 @@ Content Methods
]
);
- .. versionadded:: 4.4
-
- The ``horizontalTable()`` method was introduced in Symfony 4.4.
-
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::definitionList`
It displays the given ``key => value`` pairs as a compact list of elements::
@@ -169,10 +165,6 @@ Content Methods
['foo4' => 'bar4']
);
- .. versionadded:: 4.4
-
- The ``definitionList()`` method was introduced in Symfony 4.4.
-
: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
@@ -333,6 +325,27 @@ Result Methods
'Consectetur adipiscing elit',
]);
+:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::info`
+ It's similar to the ``success()`` method (the given string or array of strings
+ are displayed with a green background) but the ``[OK]`` label is not prefixed.
+ It's meant to be used once to display the final result of executing the given
+ command, without showing the result as a successful or failed one::
+
+ // use simple strings for short info messages
+ $io->info('Lorem ipsum dolor sit amet');
+
+ // ...
+
+ // consider using arrays when displaying long info messages
+ $io->info([
+ 'Lorem ipsum dolor sit amet',
+ 'Consectetur adipiscing elit',
+ ]);
+
+.. versionadded:: 5.2
+
+ The ``info()`` method was introduced in Symfony 5.2.
+
:method:`Symfony\\Component\\Console\\Style\\SymfonyStyle::warning`
It displays the given string or array of strings highlighted as a warning
message (with a red background and the ``[WARNING]`` label). It's meant to be
diff --git a/contributing/code/conventions.rst b/contributing/code/conventions.rst
index 3174ac93660..18ea420b841 100644
--- a/contributing/code/conventions.rst
+++ b/contributing/code/conventions.rst
@@ -146,40 +146,35 @@ A feature is marked as deprecated by adding a ``@deprecated`` PHPDoc to
relevant classes, methods, properties, ...::
/**
- * @deprecated since Symfony 2.8.
+ * @deprecated since Symfony 5.1.
*/
The deprecation message must indicate the version in which the feature was deprecated,
and whenever possible, how it was replaced::
/**
- * @deprecated since Symfony 2.8, use Replacement instead.
+ * @deprecated since Symfony 5.1, use Replacement instead.
*/
When the replacement is in another namespace than the deprecated class, its FQCN must be used::
/**
- * @deprecated since Symfony 2.8, use A\B\Replacement instead.
+ * @deprecated since Symfony 5.1, use A\B\Replacement instead.
*/
-A PHP ``E_USER_DEPRECATED`` error must also be triggered to help people with the migration::
+A deprecation must also be triggered to help people with the migration
+(requires the ``symfony/deprecation-contracts`` package)::
- @trigger_error(sprintf('The "%s" class is deprecated since Symfony 2.8, use "%s" instead.', Deprecated::class, Replacement::class), E_USER_DEPRECATED);
+ trigger_deprecation('symfony/package-name', '5.1', 'The "%s" class is deprecated, use "%s" instead.', Deprecated::class, Replacement::class);
-Without the `@-silencing operator`_, users would need to opt-out from deprecation
-notices. Silencing swaps this behavior and allows users to opt-in when they are
-ready to cope with them (by adding a custom error handler like the one used by
-the Web Debug Toolbar or by the PHPUnit bridge).
-
-When deprecating a whole class the ``trigger_error()`` call should be placed
-after the use declarations, like in this example from
-`ServiceRouterLoader`_::
+When deprecating a whole class the ``trigger_deprecation()`` call should be placed
+after the use declarations, like in this example from `ServiceRouterLoader`_::
namespace Symfony\Component\Routing\Loader\DependencyInjection;
use Symfony\Component\Routing\Loader\ContainerLoader;
- @trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use "%s" instead.', ServiceRouterLoader::class, ContainerLoader::class), E_USER_DEPRECATED);
+ trigger_deprecation('symfony/routing', '4.4', 'The "%s" class is deprecated, use "%s" instead.', ServiceRouterLoader::class, ContainerLoader::class);
/**
* @deprecated since Symfony 4.4, use Symfony\Component\Routing\Loader\ContainerLoader instead.
@@ -232,5 +227,3 @@ of the impacted component:
* Remove the `Deprecated` class, use `Replacement` instead
This task is mandatory and must be done in the same pull request.
-
-.. _`@-silencing operator`: https://www.php.net/manual/en/language.operators.errorcontrol.php
diff --git a/contributing/code/pull_requests.rst b/contributing/code/pull_requests.rst
index e69371e9d5e..ed765eb9e52 100644
--- a/contributing/code/pull_requests.rst
+++ b/contributing/code/pull_requests.rst
@@ -25,7 +25,7 @@ Before working on Symfony, setup a friendly environment with the following
software:
* Git;
-* PHP version 7.1.3 or above.
+* PHP version 7.2.5 or above.
Configure Git
~~~~~~~~~~~~~
diff --git a/contributing/code/reproducer.rst b/contributing/code/reproducer.rst
index 771bd69eeac..6efae2a8ee8 100644
--- a/contributing/code/reproducer.rst
+++ b/contributing/code/reproducer.rst
@@ -65,8 +65,9 @@ to a route definition. Then, after creating your project:
of controllers, actions, etc. as in your original application.
#. Create a small controller and add your routing definition that shows the bug.
#. Don't create or modify any other file.
-#. Execute ``composer require symfony/web-server-bundle`` and use the ``server:run``
- command to browse to the new route and see if the bug appears or not.
+#. Install the :doc:`local web server ` provided by Symfony
+ and use the ``symfony server:start`` command to browse to the new route and
+ see if the bug appears or not.
#. If you can see the bug, you're done and you can already share the code with us.
#. If you can't see the bug, you must keep making small changes. For example, if
your original route was defined using XML, forget about the previous route
diff --git a/contributing/code/standards.rst b/contributing/code/standards.rst
index 586a868aae2..88e015dc961 100644
--- a/contributing/code/standards.rst
+++ b/contributing/code/standards.rst
@@ -72,7 +72,7 @@ short example containing most features described below::
*/
public function someDeprecatedMethod()
{
- @trigger_error(sprintf('The %s() method is deprecated since vendor-name/package-name 2.8 and will be removed in 3.0. Use Acme\Baz::someMethod() instead.', __METHOD__), E_USER_DEPRECATED);
+ trigger_deprecation('symfony/package-name', '5.1', 'The %s() method is deprecated, use Acme\Baz::someMethod() instead.', __METHOD__);
return Baz::someMethod();
}
@@ -187,10 +187,6 @@ Structure
* Exception and error message strings must be concatenated using :phpfunction:`sprintf`;
-* Calls to :phpfunction:`trigger_error` with type ``E_USER_DEPRECATED`` must be
- switched to opt-in via ``@`` operator.
- Read more at :ref:`contributing-code-conventions-deprecations`;
-
* Do not use ``else``, ``elseif``, ``break`` after ``if`` and ``case`` conditions
which return or throw something;
diff --git a/contributing/community/releases.rst b/contributing/community/releases.rst
index 80d5033accd..008ebab81b7 100644
--- a/contributing/community/releases.rst
+++ b/contributing/community/releases.rst
@@ -9,10 +9,10 @@ published through a *time-based model*:
* A new **Symfony patch version** (e.g. 4.4.12, 5.1.9) comes out roughly every
month. It only contains bug fixes, so you can safely upgrade your applications;
-* A new **Symfony minor version** (e.g. 4.4, 5.0, 5.1) comes out every *six months*:
+* A new **Symfony minor version** (e.g. 4.4, 5.1) comes out every *six months*:
one in *May* and one in *November*. It contains bug fixes and new features, but
it doesn't include any breaking change, so you can safely upgrade your applications;
-* A new **Symfony major version** (e.g. 4.0, 5.0) comes out every *two years*.
+* A new **Symfony major version** (e.g. 4.0, 5.0, 6.0) comes out every *two years*.
It can contain breaking changes, so you may need to do some changes in your
applications before upgrading.
diff --git a/contributing/community/reviews.rst b/contributing/community/reviews.rst
index 14d8b71a28d..342ba431201 100644
--- a/contributing/community/reviews.rst
+++ b/contributing/community/reviews.rst
@@ -150,7 +150,7 @@ Pick a pull request from the `PRs in need of review`_ and follow these steps:
* Does the code break backward compatibility? If yes, does the PR header say
so?
* Does the PR contain deprecations? If yes, does the PR header say so? Does
- the code contain ``trigger_error()`` statements for all deprecated
+ the code contain ``trigger_deprecation()`` statements for all deprecated
features?
* Are all deprecations and backward compatibility breaks documented in the
latest UPGRADE-X.X.md file? Do those explanations contain "Before"/"After"
diff --git a/contributing/documentation/format.rst b/contributing/documentation/format.rst
index 1f6f1787918..2c465096f0b 100644
--- a/contributing/documentation/format.rst
+++ b/contributing/documentation/format.rst
@@ -104,6 +104,7 @@ Markup Format Use It to Display
``html+php`` PHP code blended with HTML
``ini`` INI
``php-annotations`` PHP Annotations
+``php-attributes`` PHP Attributes
=================== ======================================
Adding Links
@@ -173,39 +174,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:: 4.x``
+For a new feature or a behavior change use the ``.. versionadded:: 5.x``
directive:
.. code-block:: rst
- .. versionadded:: 4.2
+ .. versionadded:: 5.2
- Named autowiring aliases have been introduced in Symfony 4.2.
+ ... ... ... was introduced in Symfony 5.2.
If you are documenting a behavior change, it may be helpful to *briefly*
describe how the behavior has changed:
.. code-block:: rst
- .. versionadded:: 4.2
+ .. versionadded:: 5.2
- Support for ICU MessageFormat was introduced in Symfony 4.2. Prior to this,
- pluralization was managed by the ``transChoice`` method.
+ ... ... ... was introduced in Symfony 5.2. Prior to this,
+ ... ... ... ... ... ... ... ... .
-For a deprecation use the ``.. deprecated:: 4.x`` directive:
+For a deprecation use the ``.. deprecated:: 5.x`` directive:
.. code-block:: rst
- .. deprecated:: 4.2
+ .. deprecated:: 5.2
- Not passing the root node name to ``TreeBuilder`` was deprecated in Symfony 4.2.
+ ... ... ... was deprecated in Symfony 5.2.
-Whenever a new major version of Symfony is released (e.g. 5.0, 6.0, etc),
+Whenever a new major version of Symfony is released (e.g. 6.0, 7.0, etc),
a new branch of the documentation is created from the ``master`` branch.
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 5.0 were released today, 4.0 to 4.4 ``versionadded`` and ``deprecated``
-tags would be removed from the new ``5.0`` branch.
+Symfony 6.0 were released today, 5.0 to 5.4 ``versionadded`` and ``deprecated``
+tags would be removed from the new ``6.0`` branch.
.. _reStructuredText: https://docutils.sourceforge.io/rst.html
.. _Sphinx: https://www.sphinx-doc.org/
diff --git a/controller.rst b/controller.rst
index 212d0a2b509..a5017452832 100644
--- a/controller.rst
+++ b/controller.rst
@@ -394,7 +394,7 @@ Request object.
Managing the Session
--------------------
-Symfony provides a session service that you can use to store information
+Symfony provides a session object that you can use to store information
about the user between requests. Session is enabled by default, but will only be
started if you read or write from it.
diff --git a/controller/argument_value_resolver.rst b/controller/argument_value_resolver.rst
index 90763c591c0..c9693bbaf9b 100644
--- a/controller/argument_value_resolver.rst
+++ b/controller/argument_value_resolver.rst
@@ -53,17 +53,6 @@ In addition, some components and official bundles provide other value resolvers:
the controller can be accessed by anonymous users. It requires installing
the :doc:`Security component `.
-:class:`Symfony\\Bundle\\SecurityBundle\\SecurityUserValueResolver`
- Injects the object that represents the current logged in user if type-hinted
- with ``UserInterface``. Default value can be set to ``null`` in case
- the controller can be accessed by anonymous users. It requires installing
- the `SecurityBundle`_.
-
-.. deprecated:: 4.1
-
- The ``SecurityUserValueResolver`` was deprecated in Symfony 4.1 in favor of
- :class:`Symfony\\Component\\Security\\Http\\Controller\\UserValueResolver`.
-
``Psr7ServerRequestResolver``
Injects a `PSR-7`_ compliant version of the current request if type-hinted
with ``RequestInterface``, ``MessageInterface`` or ``ServerRequestInterface``.
@@ -238,11 +227,17 @@ and adding a priority.
.. code-block:: php
// config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
use App\ArgumentResolver\UserValueResolver;
- $container->autowire(UserValueResolver::class)
- ->addTag('controller.argument_value_resolver', ['priority' => 50])
- ;
+ return static function (ContainerConfigurator $container) {
+ $services = $configurator->services();
+
+ $services->set(UserValueResolver::class)
+ ->tag('controller.argument_value_resolver', ['priority' => 50])
+ ;
+ };
While adding a priority is optional, it's recommended to add one to make sure
the expected value is injected. The built-in ``RequestAttributeValueResolver``,
@@ -265,6 +260,5 @@ passing the user along sub-requests).
.. _`@ParamConverter`: https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
.. _`yield`: https://www.php.net/manual/en/language.generators.syntax.php
-.. _`SecurityBundle`: https://github.com/symfony/security-bundle
.. _`PSR-7`: https://www.php-fig.org/psr/psr-7/
.. _`SensioFrameworkExtraBundle`: https://github.com/sensiolabs/SensioFrameworkExtraBundle
diff --git a/controller/error_pages.rst b/controller/error_pages.rst
index f3c8256e453..337723d8605 100644
--- a/controller/error_pages.rst
+++ b/controller/error_pages.rst
@@ -317,7 +317,7 @@ error pages.
.. note::
- If your listener calls ``setResponse()`` on the
+ If your listener calls ``setThrowable()`` on the
:class:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent`,
event, propagation will be stopped and the response will be sent to
the client.
diff --git a/controller/service.rst b/controller/service.rst
index ca2f09b5d70..f8048e09def 100644
--- a/controller/service.rst
+++ b/controller/service.rst
@@ -182,12 +182,12 @@ Base Controller Methods and Their Service Replacements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The best way to see how to replace base ``Controller`` convenience methods is to
-look at the `ControllerTrait`_ that holds its logic.
+look at the `AbstractController`_ class that holds its logic.
If you want to know what type-hints to use for each service, see the
``getSubscribedServices()`` method in `AbstractController`_.
-.. _`Controller class source code`: https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php
-.. _`ControllerTrait`: https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php
+.. _`Controller class source code`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
+.. _`AbstractController`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
.. _`AbstractController`: https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php
.. _`ADR pattern`: https://en.wikipedia.org/wiki/Action%E2%80%93domain%E2%80%93responder
diff --git a/controller/upload_file.rst b/controller/upload_file.rst
index dad80ee957d..edd17ed50dc 100644
--- a/controller/upload_file.rst
+++ b/controller/upload_file.rst
@@ -129,13 +129,14 @@ Finally, you need to update the code of the controller that handles the form::
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
+ use Symfony\Component\String\Slugger\SluggerInterface;
class ProductController extends AbstractController
{
/**
* @Route("/product/new", name="app_product_new")
*/
- public function new(Request $request)
+ public function new(Request $request, SluggerInterface $slugger)
{
$product = new Product();
$form = $this->createForm(ProductType::class, $product);
@@ -150,7 +151,7 @@ Finally, you need to update the code of the controller that handles the form::
if ($brochureFile) {
$originalFilename = pathinfo($brochureFile->getClientOriginalName(), PATHINFO_FILENAME);
// this is needed to safely include the file name as part of the URL
- $safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename);
+ $safeFilename = $slugger->slug($originalFilename);
$newFilename = $safeFilename.'-'.uniqid().'.'.$brochureFile->guessExtension();
// Move the file to the directory where brochures are stored
@@ -199,19 +200,13 @@ 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::getExtension`),
- the original file size (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientSize`)
+ the original file size (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getSize`)
and the original file name (:method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientOriginalName`).
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;
-.. deprecated:: 4.1
-
- The :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::getClientSize`
- method was deprecated in Symfony 4.1 and will be removed in Symfony 5.0.
- Use ``getSize()`` instead.
-
You can use the following code to link to the PDF brochure of a product:
.. code-block:: html+twig
@@ -244,20 +239,23 @@ logic to a separate service::
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\File\UploadedFile;
+ use Symfony\Component\String\Slugger\SluggerInterface;
class FileUploader
{
private $targetDirectory;
+ private $slugger;
- public function __construct($targetDirectory)
+ public function __construct($targetDirectory, SluggerInterface $slugger)
{
$this->targetDirectory = $targetDirectory;
+ $this->slugger = $slugger;
}
public function upload(UploadedFile $file)
{
$originalFilename = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
- $safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename);
+ $safeFilename = $this->slugger->slug($originalFilename);
$fileName = $safeFilename.'-'.uniqid().'.'.$file->guessExtension();
try {
@@ -319,10 +317,17 @@ Then, define a service for this class:
.. code-block:: php
// config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
use App\Service\FileUploader;
- $container->autowire(FileUploader::class)
- ->setArgument('$targetDirectory', '%brochures_directory%');
+ return static function (ContainerConfigurator $container) {
+ $services = $configurator->services();
+
+ $services->set(FileUploader::class)
+ ->arg('$targetDirectory', '%brochures_directory%')
+ ;
+ };
Now you're ready to use this service in the controller::
diff --git a/create_framework/http_kernel_httpkernelinterface.rst b/create_framework/http_kernel_httpkernelinterface.rst
index 9bda9e5c731..a5c46c8daaa 100644
--- a/create_framework/http_kernel_httpkernelinterface.rst
+++ b/create_framework/http_kernel_httpkernelinterface.rst
@@ -118,22 +118,28 @@ The Response class contains methods that let you configure the HTTP cache. One
of the most powerful is ``setCache()`` as it abstracts the most frequently used
caching strategies into a single array::
- $date = date_create_from_format('Y-m-d H:i:s', '2005-10-15 10:00:00');
-
$response->setCache([
- 'public' => true,
- 'etag' => 'abcde',
- 'last_modified' => $date,
- 'max_age' => 10,
- 's_maxage' => 10,
+ 'must_revalidate' => false,
+ 'no_cache' => false,
+ 'no_store' => false,
+ 'no_transform' => false,
+ 'public' => true,
+ 'private' => false,
+ 'proxy_revalidate' => false,
+ 'max_age' => 600,
+ 's_maxage' => 600,
+ 'immutable' => true,
+ 'last_modified' => new \DateTime(),
+ 'etag' => 'abcdef'
]);
// it is equivalent to the following code
$response->setPublic();
+ $response->setMaxAge(600);
+ $response->setSharedMaxAge(600);
+ $response->setImmutable();
+ $response->setLastModified(new \DateTime());
$response->setEtag('abcde');
- $response->setLastModified($date);
- $response->setMaxAge(10);
- $response->setSharedMaxAge(10);
When using the validation model, the ``isNotModified()`` method allows you to
cut on the response time by short-circuiting the response generation as early as
diff --git a/create_framework/unit_testing.rst b/create_framework/unit_testing.rst
index 099ada7e704..a4d6d401c33 100644
--- a/create_framework/unit_testing.rst
+++ b/create_framework/unit_testing.rst
@@ -49,7 +49,7 @@ resolver. Modify the framework to make use of them::
namespace Simplex;
// ...
-
+
use Calendar\Controller\LeapYearController;
use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
@@ -183,7 +183,7 @@ Response::
$response = $framework->handle(new Request());
$this->assertEquals(200, $response->getStatusCode());
- $this->assertContains('Yep, this is a leap year!', $response->getContent());
+ $this->assertStringContainsString('Yep, this is a leap year!', $response->getContent());
}
In this test, we simulate a route that matches and returns a simple
diff --git a/deployment/proxies.rst b/deployment/proxies.rst
index 285dd221b11..95d1ddfd0c9 100644
--- a/deployment/proxies.rst
+++ b/deployment/proxies.rst
@@ -22,32 +22,93 @@ Solution: ``setTrustedProxies()``
---------------------------------
To fix this, you need to tell Symfony which reverse proxy IP addresses to trust
-and what headers your reverse proxy uses to send information::
+and what headers your reverse proxy uses to send information:
+
+.. configuration-block::
+
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ # ...
+ // the IP address (or range) of your proxy
+ trusted_proxies: '192.0.0.1,10.0.0.0/8'
+ // trust *all* "X-Forwarded-*" headers (the ! prefix means to not trust those headers)
+ trusted_headers: ['x-forwarded-all', '!x-forwarded-host', '!x-forwarded-prefix']
+ // or, if your proxy instead uses the "Forwarded" header
+ trusted_headers: ['forwarded', '!x-forwarded-host', '!x-forwarded-prefix']
+ // or, if you're using a wellknown proxy
+ trusted_headers: [!php/const Symfony\\Component\\HttpFoundation\\Request::HEADER_X_FORWARDED_AWS_ELB, '!x-forwarded-host', '!x-forwarded-prefix']
+ trusted_headers: [!php/const Symfony\\Component\\HttpFoundation\\Request::HEADER_X_FORWARDED_TRAEFIK, '!x-forwarded-host', '!x-forwarded-prefix']
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+ 192.0.0.1,10.0.0.0/8
+
+
+ x-forwarded-all
+ !x-forwarded-host
+ !x-forwarded-prefix
+
+
+ forwarded
+ !x-forwarded-host
+ !x-forwarded-prefix
+
+
+
+ .. code-block:: php
+
+ // config/packages/framework.php
+ use Symfony\Component\HttpFoundation\Request;
+
+ $container->loadFromExtension('framework', [
+ // the IP address (or range) of your proxy
+ 'trusted_proxies' => '192.0.0.1,10.0.0.0/8',
+ // trust *all* "X-Forwarded-*" headers (the ! prefix means to not trust those headers)
+ 'trusted_headers' => ['x-forwarded-all', '!x-forwarded-host', '!x-forwarded-prefix'],
+ // or, if your proxy instead uses the "Forwarded" header
+ 'trusted_headers' => ['forwarded', '!x-forwarded-host', '!x-forwarded-prefix'],
+ // or, if you're using a wellknown proxy
+ 'trusted_headers' => [Request::HEADER_X_FORWARDED_AWS_ELB, '!x-forwarded-host', '!x-forwarded-prefix'],
+ 'trusted_headers' => [Request::HEADER_X_FORWARDED_TRAEFIK, '!x-forwarded-host', '!x-forwarded-prefix'],
+ ]);
+
+.. deprecated:: 5.2
+
+ In previous Symfony versions, the above example used ``HEADER_X_FORWARDED_ALL``
+ to trust all "X-Forwarded-" headers, but that constant is deprecated since
+ Symfony 5.2 in favor of the individual ``HEADER_X_FORWARDED_*`` constants.
- // public/index.php
-
- // ...
- $request = Request::createFromGlobals();
-
- // tell Symfony about your reverse proxy
- Request::setTrustedProxies(
- // the IP address (or range) of your proxy
- ['192.0.0.1', '10.0.0.0/8'],
-
- // trust *all* "X-Forwarded-*" headers
- Request::HEADER_X_FORWARDED_ALL
-
- // or, if your proxy instead uses the "Forwarded" header
- // Request::HEADER_FORWARDED
+.. caution::
- // or, if you're using AWS ELB
- // Request::HEADER_X_FORWARDED_AWS_ELB
- );
+ Enabling the ``Request::HEADER_X_FORWARDED_HOST`` option exposes the
+ application to `HTTP Host header attacks`_. Make sure the proxy really
+ sends an ``x-forwarded-host`` header.
The Request object has several ``Request::HEADER_*`` constants that control exactly
*which* headers from your reverse proxy are trusted. The argument is a bit field,
so you can also pass your own value (e.g. ``0b00110``).
+.. versionadded:: 5.2
+
+ The feature to configure trusted proxies and headers with ``trusted_proxies``
+ and ``trusted_headers`` options was introduced in Symfony 5.2. In earlier
+ Symfony versions you needed to use the ``Request::setTrustedProxies()``
+ method in the ``public/index.php`` file.
+
.. caution::
The "trusted proxies" feature does not work as expected when using the
@@ -64,23 +125,19 @@ In this case, you'll need to - *very carefully* - trust *all* proxies.
other than your load balancers. For AWS, this can be done with `security groups`_.
#. Once you've guaranteed that traffic will only come from your trusted reverse
- proxies, configure Symfony to *always* trust incoming request::
+ proxies, configure Symfony to *always* trust incoming request:
- // public/index.php
+ .. code-block:: yaml
- // ...
- Request::setTrustedProxies(
- // trust *all* requests (the 'REMOTE_ADDR' string is replaced at
- // run time by $_SERVER['REMOTE_ADDR'])
- ['127.0.0.1', 'REMOTE_ADDR'],
+ # config/packages/framework.yaml
+ framework:
+ # ...
+ // trust *all* requests (the 'REMOTE_ADDR' string is replaced at
+ // run time by $_SERVER['REMOTE_ADDR'])
+ trusted_proxies: '127.0.0.1,REMOTE_ADDR'
- // if you're using ELB, otherwise use a constant from above
- Request::HEADER_X_FORWARDED_AWS_ELB
- );
-
-.. versionadded:: 4.4
-
- The support for the ``REMOTE_ADDR`` option was introduced in Symfony 4.4.
+ // if you're using ELB, otherwise use another Request::HEADER-* constant
+ trusted_headers: [!php/const Symfony\\Component\\HttpFoundation\\Request::HEADER_X_FORWARDED_AWS_ELB, '!x-forwarded-host', '!x-forwarded-prefix']
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
@@ -96,6 +153,12 @@ other information.
# .env
TRUSTED_PROXIES=127.0.0.1,REMOTE_ADDR
+ .. code-block:: yaml
+
+ # config/packages/framework.yaml
+ framework:
+ # ...
+ trusted_proxies: '%env(TRUSTED_PROXIES)%'
If you are also using a reverse proxy on top of your load balancer (e.g.
`CloudFront`_), calling ``$request->server->get('REMOTE_ADDR')`` won't be
@@ -107,11 +170,13 @@ trusted proxies.
Custom Headers When Using a Reverse Proxy
-----------------------------------------
-Some reverse proxies (like `CloudFront`_ with ``CloudFront-Forwarded-Proto``) may force you to use a custom header.
-For instance you have ``Custom-Forwarded-Proto`` instead of ``X-Forwarded-Proto``.
+Some reverse proxies (like `CloudFront`_ with ``CloudFront-Forwarded-Proto``)
+may force you to use a custom header. For instance you have
+``Custom-Forwarded-Proto`` instead of ``X-Forwarded-Proto``.
-In this case, you'll need to set the header ``X-Forwarded-Proto`` with the value of
-``Custom-Forwarded-Proto`` early enough in your application, i.e. before handling the request::
+In this case, you'll need to set the header ``X-Forwarded-Proto`` with the value
+of ``Custom-Forwarded-Proto`` early enough in your application, i.e. before
+handling the request::
// public/index.php
@@ -123,4 +188,5 @@ In this case, you'll need to set the header ``X-Forwarded-Proto`` with the value
.. _`security groups`: https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-groups.html
.. _`CloudFront`: https://en.wikipedia.org/wiki/Amazon_CloudFront
.. _`CloudFront IP ranges`: https://ip-ranges.amazonaws.com/ip-ranges.json
+.. _`HTTP Host header attacks`: https://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html
.. _`nginx realip module`: http://nginx.org/en/docs/http/ngx_http_realip_module.html
diff --git a/doctrine.rst b/doctrine.rst
index 8226bf22700..f44a1c36ff1 100644
--- a/doctrine.rst
+++ b/doctrine.rst
@@ -232,7 +232,7 @@ If everything worked, you should see something like this:
SUCCESS!
- Next: Review the new migration "src/Migrations/Version20180207231217.php"
+ Next: Review the new migration "migrations/Version20180207231217.php"
Then: Run the migration with php bin/console doctrine:migrations:migrate
If you open this file, it contains the SQL needed to update your database! To run
@@ -495,10 +495,6 @@ doesn't replace the validation configuration entirely. You still need to add
some :doc:`validation constraints ` to ensure that data
provided by the user is correct.
-.. versionadded:: 4.3
-
- The automatic validation has been added in Symfony 4.3.
-
Fetching Objects from the Database
----------------------------------
diff --git a/doctrine/events.rst b/doctrine/events.rst
index c98be25d736..3eea84aff4f 100644
--- a/doctrine/events.rst
+++ b/doctrine/events.rst
@@ -200,22 +200,28 @@ with the ``doctrine.event_listener`` tag:
.. code-block:: php
// config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
use App\EventListener\SearchIndexer;
- // listeners are applied by default to all Doctrine connections
- $container->autowire(SearchIndexer::class)
- ->addTag('doctrine.event_listener', [
- // this is the only required option for the lifecycle listener tag
- 'event' => 'postPersist',
+ return static function (ContainerConfigurator $container) {
+ $services = $configurator->services();
+
+ // listeners are applied by default to all Doctrine connections
+ $services->set(SearchIndexer::class)
+ ->tag('doctrine.event_listener', [
+ // this is the only required option for the lifecycle listener tag
+ 'event' => 'postPersist',
- // 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,
+ // 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,
- # you can also restrict listeners to a specific Doctrine connection
- 'connection' => 'default',
- ])
- ;
+ # you can also restrict listeners to a specific Doctrine connection
+ 'connection' => 'default',
+ ])
+ ;
+ };
.. tip::
@@ -316,33 +322,35 @@ with the ``doctrine.orm.entity_listener`` tag:
.. code-block:: php
// config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
use App\Entity\User;
use App\EventListener\UserChangedNotifier;
- $container->autowire(UserChangedNotifier::class)
- ->addTag('doctrine.orm.entity_listener', [
- // These are the options required to define the entity listener:
- 'event' => 'postUpdate',
- 'entity' => User::class,
-
- // These are other options that you may define if needed:
+ return static function (ContainerConfigurator $container) {
+ $services = $configurator->services();
- // set the 'lazy' option to TRUE to only instantiate listeners when they are used
- // 'lazy' => true,
+ $services->set(UserChangedNotifier::class)
+ ->tag('doctrine.orm.entity_listener', [
+ // These are the options required to define the entity listener:
+ 'event' => 'postUpdate',
+ 'entity' => User::class,
- // set the 'entity_manager' option if the listener is not associated to the default manager
- // 'entity_manager' => 'custom',
+ // These are other options that you may define if needed:
- // by default, Symfony looks for a method called after the event (e.g. postUpdate())
- // if it doesn't exist, it tries to execute the '__invoke()' method, but you can
- // configure a custom method name with the 'method' option
- // 'method' => 'checkUserChanges',
- ])
- ;
+ // set the 'lazy' option to TRUE to only instantiate listeners when they are used
+ // 'lazy' => true,
-.. versionadded:: 4.4
+ // set the 'entity_manager' option if the listener is not associated to the default manager
+ // 'entity_manager' => 'custom',
- Support for invokable listeners (using the ``__invoke()`` method) was introduced in Symfony 4.4.
+ // by default, Symfony looks for a method called after the event (e.g. postUpdate())
+ // if it doesn't exist, it tries to execute the '__invoke()' method, but you can
+ // configure a custom method name with the 'method' option
+ // 'method' => 'checkUserChanges',
+ ])
+ ;
+ };
Doctrine Lifecycle Subscribers
------------------------------
@@ -440,11 +448,17 @@ with the ``doctrine.event_subscriber`` tag:
.. code-block:: php
// config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
use App\EventListener\DatabaseActivitySubscriber;
- $container->autowire(DatabaseActivitySubscriber::class)
- ->addTag('doctrine.event_subscriber')
- ;
+ return static function (ContainerConfigurator $container) {
+ $services = $configurator->services();
+
+ $services->set(DatabaseActivitySubscriber::class)
+ ->tag('doctrine.event_subscriber')
+ ;
+ };
If you need to associate the subscriber with a specific Doctrine connection, you
can do it in the service configuration:
@@ -479,11 +493,17 @@ can do it in the service configuration:
.. code-block:: php
// config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
use App\EventListener\DatabaseActivitySubscriber;
- $container->autowire(DatabaseActivitySubscriber::class)
- ->addTag('doctrine.event_subscriber', ['connection' => 'default'])
- ;
+ return static function (ContainerConfigurator $container) {
+ $services = $configurator->services();
+
+ $services->set(DatabaseActivitySubscriber::class)
+ ->tag('doctrine.event_subscriber', ['connection' => 'default'])
+ ;
+ };
.. tip::
diff --git a/doctrine/multiple_entity_managers.rst b/doctrine/multiple_entity_managers.rst
index ba3475dbfbc..d7a546783f5 100644
--- a/doctrine/multiple_entity_managers.rst
+++ b/doctrine/multiple_entity_managers.rst
@@ -193,8 +193,8 @@ for each entity manager, but you are free to define the same connection for both
the connection or entity manager, the default (i.e. ``default``) is used.
If you use a different name than ``default`` for the default entity manager,
- you will need to redefine the default entity manager in ``prod`` environment
- configuration too:
+ you will need to redefine the default entity manager in the ``prod`` environment
+ configuration and in the Doctrine migrations configuration (if you use that):
.. code-block:: yaml
@@ -205,6 +205,13 @@ for each entity manager, but you are free to define the same connection for both
# ...
+ .. code-block:: yaml
+
+ # config/packages/doctrine_migrations.yaml
+ doctrine_migrations:
+ # ...
+ em: 'your default entity manager name'
+
When working with multiple connections to create your databases:
.. code-block:: terminal
diff --git a/event_dispatcher.rst b/event_dispatcher.rst
index 2b95a637e28..038a405b10b 100644
--- a/event_dispatcher.rst
+++ b/event_dispatcher.rst
@@ -67,12 +67,6 @@ The most common way to listen to an event is to register an **event listener**::
Check out the :doc:`Symfony events reference ` to see
what type of object each event provides.
-.. versionadded:: 4.3
-
- The :class:`Symfony\\Component\\HttpKernel\\Event\\ExceptionEvent` class was
- introduced in Symfony 4.3. In previous versions it was called
- ``Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent``.
-
Now that the class is created, you need to register it as a service and
notify Symfony that it is a "listener" on the ``kernel.exception`` event by
using a special "tag":
@@ -106,11 +100,17 @@ using a special "tag":
.. code-block:: php
// config/services.php
+ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
+
use App\EventListener\ExceptionListener;
- $container->register(ExceptionListener::class)
- ->tag('kernel.event_listener', ['event' => 'kernel.exception'])
- ;
+ return function(ContainerConfigurator $configurator) {
+ $services = $configurator->services();
+
+ $services->set(ExceptionListener::class)
+ ->tag('kernel.event_listener', ['event' => 'kernel.exception'])
+ ;
+ };
Symfony follows this logic to decide which method to call inside the event
listener class:
@@ -275,11 +275,6 @@ name (FQCN) of the corresponding event class::
}
}
-.. versionadded:: 4.3
-
- Referring Symfony's core events via the FQCN of the event class is possible
- since Symfony 4.3.
-
Internally, the event FQCN are treated as aliases for the original event names.
Since the mapping already happens when compiling the service container, event
listeners and subscribers using FQCN instead of event names will appear under
@@ -310,10 +305,6 @@ The compiler pass will always extend the existing list of aliases. Because of
that, it is safe to register multiple instances of the pass with different
configurations.
-.. versionadded:: 4.4
-
- The ``AddEventAliasesPass`` class was introduced in Symfony 4.4.
-
Debugging Event Listeners
-------------------------
@@ -331,6 +322,29 @@ its name:
$ php bin/console debug:event-dispatcher kernel.exception
+or can get everything which partial matches the event name:
+
+.. code-block:: terminal
+
+ $ php bin/console debug:event-dispatcher kernel // matches "kernel.exception", "kernel.response" etc.
+ $ php bin/console debug:event-dispatcher Security // matches "Symfony\Component\Security\Http\Event\CheckPassportEvent"
+
+.. versionadded:: 5.3
+
+ The ability to match partial event names was introduced in Symfony 5.3.
+
+The :doc:`new experimental Security `
+system adds an event dispatcher per firewall. Use the ``--dispatcher`` option to
+get the registered listeners for a particular event dispatcher:
+
+.. code-block:: terminal
+
+ $ php bin/console debug:event-dispatcher --dispatcher=security.event_dispatcher.main
+
+.. versionadded:: 5.3
+
+ The ``dispatcher`` option was introduced in Symfony 5.3.
+
Learn more
----------
diff --git a/form/bootstrap4.rst b/form/bootstrap4.rst
index ccf4659738c..6f3b878ed1b 100644
--- a/form/bootstrap4.rst
+++ b/form/bootstrap4.rst
@@ -115,10 +115,6 @@ for **all** users.
Custom Forms
------------
-.. versionadded:: 4.4
-
- Support for the ``switch-custom`` class was introduced in Symfony 4.4.
-
Bootstrap 4 has a feature called "`custom forms`_". You can enable that on your
Symfony Form ``RadioType`` and ``CheckboxType`` by adding some classes to the label:
diff --git a/form/create_custom_field_type.rst b/form/create_custom_field_type.rst
index 2d13673be33..248818aa6d3 100644
--- a/form/create_custom_field_type.rst
+++ b/form/create_custom_field_type.rst
@@ -430,12 +430,6 @@ libraries are used in your application:
{% endfor %}
{% endblock %}
-.. note::
-
- Symfony 4.2 deprecated calling ``FormRenderer::searchAndRenderBlock`` for
- fields that have already been rendered. That's why the previous example
- includes the ``... if not child.rendered`` statement.
-
The first part of the Twig block name (e.g. ``postal_address``) comes from the
class name (``PostalAddressType`` -> ``postal_address``). This can be controlled
by overriding the ``getBlockPrefix()`` method in ``PostalAddressType``. The
diff --git a/form/data_mappers.rst b/form/data_mappers.rst
index c14eabd7683..24ff0716f5f 100644
--- a/form/data_mappers.rst
+++ b/form/data_mappers.rst
@@ -98,7 +98,7 @@ in your form type::
/**
* @param Color|null $viewData
*/
- public function mapDataToForms($viewData, $forms): void
+ public function mapDataToForms($viewData, \Traversable $forms): void
{
// there is no data yet, so nothing to prepopulate
if (null === $viewData) {
@@ -119,7 +119,7 @@ in your form type::
$forms['blue']->setData($viewData->getBlue());
}
- public function mapFormsToData($forms, &$viewData): void
+ public function mapFormsToData(\Traversable $forms, &$viewData): void
{
/** @var FormInterface[] $forms */
$forms = iterator_to_array($forms);
@@ -189,6 +189,45 @@ method::
Cool! When using the ``ColorType`` form, the custom data mapper methods will
create a new ``Color`` object now.
+Mapping Form Fields Using Callbacks
+-----------------------------------
+
+Conveniently, you can also map data from and into a form field by using the
+``getter`` and ``setter`` options. For example, suppose you have a form with some
+fields and only one of them needs to be mapped in some special way or you only
+need to change how it's written into the underlying object. In that case, register
+a PHP callable that is able to write or read to/from that specific object::
+
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ // ...
+
+ $builder->add('state', ChoiceType::class, [
+ 'choices' => [
+ 'active' => true,
+ 'paused' => false,
+ ],
+ 'getter' => function (Task $task, FormInterface $form): bool {
+ return !$task->isCancelled() && !$task->isPaused();
+ },
+ 'setter' => function (Task &$task, bool $state, FormInterface $form): void {
+ if ($state) {
+ $task->activate();
+ } else {
+ $task->pause();
+ }
+ },
+ ]);
+ }
+
+If available, these options have priority over the property path accessor and
+the default data mapper will still use the :doc:`PropertyAccess component `
+for the other form fields.
+
+.. versionadded:: 5.2
+
+ The ``getter`` and ``setter`` options were introduced in Symfony 5.2.
+
.. caution::
When a form has the ``inherit_data`` option set to ``true``, it does not use the data mapper and
diff --git a/form/data_transformers.rst b/form/data_transformers.rst
index aa0e88789bf..3c93fd66012 100644
--- a/form/data_transformers.rst
+++ b/form/data_transformers.rst
@@ -324,10 +324,6 @@ end-user error message in the data transformer using the
}
}
-.. versionadded:: 4.3
-
- The ``setInvalidMessage()`` method was introduced in Symfony 4.3.
-
That's it! If you're using the
:ref:`default services.yaml configuration `,
Symfony will automatically know to pass your ``TaskType`` an instance of the
diff --git a/form/form_customization.rst b/form/form_customization.rst
index 81d4007ce9e..125b01e749a 100644
--- a/form/form_customization.rst
+++ b/form/form_customization.rst
@@ -320,10 +320,6 @@ spot (since it'll render the field for you).
form_parent(form_view)
......................
-.. versionadded:: 4.3
-
- The ``form_parent()`` function was introduced in Symfony 4.3.
-
Returns the parent form view or ``null`` if the form view already is the
root form. Using this function should be preferred over accessing the parent
form using ``form.parent``. The latter way will produce different results
diff --git a/form/form_themes.rst b/form/form_themes.rst
index 1b5a3594a2d..927da208176 100644
--- a/form/form_themes.rst
+++ b/form/form_themes.rst
@@ -35,7 +35,14 @@ in a single Twig template and they are enabled in the
``bootstrap_3_horizontal_layout.html.twig`` but updated for Bootstrap 4 styles.
* `foundation_5_layout.html.twig`_, wraps each form field inside a ``