summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Zend/tests/return_types/030.phpt23
-rw-r--r--Zend/tests/return_types/031.phpt14
-rw-r--r--Zend/tests/return_types/032.phpt14
-rw-r--r--Zend/zend_compile.c5
-rw-r--r--Zend/zend_compile.h2
-rw-r--r--Zend/zend_inheritance.c10
-rw-r--r--Zend/zend_language_parser.y1
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: