From 8d751dd38f4d089715e748863d0d8e4113bc408c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= Date: Wed, 13 Sep 2023 15:16:31 +0200 Subject: [PATCH] fix: ReflectionAttribute::newInstance() with nested class using named arguments --- src/NodeCompiler/CompileNodeToValue.php | 5 ++++- test/unit/Fixture/Attributes.php | 20 +++++++++++++++++ .../Adapter/ReflectionAttributeTest.php | 22 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/NodeCompiler/CompileNodeToValue.php b/src/NodeCompiler/CompileNodeToValue.php index 1ecce72a4..60ca8c158 100644 --- a/src/NodeCompiler/CompileNodeToValue.php +++ b/src/NodeCompiler/CompileNodeToValue.php @@ -271,7 +271,10 @@ private function compileNew(Node\Expr\New_ $node, CompilerContext $context): obj throw Exception\UnableToCompileNode::becauseOfClassCannotBeLoaded($context, $node, $className); } - $arguments = array_map(fn (Node\Arg $arg): mixed => $this($arg->value, $context)->value, $node->args); + $arguments = []; + foreach ($node->args as $argNo => $arg) { + $arguments[(($argName = $arg->name) ? $argName->toString() : null) ?? $argNo] = $this($arg->value, $context)->value; + } return new $className(...$arguments); } diff --git a/test/unit/Fixture/Attributes.php b/test/unit/Fixture/Attributes.php index 43045adfc..6379a1491 100644 --- a/test/unit/Fixture/Attributes.php +++ b/test/unit/Fixture/Attributes.php @@ -101,3 +101,23 @@ class ClassWithAttributeThatAcceptsArgument { } + +class NestedClassUsingNamedArguments +{ + public function __construct(public ?SomeEnum $e = null, public string $s = '') + { + } +} + +#[Attribute] +class AttributeThatHasNestedClassUsingNamedArguments +{ + public function __construct(public NestedClassUsingNamedArguments $nested) + { + } +} + +#[AttributeThatHasNestedClassUsingNamedArguments(new NestedClassUsingNamedArguments(s: 'string'))] +class ClassWithAttributeThatHasNestedClassUsingNamedArguments +{ +} diff --git a/test/unit/Reflection/Adapter/ReflectionAttributeTest.php b/test/unit/Reflection/Adapter/ReflectionAttributeTest.php index 851d8cf98..12d4676de 100644 --- a/test/unit/Reflection/Adapter/ReflectionAttributeTest.php +++ b/test/unit/Reflection/Adapter/ReflectionAttributeTest.php @@ -20,7 +20,11 @@ use Roave\BetterReflection\SourceLocator\Type\SingleFileSourceLocator; use Roave\BetterReflectionTest\BetterReflectionSingleton; use Roave\BetterReflectionTest\Fixture\AttributeThatAcceptsArgument; +use Roave\BetterReflectionTest\Fixture\AttributeThatHasNestedClassUsingNamedArguments; +use Roave\BetterReflectionTest\Fixture\AttributeThatNeedsNamedArguments; use Roave\BetterReflectionTest\Fixture\ClassWithAttributeThatAcceptsArgument; +use Roave\BetterReflectionTest\Fixture\ClassWithAttributeThatHasNestedClassUsingNamedArguments; +use Roave\BetterReflectionTest\Fixture\ClassWithAttributeThatNeedsNamedArguments; use Roave\BetterReflectionTest\Fixture\SomeEnum; use function array_combine; use function array_map; @@ -98,4 +102,22 @@ public function testNewInstanceWithEnum(): void $this->assertSame('ONE', $instance->e->name); $this->assertSame(1, $instance->e->value); } + + public function testNewInstanceWithNestedClassUsingNamedArguments(): void + { + $astLocator = BetterReflectionSingleton::instance()->astLocator(); + $path = __DIR__ . '/../../Fixture/Attributes.php'; + require_once($path); + + $betterReflection = BetterReflectionSingleton::instance(); + $reflector = new DefaultReflector(new AggregateSourceLocator([new SingleFileSourceLocator($path, $astLocator), new PhpInternalSourceLocator($astLocator, $betterReflection->sourceStubber())])); + $reflection = $reflector->reflectClass(ClassWithAttributeThatHasNestedClassUsingNamedArguments::class); + $attributes = $reflection->getAttributesByName(AttributeThatHasNestedClassUsingNamedArguments::class); + $this->assertCount(1, $attributes); + $adapter = new ReflectionAttributeAdapter($attributes[0]); + $instance = $adapter->newInstance(); + $this->assertInstanceOf(AttributeThatHasNestedClassUsingNamedArguments::class, $instance); + $this->assertNull($instance->nested->e); + $this->assertSame('string', $instance->nested->s); + } }