mirror of
https://github.com/symfony/error-handler.git
synced 2026-03-24 00:02:09 +01:00
Merge branch '8.0' into 8.1
* 8.0: Fix merge [VarDumper] Wrong dumper output for Accept: aplication/json requests [HttpKernel] Reset router locale to default when finishing main request Only decrement pendingRequests when it's more than zero [Dotenv] Fix self-referencing variables with defaults and env key resolution during deferred expansion Improve Bulgarian translations in validators.bg.xlf [Cache] Fix ChainAdapter ignoring item expiry when propagating to earlier adapters [Form] Fix typed property initialization in ValidatorExtension [Messenger] Fix duplicate pending messages in Redis transport with batch handlers Fix deprecation notices for "@method" annotations when implementing interfaces directly
This commit is contained in:
@@ -468,6 +468,15 @@ class DebugClassLoader
|
||||
}
|
||||
}
|
||||
|
||||
// When the parent is a concrete class, we will trigger deprecation notices to make it aware that it needs
|
||||
// to add the new methods announced with @method. The parent will have to provide all those methods.
|
||||
// For child classes this means they will not need to deal with @method coming from any of the interfaces
|
||||
// the parent implements.
|
||||
// Put those interfaces that we can ignore into $parentInterfaces.
|
||||
// The ternary makes use of the fact that abstract parent classes will accumulate the methods in self::$method,
|
||||
// so !isset(self::$method[$parent]) indicates a concrete parent class.
|
||||
$parentInterfaces = ($parent && !isset(self::$method[$parent])) ? class_implements($parent, false) : [];
|
||||
|
||||
// Detect if the parent is annotated
|
||||
foreach ($parentAndOwnInterfaces + class_uses($class, false) as $use) {
|
||||
if (!isset(self::$checkedClasses[$use])) {
|
||||
@@ -483,7 +492,9 @@ class DebugClassLoader
|
||||
$deprecations[] = \sprintf('The "%s" %s is considered internal%s It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $className);
|
||||
}
|
||||
if (isset(self::$method[$use])) {
|
||||
if ($refl->isAbstract()) {
|
||||
if ($refl->isAbstract() || $refl->isInterface()) {
|
||||
// Abstract classes and interfaces inherit @method from interfaces they
|
||||
// implement directly or through inheritance.
|
||||
if (isset(self::$method[$class])) {
|
||||
self::$method[$class] = array_merge(self::$method[$class], self::$method[$use]);
|
||||
} else {
|
||||
@@ -499,6 +510,11 @@ class DebugClassLoader
|
||||
continue;
|
||||
}
|
||||
foreach (self::$method[$use] as [$interface, $static, $returnType, $name, $description]) {
|
||||
if (isset($parentInterfaces[$interface])) {
|
||||
// The @method annotation comes from an interface that has already been implemented by a concrete parent class,
|
||||
// so we can ignore it here.
|
||||
continue;
|
||||
}
|
||||
$realName = substr($name, 0, strpos($name, '('));
|
||||
if (!$refl->hasMethod($realName) || !($methodRefl = $refl->getMethod($realName))->isPublic() || ($static xor $methodRefl->isStatic())) {
|
||||
$deprecations[] = \sprintf('Class "%s" should implement method "%s::%s%s"%s', $className, ($static ? 'static ' : '').$interface, $name, $returnType ? ': '.$returnType : '', null === $description ? '.' : ': '.$description);
|
||||
|
||||
@@ -342,6 +342,40 @@ class DebugClassLoaderTest extends TestCase
|
||||
], $deprecations);
|
||||
}
|
||||
|
||||
public function testVirtualUseWithInheritedInterface()
|
||||
{
|
||||
// A concrete class implementing a child interface should also receive notices for @method
|
||||
// annotations declared on parent interfaces, even without abstract classes in between.
|
||||
// (ExtendsVirtualSubInterfaceDirect implements VirtualSubInterface, which extends VirtualInterface)
|
||||
|
||||
$deprecations = [];
|
||||
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
|
||||
$e = error_reporting(E_USER_DEPRECATED);
|
||||
|
||||
class_exists('Test\\'.ExtendsVirtualSubInterfaceDirect::class, true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
|
||||
$this->assertSame([
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualSubInterface::subInterfaceMethod(): string".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::interfaceMethod(): string".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticReturningMethod(): static".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::sameLineInterfaceMethod($arg)".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::sameLineInterfaceMethodNoBraces()".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::newLineInterfaceMethod()": Some description!',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::newLineInterfaceMethodNoBraces(): \stdClass": Description.',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::invalidInterfaceMethod(): unknownType".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::invalidInterfaceMethodNoBraces(): unknownType|string".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::complexInterfaceMethod($arg, ...$args)".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::complexInterfaceMethodTyped($arg, int ...$args): array<string, int>|string[]|int": Description ...',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethod(): Foo&Bar".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodNoBraces(): mixed".',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodTyped(int $arg): \stdClass": Description.',
|
||||
'Class "Test\Symfony\Component\ErrorHandler\Tests\ExtendsVirtualSubInterfaceDirect" should implement method "static Symfony\Component\ErrorHandler\Tests\Fixtures\VirtualInterface::staticMethodTypedNoBraces(): \stdClass[]".',
|
||||
], $deprecations);
|
||||
}
|
||||
|
||||
public function testVirtualUseWithMagicCall()
|
||||
{
|
||||
// This is like the preceding testVirtualUse() test, but this time the class contains
|
||||
@@ -678,6 +712,9 @@ class ClassLoader
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; abstract class ExtendsVirtualAbstractBase extends \\'.__NAMESPACE__.'\Fixtures\VirtualClass implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface {
|
||||
public function ownAbstractBaseMethod() { }
|
||||
}');
|
||||
} elseif ('Test\\'.ExtendsVirtualSubInterfaceDirect::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtualSubInterfaceDirect implements \\'.__NAMESPACE__.'\Fixtures\VirtualSubInterface {
|
||||
}');
|
||||
} elseif ('Test\\'.ExtendsVirtualMagicCall::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtualMagicCall extends \\'.__NAMESPACE__.'\Fixtures\VirtualClassMagicCall implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface {
|
||||
}');
|
||||
|
||||
Reference in New Issue
Block a user