PHP 8.0: Fatal errors on incompatible method signatures
Prior to PHP 8, PHP was inconsistent the way it handled method signature checks when a parent class was extended.
Extending class methods
class Foo {
public function process(stdClass $item): array{}
}
class SuperFoo extends Foo{
public function process(array $items): array{}
// ^^^^^ mismatch
}
In the snippet above, SuperFoo::process
method has a mismatching signature to its parent Foo
. This is a clear violation of Liskov Substitution Principle (LSP), but PHP only raised warning:
Warning: Declaration of SuperFoo::process(array $items): array should be compatible with Foo::process(stdClass $item): array in ... on line ...
In PHP 8, such signature mismatches result in fatal error.
Fatal error: Declaration of SuperFoo::process(array $items): array must be compatible with Foo::process(stdClass $item): array in ... on line ...
Note that this fatal error will only be triggered if it violates LSP. Changing the type of method signatures is allowed as long it follows LSP.
Implementing abstract trait methods
Prior to PHP 8, PHP did not enforce any signature checking when a trait method is extended. In PHP 8.0 and later, abtract
methods with mismatching signatures will fail with a fatal error.
trait Foo {
abstract public function inParams(stdClass $item): array;
abstract public function inReturn(stdClass $item): int;
}
class SuperFoo{
use Foo;
public function inParams(array $items): array{}
// ^^^^^ Mismatch
public function inReturn(stdClass $item): int{}
// ^^^ Mismatch
}
In PHP 8, the snippet above will result in a fatal error:
Fatal error: Declaration of SuperFoo::inParams(array $items): array must be compatible with Foo::inParams(stdClass $item): array in ... on line ...
Note that this fatal error will only be triggered if it violates LSP. Changing the type of method signatures is allowed as long it follows LSP.
PHP LSP enforcement
This change will finally bring PHP 8 to actively enforce signature checks on all extent/implement patterns:
The following chart describes how PHP versions prior to 8.0 enforced signature mismatches, and how it changes in PHP 8
PHP <8 | PHP >=8 | |
---|---|---|
class implements interface : method parameters |
Fatal Error | Fatal Error |
class implements interface : return type |
Fatal Error | Fatal Error |
class extends abstract method: method parameters |
Fatal Error | Fatal Error |
class extends abstract method: return type |
Fatal Error | Fatal Error |
class extends class: Method parameters |
Warning | Fatal Error |
class extends class: Method return type |
Fatal Error | Fatal Error |
trait use and extend: Method parameters |
none | none |
trait use and extend: Method return type |
none | none |
trait use and implement: abstract Method parameters |
none | Fatal Error |
trait use and implement: abstract Method return type |
none | Fatal Error |
Backwards compatibility impact
If you had classes that extended a parent class with mismatching signatures, they will now fail with a fatal error. Prior PHP 8, those errors raised a warning.
If your PHP 7 code base does not raise such warnings, you will be able to upgrade to PHP 8 without problems. Fixing the code for PHP 8 will fix the warning in PHP 7 as well.
abtract
trait method mismatches did not emit any warnings whatsoever in any PHP versions prior to PHP 8, and they will fail with a fatal error in PHP. As pointed in a Reddit discussion, this change can introduce unexpected errors that are not currently detected by any code analysis tools because that was technically allowed.
Symfony, for example, used this pattern that needed to be fixed.