diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index ecf92516c3..b84c22df3c 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -246,25 +246,23 @@ class DebugClassLoader continue; } - // Method from a trait - if ($method->getFilename() !== $refl->getFileName()) { - continue; - } - if ($isClass && $parent && isset(self::$finalMethods[$parent][$method->name])) { list($declaringClass, $message) = self::$finalMethods[$parent][$method->name]; @trigger_error(sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED); } - foreach ($parentAndTraits as $use) { - if (isset(self::$internalMethods[$use][$method->name])) { - list($declaringClass, $message) = self::$internalMethods[$use][$method->name]; - if (\strncmp($ns, $declaringClass, $len)) { - @trigger_error(sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED); - } + if (isset(self::$internalMethods[$name][$method->name])) { + list($declaringClass, $message) = self::$internalMethods[$name][$method->name]; + if (\strncmp($ns, $declaringClass, $len)) { + @trigger_error(sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED); } } + // Method from a trait + if ($method->getFilename() !== $refl->getFileName()) { + continue; + } + // Detect method annotations if (false === $doc = $method->getDocComment()) { continue; diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php index 1580ca993b..d323d795b9 100644 --- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php +++ b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php @@ -220,24 +220,21 @@ class DebugClassLoaderTest extends TestCase public function testExtendedFinalMethod() { - set_error_handler(function () { return false; }); - $e = error_reporting(0); - trigger_error('', E_USER_NOTICE); + $deprecations = array(); + set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; }); + $e = error_reporting(E_USER_DEPRECATED); class_exists(__NAMESPACE__.'\\Fixtures\\ExtendedFinalMethod', true); error_reporting($e); restore_error_handler(); - $lastError = error_get_last(); - unset($lastError['file'], $lastError['line']); - $xError = array( - 'type' => E_USER_DEPRECATED, - 'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".', + 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".', + 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".', ); - $this->assertSame($xError, $lastError); + $this->assertSame($xError, $deprecations); } public function testExtendedDeprecatedMethodDoesntTriggerAnyNotice() diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/ExtendedFinalMethod.php b/src/Symfony/Component/Debug/Tests/Fixtures/ExtendedFinalMethod.php index 2bd337e5a2..050d19ff5e 100644 --- a/src/Symfony/Component/Debug/Tests/Fixtures/ExtendedFinalMethod.php +++ b/src/Symfony/Component/Debug/Tests/Fixtures/ExtendedFinalMethod.php @@ -4,6 +4,8 @@ namespace Symfony\Component\Debug\Tests\Fixtures; class ExtendedFinalMethod extends FinalMethod { + use FinalMethod2Trait; + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/FinalMethod.php b/src/Symfony/Component/Debug/Tests/Fixtures/FinalMethod.php index d8c673a5c5..03e47e8a7a 100644 --- a/src/Symfony/Component/Debug/Tests/Fixtures/FinalMethod.php +++ b/src/Symfony/Component/Debug/Tests/Fixtures/FinalMethod.php @@ -11,6 +11,13 @@ class FinalMethod { } + /** + * @final + */ + public function finalMethod2() + { + } + public function anotherMethod() { } diff --git a/src/Symfony/Component/Debug/Tests/Fixtures/FinalMethod2Trait.php b/src/Symfony/Component/Debug/Tests/Fixtures/FinalMethod2Trait.php new file mode 100644 index 0000000000..8547f3afed --- /dev/null +++ b/src/Symfony/Component/Debug/Tests/Fixtures/FinalMethod2Trait.php @@ -0,0 +1,10 @@ + --EXPECTF-- The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod". +The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod". diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php index a2ab592ad0..e7df546b66 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/ServiceValueResolver.php @@ -48,6 +48,10 @@ final class ServiceValueResolver implements ArgumentValueResolverInterface $controller = ltrim($controller, '\\'); } + if (!$this->container->has($controller) && false !== $i = strrpos($controller, ':')) { + $controller = substr($controller, 0, $i).strtolower(substr($controller, $i)); + } + return $this->container->has($controller) && $this->container->get($controller)->has($argument->getName()); } @@ -64,6 +68,11 @@ final class ServiceValueResolver implements ArgumentValueResolverInterface $controller = ltrim($controller, '\\'); } + if (!$this->container->has($controller)) { + $i = strrpos($controller, ':'); + $controller = substr($controller, 0, $i).strtolower(substr($controller, $i)); + } + try { yield $this->container->get($controller)->get($argument->getName()); } catch (RuntimeException $e) { diff --git a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php index 33b64d5448..0b38af9556 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Controller/ArgumentResolver/ServiceValueResolverTest.php @@ -68,6 +68,24 @@ class ServiceValueResolverTest extends TestCase $this->assertYieldEquals(array(new DummyService()), $resolver->resolve($request, $argument)); } + public function testExistingControllerWithMethodNameStartUppercase() + { + $resolver = new ServiceValueResolver(new ServiceLocator(array( + 'App\\Controller\\Mine::method' => function () { + return new ServiceLocator(array( + 'dummy' => function () { + return new DummyService(); + }, + )); + }, + ))); + $request = $this->requestWithAttributes(array('_controller' => 'App\\Controller\\Mine::Method')); + $argument = new ArgumentMetadata('dummy', DummyService::class, false, false, null); + + $this->assertTrue($resolver->supports($request, $argument)); + $this->assertYieldEquals(array(new DummyService()), $resolver->resolve($request, $argument)); + } + public function testControllerNameIsAnArray() { $resolver = new ServiceValueResolver(new ServiceLocator(array(