From e06d15b61c6c7e50bbc134eaa28ec213ffc6d83e Mon Sep 17 00:00:00 2001 From: Jeroen Versteeg Date: Fri, 23 Aug 2024 16:48:52 +0200 Subject: [PATCH] TypesNode validates mapping Keys must be NameExpressions, values must be string constants. --- src/Node/TypesNode.php | 28 ++++++++++++++- tests/Node/TypesTest.php | 75 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/src/Node/TypesNode.php b/src/Node/TypesNode.php index b270b7c4b26..4473aa446f8 100644 --- a/src/Node/TypesNode.php +++ b/src/Node/TypesNode.php @@ -4,20 +4,46 @@ use Twig\Compiler; use Twig\Node\Expression\ArrayExpression; +use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Expression\NameExpression; /** * Represents a types node. * * @author Jeroen Versteeg - * @see https://github.com/twigphp/Twig/issues/4165 + * @see https://github.com/twigphp/Twig/issues/4165 */ class TypesNode extends Node implements NodeCaptureInterface { public function __construct(ArrayExpression $typesNode, int $lineno, ?string $tag = null) { + $this->validateMapping($typesNode); + parent::__construct(['mapping' => $typesNode], [], $lineno, $tag); } + protected function validateMapping(ArrayExpression $typesNode): void + { + foreach ($typesNode->getKeyValuePairs() as $i => $pair) { + $keyExpression = $pair['key']; + $valueExpression = $pair['value']; + + if (!$keyExpression instanceof NameExpression) { + throw new \InvalidArgumentException("Key at index $i is not a NameExpression"); + } + $name = $keyExpression->getAttribute('name'); + + if (!$valueExpression instanceof ConstantExpression) { + throw new \InvalidArgumentException("Value for key \"$name\" is not a ConstantExpression"); + } + $value = $valueExpression->getAttribute('value'); + + if (!is_string($value)) { + throw new \InvalidArgumentException("Value for key \"$name\" is not a string"); + } + } + } + public function compile(Compiler $compiler) { // Don't compile anything. diff --git a/tests/Node/TypesTest.php b/tests/Node/TypesTest.php index cc02783e897..291091917ec 100644 --- a/tests/Node/TypesTest.php +++ b/tests/Node/TypesTest.php @@ -11,7 +11,7 @@ class TypesTest extends NodeTestCase { /** @return ArrayExpression */ - private function createArrayExpression() + private function getValidMapping() { // {foo: 'string', bar: 'int'} return new ArrayExpression([ @@ -25,18 +25,87 @@ private function createArrayExpression() public function testConstructor() { - $types = $this->createArrayExpression(); + $types = $this->getValidMapping(); $node = new TypesNode($types, 1); $this->assertEquals($types, $node->getNode('mapping')); } + /** @return array> */ + public function getInvalidMappings() + { + return [ + // {'foo': string} + [ + new ArrayExpression([ + new ConstantExpression('foo', 1), + new ConstantExpression('string', 1), + ], 1), + 'Key at index 0 is not a NameExpression' + ], + + // [13, 37] + [ + new ArrayExpression([ + new ConstantExpression(13, 1), + new ConstantExpression(37, 1), + ], 1), + 'Key at index 0 is not a NameExpression' + ], + + // {foo: bar} + [ + new ArrayExpression([ + new NameExpression('foo', 1), + new NameExpression('bar', 1), + ], 1), + 'Value for key "foo" is not a ConstantExpression' + ], + + // {foo: true} + [ + new ArrayExpression([ + new NameExpression('foo', 1), + new ConstantExpression(true, 1), + ], 1), + 'Value for key "foo" is not a string' + ], + + // {foo: 123} + [ + new ArrayExpression([ + new NameExpression('foo', 1), + new ConstantExpression(123, 1), + ], 1), + 'Value for key "foo" is not a string' + ], + + // {foo: {}}} + [ + new ArrayExpression([ + new NameExpression('foo', 1), + new ConstantExpression(new ArrayExpression([], 1), 1), + ], 1), + 'Value for key "foo" is not a string' + ], + ]; + } + + /** @dataProvider getInvalidMappings */ + public function testConstructorThrowsOnInvalidMapping(ArrayExpression $mapping, string $message) + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage($message); + + new TypesNode($mapping, 1); + } + public function getTests() { return [ // 1st test: Node shouldn't compile at all [ - new TypesNode($this->createArrayExpression(), 1), + new TypesNode($this->getValidMapping(), 1), '' ] ];