Merge branch '3.x' into 4.x

* 3.x:
  Escaper performance: avoid static variables
  Fix compatibility with Symfony 8
  Add documentation for use_yield option
  Stof suggestion about empty content
  Update templates.rst: Removing duplication
  Update configuration keys + allow extra keys for extensions
  replace typographic quote with straigt single quote
  Add TwigCsFixer in tools list
  Update reduce.rst
  Bump version
  Prepare the 3.21.1 release
  Update CHANGELOG
  Fix warning
  Fix instantiation
This commit is contained in:
Nicolas Grekas
2025-07-29 10:22:22 +02:00
8 changed files with 46 additions and 38 deletions
+11
View File
@@ -143,6 +143,17 @@ The following options are available:
(default to ``-1`` -- all optimizations are enabled; set it to ``0`` to
disable).
* ``use_yield`` *boolean*
``true``: forces templates to exclusively use ``yield`` instead of ``echo``
(all extensions must be yield ready)
``false`` (default): allows templates to use a mix of ``yield`` and ``echo``
calls to allow for a progressive migration.
Switch to ``true`` when possible as this will be the only supported mode in
Twig 4.0.
Loaders
-------
+4 -3
View File
@@ -11,12 +11,13 @@ When writing Twig templates, we recommend you to follow these official coding
standards:
* Put exactly one space after the start of a delimiter (``{{``, ``{%``,
and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``):
and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``)
if the content is non empty:
.. code-block:: twig
{{ user }}
{# comment #}
{# comment #} {##}
{% if user %}{% endif %}
When using the whitespace control character, do not put any spaces between
@@ -25,7 +26,7 @@ standards:
.. code-block:: twig
{{- user -}}
{#- comment -#}
{#- comment -#} {#--#}
{%- if user -%}{%- endif -%}
* Put exactly one space before and after the following operators:
+2 -8
View File
@@ -10,14 +10,14 @@ value and key of the sequence or mapping:
{% set numbers = [1, 2, 3] %}
{{ numbers|reduce((carry, v, k) => carry + v * k) }}
{{ numbers|reduce((carry, value, key) => carry + value * key) }}
{# output 8 #}
The ``reduce`` filter takes an ``initial`` value as a second argument:
.. code-block:: twig
{{ numbers|reduce((carry, v, k) => carry + v * k, 10) }}
{{ numbers|reduce((carry, value, key) => carry + value * key, 10) }}
{# output 18 #}
Note that the arrow function has access to the current context.
@@ -27,9 +27,3 @@ Arguments
* ``arrow``: The arrow function
* ``initial``: The initial value
.. note::
Internally, Twig uses the PHP `array_reduce`_ function.
.. _`array_reduce`: https://www.php.net/array_reduce
+1 -1
View File
@@ -9,7 +9,7 @@ Converts an argument to a date to allow date comparison:
{# do something #}
{% endif %}
The argument must be in one of PHPs supported `date and time formats`_.
The argument must be in one of PHP's supported `date and time formats`_.
You can pass a timezone as the second argument:
+2 -5
View File
@@ -68,11 +68,10 @@ Many IDEs support syntax highlighting and auto-completion for Twig:
You might also be interested in:
* `Twig CS Fixer`_: a tool to check/fix your templates code style
* `Twig Language Server`_: provides some language features like syntax
highlighting, diagnostics, auto complete, ...
* `TwigQI`_: an extension which analyzes your templates for common bugs during compilation
* `TwigStan`_: a static analyzer for Twig templates powered by PHPStan
Variables
@@ -858,9 +857,6 @@ The following operators don't fit into any of the other categories:
{{ user.name }}
Twig supports a specific syntax via the ``[]`` operator for accessing items
on sequences and mappings, like in ``user['name']``:
After the ``.``, you can use any expression by wrapping it with parenthesis
``()``.
@@ -1077,6 +1073,7 @@ Twig can be extended. If you want to create your own extensions, read the
.. _`TwigStan`: https://github.com/twigstan/twigstan
.. _`Twig pack`: https://marketplace.visualstudio.com/items?itemName=bajdzis.vscode-twig-pack
.. _`Modern Twig`: https://marketplace.visualstudio.com/items?itemName=Stanislav.vscode-twig
.. _`Twig CS Fixer`: https://github.com/VincentLanglet/Twig-CS-Fixer
.. _`Twig Language Server`: https://github.com/kaermorchen/twig-language-server/tree/master/packages/language-server
.. _`Twiggy`: https://marketplace.visualstudio.com/items?itemName=moetelo.twiggy
.. _`PHP spaceship operator documentation`: https://www.php.net/manual/en/language.operators.comparison.php
@@ -19,13 +19,14 @@ class MissingExtensionSuggestorPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if ($container->getParameter('kernel.debug')) {
$twigDefinition = $container->getDefinition('twig');
$twigDefinition
->addMethodCall('registerUndefinedFilterCallback', [[new Reference('twig.missing_extension_suggestor'), 'suggestFilter']])
->addMethodCall('registerUndefinedFunctionCallback', [[new Reference('twig.missing_extension_suggestor'), 'suggestFunction']])
->addMethodCall('registerUndefinedTokenParserCallback', [[new Reference('twig.missing_extension_suggestor'), 'suggestTag']])
;
if (!$container->getParameter('kernel.debug')) {
return;
}
$twigDefinition = $container->getDefinition('twig');
$twigDefinition
->addMethodCall('registerUndefinedFilterCallback', [[new Reference('twig.missing_extension_suggestor'), 'suggestFilter']])
->addMethodCall('registerUndefinedFunctionCallback', [[new Reference('twig.missing_extension_suggestor'), 'suggestFunction']])
->addMethodCall('registerUndefinedTokenParserCallback', [[new Reference('twig.missing_extension_suggestor'), 'suggestTag']])
;
}
}
@@ -39,14 +39,14 @@ class Configuration implements ConfigurationInterface
}
/**
* Full configuration from {@link https://commonmark.thephpleague.com/2.3/configuration}.
* Full configuration from {@link https://commonmark.thephpleague.com/2.7/configuration}.
*/
private function addCommonMarkConfiguration(ArrayNodeDefinition $rootNode): void
{
$rootNode
->children()
->arrayNode('commonmark')
->ignoreExtraKeys()
->ignoreExtraKeys(false)
->children()
->arrayNode('renderer')
->info('Array of options for rendering HTML.')
@@ -68,6 +68,10 @@ class Configuration implements ConfigurationInterface
->info('The maximum nesting level for blocks.')
->defaultValue(\PHP_INT_MAX)
->end()
->integerNode('max_delimiters_per_line')
->info('The maximum number of strong/emphasis delimiters per line.')
->defaultValue(\PHP_INT_MAX)
->end()
->arrayNode('slug_normalizer')
->info('Array of options for configuring how URL-safe slugs are created.')
->children()
+12 -12
View File
@@ -140,6 +140,10 @@ final class EscaperRuntime implements RuntimeExtensionInterface
case 'html':
// see https://www.php.net/htmlspecialchars
if ('UTF-8' === $charset) {
return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8');
}
// Using a static variable to avoid initializing the array
// each time the function is called. Moving the declaration on the
// top of the function slow downs other escaping strategies.
@@ -195,7 +199,7 @@ final class EscaperRuntime implements RuntimeExtensionInterface
* Escape sequences supported only by JavaScript, not JSON, are omitted.
* \" is also supported but omitted, because the resulting string is not HTML safe.
*/
static $shortMap = [
$short = match ($char) {
'\\' => '\\\\',
'/' => '\\/',
"\x08" => '\b',
@@ -203,10 +207,11 @@ final class EscaperRuntime implements RuntimeExtensionInterface
"\x0A" => '\n',
"\x0D" => '\r',
"\x09" => '\t',
];
default => false,
};
if (isset($shortMap[$char])) {
return $shortMap[$char];
if ($short) {
return $short;
}
$codepoint = mb_ord($char, 'UTF-8');
@@ -288,18 +293,13 @@ final class EscaperRuntime implements RuntimeExtensionInterface
* entities that XML supports. Using HTML entities would result in this error:
* XML Parsing Error: undefined entity
*/
static $entityMap = [
return match ($ord) {
34 => '"', /* quotation mark */
38 => '&', /* ampersand */
60 => '<', /* less-than sign */
62 => '>', /* greater-than sign */
];
if (isset($entityMap[$ord])) {
return $entityMap[$ord];
}
return \sprintf('&#x%02X;', $ord);
default => \sprintf('&#x%02X;', $ord),
};
}
/*