--- /dev/null
+--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
--- /dev/null
+--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
--- /dev/null
+--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
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++;
#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
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;
}
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);
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 {
return_type:
/* empty */ { $$ = NULL; }
| ':' type { $$ = $2; }
+ | ':' '?' type { $$ = $3; $$->attr |= ZEND_TYPE_NULLABLE; }
;
argument_list: