diff options
55 files changed, 927 insertions, 15 deletions
diff --git a/Zend/tests/type_declarations/mixed/casting/mixed_cast_error.phpt b/Zend/tests/type_declarations/mixed/casting/mixed_cast_error.phpt new file mode 100644 index 0000000000..416bddddc2 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/casting/mixed_cast_error.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test that a mixed casting is not supported +--FILE-- +<?php + +$foo = (mixed) 12; + +?> +--EXPECTF-- +Parse error: syntax error, unexpected '12' (T_LNUMBER) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error1.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error1.phpt new file mode 100644 index 0000000000..88e4a3da50 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error1.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a mixed parameter type can't be overridden by a built-in type +--FILE-- +<?php + +class Foo +{ + public function method(mixed $a) {} +} + +class Bar extends Foo +{ + public function method(bool $a) {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Bar::method(bool $a) must be compatible with Foo::method(mixed $a) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error2.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error2.phpt new file mode 100644 index 0000000000..e0a4975f86 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error2.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a mixed parameter type can't be overridden by a nullable built-in type +--FILE-- +<?php + +class Foo +{ + public function method(mixed $a) {} +} + +class Bar extends Foo +{ + public function method(?int $a) {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Bar::method(?int $a) must be compatible with Foo::method(mixed $a) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error3.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error3.phpt new file mode 100644 index 0000000000..705813d30d --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error3.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a mixed parameter type can't be overridden by a union of all built-in types +--FILE-- +<?php + +class Foo +{ + public function method(mixed $a) {} +} + +class Bar extends Foo +{ + public function method(bool|int|float|string|array|object|null $a) {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Bar::method(object|array|string|int|float|bool|null $a) must be compatible with Foo::method(mixed $a) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error4.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error4.phpt new file mode 100644 index 0000000000..5bffe6495e --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_error4.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a mixed parameter type can't be overridden by a union type of classes +--FILE-- +<?php + +class Foo +{ + public function method(mixed $a) {} +} + +class Bar extends Foo +{ + public function method(stdClass|Foo $a) {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Bar::method(stdClass|Foo $a) must be compatible with Foo::method(mixed $a) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success1.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success1.phpt new file mode 100644 index 0000000000..7b3403c6de --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success1.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a mixed parameter type supports invariance +--FILE-- +<?php + +class Foo +{ + public function method(mixed $a) {} +} + +class Bar extends Foo +{ + public function method(mixed $a) {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success2.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success2.phpt new file mode 100644 index 0000000000..9bbd78a427 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success2.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a mixed parameter type can be overridden by no type +--FILE-- +<?php + +class Foo +{ + public function method(mixed $a) {} +} + +class Bar extends Foo +{ + public function method($a) {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success3.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success3.phpt new file mode 100644 index 0000000000..bc3be65688 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success3.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a parameter of no type can be overridden by the mixed type +--FILE-- +<?php + +class Foo +{ + public function method($a) {} +} + +class Bar extends Foo +{ + public function method(mixed $a) {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success4.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success4.phpt new file mode 100644 index 0000000000..365ea6aeb7 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success4.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a parameter of a built-in type can be overridden by the mixed type +--FILE-- +<?php + +class Foo +{ + public function method(int $a) {} +} + +class Bar extends Foo +{ + public function method(mixed $a) {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success5.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success5.phpt new file mode 100644 index 0000000000..9cd40776c5 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success5.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a parameter of a nullable built-in type can be overridden by the mixed type +--FILE-- +<?php + +class Foo +{ + public function method(?int $a) {} +} + +class Bar extends Foo +{ + public function method(mixed $a) {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success6.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success6.phpt new file mode 100644 index 0000000000..0621d5a872 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success6.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a parameter of a union of all built-in types can be overridden by the mixed type +--FILE-- +<?php + +class Foo +{ + public function method(bool|int|float|string|array|object|null $a) {} +} + +class Bar extends Foo +{ + public function method(mixed $a) {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success7.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success7.phpt new file mode 100644 index 0000000000..e5efc82072 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_parameter_inheritance_success7.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a parameter of a union type of classes can be overridden by the mixed type +--FILE-- +<?php + +class Foo +{ + public function method(stdClass|Foo $a) {} +} + +class Bar extends Foo +{ + public function method(mixed $a) {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error1.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error1.phpt new file mode 100644 index 0000000000..b1fba7a716 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error1.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a property of mixed type can't be overridden by a property of a built-in type +--FILE-- +<?php + +class Foo +{ + public mixed $property1; +} + +class Bar extends Foo +{ + public int $property1; +} + +?> +--EXPECTF-- +Fatal error: Type of Bar::$property1 must be mixed (as in class Foo) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error2.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error2.phpt new file mode 100644 index 0000000000..8052dfcec1 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error2.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a property of mixed type can't be overridden by a property of class type +--FILE-- +<?php + +class Foo +{ + public mixed $property1; +} + +class Bar extends Foo +{ + public stdClass $property1; +} + +?> +--EXPECTF-- +Fatal error: Type of Bar::$property1 must be mixed (as in class Foo) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error3.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error3.phpt new file mode 100644 index 0000000000..4199bcd3a2 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error3.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a property of mixed type can't be overridden by an untyped property +--FILE-- +<?php + +class Foo +{ + public mixed $property1; +} + +class Bar extends Foo +{ + public $property1; +} + +?> +--EXPECTF-- +Fatal error: Type of Bar::$property1 must be mixed (as in class Foo) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error4.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error4.phpt new file mode 100644 index 0000000000..bc79d757ee --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error4.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a property of mixed type can't be overridden by a property of a union type +--FILE-- +<?php + +class Foo +{ + public mixed $property1; +} + +class Bar extends Foo +{ + public bool|int|float|string|array|object|null $property1; +} + +?> +--EXPECTF-- +Fatal error: Type of Bar::$property1 must be mixed (as in class Foo) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error5.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error5.phpt new file mode 100644 index 0000000000..8f2c6dc9be --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error5.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a property of a built-in type can't be overridden by a property of mixed type +--FILE-- +<?php + +class Foo +{ + public int $property1; +} + +class Bar extends Foo +{ + public mixed $property1; +} + +?> +--EXPECTF-- +Fatal error: Type of Bar::$property1 must be int (as in class Foo) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error6.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error6.phpt new file mode 100644 index 0000000000..2ba936c119 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error6.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a property of class type can't be overridden by a property of mixed type +--FILE-- +<?php + +class Foo +{ + public stdClass $property1; +} + +class Bar extends Foo +{ + public mixed $property1; +} + +?> +--EXPECTF-- +Fatal error: Type of Bar::$property1 must be stdClass (as in class Foo) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error7.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error7.phpt new file mode 100644 index 0000000000..138952b3df --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error7.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that an untyped property can't be overridden by a property of mixed type +--FILE-- +<?php + +class Foo +{ + public $property1; +} + +class Bar extends Foo +{ + public mixed $property1; +} + +?> +--EXPECTF-- +Fatal error: Type of Bar::$property1 must not be defined (as in class Foo) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error8.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error8.phpt new file mode 100644 index 0000000000..6e564fa4ae --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_error8.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a property of a union type can't be overridden by a property of mixed type +--FILE-- +<?php + +class Foo +{ + public bool|int|float|string|array|object|null $property1; +} + +class Bar extends Foo +{ + public mixed $property1; +} + +?> +--EXPECTF-- +Fatal error: Type of Bar::$property1 must be object|array|string|int|float|bool|null (as in class Foo) in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_success.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_success.phpt new file mode 100644 index 0000000000..9f4f4fa546 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_property_inheritance_success.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a property of mixed property type can be overridden by a property of mixed type +--FILE-- +<?php + +class Foo +{ + public mixed $property1; +} + +class Bar extends Foo +{ + public mixed $property1; +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_error1.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_error1.phpt new file mode 100644 index 0000000000..f0cdf26b2d --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_error1.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a mixed return type can't be overridden by the void type +--FILE-- +<?php + +class Foo +{ + public function method(): mixed {} +} + +class Bar extends Foo +{ + public function method(): void {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Bar::method(): void must be compatible with Foo::method(): mixed in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_error2.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_error2.phpt new file mode 100644 index 0000000000..1bca4f481a --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_error2.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that a mixed return type can't be overridden by no return type +--FILE-- +<?php + +class Foo +{ + public function method(): mixed {} +} + +class Bar extends Foo +{ + public function method() {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Bar::method() must be compatible with Foo::method(): mixed in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_error3.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_error3.phpt new file mode 100644 index 0000000000..a16249ec0b --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_error3.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that the void return type can't be overridden by the mixed type +--FILE-- +<?php + +class Foo +{ + public function method(): void {} +} + +class Bar extends Foo +{ + public function method(): mixed {} +} + +?> +--EXPECTF-- +Fatal error: Declaration of Bar::method(): mixed must be compatible with Foo::method(): void in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success1.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success1.phpt new file mode 100644 index 0000000000..3b46f4c20f --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success1.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a mixed return value supports invariance +--FILE-- +<?php + +class Foo +{ + public function method(): mixed {} +} + +class Bar extends Foo +{ + public function method(): mixed {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success2.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success2.phpt new file mode 100644 index 0000000000..6d0f7efcfd --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success2.phpt @@ -0,0 +1,57 @@ +--TEST-- +Test that a mixed return type can be overridden by any single (and nullable) type except void +--FILE-- +<?php + +class Foo +{ + public function method(): mixed {} +} + +class Bar1 extends Foo +{ + public function method(): bool {} +} + +class Bar2 extends Foo +{ + public function method(): int {} +} + +class Bar3 extends Foo +{ + public function method(): float {} +} + +class Bar4 extends Foo +{ + public function method(): string {} +} + +class Bar5 extends Foo +{ + public function method(): array {} +} + +class Bar6 extends Foo +{ + public function method(): object {} +} + +class Bar7 extends Foo +{ + public function method(): stdClass {} +} + +class Bar8 extends Foo +{ + public function method(): ?int {} +} + +class Bar9 extends Foo +{ + public function method(): ?stdClass {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success3.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success3.phpt new file mode 100644 index 0000000000..4b11fe2ef0 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success3.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test that a mixed return type can be overridden by any union return type +--FILE-- +<?php + +class Foo +{ + public function method(): mixed {} +} + +class Bar1 extends Foo +{ + public function method(): bool|int|null {} +} + +class Bar3 extends Foo +{ + public function method(): bool|int|float|string|array|object|null {} +} + +class Bar4 extends Foo +{ + public function method(): stdClass|Foo {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success4.phpt b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success4.phpt new file mode 100644 index 0000000000..ce652bc1cb --- /dev/null +++ b/Zend/tests/type_declarations/mixed/inheritance/mixed_return_inheritance_success4.phpt @@ -0,0 +1,17 @@ +--TEST-- +Test that a no return type can be overridden by the mixed type +--FILE-- +<?php + +class Foo +{ + public function method() {} +} + +class Bar extends Foo +{ + public function method(): mixed {} +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/syntax/mixed_class_error.phpt b/Zend/tests/type_declarations/mixed/syntax/mixed_class_error.phpt new file mode 100644 index 0000000000..8a5a385576 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/mixed_class_error.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test that mixed is a reserved class name +--FILE-- +<?php + +class mixed +{ +} + +?> +--EXPECTF-- +Fatal error: Cannot use 'mixed' as class name as it is reserved in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_error1.phpt b/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_error1.phpt new file mode 100644 index 0000000000..a240dc3bfc --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_error1.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test that the mixed parameter type can't be used together with any other type +--FILE-- +<?php + +function foo(mixed|int|null $a) +{ +} + +?> +--EXPECTF-- +Fatal error: Type mixed can only be used as a standalone type in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_error2.phpt b/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_error2.phpt new file mode 100644 index 0000000000..8f1d172810 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_error2.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test that the nullable mixed parameter type is not valid even though a null default value +--FILE-- +<?php + +function foo(?mixed $a = null) +{ +} + +?> +--EXPECTF-- +Fatal error: Type mixed cannot be marked as nullable since mixed already includes null in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_success.phpt b/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_success.phpt new file mode 100644 index 0000000000..91c72686a0 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_success.phpt @@ -0,0 +1,11 @@ +--TEST-- +Test that mixed is a valid parameter type +--FILE-- +<?php + +function foo(mixed $a) +{ +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_sucess4.phpt b/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_sucess4.phpt new file mode 100644 index 0000000000..89f8bfa543 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/mixed_parameter_sucess4.phpt @@ -0,0 +1,11 @@ +--TEST-- +Test that the mixed parameter type can have any default value +--FILE-- +<?php + +function foo(mixed $a = null, mixed $b = false, mixed $c = 1, mixed $d = 3.13, mixed $e = "", mixed $f = []) +{ +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/syntax/mixed_return_error.phpt b/Zend/tests/type_declarations/mixed/syntax/mixed_return_error.phpt new file mode 100644 index 0000000000..7bb8e7d4bb --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/mixed_return_error.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test that the mixed return type can't be used together with any other type +--FILE-- +<?php + +function foo(): mixed|string|null +{ + return null; +} + +?> +--EXPECTF-- +Fatal error: Type mixed can only be used as a standalone type in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/syntax/mixed_return_success.phpt b/Zend/tests/type_declarations/mixed/syntax/mixed_return_success.phpt new file mode 100644 index 0000000000..d8b39a80d9 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/mixed_return_success.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test that mixed is a valid return type +--FILE-- +<?php + +function foo(): mixed +{ + return null; +} + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/syntax/mixed_void_return_error.phpt b/Zend/tests/type_declarations/mixed/syntax/mixed_void_return_error.phpt new file mode 100644 index 0000000000..7c66e310a5 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/mixed_void_return_error.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test that the mixed|void return type is not valid +--FILE-- +<?php + +function foo(): mixed|void +{ + return null; +} + +?> +--EXPECTF-- +Fatal error: Type mixed can only be used as a standalone type in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/syntax/nullable_mixed_parameter_error.phpt b/Zend/tests/type_declarations/mixed/syntax/nullable_mixed_parameter_error.phpt new file mode 100644 index 0000000000..f89238ec22 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/nullable_mixed_parameter_error.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test that the nullable mixed parameter type is not valid +--FILE-- +<?php + +function foo(?mixed $a) +{ +} + +?> +--EXPECTF-- +Fatal error: Type mixed cannot be marked as nullable since mixed already includes null in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/syntax/nullable_mixed_property_error.phpt b/Zend/tests/type_declarations/mixed/syntax/nullable_mixed_property_error.phpt new file mode 100644 index 0000000000..f5c8dadce7 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/nullable_mixed_property_error.phpt @@ -0,0 +1,13 @@ +--TEST-- +Test that the nullable mixed property type is not valid +--FILE-- +<?php + +class Foo +{ + public ?mixed $property1; +} + +?> +--EXPECTF-- +Fatal error: Type mixed cannot be marked as nullable since mixed already includes null in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/syntax/nullable_mixed_return_error.phpt b/Zend/tests/type_declarations/mixed/syntax/nullable_mixed_return_error.phpt new file mode 100644 index 0000000000..945e7dcc8d --- /dev/null +++ b/Zend/tests/type_declarations/mixed/syntax/nullable_mixed_return_error.phpt @@ -0,0 +1,12 @@ +--TEST-- +Test that the nullable mixed return type is not valid +--FILE-- +<?php + +function foo(): ?mixed +{ +} + +?> +--EXPECTF-- +Fatal error: Type mixed cannot be marked as nullable since mixed already includes null in %s on line %d diff --git a/Zend/tests/type_declarations/mixed/validation/mixed_parameter_strict_success.phpt b/Zend/tests/type_declarations/mixed/validation/mixed_parameter_strict_success.phpt new file mode 100644 index 0000000000..01cb09f269 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/validation/mixed_parameter_strict_success.phpt @@ -0,0 +1,20 @@ +--TEST-- +Test that the mixed parameter type accepts any kind of arguments in strict mode +--FILE-- +<?php +declare(strict_types=1); + +function foo(mixed $a) +{ +} + +foo(null); +foo(false); +foo(1); +foo(3.14); +foo(""); +foo([]); +foo(new stdClass()); + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/validation/mixed_parameter_weak_success.phpt b/Zend/tests/type_declarations/mixed/validation/mixed_parameter_weak_success.phpt new file mode 100644 index 0000000000..e84a41f45f --- /dev/null +++ b/Zend/tests/type_declarations/mixed/validation/mixed_parameter_weak_success.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test that the mixed parameter type accepts any kind of arguments in weak mode +--FILE-- +<?php + +function foo(mixed $a) +{ +} + +foo(null); +foo(false); +foo(1); +foo(3.14); +foo(""); +foo([]); +foo(new stdClass()); + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/validation/mixed_property_strict_success.phpt b/Zend/tests/type_declarations/mixed/validation/mixed_property_strict_success.phpt new file mode 100644 index 0000000000..8a2c600020 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/validation/mixed_property_strict_success.phpt @@ -0,0 +1,36 @@ +--TEST-- +Test that the mixed property type accepts any kind of value in strict mode +--FILE-- +<?php +declare(strict_types=1); + +class Foo +{ + public mixed $property1; + public mixed $property2 = null; + public mixed $property3 = false; + public mixed $property4 = true; + public mixed $property5 = 1; + public mixed $property6 = 3.14; + public mixed $property7 = "foo"; + public mixed $property8 = []; + public mixed $property9; + + public function __construct() + { + $this->property9 = fopen(__FILE__, "r"); + $this->property9 = new stdClass(); + } +} + +$foo = new Foo(); + +try { + $foo->property1; +} catch (Error $exception) { + echo $exception->getMessage() . "\n"; +} + +?> +--EXPECT-- +Typed property Foo::$property1 must not be accessed before initialization diff --git a/Zend/tests/type_declarations/mixed/validation/mixed_property_weak_success.phpt b/Zend/tests/type_declarations/mixed/validation/mixed_property_weak_success.phpt new file mode 100644 index 0000000000..e83023d54c --- /dev/null +++ b/Zend/tests/type_declarations/mixed/validation/mixed_property_weak_success.phpt @@ -0,0 +1,35 @@ +--TEST-- +Test that the mixed property type accepts any kind of value in weak mode +--FILE-- +<?php + +class Foo +{ + public mixed $property1; + public mixed $property2 = null; + public mixed $property3 = false; + public mixed $property4 = true; + public mixed $property5 = 1; + public mixed $property6 = 3.14; + public mixed $property7 = "foo"; + public mixed $property8 = []; + public mixed $property9; + + public function __construct() + { + $this->property9 = fopen(__FILE__, "r"); + $this->property9 = new stdClass(); + } +} + +$foo = new Foo(); + +try { + $foo->property1; +} catch (Error $exception) { + echo $exception->getMessage() . "\n"; +} + +?> +--EXPECT-- +Typed property Foo::$property1 must not be accessed before initialization diff --git a/Zend/tests/type_declarations/mixed/validation/mixed_return_strict_error.phpt b/Zend/tests/type_declarations/mixed/validation/mixed_return_strict_error.phpt new file mode 100644 index 0000000000..ded74b0467 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/validation/mixed_return_strict_error.phpt @@ -0,0 +1,20 @@ +--TEST-- +Test that the mixed return type is not compatible with a void return value in strict mode +--FILE-- +<?php +declare(strict_types=1); + +function foo(): mixed +{ +} + +try { + foo(); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +?> +--EXPECTF-- +Return value of foo() must be of type mixed, none returned + diff --git a/Zend/tests/type_declarations/mixed/validation/mixed_return_strict_success.phpt b/Zend/tests/type_declarations/mixed/validation/mixed_return_strict_success.phpt new file mode 100644 index 0000000000..d9f9ae4ba9 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/validation/mixed_return_strict_success.phpt @@ -0,0 +1,20 @@ +--TEST-- +Test that the mixed return type is compatible with any kind of return value in strict mode +--FILE-- +<?php +declare(strict_types=1); + +function foo($a): mixed +{ + return $a; +} + +foo(null); +foo(false); +foo(1); +foo(""); +foo([]); +foo(new stdClass()); + +?> +--EXPECT-- diff --git a/Zend/tests/type_declarations/mixed/validation/mixed_return_weak_error.phpt b/Zend/tests/type_declarations/mixed/validation/mixed_return_weak_error.phpt new file mode 100644 index 0000000000..316f465877 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/validation/mixed_return_weak_error.phpt @@ -0,0 +1,18 @@ +--TEST-- +Test that the mixed return type is not compatible with a void return value +--FILE-- +<?php + +function foo(): mixed +{ +} + +try { + foo(); +} catch (TypeError $exception) { + echo $exception->getMessage() . "\n"; +} + +?> +--EXPECT-- +Return value of foo() must be of type mixed, none returned diff --git a/Zend/tests/type_declarations/mixed/validation/mixed_return_weak_success.phpt b/Zend/tests/type_declarations/mixed/validation/mixed_return_weak_success.phpt new file mode 100644 index 0000000000..23f56c13e7 --- /dev/null +++ b/Zend/tests/type_declarations/mixed/validation/mixed_return_weak_success.phpt @@ -0,0 +1,19 @@ +--TEST-- +Test that the mixed return type is compatible with any kind of return value in weak mode +--FILE-- +<?php + +function foo($a): mixed +{ + return $a; +} + +foo(null); +foo(false); +foo(1); +foo(""); +foo([]); +foo(new stdClass()); + +?> +--EXPECT-- diff --git a/Zend/zend_API.c b/Zend/zend_API.c index cd72a6f156..d7edc4b0a5 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -123,6 +123,8 @@ ZEND_API const char *zend_get_type_by_const(int type) /* {{{ */ return "array"; case IS_VOID: return "void"; + case IS_MIXED: + return "mixed"; case _IS_NUMBER: return "number"; EMPTY_SWITCH_DEFAULT_CASE() diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 7cfc0450fd..2b5d1ea324 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1572,6 +1572,7 @@ simple_list: case IS_ARRAY: APPEND_STR("array"); case IS_CALLABLE: APPEND_STR("callable"); case IS_STATIC: APPEND_STR("static"); + case IS_MIXED: APPEND_STR("mixed"); EMPTY_SWITCH_DEFAULT_CASE(); } break; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 9c6546ac41..9206a15be5 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -172,6 +172,7 @@ static const struct reserved_class_name reserved_class_names[] = { {ZEND_STRL("void")}, {ZEND_STRL("iterable")}, {ZEND_STRL("object")}, + {ZEND_STRL("mixed")}, {NULL, 0} }; @@ -220,6 +221,7 @@ static const builtin_type_info builtin_types[] = { {ZEND_STRL("void"), IS_VOID}, {ZEND_STRL("iterable"), IS_ITERABLE}, {ZEND_STRL("object"), IS_OBJECT}, + {ZEND_STRL("mixed"), IS_MIXED}, {NULL, 0, IS_UNDEF} }; @@ -1166,6 +1168,7 @@ static zend_string *resolve_class_name(zend_string *name, zend_class_entry *scop zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope) { zend_string *str = NULL; + if (ZEND_TYPE_HAS_LIST(type)) { zend_type *list_type; ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(type), list_type) { @@ -1182,6 +1185,12 @@ zend_string *zend_type_to_string_resolved(zend_type type, zend_class_entry *scop } uint32_t type_mask = ZEND_TYPE_FULL_MASK(type); + + if (type_mask == MAY_BE_ANY) { + str = add_type_string(str, ZSTR_KNOWN(ZEND_STR_MIXED)); + + return str; + } if (type_mask & MAY_BE_STATIC) { zend_string *name = ZSTR_KNOWN(ZEND_STR_STATIC); if (scope) { @@ -2296,11 +2305,14 @@ static void zend_emit_return_type_check( } } - if (expr && expr->op_type == IS_CONST) { - if (ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE(expr->u.constant))) { - /* we don't need run-time check */ - return; - } + if (expr && ZEND_TYPE_PURE_MASK(type) == MAY_BE_ANY) { + /* we don't need run-time check for mixed return type */ + return; + } + + if (expr && expr->op_type == IS_CONST && ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE(expr->u.constant))) { + /* we don't need run-time check */ + return; } opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL); @@ -5576,8 +5588,13 @@ static zend_type zend_compile_typename( for (uint32_t i = 0; i < list->children; i++) { zend_ast *type_ast = list->child[i]; zend_type single_type = zend_compile_single_typename(type_ast); - uint32_t type_mask_overlap = - ZEND_TYPE_PURE_MASK(type) & ZEND_TYPE_PURE_MASK(single_type); + uint32_t single_type_mask = ZEND_TYPE_PURE_MASK(single_type); + + if (single_type_mask == MAY_BE_ANY) { + zend_error_noreturn(E_COMPILE_ERROR, "Type mixed can only be used as a standalone type"); + } + + uint32_t type_mask_overlap = ZEND_TYPE_PURE_MASK(type) & single_type_mask; if (type_mask_overlap) { zend_type overlap_type = ZEND_TYPE_INIT_MASK(type_mask_overlap); zend_string *overlap_type_str = zend_type_to_string(overlap_type); @@ -5654,6 +5671,10 @@ static zend_type zend_compile_typename( ZSTR_VAL(type_str)); } + if (type_mask == MAY_BE_ANY && (orig_ast_attr & ZEND_TYPE_NULLABLE)) { + zend_error_noreturn(E_COMPILE_ERROR, "Type mixed cannot be marked as nullable since mixed already includes null"); + } + if ((type_mask & MAY_BE_OBJECT) && (ZEND_TYPE_HAS_CLASS(type) || (type_mask & MAY_BE_STATIC))) { zend_string *type_str = zend_type_to_string(type); zend_error_noreturn(E_COMPILE_ERROR, diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 72f4888228..f42998a8f8 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -507,8 +507,8 @@ static inheritance_status zend_do_perform_arg_type_hint_check( zend_class_entry *fe_scope, zend_arg_info *fe_arg_info, zend_class_entry *proto_scope, zend_arg_info *proto_arg_info) /* {{{ */ { - if (!ZEND_TYPE_IS_SET(fe_arg_info->type)) { - /* Child with no type is always compatible */ + if (!ZEND_TYPE_IS_SET(fe_arg_info->type) || ZEND_TYPE_PURE_MASK(fe_arg_info->type) == MAY_BE_ANY) { + /* Child with no type or mixed type is always compatible */ return INHERITANCE_SUCCESS; } diff --git a/Zend/zend_string.h b/Zend/zend_string.h index 45ef3cbb01..fa794278e5 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -522,6 +522,7 @@ EMPTY_SWITCH_DEFAULT_CASE() _(ZEND_STR_VOID, "void") \ _(ZEND_STR_FALSE, "false") \ _(ZEND_STR_NULL_LOWERCASE, "null") \ + _(ZEND_STR_MIXED, "mixed") \ typedef enum _zend_known_string_id { diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 3543277b77..026177cf17 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -257,7 +257,7 @@ typedef struct { { NULL, (_type_mask) } #define ZEND_TYPE_INIT_CODE(code, allow_null, extra_flags) \ - ZEND_TYPE_INIT_MASK(((code) == _IS_BOOL ? MAY_BE_BOOL : (1 << (code))) \ + ZEND_TYPE_INIT_MASK(((code) == _IS_BOOL ? MAY_BE_BOOL : ((code) == IS_MIXED ? MAY_BE_ANY : (1 << (code)))) \ | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) | (extra_flags)) #define ZEND_TYPE_INIT_PTR(ptr, type_kind, allow_null, extra_flags) \ @@ -534,6 +534,7 @@ struct _zend_ast_ref { #define IS_ITERABLE 13 #define IS_VOID 14 #define IS_STATIC 15 +#define IS_MIXED 16 /* internal types */ #define IS_INDIRECT 12 @@ -542,8 +543,8 @@ struct _zend_ast_ref { #define _IS_ERROR 15 /* used for casts */ -#define _IS_BOOL 16 -#define _IS_NUMBER 17 +#define _IS_BOOL 17 +#define _IS_NUMBER 18 static zend_always_inline zend_uchar zval_get_type(const zval* pz) { return pz->u1.v.type; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 1ed057d902..9aac065e38 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1187,13 +1187,13 @@ static void reflection_type_factory(zend_type type, zval *object, zend_bool lega reflection_object *intern; type_reference *reference; zend_bool is_union = is_union_type(type); + zend_bool is_mixed = ZEND_TYPE_PURE_MASK(type) == MAY_BE_ANY; - reflection_instantiate( - is_union ? reflection_union_type_ptr : reflection_named_type_ptr, object); + reflection_instantiate(is_union && !is_mixed ? reflection_union_type_ptr : reflection_named_type_ptr, object); intern = Z_REFLECTION_P(object); reference = (type_reference*) emalloc(sizeof(type_reference)); reference->type = type; - reference->legacy_behavior = legacy_behavior && !is_union; + reference->legacy_behavior = legacy_behavior && !is_union && !is_mixed; intern->ptr = reference; intern->ref_type = REF_TYPE_TYPE; diff --git a/ext/reflection/tests/mixed_type.phpt b/ext/reflection/tests/mixed_type.phpt new file mode 100644 index 0000000000..d44b91789c --- /dev/null +++ b/ext/reflection/tests/mixed_type.phpt @@ -0,0 +1,32 @@ +--TEST-- +Test that the mixed type is reflectable +--FILE-- +<?php +class A +{ + public mixed $a; + + public function test(mixed $a): mixed {} +} + +$a = new A(); + +$object = new ReflectionObject($a); +$method = new ReflectionMethod($a, "test"); + +var_dump($object->getProperty("a")->getType()->getName()); +var_dump($method->getParameters()[0]->getType()->getName()); +var_dump($method->getReturnType()->getName()); + +var_dump((string) $object->getProperty("a")->getType()); +var_dump((string) $method->getParameters()[0]->getType()); +var_dump((string) $method->getReturnType()); + +?> +--EXPECT-- +string(5) "mixed" +string(5) "mixed" +string(5) "mixed" +string(5) "mixed" +string(5) "mixed" +string(5) "mixed" |