diff --git a/CHANGELOG b/CHANGELOG index ef2f219bc..c029bb8e5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ # 3.15.0 (2024-XX-XX) + * Support Markup instances (and any other \Stringable) as dynamic mapping keys * Deprecate the `sandbox` tag * Improve the way one can deprecate a Twig callable (use `deprecation_info` instead of the other callable options) diff --git a/src/Node/Expression/ArrayExpression.php b/src/Node/Expression/ArrayExpression.php index 5f8b0f63f..9b6c55c29 100644 --- a/src/Node/Expression/ArrayExpression.php +++ b/src/Node/Expression/ArrayExpression.php @@ -98,6 +98,9 @@ class ArrayExpression extends AbstractExpression ++$nextIndex; } else { $key = $pair['key'] instanceof ConstantExpression ? $pair['key']->getAttribute('value') : null; + if ($pair['key'] instanceof NameExpression) { + $pair['key']->setAttribute('stringify', true); + } if ($nextIndex !== $key) { if (\is_int($key)) { diff --git a/src/Node/Expression/NameExpression.php b/src/Node/Expression/NameExpression.php index 286aa5ae2..a3f42cd0c 100644 --- a/src/Node/Expression/NameExpression.php +++ b/src/Node/Expression/NameExpression.php @@ -24,7 +24,7 @@ class NameExpression extends AbstractExpression public function __construct(string $name, int $lineno) { - parent::__construct([], ['name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false], $lineno); + parent::__construct([], ['name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false, 'stringify' => false], $lineno); } public function compile(Compiler $compiler): void @@ -54,6 +54,9 @@ class NameExpression extends AbstractExpression } elseif (isset($this->specialVars[$name])) { $compiler->raw($this->specialVars[$name]); } elseif ($this->getAttribute('always_defined')) { + if ($this->getAttribute('stringify')) { + $compiler->raw(' (string)'); + } $compiler ->raw('$context[') ->string($name) @@ -61,6 +64,9 @@ class NameExpression extends AbstractExpression ; } else { if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) { + if ($this->getAttribute('stringify')) { + $compiler->raw(' (string)'); + } $compiler ->raw('($context[') ->string($name) @@ -72,7 +78,13 @@ class NameExpression extends AbstractExpression ->string($name) ->raw(']) || array_key_exists(') ->string($name) - ->raw(', $context) ? $context[') + ->raw(', $context) ?') + ; + if ($this->getAttribute('stringify')) { + $compiler->raw(' (string)'); + } + $compiler + ->raw(' $context[') ->string($name) ->raw('] : (function () { throw new RuntimeError(\'Variable ') ->string($name) diff --git a/tests/Fixtures/expressions/array.test b/tests/Fixtures/expressions/array.test index 35579dc13..1de76e93e 100644 --- a/tests/Fixtures/expressions/array.test +++ b/tests/Fixtures/expressions/array.test @@ -34,7 +34,8 @@ Twig supports array notation {# keys can be any expression #} {% set a = 1 %} {% set b = "foo" %} -{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd' } %} +{% set markup_instance %}fooe{% endset %} +{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd', (markup_instance): 'e' } %} {{ ary|keys|join(',') }} {{ ary|join(',') }} @@ -65,8 +66,8 @@ FOO,BAR, 1,2 -1,foo,c,1foo -a,b,c,d +1,foo,c,1foo,fooe +a,b,c,d,e b @@ -95,8 +96,8 @@ FOO,BAR, 1,2 -1,foo,c,1foo -a,b,c,d +1,foo,c,1foo,fooe +a,b,c,d,e b