mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
Add support for the mixed type
RFC: https://wiki.php.net/rfc/mixed_type_v2 Closes GH-5313 Co-authored-by: Dan Ackroyd <danack@basereality.com>
This commit is contained in:
parent
4bc1d8333a
commit
aec4c0fd03
55 changed files with 927 additions and 15 deletions
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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--
|
|
@ -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--
|
|
@ -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--
|
|
@ -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--
|
|
@ -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--
|
|
@ -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--
|
|
@ -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--
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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--
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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--
|
|
@ -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--
|
|
@ -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--
|
|
@ -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--
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,11 @@
|
|||
--TEST--
|
||||
Test that mixed is a valid parameter type
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function foo(mixed $a)
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
|
@ -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--
|
|
@ -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
|
|
@ -0,0 +1,12 @@
|
|||
--TEST--
|
||||
Test that mixed is a valid return type
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function foo(): mixed
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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--
|
|
@ -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--
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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--
|
|
@ -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
|
|
@ -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--
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
32
ext/reflection/tests/mixed_type.phpt
Normal file
32
ext/reflection/tests/mixed_type.phpt
Normal file
|
@ -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"
|
Loading…
Add table
Add a link
Reference in a new issue