mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-19 05:46:41 +00:00
Rename array to sequence
This commit is contained in:
@@ -4,6 +4,9 @@
|
|||||||
* Deprecate `Twig\ExpressionParser\parseHashExpression()` in favor of
|
* Deprecate `Twig\ExpressionParser\parseHashExpression()` in favor of
|
||||||
`Twig\ExpressionParser::parseMappingExpression()`
|
`Twig\ExpressionParser::parseMappingExpression()`
|
||||||
|
|
||||||
|
* Deprecate `Twig\ExpressionParser\parseArrayExpression()`` in favor of
|
||||||
|
`Twig\ExpressionParser::parseSequenceExpression()`
|
||||||
|
|
||||||
# 3.10.3 (2024-05-16)
|
# 3.10.3 (2024-05-16)
|
||||||
|
|
||||||
* Fix missing ; in generated code
|
* Fix missing ; in generated code
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ standards:
|
|||||||
{{ foo ~ bar }}
|
{{ foo ~ bar }}
|
||||||
{{ true ? true : false }}
|
{{ true ? true : false }}
|
||||||
|
|
||||||
* Put exactly one space after the ``:`` sign in mappings and ``,`` in arrays
|
* Put exactly one space after the ``:`` sign in mappings and ``,`` in sequences
|
||||||
and mappings:
|
and mappings:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
@@ -80,8 +80,8 @@ standards:
|
|||||||
{{ foo|default('foo') }}
|
{{ foo|default('foo') }}
|
||||||
{{ range(1..10) }}
|
{{ range(1..10) }}
|
||||||
|
|
||||||
* Do not put any spaces before and after the opening and the closing of arrays
|
* Do not put any spaces before and after the opening and the closing of
|
||||||
and mappings:
|
sequences and mappings:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,9 @@ Parser
|
|||||||
* The ``Twig\ExpressionParser::parseHashExpression()`` method is deprecated, use
|
* The ``Twig\ExpressionParser::parseHashExpression()`` method is deprecated, use
|
||||||
``Twig\ExpressionParser::parseMappingExpression()`` instead.
|
``Twig\ExpressionParser::parseMappingExpression()`` instead.
|
||||||
|
|
||||||
|
* The ``Twig\ExpressionParser::parseArrayExpression()`` method is deprecated, use
|
||||||
|
``Twig\ExpressionParser::parseSequenceExpression()`` instead.
|
||||||
|
|
||||||
Templates
|
Templates
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,6 @@ Arguments
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
* ``mime``: The mime type
|
* ``mime``: The mime type
|
||||||
* ``parameters``: An array of parameters
|
* ``parameters``: A mapping of parameters
|
||||||
|
|
||||||
.. _RFC 2397: https://tools.ietf.org/html/rfc2397
|
.. _RFC 2397: https://tools.ietf.org/html/rfc2397
|
||||||
|
|||||||
+10
-4
@@ -1,14 +1,20 @@
|
|||||||
``keys``
|
``keys``
|
||||||
========
|
========
|
||||||
|
|
||||||
The ``keys`` filter returns the keys of an array. It is useful when you want to
|
The ``keys`` filter returns the keys of a sequence or a mapping. It is useful
|
||||||
iterate over the keys of an array:
|
when you want to iterate over the keys of a sequence or a mapping:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
{% for key in array|keys %}
|
{% for key in [1, 2, 3, 4]|keys %}
|
||||||
...
|
{{ key }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{# outputs: 1 2 3 4 #}
|
||||||
|
|
||||||
|
{% for key in {a: 'a_value', b: 'b_value'}|keys %}
|
||||||
|
{{ key }}
|
||||||
|
{% endfor %}
|
||||||
|
{# outputs: a b #}
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
``merge``
|
``merge``
|
||||||
=========
|
=========
|
||||||
|
|
||||||
The ``merge`` filter merges an array with another array:
|
The ``merge`` filter merges sequences and mappings.
|
||||||
|
|
||||||
|
The ``merge`` filter also works on sequences:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
@@ -29,7 +31,7 @@ overridden.
|
|||||||
|
|
||||||
.. tip::
|
.. tip::
|
||||||
|
|
||||||
If you want to ensure that some values are defined in an array (by given
|
If you want to ensure that some values are defined in a mapping (by given
|
||||||
default values), reverse the two elements in the call:
|
default values), reverse the two elements in the call:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
``sort``
|
``sort``
|
||||||
========
|
========
|
||||||
|
|
||||||
The ``sort`` filter sorts an array:
|
The ``sort`` filter sorts sequences and mappings:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ The ``sort`` filter sorts an array:
|
|||||||
association. It supports Traversable objects by transforming
|
association. It supports Traversable objects by transforming
|
||||||
those to arrays.
|
those to arrays.
|
||||||
|
|
||||||
You can pass an arrow function to sort the array:
|
You can pass an arrow function to configure the sorting:
|
||||||
|
|
||||||
.. code-block:: html+twig
|
.. code-block:: html+twig
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ of strings:
|
|||||||
|
|
||||||
You can also pass a ``limit`` argument:
|
You can also pass a ``limit`` argument:
|
||||||
|
|
||||||
* If ``limit`` is positive, the returned array will contain a maximum of
|
* If ``limit`` is positive, the returned sequence will contain a maximum of
|
||||||
limit elements with the last element containing the rest of string;
|
limit elements with the last element containing the rest of string;
|
||||||
|
|
||||||
* If ``limit`` is negative, all components except the last -limit are
|
* If ``limit`` is negative, all components except the last -limit are
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
``url_encode``
|
``url_encode``
|
||||||
==============
|
==============
|
||||||
|
|
||||||
The ``url_encode`` filter percent encodes a given string as URL segment
|
The ``url_encode`` filter percent encodes a given string as URL segment or a
|
||||||
or an array as query string:
|
mapping as query string:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
``cycle``
|
``cycle``
|
||||||
=========
|
=========
|
||||||
|
|
||||||
The ``cycle`` function cycles on an array of values:
|
The ``cycle`` function cycles on a sequence or mapping:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
``for``
|
``for``
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Loop over each item in a sequence. For example, to display a list of users
|
Loop over each item in a sequence or a mapping. For example, to display a list
|
||||||
provided in a variable called ``users``:
|
of users provided in a variable called ``users``:
|
||||||
|
|
||||||
.. code-block:: html+twig
|
.. code-block:: html+twig
|
||||||
|
|
||||||
@@ -15,8 +15,8 @@ provided in a variable called ``users``:
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
A sequence can be either an array or an object implementing the
|
A sequence or a mapping can be either an array or an object implementing
|
||||||
``Traversable`` interface.
|
the ``Traversable`` interface.
|
||||||
|
|
||||||
If you do need to iterate over a sequence of numbers, you can use the ``..``
|
If you do need to iterate over a sequence of numbers, you can use the ``..``
|
||||||
operator:
|
operator:
|
||||||
|
|||||||
+5
-3
@@ -12,7 +12,7 @@ In the simplest form you can use it to test if an expression evaluates to
|
|||||||
<p>Our website is in maintenance mode. Please, come back later.</p>
|
<p>Our website is in maintenance mode. Please, come back later.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
You can also test if an array is not empty:
|
You can also test if a sequence or a mapping is not empty:
|
||||||
|
|
||||||
.. code-block:: html+twig
|
.. code-block:: html+twig
|
||||||
|
|
||||||
@@ -71,8 +71,10 @@ use more complex ``expressions`` there too:
|
|||||||
INF (Infinity) true
|
INF (Infinity) true
|
||||||
whitespace-only string true
|
whitespace-only string true
|
||||||
string "0" or '0' false
|
string "0" or '0' false
|
||||||
empty array false
|
empty sequence false
|
||||||
|
empty mapping false
|
||||||
null false
|
null false
|
||||||
non-empty array true
|
non-empty sequence true
|
||||||
|
non-empty mapping true
|
||||||
object true
|
object true
|
||||||
====================== ====================
|
====================== ====================
|
||||||
|
|||||||
+8
-8
@@ -102,7 +102,7 @@ If a variable or attribute does not exist, the behavior depends on the
|
|||||||
For convenience's sake ``foo.bar`` does the following things on the PHP
|
For convenience's sake ``foo.bar`` does the following things on the PHP
|
||||||
layer:
|
layer:
|
||||||
|
|
||||||
* check if ``foo`` is an array and ``bar`` a valid element;
|
* check if ``foo`` is a sequence or a mapping and ``bar`` a valid element;
|
||||||
* if not, and if ``foo`` is an object, check that ``bar`` is a valid property;
|
* if not, and if ``foo`` is an object, check that ``bar`` is a valid property;
|
||||||
* if not, and if ``foo`` is an object, check that ``bar`` is a valid method
|
* if not, and if ``foo`` is an object, check that ``bar`` is a valid method
|
||||||
(even if ``bar`` is the constructor - use ``__construct()`` instead);
|
(even if ``bar`` is the constructor - use ``__construct()`` instead);
|
||||||
@@ -115,7 +115,7 @@ If a variable or attribute does not exist, the behavior depends on the
|
|||||||
Twig also supports a specific syntax for accessing items on PHP arrays,
|
Twig also supports a specific syntax for accessing items on PHP arrays,
|
||||||
``foo['bar']``:
|
``foo['bar']``:
|
||||||
|
|
||||||
* check if ``foo`` is an array and ``bar`` a valid element;
|
* check if ``foo`` is a sequence or a mapping and ``bar`` a valid element;
|
||||||
* if not, and if ``strict_variables`` is ``false``, return ``null``;
|
* if not, and if ``strict_variables`` is ``false``, return ``null``;
|
||||||
* if not, throw an exception.
|
* if not, throw an exception.
|
||||||
|
|
||||||
@@ -531,7 +531,7 @@ exist:
|
|||||||
writing the number down. If a dot is present the number is a float,
|
writing the number down. If a dot is present the number is a float,
|
||||||
otherwise an integer.
|
otherwise an integer.
|
||||||
|
|
||||||
* ``["foo", "bar"]``: Arrays are defined by a sequence of expressions
|
* ``["foo", "bar"]``: Sequences are defined by a sequence of expressions
|
||||||
separated by a comma (``,``) and wrapped with squared brackets (``[]``).
|
separated by a comma (``,``) and wrapped with squared brackets (``[]``).
|
||||||
|
|
||||||
* ``{"foo": "bar"}``: Mappings are defined by a list of keys and values
|
* ``{"foo": "bar"}``: Mappings are defined by a list of keys and values
|
||||||
@@ -563,7 +563,7 @@ exist:
|
|||||||
* ``null``: ``null`` represents no specific value. This is the value returned
|
* ``null``: ``null`` represents no specific value. This is the value returned
|
||||||
when a variable does not exist. ``none`` is an alias for ``null``.
|
when a variable does not exist. ``none`` is an alias for ``null``.
|
||||||
|
|
||||||
Arrays and mappings can be nested:
|
Sequences and mappings can be nested:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
@@ -698,8 +698,8 @@ operand is contained in the right:
|
|||||||
|
|
||||||
.. tip::
|
.. tip::
|
||||||
|
|
||||||
You can use this filter to perform a containment test on strings, arrays,
|
You can use this filter to perform a containment test on strings,
|
||||||
or objects implementing the ``Traversable`` interface.
|
sequences, mappings, or objects implementing the ``Traversable`` interface.
|
||||||
|
|
||||||
To perform a negative test, use the ``not in`` operator:
|
To perform a negative test, use the ``not in`` operator:
|
||||||
|
|
||||||
@@ -785,7 +785,7 @@ The following operators don't fit into any of the other categories:
|
|||||||
{# returns the value of foo if it is defined and not null, 'no' otherwise #}
|
{# returns the value of foo if it is defined and not null, 'no' otherwise #}
|
||||||
{{ foo ?? 'no' }}
|
{{ foo ?? 'no' }}
|
||||||
|
|
||||||
* ``...``: The spread operator can be used to expand arrays or mappings (it
|
* ``...``: The spread operator can be used to expand sequences or mappings (it
|
||||||
cannot be used to expand the arguments of a function call):
|
cannot be used to expand the arguments of a function call):
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
@@ -827,7 +827,7 @@ Operator Score of precedence Description
|
|||||||
``**`` 200 Raises a number to the power of another
|
``**`` 200 Raises a number to the power of another
|
||||||
``??`` 300 Default value when a variable is null
|
``??`` 300 Default value when a variable is null
|
||||||
``+``, ``-`` 500 Unary operations on numbers
|
``+``, ``-`` 500 Unary operations on numbers
|
||||||
``|``,``[]``,``.`` - Filters, array, mapping, and attribute access
|
``|``,``[]``,``.`` - Filters, sequence, mapping, and attribute access
|
||||||
============================= =================================== =====================================================
|
============================= =================================== =====================================================
|
||||||
|
|
||||||
Without using any parentheses, the operator precedence rules are used to
|
Without using any parentheses, the operator precedence rules are used to
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
``empty``
|
``empty``
|
||||||
=========
|
=========
|
||||||
|
|
||||||
``empty`` checks if a variable is an empty string, an empty array, an empty
|
``empty`` checks if a variable is an empty string, an empty sequence, an empty
|
||||||
mapping, exactly ``false``, or exactly ``null``.
|
mapping, exactly ``false``, or exactly ``null``.
|
||||||
|
|
||||||
For objects that implement the ``Countable`` interface, ``empty`` will check the
|
For objects that implement the ``Countable`` interface, ``empty`` will check the
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ class ExpressionParser
|
|||||||
// no break
|
// no break
|
||||||
default:
|
default:
|
||||||
if ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '[')) {
|
if ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '[')) {
|
||||||
$node = $this->parseArrayExpression();
|
$node = $this->parseSequenceExpression();
|
||||||
} elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '{')) {
|
} elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '{')) {
|
||||||
$node = $this->parseMappingExpression();
|
$node = $this->parseMappingExpression();
|
||||||
} elseif ($token->test(/* Token::OPERATOR_TYPE */ 8, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) {
|
} elseif ($token->test(/* Token::OPERATOR_TYPE */ 8, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) {
|
||||||
@@ -320,15 +320,22 @@ class ExpressionParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function parseArrayExpression()
|
public function parseArrayExpression()
|
||||||
|
{
|
||||||
|
trigger_deprecation('twig/twig', '3.11', 'Calling "%s()" is deprecated, use "parseSequenceExpression()" instead.', __METHOD__);
|
||||||
|
|
||||||
|
return $this->parseSequenceExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseSequenceExpression()
|
||||||
{
|
{
|
||||||
$stream = $this->parser->getStream();
|
$stream = $this->parser->getStream();
|
||||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '[', 'An array element was expected');
|
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '[', 'A sequence element was expected');
|
||||||
|
|
||||||
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
|
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
|
||||||
$first = true;
|
$first = true;
|
||||||
while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
|
while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
|
||||||
if (!$first) {
|
if (!$first) {
|
||||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'An array element must be followed by a comma');
|
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'A sequence element must be followed by a comma');
|
||||||
|
|
||||||
// trailing ,?
|
// trailing ,?
|
||||||
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
|
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
|
||||||
@@ -346,7 +353,7 @@ class ExpressionParser
|
|||||||
$node->addElement($this->parseExpression());
|
$node->addElement($this->parseExpression());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']', 'An opened array is not properly closed');
|
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']', 'An opened sequence is not properly closed');
|
||||||
|
|
||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
@@ -641,7 +648,7 @@ class ExpressionParser
|
|||||||
$value = $this->parsePrimaryExpression();
|
$value = $this->parsePrimaryExpression();
|
||||||
|
|
||||||
if (!$this->checkConstantExpression($value)) {
|
if (!$this->checkConstantExpression($value)) {
|
||||||
throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, or an array).', $token->getLine(), $stream->getSourceContext());
|
throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping).', $token->getLine(), $stream->getSourceContext());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$value = $this->parseExpression(0, $allowArrow);
|
$value = $this->parseExpression(0, $allowArrow);
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!\count($values)) {
|
if (!\count($values)) {
|
||||||
throw new RuntimeError('The "cycle" function does not work on empty arrays.');
|
throw new RuntimeError('The "cycle" function does not work on empty sequences/mappings.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $values[$position % \count($values)];
|
return $values[$position % \count($values)];
|
||||||
@@ -399,7 +399,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
$values = self::toArray($values);
|
$values = self::toArray($values);
|
||||||
|
|
||||||
if (0 === \count($values)) {
|
if (0 === \count($values)) {
|
||||||
throw new RuntimeError('The random function cannot pick from an empty array.');
|
throw new RuntimeError('The random function cannot pick from an empty sequence/mapping.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $values[array_rand($values, 1)];
|
return $values[array_rand($values, 1)];
|
||||||
@@ -536,7 +536,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
public static function replace($str, $from): string
|
public static function replace($str, $from): string
|
||||||
{
|
{
|
||||||
if (!is_iterable($from)) {
|
if (!is_iterable($from)) {
|
||||||
throw new RuntimeError(\sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".', \is_object($from) ? \get_class($from) : \gettype($from)));
|
throw new RuntimeError(\sprintf('The "replace" filter expects a sequence/mapping or "Traversable" as replace values, got "%s".', \is_object($from) ? \get_class($from) : \gettype($from)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return strtr($str ?? '', self::toArray($from));
|
return strtr($str ?? '', self::toArray($from));
|
||||||
@@ -633,7 +633,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
|
|
||||||
foreach ($arrays as $argNumber => $array) {
|
foreach ($arrays as $argNumber => $array) {
|
||||||
if (!is_iterable($array)) {
|
if (!is_iterable($array)) {
|
||||||
throw new RuntimeError(\sprintf('The merge filter only works with arrays or "Traversable", got "%s" for argument %d.', \gettype($array), $argNumber + 1));
|
throw new RuntimeError(\sprintf('The merge filter only works with sequences/mappings or "Traversable", got "%s" for argument %d.', \gettype($array), $argNumber + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = array_merge($result, self::toArray($array));
|
$result = array_merge($result, self::toArray($array));
|
||||||
@@ -907,7 +907,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
if ($array instanceof \Traversable) {
|
if ($array instanceof \Traversable) {
|
||||||
$array = iterator_to_array($array);
|
$array = iterator_to_array($array);
|
||||||
} elseif (!\is_array($array)) {
|
} elseif (!\is_array($array)) {
|
||||||
throw new RuntimeError(\sprintf('The sort filter only works with arrays or "Traversable", got "%s".', \gettype($array)));
|
throw new RuntimeError(\sprintf('The sort filter only works with sequences/mappings or "Traversable", got "%s".', \gettype($array)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null !== $arrow) {
|
if (null !== $arrow) {
|
||||||
@@ -1424,7 +1424,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
public static function batch($items, $size, $fill = null, $preserveKeys = true): array
|
public static function batch($items, $size, $fill = null, $preserveKeys = true): array
|
||||||
{
|
{
|
||||||
if (!is_iterable($items)) {
|
if (!is_iterable($items)) {
|
||||||
throw new RuntimeError(\sprintf('The "batch" filter expects an array or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($items)));
|
throw new RuntimeError(\sprintf('The "batch" filter expects a sequence/mapping or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($items)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$size = ceil($size);
|
$size = ceil($size);
|
||||||
@@ -1491,9 +1491,9 @@ final class CoreExtension extends AbstractExtension
|
|||||||
$message = \sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, \get_class($object));
|
$message = \sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, \get_class($object));
|
||||||
} elseif (\is_array($object)) {
|
} elseif (\is_array($object)) {
|
||||||
if (empty($object)) {
|
if (empty($object)) {
|
||||||
$message = \sprintf('Key "%s" does not exist as the array is empty.', $arrayItem);
|
$message = \sprintf('Key "%s" does not exist as the sequence/mapping is empty.', $arrayItem);
|
||||||
} else {
|
} else {
|
||||||
$message = \sprintf('Key "%s" for array with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object)));
|
$message = \sprintf('Key "%s" for sequence/mapping with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object)));
|
||||||
}
|
}
|
||||||
} elseif (/* Template::ARRAY_CALL */ 'array' === $type) {
|
} elseif (/* Template::ARRAY_CALL */ 'array' === $type) {
|
||||||
if (null === $object) {
|
if (null === $object) {
|
||||||
@@ -1523,7 +1523,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
if (null === $object) {
|
if (null === $object) {
|
||||||
$message = \sprintf('Impossible to invoke a method ("%s") on a null variable.', $item);
|
$message = \sprintf('Impossible to invoke a method ("%s") on a null variable.', $item);
|
||||||
} elseif (\is_array($object)) {
|
} elseif (\is_array($object)) {
|
||||||
$message = \sprintf('Impossible to invoke a method ("%s") on an array.', $item);
|
$message = \sprintf('Impossible to invoke a method ("%s") on a sequence/mapping.', $item);
|
||||||
} else {
|
} else {
|
||||||
$message = \sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
|
$message = \sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, \gettype($object), $object);
|
||||||
}
|
}
|
||||||
@@ -1661,7 +1661,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
if ($array instanceof \Traversable) {
|
if ($array instanceof \Traversable) {
|
||||||
$array = iterator_to_array($array);
|
$array = iterator_to_array($array);
|
||||||
} elseif (!\is_array($array)) {
|
} elseif (!\is_array($array)) {
|
||||||
throw new RuntimeError(\sprintf('The column filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
|
throw new RuntimeError(\sprintf('The column filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', \gettype($array)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_column($array, $name, $index);
|
return array_column($array, $name, $index);
|
||||||
@@ -1673,7 +1673,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
public static function filter(Environment $env, $array, $arrow)
|
public static function filter(Environment $env, $array, $arrow)
|
||||||
{
|
{
|
||||||
if (!is_iterable($array)) {
|
if (!is_iterable($array)) {
|
||||||
throw new RuntimeError(\sprintf('The "filter" filter expects an array or "Traversable", got "%s".', \is_object($array) ? \get_class($array) : \gettype($array)));
|
throw new RuntimeError(\sprintf('The "filter" filter expects a sequence/mapping or "Traversable", got "%s".', \is_object($array) ? \get_class($array) : \gettype($array)));
|
||||||
}
|
}
|
||||||
|
|
||||||
self::checkArrowInSandbox($env, $arrow, 'filter', 'filter');
|
self::checkArrowInSandbox($env, $arrow, 'filter', 'filter');
|
||||||
@@ -1709,7 +1709,7 @@ final class CoreExtension extends AbstractExtension
|
|||||||
self::checkArrowInSandbox($env, $arrow, 'reduce', 'filter');
|
self::checkArrowInSandbox($env, $arrow, 'reduce', 'filter');
|
||||||
|
|
||||||
if (!\is_array($array) && !$array instanceof \Traversable) {
|
if (!\is_array($array) && !$array instanceof \Traversable) {
|
||||||
throw new RuntimeError(\sprintf('The "reduce" filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array)));
|
throw new RuntimeError(\sprintf('The "reduce" filter only works with sequences/mappings or "Traversable", got "%s" as first argument.', \gettype($array)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$accumulator = $initial;
|
$accumulator = $initial;
|
||||||
|
|||||||
@@ -55,9 +55,9 @@ class ExpressionParserTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider getTestsForArray
|
* @dataProvider getTestsForSequence
|
||||||
*/
|
*/
|
||||||
public function testArrayExpression($template, $expected)
|
public function testSequenceExpression($template, $expected)
|
||||||
{
|
{
|
||||||
$env = new Environment($this->createMock(LoaderInterface::class), ['cache' => false, 'autoescape' => false]);
|
$env = new Environment($this->createMock(LoaderInterface::class), ['cache' => false, 'autoescape' => false]);
|
||||||
$stream = $env->tokenize($source = new Source($template, ''));
|
$stream = $env->tokenize($source = new Source($template, ''));
|
||||||
@@ -68,9 +68,9 @@ class ExpressionParserTest extends TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider getFailingTestsForArray
|
* @dataProvider getFailingTestsForSequence
|
||||||
*/
|
*/
|
||||||
public function testArraySyntaxError($template)
|
public function testSequenceSyntaxError($template)
|
||||||
{
|
{
|
||||||
$this->expectException(SyntaxError::class);
|
$this->expectException(SyntaxError::class);
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ class ExpressionParserTest extends TestCase
|
|||||||
$parser->parse($env->tokenize(new Source($template, 'index')));
|
$parser->parse($env->tokenize(new Source($template, 'index')));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFailingTestsForArray()
|
public function getFailingTestsForSequence()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['{{ [1, "a": "b"] }}'],
|
['{{ [1, "a": "b"] }}'],
|
||||||
@@ -88,10 +88,10 @@ class ExpressionParserTest extends TestCase
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTestsForArray()
|
public function getTestsForSequence()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
// simple array
|
// simple sequence
|
||||||
['{{ [1, 2] }}', new ArrayExpression([
|
['{{ [1, 2] }}', new ArrayExpression([
|
||||||
new ConstantExpression(0, 1),
|
new ConstantExpression(0, 1),
|
||||||
new ConstantExpression(1, 1),
|
new ConstantExpression(1, 1),
|
||||||
@@ -101,7 +101,7 @@ class ExpressionParserTest extends TestCase
|
|||||||
], 1),
|
], 1),
|
||||||
],
|
],
|
||||||
|
|
||||||
// array with trailing ,
|
// sequence with trailing ,
|
||||||
['{{ [1, 2, ] }}', new ArrayExpression([
|
['{{ [1, 2, ] }}', new ArrayExpression([
|
||||||
new ConstantExpression(0, 1),
|
new ConstantExpression(0, 1),
|
||||||
new ConstantExpression(1, 1),
|
new ConstantExpression(1, 1),
|
||||||
@@ -131,7 +131,7 @@ class ExpressionParserTest extends TestCase
|
|||||||
], 1),
|
], 1),
|
||||||
],
|
],
|
||||||
|
|
||||||
// mapping in an array
|
// mapping in a sequence
|
||||||
['{{ [1, {"a": "b", "b": "c"}] }}', new ArrayExpression([
|
['{{ [1, {"a": "b", "b": "c"}] }}', new ArrayExpression([
|
||||||
new ConstantExpression(0, 1),
|
new ConstantExpression(0, 1),
|
||||||
new ConstantExpression(1, 1),
|
new ConstantExpression(1, 1),
|
||||||
@@ -147,7 +147,7 @@ class ExpressionParserTest extends TestCase
|
|||||||
], 1),
|
], 1),
|
||||||
],
|
],
|
||||||
|
|
||||||
// array in a mapping
|
// sequence in a mapping
|
||||||
['{{ {"a": [1, 2], "b": "c"} }}', new ArrayExpression([
|
['{{ {"a": [1, 2], "b": "c"} }}', new ArrayExpression([
|
||||||
new ConstantExpression('a', 1),
|
new ConstantExpression('a', 1),
|
||||||
new ArrayExpression([
|
new ArrayExpression([
|
||||||
@@ -168,7 +168,7 @@ class ExpressionParserTest extends TestCase
|
|||||||
new NameExpression('b', 1),
|
new NameExpression('b', 1),
|
||||||
], 1)],
|
], 1)],
|
||||||
|
|
||||||
// array with spread operator
|
// sequence with spread operator
|
||||||
['{{ [1, 2, ...foo] }}',
|
['{{ [1, 2, ...foo] }}',
|
||||||
new ArrayExpression([
|
new ArrayExpression([
|
||||||
new ConstantExpression(0, 1),
|
new ConstantExpression(0, 1),
|
||||||
@@ -301,7 +301,7 @@ class ExpressionParserTest extends TestCase
|
|||||||
public function testMacroDefinitionDoesNotSupportNonConstantDefaultValues($template)
|
public function testMacroDefinitionDoesNotSupportNonConstantDefaultValues($template)
|
||||||
{
|
{
|
||||||
$this->expectException(SyntaxError::class);
|
$this->expectException(SyntaxError::class);
|
||||||
$this->expectExceptionMessage('A default value for an argument must be a constant (a boolean, a string, a number, or an array) in "index" at line 1');
|
$this->expectExceptionMessage('A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping) in "index" at line 1');
|
||||||
|
|
||||||
$env = new Environment($this->createMock(LoaderInterface::class), ['cache' => false, 'autoescape' => false]);
|
$env = new Environment($this->createMock(LoaderInterface::class), ['cache' => false, 'autoescape' => false]);
|
||||||
$parser = new Parser($env);
|
$parser = new Parser($env);
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ Exception thrown from a child for an extension error
|
|||||||
--DATA--
|
--DATA--
|
||||||
return []
|
return []
|
||||||
--EXCEPTION--
|
--EXCEPTION--
|
||||||
Twig\Error\RuntimeError: The random function cannot pick from an empty array in "base.twig" at line 4.
|
Twig\Error\RuntimeError: The random function cannot pick from an empty sequence/mapping in "base.twig" at line 4.
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ Exception thrown from an include for an extension error
|
|||||||
--DATA--
|
--DATA--
|
||||||
return []
|
return []
|
||||||
--EXCEPTION--
|
--EXCEPTION--
|
||||||
Twig\Error\RuntimeError: The random function cannot pick from an empty array in "content.twig" at line 4.
|
Twig\Error\RuntimeError: The random function cannot pick from an empty sequence/mapping in "content.twig" at line 4.
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Exception for invalid argument type in replace call
|
|||||||
--DATA--
|
--DATA--
|
||||||
return ['stdClass' => new \stdClass()]
|
return ['stdClass' => new \stdClass()]
|
||||||
--EXCEPTION--
|
--EXCEPTION--
|
||||||
Twig\Error\RuntimeError: The "replace" filter expects an array or "Traversable" as replace values, got "stdClass" in "index.twig" at line 2.
|
Twig\Error\RuntimeError: The "replace" filter expects a sequence/mapping or "Traversable" as replace values, got "stdClass" in "index.twig" at line 2.
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
--TEST--
|
|
||||||
"cycle" function returns an error on empty arrays
|
|
||||||
--TEMPLATE--
|
|
||||||
{{ cycle([], 0) }}
|
|
||||||
--DATA--
|
|
||||||
return []
|
|
||||||
--EXCEPTION--
|
|
||||||
Twig\Error\RuntimeError: The "cycle" function does not work on empty arrays in "index.twig" at line 2.
|
|
||||||
+15
-15
@@ -66,17 +66,17 @@ class TemplateTest extends TestCase
|
|||||||
return [
|
return [
|
||||||
['{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1.'],
|
['{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1.'],
|
||||||
['{{ null["a"] }}', 'Impossible to access a key ("a") on a null variable in "%s" at line 1.'],
|
['{{ null["a"] }}', 'Impossible to access a key ("a") on a null variable in "%s" at line 1.'],
|
||||||
['{{ empty_array["a"] }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.'],
|
['{{ empty_array["a"] }}', 'Key "a" does not exist as the sequence/mapping is empty in "%s" at line 1.'],
|
||||||
['{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.'],
|
['{{ array["a"] }}', 'Key "a" for sequence/mapping with keys "foo" does not exist in "%s" at line 1.'],
|
||||||
['{{ array_access["a"] }}', 'Key "a" in object with ArrayAccess of class "Twig\Tests\TemplateArrayAccessObject" does not exist in "%s" at line 1.'],
|
['{{ array_access["a"] }}', 'Key "a" in object with ArrayAccess of class "Twig\Tests\TemplateArrayAccessObject" does not exist in "%s" at line 1.'],
|
||||||
['{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1.'],
|
['{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1.'],
|
||||||
['{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1.'],
|
['{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1.'],
|
||||||
['{{ null.a }}', 'Impossible to access an attribute ("a") on a null variable in "%s" at line 1.'],
|
['{{ null.a }}', 'Impossible to access an attribute ("a") on a null variable in "%s" at line 1.'],
|
||||||
['{{ null.a() }}', 'Impossible to invoke a method ("a") on a null variable in "%s" at line 1.'],
|
['{{ null.a() }}', 'Impossible to invoke a method ("a") on a null variable in "%s" at line 1.'],
|
||||||
['{{ array.a() }}', 'Impossible to invoke a method ("a") on an array in "%s" at line 1.'],
|
['{{ array.a() }}', 'Impossible to invoke a method ("a") on a sequence/mapping in "%s" at line 1.'],
|
||||||
['{{ empty_array.a }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.'],
|
['{{ empty_array.a }}', 'Key "a" does not exist as the sequence/mapping is empty in "%s" at line 1.'],
|
||||||
['{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.'],
|
['{{ array.a }}', 'Key "a" for sequence/mapping with keys "foo" does not exist in "%s" at line 1.'],
|
||||||
['{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1.'],
|
['{{ attribute(array, -10) }}', 'Key "-10" for sequence/mapping with keys "foo" does not exist in "%s" at line 1.'],
|
||||||
['{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()"/"hasa()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'],
|
['{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()"/"hasa()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'],
|
||||||
['{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()"/"hasmissing_method()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'],
|
['{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()"/"hasmissing_method()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'],
|
||||||
['{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.'],
|
['{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.'],
|
||||||
@@ -193,14 +193,14 @@ class TemplateTest extends TestCase
|
|||||||
$this->assertSame('IntegerButStringWithLeadingZeros', $array['01']);
|
$this->assertSame('IntegerButStringWithLeadingZeros', $array['01']);
|
||||||
$this->assertSame('EmptyString', $array[null]);
|
$this->assertSame('EmptyString', $array[null]);
|
||||||
|
|
||||||
$this->assertSame('Zero', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, false), 'false is treated as 0 when accessing an array (equals PHP behavior)');
|
$this->assertSame('Zero', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, false), 'false is treated as 0 when accessing a sequence/mapping (equals PHP behavior)');
|
||||||
$this->assertSame('One', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, true), 'true is treated as 1 when accessing an array (equals PHP behavior)');
|
$this->assertSame('One', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, true), 'true is treated as 1 when accessing a sequence/mapping (equals PHP behavior)');
|
||||||
$this->assertSame('One', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, 1.5), 'float is casted to int when accessing an array (equals PHP behavior)');
|
$this->assertSame('One', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, 1.5), 'float is casted to int when accessing a sequence/mapping (equals PHP behavior)');
|
||||||
$this->assertSame('One', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, '1'), '"1" is treated as integer 1 when accessing an array (equals PHP behavior)');
|
$this->assertSame('One', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, '1'), '"1" is treated as integer 1 when accessing a sequence/mapping (equals PHP behavior)');
|
||||||
$this->assertSame('MinusOne', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, -1.5), 'negative float is casted to int when accessing an array (equals PHP behavior)');
|
$this->assertSame('MinusOne', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, -1.5), 'negative float is casted to int when accessing a sequence/mapping (equals PHP behavior)');
|
||||||
$this->assertSame('FloatButString', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, '1.5'), '"1.5" is treated as-is when accessing an array (equals PHP behavior)');
|
$this->assertSame('FloatButString', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, '1.5'), '"1.5" is treated as-is when accessing a sequence/mapping (equals PHP behavior)');
|
||||||
$this->assertSame('IntegerButStringWithLeadingZeros', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, '01'), '"01" is treated as-is when accessing an array (equals PHP behavior)');
|
$this->assertSame('IntegerButStringWithLeadingZeros', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, '01'), '"01" is treated as-is when accessing a sequence/mapping (equals PHP behavior)');
|
||||||
$this->assertSame('EmptyString', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, null), 'null is treated as "" when accessing an array (equals PHP behavior)');
|
$this->assertSame('EmptyString', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, null), 'null is treated as "" when accessing a sequence/mapping (equals PHP behavior)');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -395,7 +395,7 @@ class TemplateTest extends TestCase
|
|||||||
$tests = array_merge($tests, [
|
$tests = array_merge($tests, [
|
||||||
[false, null, 42, 'a', [], $anyType, 'Impossible to access an attribute ("a") on a integer variable ("42") in "index.twig".'],
|
[false, null, 42, 'a', [], $anyType, 'Impossible to access an attribute ("a") on a integer variable ("42") in "index.twig".'],
|
||||||
[false, null, 'string', 'a', [], $anyType, 'Impossible to access an attribute ("a") on a string variable ("string") in "index.twig".'],
|
[false, null, 'string', 'a', [], $anyType, 'Impossible to access an attribute ("a") on a string variable ("string") in "index.twig".'],
|
||||||
[false, null, [], 'a', [], $anyType, 'Key "a" does not exist as the array is empty in "index.twig".'],
|
[false, null, [], 'a', [], $anyType, 'Key "a" does not exist as the sequence/mapping is empty in "index.twig".'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $tests;
|
return $tests;
|
||||||
|
|||||||
Reference in New Issue
Block a user