diff options
| -rw-r--r-- | Zend/tests/return_types/030.phpt | 23 | ||||
| -rw-r--r-- | Zend/tests/return_types/031.phpt | 14 | ||||
| -rw-r--r-- | Zend/tests/return_types/032.phpt | 14 | ||||
| -rw-r--r-- | Zend/zend_compile.c | 5 | ||||
| -rw-r--r-- | Zend/zend_compile.h | 2 | ||||
| -rw-r--r-- | Zend/zend_inheritance.c | 10 | ||||
| -rw-r--r-- | Zend/zend_language_parser.y | 1 |
7 files changed, 68 insertions, 1 deletions
diff --git a/Zend/tests/return_types/030.phpt b/Zend/tests/return_types/030.phpt new file mode 100644 index 0000000000..d1ceac8329 --- /dev/null +++ b/Zend/tests/return_types/030.phpt @@ -0,0 +1,23 @@ +--TEST-- +Nullable return value +--FILE-- +<?php +function foo($x) : ?array { + return $x; +} + +foo([]); +echo "ok\n"; +foo(null); +echo "ok\n"; +foo(0); +?> +--EXPECTF-- +ok +ok + +Fatal error: Uncaught TypeError: Return value of foo() must be of the type array, integer returned in %s030.php:3 +Stack trace: +#0 %s030.php(10): foo(0) +#1 {main} + thrown in %s030.php on line 3 diff --git a/Zend/tests/return_types/031.phpt b/Zend/tests/return_types/031.phpt new file mode 100644 index 0000000000..91ee2f8ce4 --- /dev/null +++ b/Zend/tests/return_types/031.phpt @@ -0,0 +1,14 @@ +--TEST-- +Nullable return type inheritance rules (non-nullable and nullable) +--FILE-- +<?php +class A { + function foo(): int {} +} +class B extends A { + function foo(): ?int {} +} +?> +DONE +--EXPECTF-- +Fatal error: Declaration of B::foo(): ?int must be compatible with A::foo(): int in %s031.php on line 7
\ No newline at end of file diff --git a/Zend/tests/return_types/032.phpt b/Zend/tests/return_types/032.phpt new file mode 100644 index 0000000000..00790b5d60 --- /dev/null +++ b/Zend/tests/return_types/032.phpt @@ -0,0 +1,14 @@ +--TEST-- +Nullable return type inheritance rules (nullable and non-nullable) +--FILE-- +<?php +class A { + function foo(): ?int {} +} +class B extends A { + function foo(): int {} +} +?> +DONE +--EXPECT-- +DONE diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 50bcb3b7ad..fe81aadcf8 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -4906,6 +4906,11 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ arg_infos->allow_null = 0; arg_infos->class_name = NULL; + if (return_type_ast->attr & ZEND_TYPE_NULLABLE) { + arg_infos->allow_null = 1; + return_type_ast->attr &= ~ZEND_TYPE_NULLABLE; + } + zend_compile_typename(return_type_ast, arg_infos); arg_infos++; diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index f52d22a384..1f6f49e215 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -836,6 +836,8 @@ ZEND_API void zend_assert_valid_class_name(const zend_string *const_name); #define ZEND_NAME_NOT_FQ 1 #define ZEND_NAME_RELATIVE 2 +#define ZEND_TYPE_NULLABLE (1<<8) + /* var status for backpatching */ #define BP_VAR_R 0 #define BP_VAR_W 1 diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 42283f94dc..df7dbfd63e 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -344,6 +344,10 @@ static zend_bool zend_do_perform_implementation_check(const zend_function *fe, c if (!zend_do_perform_type_hint_check(fe, fe->common.arg_info - 1, proto, proto->common.arg_info - 1)) { return 0; } + + if (fe->common.arg_info[-1].allow_null && !proto->common.arg_info[-1].allow_null) { + return 0; + } } return 1; } @@ -506,6 +510,9 @@ static ZEND_COLD zend_string *zend_get_function_declaration(const zend_function if (fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) { smart_str_appends(&str, ": "); + if (fptr->common.arg_info[-1].allow_null) { + smart_str_appendc(&str, '?'); + } zend_append_type_hint(&str, fptr, fptr->common.arg_info - 1, 1); } smart_str_0(&str); @@ -590,7 +597,8 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function * error_verb = "must"; } else if ((parent->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && (!(child->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || - !zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1))) { + !zend_do_perform_type_hint_check(child, child->common.arg_info - 1, parent, parent->common.arg_info - 1) || + (child->common.arg_info[-1].allow_null && !parent->common.arg_info[-1].allow_null))) { error_level = E_COMPILE_ERROR; error_verb = "must"; } else { diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 4722846ce7..c995e5fd4d 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -656,6 +656,7 @@ type: return_type: /* empty */ { $$ = NULL; } | ':' type { $$ = $2; } + | ':' '?' type { $$ = $3; $$->attr |= ZEND_TYPE_NULLABLE; } ; argument_list: |
