Fix array access with a Stringable key on ArrayAccess objects using object keys

This commit is contained in:
Fabien Potencier
2026-07-06 22:42:11 +02:00
parent d1abdf483a
commit 679b8fd610
3 changed files with 31 additions and 5 deletions
+1
View File
@@ -1,5 +1,6 @@
# 3.28.1 (2026-XX-XX)
* Fix array access with a `Stringable` key coercing the key to string for `ArrayAccess` objects that use object keys (such as `SplObjectStorage`)
* Fix duplicated macro argument names triggering a PHP fatal error instead of a `SyntaxError`
# 3.28.0 (2026-07-03)
+7 -5
View File
@@ -74,7 +74,7 @@ class GetAttrExpression extends AbstractExpression implements SupportDefinedTest
->raw($var)
->raw('[')
;
$this->compileArrayKey($compiler);
$this->compileArrayKey($compiler, $var);
$compiler->raw('] ?? null) : null)');
return;
@@ -91,7 +91,7 @@ class GetAttrExpression extends AbstractExpression implements SupportDefinedTest
->raw($var)
->raw('[')
;
$this->compileArrayKey($compiler);
$this->compileArrayKey($compiler, $var);
$compiler->raw('] ?? null) : ');
}
@@ -177,9 +177,11 @@ class GetAttrExpression extends AbstractExpression implements SupportDefinedTest
/**
* Coerces a Stringable array key to string so the optimized path matches
* CoreExtension::getAttribute(); scalars are left to PHP's native offset coercion.
* CoreExtension::getAttribute(): only arrays coerce the key, while ArrayAccess
* objects (e.g. SplObjectStorage) receive it untouched. Scalars are left to
* PHP's native offset coercion.
*/
private function compileArrayKey(Compiler $compiler): void
private function compileArrayKey(Compiler $compiler, string $var): void
{
$attribute = $this->getNode('attribute');
@@ -193,7 +195,7 @@ class GetAttrExpression extends AbstractExpression implements SupportDefinedTest
$compiler
->raw('(('.$key.' = ')
->subcompile($attribute)
->raw(') instanceof \Stringable ? (string) '.$key.' : '.$key.')')
->raw(') instanceof \Stringable && is_array('.$var.') ? (string) '.$key.' : '.$key.')')
;
}
+23
View File
@@ -284,6 +284,29 @@ class TemplateTest extends TestCase
yield 'strict' => [true];
}
/**
* @dataProvider getStrictVariablesModes
*/
#[DataProvider('getStrictVariablesModes')]
public function testArrayAccessWithObjectKeyKeepsTheObjectKey(bool $strict)
{
$twig = new Environment(new ArrayLoader(['index' => '{{ data[object] }}']), [
'strict_variables' => $strict,
'autoescape' => false,
]);
$object = new class implements \Stringable {
public function __toString(): string
{
return 'string';
}
};
$data = new \SplObjectStorage();
$data[$object] = 'value';
$this->assertSame('value', $twig->render('index', ['data' => $data, 'object' => $object]));
}
public function testArrayAccessWithStringableKeyIsCheckedBySandbox()
{
$object = new class implements \Stringable {