Skip to content

Commit e3d06fc

Browse files
committed
Ensure correct signatures for magic methods
1 parent 650801c commit e3d06fc

30 files changed

+506
-3
lines changed

UPGRADING

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,26 @@ PHP 8.0 UPGRADE NOTES
236236
"Illegal string offset 'string'" for illegal string offsets. The behavior
237237
of explicit casts to int/float from strings has not been changed.
238238
RFC: https://wiki.php.net/rfc/saner-numeric-strings
239+
. Magic Methods will now have their arguments and return types
240+
checked if they have them declared. The signatures should
241+
match the following list:
242+
243+
__call(string $name, array $arguments): mixed
244+
__callStatic(string $name, array $arguments): mixed
245+
__clone(): void
246+
__debugInfo(): ?array
247+
__get(string $name): mixed
248+
__invoke(mixed $arguments): mixed
249+
__isset(string $name): bool
250+
__serialize(): array
251+
__set(string $name, mixed $value): void
252+
__set_state(array $properties): object
253+
__sleep(): array
254+
__unserialize(array $data): void
255+
__unset(string $name): void
256+
__wakeup(): void
257+
258+
RFC: https://wiki.php.net/rfc/magic-methods-signature
239259

240260
- COM:
241261
. Removed the ability to import case-insensitive constants from type

Zend/tests/magic_methods_011.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__set first parameter should be a string when typed
3+
--FILE--
4+
<?php
5+
class Foo {
6+
function __set(\Countable $name, $value) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__set(): Parameter #1 ($name) must be of type string when declared in %s on line %d

Zend/tests/magic_methods_012.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__get first parameter should be a string when typed
3+
--FILE--
4+
<?php
5+
class Foo {
6+
function __get(int $name) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__get(): Parameter #1 ($name) must be of type string when declared in %s on line %d

Zend/tests/magic_methods_013.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__isset first parameter should be a string when typed
3+
--FILE--
4+
<?php
5+
class Foo {
6+
function __isset(\stdClass $name) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__isset(): Parameter #1 ($name) must be of type string when declared in %s on line %d

Zend/tests/magic_methods_014.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__unset first parameter should be a string when typed
3+
--FILE--
4+
<?php
5+
class Foo {
6+
function __unset(array $name) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__unset(): Parameter #1 ($name) must be of type string when declared in %s on line %d

Zend/tests/magic_methods_015.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__call first parameter should be a string typed
3+
--FILE--
4+
<?php
5+
class Foo {
6+
function __call(int $name, array $arguments) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__call(): Parameter #1 ($name) must be of type string when declared in %s on line %d

Zend/tests/magic_methods_016.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__call second parameter should be an array when typed
3+
--FILE--
4+
<?php
5+
class Foo {
6+
function __call(string $name, \Arguments $arguments) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__call(): Parameter #2 ($arguments) must be of type array when declared in %s on line %d

Zend/tests/magic_methods_017.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__callStatic first parameter should be a string typed
3+
--FILE--
4+
<?php
5+
class Foo {
6+
static function __callStatic(int $name, array $arguments) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__callStatic(): Parameter #1 ($name) must be of type string when declared in %s on line %d

Zend/tests/magic_methods_018.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__callStatic second parameter should be an array typed
3+
--FILE--
4+
<?php
5+
class Foo {
6+
static function __callStatic(string $name, \Arguments $args) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__callStatic(): Parameter #2 ($args) must be of type array when declared in %s on line %d

Zend/tests/magic_methods_019.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__unserialize first parameter must be an array
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __unserialize(string $name) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__unserialize(): Parameter #1 ($name) must be of type array when declared in %s on line %d
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
--TEST--
2+
Magic Methods inheritance rules
3+
--FILE--
4+
<?php
5+
class ValidMagicMethods {
6+
public function __call(string $name, array $arguments): mixed {}
7+
8+
public static function __callStatic(string $name, array $arguments): mixed {}
9+
10+
public function __clone(): void {}
11+
12+
public function __debugInfo(): ?array {}
13+
14+
public function __get(string $name): mixed {}
15+
16+
public function __invoke(mixed $arguments): mixed {}
17+
18+
public function __isset(string $name): bool {}
19+
20+
public function __serialize(): array {}
21+
22+
public function __set(string $name, mixed $value): void {}
23+
24+
public static function __set_state(array $properties): object {}
25+
26+
public function __sleep(): array {}
27+
28+
public function __toString(): string {}
29+
30+
public function __unserialize(array $data): void {}
31+
32+
public function __unset(string $name): void {}
33+
34+
public function __wakeup(): void {}
35+
}
36+
37+
class NarrowedReturnType extends ValidMagicMethods {
38+
public function __call(string $name, array $arguments): string|float|null {}
39+
40+
public static function __callStatic(string $name, array $arguments): ?array {}
41+
42+
public function __debugInfo(): array {}
43+
44+
public function __get(string $name): int|string {}
45+
46+
public function __invoke(mixed $arguments): object {}
47+
}
48+
49+
class WidenedArgumentType extends NarrowedReturnType {
50+
public function __call(string|array $name, array|string $arguments): string|float|null {}
51+
52+
public static function __callStatic(string|object $name, array|object $arguments): ?array {}
53+
54+
public function __get(string|array $name): int|string {}
55+
56+
public function __isset(string|bool $name): bool {}
57+
58+
public function __set(string|bool|float $name, mixed $value): void {}
59+
60+
public static function __set_state(string|array $properties): object {}
61+
62+
public function __unserialize(array|string $data): void {}
63+
64+
public function __unset(string|array $name): void {}
65+
}
66+
67+
echo 'No problems!';
68+
?>
69+
--EXPECT--
70+
No problems!
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Magic Methods inheritance rules on a non-trivial class hierarchy
3+
--FILE--
4+
<?php
5+
class A {
6+
public function __get(string|array $name): mixed {} // valid
7+
}
8+
9+
class B extends A {
10+
public function __get(string|array|object $name): int {} // also valid
11+
}
12+
13+
class C extends B {
14+
public function __get(string|array $name): int {} // this is invalid
15+
}
16+
?>
17+
--EXPECTF--
18+
Fatal error: Declaration of C::__get(array|string $name): int must be compatible with B::__get(object|array|string $name): int in %s on line %d
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Magic Methods inheritance rules on a non-trivial class hierarchy
3+
--FILE--
4+
<?php
5+
class A {
6+
public function __get(string|array $name): mixed {} // valid
7+
}
8+
9+
class B extends A {
10+
public function __get(string|array|object $name): int {} // also valid
11+
}
12+
13+
class C extends B {
14+
public function __get(string|array|object $name): int|float {} // this is invalid
15+
}
16+
?>
17+
--EXPECTF--
18+
Fatal error: Declaration of C::__get(object|array|string $name): int|float must be compatible with B::__get(object|array|string $name): int in %s on line %d

Zend/tests/magic_methods_sleep.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__sleep cannot take arguments
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __sleep(string $name) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Method Foo::__sleep() cannot take arguments in %s on line %d

Zend/tests/magic_methods_wakeup.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__wakeup cannot take arguments
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __wakeup(string $name) {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Method Foo::__wakeup() cannot take arguments in %s on line %d

Zend/tests/return_types/019.phpt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
--TEST--
2-
__clone cannot declare a return type
2+
__clone can only declare void return
33
--FILE--
44
<?php
55

66
class Foo {
77
function __clone() : Foo {}
88
}
9+
?>
910
--EXPECTF--
10-
Fatal error: %s::%s() cannot declare a return type in %s on line %d
11+
Fatal error: Foo::__clone(): Return type must be void when declared in %s on line %d

Zend/tests/return_types/033.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__set can only declare void return
3+
--FILE--
4+
<?php
5+
class Foo {
6+
function __set($name, $value) : string {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__set(): Return type must be void when declared in %s on line %d

Zend/tests/return_types/034.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__isset can only declare a boolean return type
3+
--FILE--
4+
<?php
5+
class Foo {
6+
function __isset($name) : \stdClass|bool {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__isset(): Return type must be bool when declared in %s on line %d

Zend/tests/return_types/035.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__unset can only declare void return
3+
--FILE--
4+
<?php
5+
class Foo {
6+
function __unset($name) : bool {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__unset(): Return type must be void when declared in %s on line %d

Zend/tests/return_types/036.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
__toString can only declare string return type
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __toString(): bool {
7+
}
8+
}
9+
?>
10+
--EXPECTF--
11+
Fatal error: Declaration of Foo::__toString(): bool must be compatible with Stringable::__toString(): string in %s on line %d

Zend/tests/return_types/037.phpt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
__debugInfo can only declare array as return type
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __debugInfo(): bool {
7+
}
8+
}
9+
?>
10+
--EXPECTF--
11+
Fatal error: Foo::__debugInfo(): Return type must be ?array when declared in %s on line %d

Zend/tests/return_types/038.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__serialize can only declare array as return type
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __serialize(): \stdClass {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__serialize(): Return type must be array when declared in %s on line %d

Zend/tests/return_types/039.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__unserialize can only declare void return
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __unserialize(array $data): array {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__unserialize(): Return type must be void when declared in %s on line %d

Zend/tests/return_types/040.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__sleep can only declare return as array
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __sleep(): bool|int {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__sleep(): Return type must be array when declared in %s on line %d

Zend/tests/return_types/041.phpt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
__wakeup can only declare return void
3+
--FILE--
4+
<?php
5+
class Foo {
6+
public function __wakeup(): bool {}
7+
}
8+
?>
9+
--EXPECTF--
10+
Fatal error: Foo::__wakeup(): Return type must be void when declared in %s on line %d

Zend/tests/return_types/042.phpt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
__debugInfo can declare union return type
3+
--FILE--
4+
<?php
5+
class UnionType {
6+
public function __debugInfo(): array|null {}
7+
}
8+
9+
class UnionType2 {
10+
public function __debugInfo(): null|array {}
11+
}
12+
13+
class UnionTypeOldStyle {
14+
public function __debugInfo(): ?array {}
15+
}
16+
17+
class JustAnArray {
18+
public function __debugInfo(): array {}
19+
}
20+
21+
echo 'No problems!';
22+
?>
23+
--EXPECT--
24+
No problems!

0 commit comments

Comments
 (0)