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:
Máté Kocsis 2020-03-27 23:39:49 +01:00
parent 4bc1d8333a
commit aec4c0fd03
No known key found for this signature in database
GPG key ID: FD055E41728BF310
55 changed files with 927 additions and 15 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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--

View file

@ -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--

View file

@ -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--

View file

@ -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--

View file

@ -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--

View file

@ -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--

View file

@ -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--

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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--

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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--

View file

@ -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--

View file

@ -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--

View file

@ -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--

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,11 @@
--TEST--
Test that mixed is a valid parameter type
--FILE--
<?php
function foo(mixed $a)
{
}
?>
--EXPECT--

View file

@ -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--

View file

@ -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

View file

@ -0,0 +1,12 @@
--TEST--
Test that mixed is a valid return type
--FILE--
<?php
function foo(): mixed
{
return null;
}
?>
--EXPECT--

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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--

View file

@ -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--

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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--

View file

@ -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

View file

@ -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--

View file

@ -123,6 +123,8 @@ ZEND_API const char *zend_get_type_by_const(int type) /* {{{ */
return "array"; return "array";
case IS_VOID: case IS_VOID:
return "void"; return "void";
case IS_MIXED:
return "mixed";
case _IS_NUMBER: case _IS_NUMBER:
return "number"; return "number";
EMPTY_SWITCH_DEFAULT_CASE() EMPTY_SWITCH_DEFAULT_CASE()

View file

@ -1572,6 +1572,7 @@ simple_list:
case IS_ARRAY: APPEND_STR("array"); case IS_ARRAY: APPEND_STR("array");
case IS_CALLABLE: APPEND_STR("callable"); case IS_CALLABLE: APPEND_STR("callable");
case IS_STATIC: APPEND_STR("static"); case IS_STATIC: APPEND_STR("static");
case IS_MIXED: APPEND_STR("mixed");
EMPTY_SWITCH_DEFAULT_CASE(); EMPTY_SWITCH_DEFAULT_CASE();
} }
break; break;

View file

@ -172,6 +172,7 @@ static const struct reserved_class_name reserved_class_names[] = {
{ZEND_STRL("void")}, {ZEND_STRL("void")},
{ZEND_STRL("iterable")}, {ZEND_STRL("iterable")},
{ZEND_STRL("object")}, {ZEND_STRL("object")},
{ZEND_STRL("mixed")},
{NULL, 0} {NULL, 0}
}; };
@ -220,6 +221,7 @@ static const builtin_type_info builtin_types[] = {
{ZEND_STRL("void"), IS_VOID}, {ZEND_STRL("void"), IS_VOID},
{ZEND_STRL("iterable"), IS_ITERABLE}, {ZEND_STRL("iterable"), IS_ITERABLE},
{ZEND_STRL("object"), IS_OBJECT}, {ZEND_STRL("object"), IS_OBJECT},
{ZEND_STRL("mixed"), IS_MIXED},
{NULL, 0, IS_UNDEF} {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 *zend_type_to_string_resolved(zend_type type, zend_class_entry *scope) {
zend_string *str = NULL; zend_string *str = NULL;
if (ZEND_TYPE_HAS_LIST(type)) { if (ZEND_TYPE_HAS_LIST(type)) {
zend_type *list_type; zend_type *list_type;
ZEND_TYPE_LIST_FOREACH(ZEND_TYPE_LIST(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); 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) { if (type_mask & MAY_BE_STATIC) {
zend_string *name = ZSTR_KNOWN(ZEND_STR_STATIC); zend_string *name = ZSTR_KNOWN(ZEND_STR_STATIC);
if (scope) { if (scope) {
@ -2296,11 +2305,14 @@ static void zend_emit_return_type_check(
} }
} }
if (expr && expr->op_type == IS_CONST) { if (expr && ZEND_TYPE_PURE_MASK(type) == MAY_BE_ANY) {
if (ZEND_TYPE_CONTAINS_CODE(type, Z_TYPE(expr->u.constant))) { /* we don't need run-time check for mixed return type */
/* we don't need run-time check */ return;
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); 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++) { for (uint32_t i = 0; i < list->children; i++) {
zend_ast *type_ast = list->child[i]; zend_ast *type_ast = list->child[i];
zend_type single_type = zend_compile_single_typename(type_ast); zend_type single_type = zend_compile_single_typename(type_ast);
uint32_t type_mask_overlap = uint32_t single_type_mask = ZEND_TYPE_PURE_MASK(single_type);
ZEND_TYPE_PURE_MASK(type) & 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) { if (type_mask_overlap) {
zend_type overlap_type = ZEND_TYPE_INIT_MASK(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); 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)); 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))) { 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_string *type_str = zend_type_to_string(type);
zend_error_noreturn(E_COMPILE_ERROR, zend_error_noreturn(E_COMPILE_ERROR,

View file

@ -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 *fe_scope, zend_arg_info *fe_arg_info,
zend_class_entry *proto_scope, zend_arg_info *proto_arg_info) /* {{{ */ zend_class_entry *proto_scope, zend_arg_info *proto_arg_info) /* {{{ */
{ {
if (!ZEND_TYPE_IS_SET(fe_arg_info->type)) { if (!ZEND_TYPE_IS_SET(fe_arg_info->type) || ZEND_TYPE_PURE_MASK(fe_arg_info->type) == MAY_BE_ANY) {
/* Child with no type is always compatible */ /* Child with no type or mixed type is always compatible */
return INHERITANCE_SUCCESS; return INHERITANCE_SUCCESS;
} }

View file

@ -522,6 +522,7 @@ EMPTY_SWITCH_DEFAULT_CASE()
_(ZEND_STR_VOID, "void") \ _(ZEND_STR_VOID, "void") \
_(ZEND_STR_FALSE, "false") \ _(ZEND_STR_FALSE, "false") \
_(ZEND_STR_NULL_LOWERCASE, "null") \ _(ZEND_STR_NULL_LOWERCASE, "null") \
_(ZEND_STR_MIXED, "mixed") \
typedef enum _zend_known_string_id { typedef enum _zend_known_string_id {

View file

@ -257,7 +257,7 @@ typedef struct {
{ NULL, (_type_mask) } { NULL, (_type_mask) }
#define ZEND_TYPE_INIT_CODE(code, allow_null, extra_flags) \ #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)) | ((allow_null) ? _ZEND_TYPE_NULLABLE_BIT : 0) | (extra_flags))
#define ZEND_TYPE_INIT_PTR(ptr, type_kind, allow_null, 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_ITERABLE 13
#define IS_VOID 14 #define IS_VOID 14
#define IS_STATIC 15 #define IS_STATIC 15
#define IS_MIXED 16
/* internal types */ /* internal types */
#define IS_INDIRECT 12 #define IS_INDIRECT 12
@ -542,8 +543,8 @@ struct _zend_ast_ref {
#define _IS_ERROR 15 #define _IS_ERROR 15
/* used for casts */ /* used for casts */
#define _IS_BOOL 16 #define _IS_BOOL 17
#define _IS_NUMBER 17 #define _IS_NUMBER 18
static zend_always_inline zend_uchar zval_get_type(const zval* pz) { static zend_always_inline zend_uchar zval_get_type(const zval* pz) {
return pz->u1.v.type; return pz->u1.v.type;

View file

@ -1187,13 +1187,13 @@ static void reflection_type_factory(zend_type type, zval *object, zend_bool lega
reflection_object *intern; reflection_object *intern;
type_reference *reference; type_reference *reference;
zend_bool is_union = is_union_type(type); zend_bool is_union = is_union_type(type);
zend_bool is_mixed = ZEND_TYPE_PURE_MASK(type) == MAY_BE_ANY;
reflection_instantiate( reflection_instantiate(is_union && !is_mixed ? reflection_union_type_ptr : reflection_named_type_ptr, object);
is_union ? reflection_union_type_ptr : reflection_named_type_ptr, object);
intern = Z_REFLECTION_P(object); intern = Z_REFLECTION_P(object);
reference = (type_reference*) emalloc(sizeof(type_reference)); reference = (type_reference*) emalloc(sizeof(type_reference));
reference->type = type; reference->type = type;
reference->legacy_behavior = legacy_behavior && !is_union; reference->legacy_behavior = legacy_behavior && !is_union && !is_mixed;
intern->ptr = reference; intern->ptr = reference;
intern->ref_type = REF_TYPE_TYPE; intern->ref_type = REF_TYPE_TYPE;

View 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"