From ca69a18d9a8a7390cb03cca9b601e1702cedcbe2 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 17 Feb 2022 20:25:49 +0100 Subject: [PATCH] [HttpKernel] Fix extracting controller name from closures --- .../ArgumentMetadataFactory.php | 20 +++++++++++-------- .../ArgumentMetadataFactoryTest.php | 11 ++++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/ControllerMetadata/ArgumentMetadataFactory.php b/ControllerMetadata/ArgumentMetadataFactory.php index 05a68229a3..6141b53edd 100644 --- a/ControllerMetadata/ArgumentMetadataFactory.php +++ b/ControllerMetadata/ArgumentMetadataFactory.php @@ -27,14 +27,19 @@ public function createArgumentMetadata($controller): array if (\is_array($controller)) { $reflection = new \ReflectionMethod($controller[0], $controller[1]); + $class = $reflection->class; } elseif (\is_object($controller) && !$controller instanceof \Closure) { - $reflection = (new \ReflectionObject($controller))->getMethod('__invoke'); + $reflection = new \ReflectionMethod($controller, '__invoke'); + $class = $reflection->class; } else { $reflection = new \ReflectionFunction($controller); + if ($class = str_contains($reflection->name, '{closure}') ? null : $reflection->getClosureScopeClass()) { + $class = $class->name; + } } foreach ($reflection->getParameters() as $param) { - $arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param, $reflection), $param->isVariadic(), $param->isDefaultValueAvailable(), $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null, $param->allowsNull()); + $arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param, $reflection, $class), $param->isVariadic(), $param->isDefaultValueAvailable(), $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null, $param->allowsNull()); } return $arguments; @@ -43,20 +48,19 @@ public function createArgumentMetadata($controller): array /** * Returns an associated type to the given parameter if available. */ - private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function): ?string + private function getType(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $function, ?string $class): ?string { if (!$type = $parameter->getType()) { return null; } $name = $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type; - if ($function instanceof \ReflectionMethod) { - $lcName = strtolower($name); - switch ($lcName) { + if (null !== $class) { + switch (strtolower($name)) { case 'self': - return $function->getDeclaringClass()->name; + return $class; case 'parent': - return ($parent = $function->getDeclaringClass()->getParentClass()) ? $parent->name : null; + return get_parent_class($class) ?: null; } } diff --git a/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php b/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php index dfab909802..cbef2d3cf2 100644 --- a/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php +++ b/Tests/ControllerMetadata/ArgumentMetadataFactoryTest.php @@ -105,6 +105,17 @@ public function testBasicTypesSignature() ], $arguments); } + public function testNamedClosure() + { + $arguments = $this->factory->createArgumentMetadata(\Closure::fromCallable([$this, 'signature1'])); + + $this->assertEquals([ + new ArgumentMetadata('foo', self::class, false, false, null), + new ArgumentMetadata('bar', 'array', false, false, null), + new ArgumentMetadata('baz', 'callable', false, false, null), + ], $arguments); + } + public function testNullableTypesSignature() { $arguments = $this->factory->createArgumentMetadata([new NullableController(), 'action']);