PHP RFC: Ensure correct signatures of magic methods
- Version: 1.0
- Date: 2020-04-05
- Author: Gabriel Caruso (firstname.lastname@example.org)
- Status: Implemented
- Target Version: PHP 8.0
- Implementation: https://github.com/php/php-src/pull/4177
It is currently possible to write magic methods that have signatures that don't match the signature expected, such as __clone(): float or __isset(): Closure.
This behavior of allowing incorrect signatures was reported as a bug.
As ensuring magic methods have correct signatures is a backward compatible break for code that currently has incorrect signatures, this change would only be appropriate in a major PHP release.
PHP's Magic Methods is something that PHP provides allowing developers to track and act on specific changes of behavior of a certain class. Given that fact, the same should ensure that the end-users are using these methods consistently across different codebases.
Since the introduction of types in PHP 7.0, only the checks list below were introduced to make sure that developers are using PHP's magic methods correctly:
- __clone() return type: https://3v4l.org/Ub54p
- __construct() return type: https://3v4l.org/CCL11
- __destruct() return type: https://3v4l.org/HNkgW
- __toString() return type: https://3v4l.org/jIg7b/rfc#git-php-master
For PHP 8, this RFC aims to expand these checks.
This RFC proposes to add parameter and return types checks per the following details. Other magic methods will be not modified.
The proposal follows the Magic Methods documentation.
Foo::__call(string $name, array $arguments): mixed; Foo::__callStatic(string $name, array $arguments): mixed; Foo::__clone(): void; Foo::__debugInfo(): ?array; Foo::__get(string $name): mixed; Foo::__invoke(mixed $arguments): mixed; Foo::__isset(string $name): bool; Foo::__serialize(): array; Foo::__set(string $name, mixed $value): void; Foo::__set_state(array $properties): object; Foo::__sleep(): array; Foo::__unserialize(array $data): void; Foo::__unset(string $name): void; Foo::__wakeup(): void;
Note: The __construct() and __destruct() methods won't suffer any changes. They won't allow void as a return type given the fact that (almost) all languages, including PHP, don't have the concept of Constructors and Destructors “returning” something after their execution.
Backward Incompatible Changes
To Magic Methods without types declared
To Magic Methods with the wrong signatures
Magic methods' signatures not matching the ones listed above, an error will be thrown, a Fatal Error more specific, following the errors of the same kind that are placed today.
Scraping the top 1000 Composer packages (using Nikita's script), the results show only 7 occurrences of not matching signatures.
Luckily, none of them is a problem as __call(), __callStatic() and __get() do not have checks at this time.
Even with a mixed RFC that wouldn't be a problem, as a specific type can override it, by the Liskov Substitution Principle.
This RFC only aims to add checks for the methods' signatures but as a Future Scope, a runtime check of what is been returning in the methods could be added, same as
- __serialize(): https://3v4l.org/HLiTj
- __toString(): https://3v4l.org/Dbe6G
- __debugInfo(): https://3v4l.org/0EEPh
- __sleep(): https://3v4l.org/dH96A
Voting started on 2020-05-29 at 18h (CEST) and ends on 2020-06-19 at 18h (CEST).
- Discussion thread: https://externals.io/message/109542