diff --git a/doc/filters/country_name.rst b/doc/filters/country_name.rst index 434b0bda7..a30184de9 100644 --- a/doc/filters/country_name.rst +++ b/doc/filters/country_name.rst @@ -1,8 +1,7 @@ ``country_name`` ================ -The ``country_name`` filter returns the country name given its ISO-3166 -two-letter code: +The ``country_name`` filter returns the country name given its ISO-3166 code: .. code-block:: twig @@ -16,6 +15,9 @@ By default, the filter uses the current locale. You can pass it explicitly: {# États-Unis #} {{ 'US'|country_name('fr') }} + {# 美國 #} + {{ 'US'|country_name('zh_Hant_HK') }} + .. note:: The ``country_name`` filter is part of the ``IntlExtension`` which is not @@ -41,4 +43,6 @@ By default, the filter uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/currency_name.rst b/doc/filters/currency_name.rst index a35c49998..498dc9423 100644 --- a/doc/filters/currency_name.rst +++ b/doc/filters/currency_name.rst @@ -1,8 +1,7 @@ ``currency_name`` ================= -The ``currency_name`` filter returns the currency name given its three-letter -code: +The ``currency_name`` filter returns the currency name given its ISO 4217 code: .. code-block:: twig @@ -44,4 +43,6 @@ By default, the filter uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/currency_symbol.rst b/doc/filters/currency_symbol.rst index 84a048ed5..80843fba0 100644 --- a/doc/filters/currency_symbol.rst +++ b/doc/filters/currency_symbol.rst @@ -1,7 +1,7 @@ ``currency_symbol`` =================== -The ``currency_symbol`` filter returns the currency symbol given its three-letter +The ``currency_symbol`` filter returns the currency symbol given its ISO 4217 code: .. code-block:: twig @@ -44,4 +44,6 @@ By default, the filter uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/format_currency.rst b/doc/filters/format_currency.rst index c4c364a00..6f1036d33 100644 --- a/doc/filters/format_currency.rst +++ b/doc/filters/format_currency.rst @@ -74,4 +74,6 @@ Arguments * ``currency``: The currency * ``attrs``: A map of attributes -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/format_date.rst b/doc/filters/format_date.rst index cd6beba9f..ab0d609df 100644 --- a/doc/filters/format_date.rst +++ b/doc/filters/format_date.rst @@ -29,8 +29,10 @@ the :doc:`format_datetime` filter, but without the time. Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ * ``dateFormat``: The date format * ``pattern``: A date time pattern * ``timezone``: The date timezone * ``calendar``: The calendar ("gregorian" by default) + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/format_datetime.rst b/doc/filters/format_datetime.rst index 9caf1992c..7dcc7095f 100644 --- a/doc/filters/format_datetime.rst +++ b/doc/filters/format_datetime.rst @@ -94,7 +94,7 @@ The default timezone can also be set globally by calling ``setTimezone()``:: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ * ``dateFormat``: The date format * ``timeFormat``: The time format * ``pattern``: A date time pattern @@ -102,3 +102,4 @@ Arguments * ``calendar``: The calendar ("gregorian" by default) .. _ICU user guide: https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/format_number.rst b/doc/filters/format_number.rst index 900fa5ea9..e1bedc2fc 100644 --- a/doc/filters/format_number.rst +++ b/doc/filters/format_number.rst @@ -112,6 +112,8 @@ By default, the filter uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ * ``attrs``: A map of attributes * ``style``: The style of the number output + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/format_time.rst b/doc/filters/format_time.rst index 1e213e616..8709a6bcf 100644 --- a/doc/filters/format_time.rst +++ b/doc/filters/format_time.rst @@ -29,8 +29,10 @@ the :doc:`format_datetime` filter, but without the date. Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ * ``timeFormat``: The time format * ``pattern``: A date time pattern * ``timezone``: The date timezone * ``calendar``: The calendar ("gregorian" by default) + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/language_name.rst b/doc/filters/language_name.rst index 55c243920..ccffd20eb 100644 --- a/doc/filters/language_name.rst +++ b/doc/filters/language_name.rst @@ -1,8 +1,8 @@ ``language_name`` ================= -The ``language_name`` filter returns the language name given its two-letter -code: +The ``language_name`` filter returns the language name based on its ISO 639-1 +code, ISO 639-2 code, or other specific localized code: .. code-block:: twig @@ -44,4 +44,6 @@ By default, the filter uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/locale_name.rst b/doc/filters/locale_name.rst index c6d34cbc0..9e0df0738 100644 --- a/doc/filters/locale_name.rst +++ b/doc/filters/locale_name.rst @@ -1,8 +1,7 @@ ``locale_name`` =============== -The ``locale_name`` filter returns the locale name given its two-letter -code: +The ``locale_name`` filter returns the locale name given its code: .. code-block:: twig @@ -44,4 +43,6 @@ By default, the filter uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/plural.rst b/doc/filters/plural.rst index 536d9b382..b72f4a164 100644 --- a/doc/filters/plural.rst +++ b/doc/filters/plural.rst @@ -7,11 +7,11 @@ plural version: .. code-block:: twig {# English (en) rules are used by default #} - {{ 'partition'|pluralize() }} - partitions + {{ 'animal'|plural() }} + animals - {{ 'partition'|pluralize('fr') }} - partitions + {{ 'animal'|plural('fr') }} + animaux .. note:: diff --git a/doc/filters/slug.rst b/doc/filters/slug.rst index 773a42fac..f5b91b2da 100644 --- a/doc/filters/slug.rst +++ b/doc/filters/slug.rst @@ -56,4 +56,6 @@ Arguments --------- * ``separator``: The separator that is used to join words (defaults to ``-``) -* ``locale``: The locale of the original string (if none is specified, it will be automatically detected) +* ``locale``: The locale code of the original string as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/filters/timezone_name.rst b/doc/filters/timezone_name.rst index dfb22818b..26c6f8917 100644 --- a/doc/filters/timezone_name.rst +++ b/doc/filters/timezone_name.rst @@ -1,7 +1,7 @@ ``timezone_name`` ================= -The ``timezone_name`` filter returns the timezone name given a timezone identifier: +The ``timezone_name`` filter returns the timezone name given its ISO 8601 timezone identifier: .. code-block:: twig @@ -43,4 +43,6 @@ By default, the filter uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/functions/country_names.rst b/doc/functions/country_names.rst index 6064f946a..6b96f6186 100644 --- a/doc/functions/country_names.rst +++ b/doc/functions/country_names.rst @@ -40,4 +40,6 @@ By default, the function uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/functions/country_timezones.rst b/doc/functions/country_timezones.rst index ecbbc1c99..370a4beff 100644 --- a/doc/functions/country_timezones.rst +++ b/doc/functions/country_timezones.rst @@ -2,13 +2,15 @@ ===================== The ``country_timezones`` function returns the names of the timezones associated -with a given country code: +with a given country its ISO-3166 code: .. code-block:: twig {# Europe/Paris #} {{ country_timezones('FR')|join(', ') }} +If the specified country were to be unknown, it will return an empty array + .. note:: The ``country_timezones`` function is part of the ``IntlExtension`` which is not @@ -30,3 +32,8 @@ with a given country code: $twig = new \Twig\Environment(...); $twig->addExtension(new IntlExtension()); + +Arguments +--------- + +* ``country``: The country code \ No newline at end of file diff --git a/doc/functions/currency_names.rst b/doc/functions/currency_names.rst index 63c83bde3..c74882286 100644 --- a/doc/functions/currency_names.rst +++ b/doc/functions/currency_names.rst @@ -40,4 +40,6 @@ By default, the function uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/functions/language_names.rst b/doc/functions/language_names.rst index be3f8f703..91409eca6 100644 --- a/doc/functions/language_names.rst +++ b/doc/functions/language_names.rst @@ -40,4 +40,6 @@ By default, the function uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/functions/locale_names.rst b/doc/functions/locale_names.rst index af18bc946..bf587c387 100644 --- a/doc/functions/locale_names.rst +++ b/doc/functions/locale_names.rst @@ -40,4 +40,6 @@ By default, the function uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/functions/script_names.rst b/doc/functions/script_names.rst index 49b1282b4..7ce6ec4b7 100644 --- a/doc/functions/script_names.rst +++ b/doc/functions/script_names.rst @@ -40,4 +40,6 @@ By default, the function uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/functions/timezone_names.rst b/doc/functions/timezone_names.rst index 053176942..733af3dc0 100644 --- a/doc/functions/timezone_names.rst +++ b/doc/functions/timezone_names.rst @@ -40,4 +40,6 @@ By default, the function uses the current locale. You can pass it explicitly: Arguments --------- -* ``locale``: The locale +* ``locale``: The locale code as defined in `RFC 5646`_ + +.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646 diff --git a/doc/templates.rst b/doc/templates.rst index c7be3d950..f734631fb 100644 --- a/doc/templates.rst +++ b/doc/templates.rst @@ -122,9 +122,9 @@ You can assign values to variables inside code blocks. Assignments use the Filters ------- -Variables can be modified by **filters**. Filters are separated from the -variable by a pipe symbol (``|``). Multiple filters can be chained. The output -of one filter is applied to the next. +Variables and expressions can be modified by **filters**. Filters are separated +from the variable by a pipe symbol (``|``). Multiple filters can be chained. +The output of one filter is applied to the next. The following example removes all HTML tags from the ``name`` and title-cases it: @@ -152,6 +152,40 @@ To apply a filter on a section of code, wrap it with the Go to the :doc:`filters` page to learn more about built-in filters. +.. warning:: + + As the ``filter`` operator has the highest :ref:`precedence + `, use parentheses when filtering more "complex" + expressions: + + .. code-block:: twig + + {{ (1..5)|join(', ') }} + + {{ ('HELLO' ~ 'FABIEN')|lower }} + + A common mistake is to forget using parentheses for filters on negative + numbers as a negative number in Twig is represented by the ``-`` operator + followed by a positive number. As the ``-`` operator has a lower precedence + than the filter operator, it can lead to confusion: + + .. code-block:: twig + + {{ -1|abs }} {# returns -1 #} + {{ -1**0 }} {% returns -1 %} + + {# as it is equivalent to #} + + {{ -(1|abs) }} + {{ -(1**0) }} + + For such cases, use parentheses to force the precedence: + + .. code-block:: twig + + {{ (-1)|abs }} {# returns 1 as expected #} + {{ (-1)**0 }} {% returns 1 %} + Functions --------- @@ -786,7 +820,7 @@ The following operators don't fit into any of the other categories: .. code-block:: twig - (1..5)|join(', ') + {{ (1..5)|join(', ') }} * ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello " ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello diff --git a/src/ExpressionParser.php b/src/ExpressionParser.php index 4adda5063..467b6d465 100644 --- a/src/ExpressionParser.php +++ b/src/ExpressionParser.php @@ -167,7 +167,12 @@ class ExpressionParser $expr = $this->parseExpression(); $this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); - return $this->parsePostfixExpression($expr); + $expr = $this->parsePostfixExpression($expr); + if ($expr instanceof NegUnary) { + $expr->wrapInParentheses(); + } + + return $expr; } return $this->parsePrimaryExpression(); diff --git a/src/Node/Expression/Unary/AbstractUnary.php b/src/Node/Expression/Unary/AbstractUnary.php index e31e3f84b..b9b44ed4f 100644 --- a/src/Node/Expression/Unary/AbstractUnary.php +++ b/src/Node/Expression/Unary/AbstractUnary.php @@ -20,14 +20,26 @@ abstract class AbstractUnary extends AbstractExpression { public function __construct(Node $node, int $lineno) { - parent::__construct(['node' => $node], [], $lineno); + parent::__construct(['node' => $node], ['with_parentheses' => false], $lineno); + } + + public function wrapInParentheses(): void + { + $this->setAttribute('with_parentheses', true); } public function compile(Compiler $compiler): void { - $compiler->raw(' '); + if ($this->getAttribute('with_parentheses')) { + $compiler->raw('('); + } else { + $compiler->raw(' '); + } $this->operator($compiler); $compiler->subcompile($this->getNode('node')); + if ($this->getAttribute('with_parentheses')) { + $compiler->raw(')'); + } } abstract public function operator(Compiler $compiler): Compiler; diff --git a/tests/Fixtures/expressions/power.test b/tests/Fixtures/expressions/power.test index 84fd23692..5fb3fa4b5 100644 --- a/tests/Fixtures/expressions/power.test +++ b/tests/Fixtures/expressions/power.test @@ -8,6 +8,10 @@ Twig parses power expressions {{ a ** b }} {{ b ** a }} {{ b ** b }} +{{ -1**0 }} +{{ (-1)**0 }} +{{ -a**0 }} +{{ (-a)**0 }} --DATA-- return ['a' => 4, 'b' => -2] --EXPECT-- @@ -18,3 +22,7 @@ return ['a' => 4, 'b' => -2] 0.0625 16 0.25 +-1 +1 +-1 +1