Core:
. Fixed bug #62210 (Exceptions can leak temporary variables). (Dmitry, Bob)
+ . Added void return type. (Andrea)
Standard
. Implemented FR #55716 (Add an option to pass a custom stream context to
========================================
1. Backward Incompatible Changes
========================================
+- Core:
+ . 'void' can no longer be used as the name of a class, interface, or trait.
+ This applies to declarations, class_alias() and use statements.
========================================
2. New Features
========================================
+- Core
+ . Added void return type, which requires that a function not return a value.
+ (RFC: https://wiki.php.net/rfc/void_return_type)
========================================
3. Changes in SAPI modules
--- /dev/null
+--TEST--
+void return type: acceptable cases
+--FILE--
+<?php
+
+function foo(): void {
+ // okay
+}
+
+foo();
+
+function bar(): void {
+ return; // okay
+}
+
+bar();
+
+echo "OK!", PHP_EOL;
+--EXPECT--
+OK!
--- /dev/null
+--TEST--
+void return type: unacceptable cases: explicit NULL return
+--FILE--
+<?php
+
+function foo(): void {
+ return NULL; // not permitted in a void function
+}
+
+// Note the lack of function call: function validated at compile-time
+--EXPECTF--
+Fatal error: A void function must not return a value in %s on line %d
--- /dev/null
+--TEST--
+void return type: unacceptable cases: explicit return of some other value
+--FILE--
+<?php
+
+function foo(): void {
+ return -1; // not permitted in a void function
+}
+
+// Note the lack of function call: function validated at compile-time
+--EXPECTF--
+Fatal error: A void function must not return a value in %s on line %d
--- /dev/null
+--TEST--
+void return type: not valid as a parameter type
+--FILE--
+<?php
+
+function foobar(void $a) {}
+--EXPECTF--
+Fatal error: void cannot be used as a parameter type in %s on line %d
return "callable";
case IS_ARRAY:
return "array";
+ case IS_VOID:
+ return "void";
default:
return "unknown";
}
{ZEND_STRL("static")},
{ZEND_STRL("string")},
{ZEND_STRL("true")},
+ {ZEND_STRL("void")},
{NULL, 0}
};
{ZEND_STRL("float"), IS_DOUBLE},
{ZEND_STRL("string"), IS_STRING},
{ZEND_STRL("bool"), _IS_BOOL},
+ {ZEND_STRL("void"), IS_VOID},
{NULL, 0, IS_UNDEF}
};
static void zend_emit_return_type_check(znode *expr, zend_arg_info *return_info) /* {{{ */
{
+ /* `return ...;` is illegal in a void function (but `return;` isn't) */
+ if (expr && return_info->type_hint == IS_VOID) {
+ zend_error_noreturn(E_COMPILE_ERROR, "A void function must not return a value");
+ }
+
if (return_info->type_hint != IS_UNDEF) {
zend_op *opline = zend_emit_op(NULL, ZEND_VERIFY_RETURN_TYPE, expr, NULL);
if (expr && expr->op_type == IS_CONST) {
zend_compile_typename(type_ast, arg_info);
+ if (arg_info->type_hint == IS_VOID) {
+ zend_error_noreturn(E_COMPILE_ERROR, "void cannot be used as a parameter type");
+ }
+
if (type_ast->kind == ZEND_AST_TYPE) {
if (arg_info->type_hint == IS_ARRAY) {
if (default_ast && !has_null_default
fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind);
}
+static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, const char *returned_msg, const char *returned_kind)
+{
+ const char *fname = ZSTR_VAL(zf->common.function_name);
+ const char *fsep;
+ const char *fclass;
+
+ if (zf->common.scope) {
+ fsep = "::";
+ fclass = ZSTR_VAL(zf->common.scope->name);
+ } else {
+ fsep = "";
+ fclass = "";
+ }
+
+ zend_type_error("%s%s%s() must not return a value, %s%s returned",
+ fclass, fsep, fname, returned_msg, returned_kind);
+}
+
#if ZEND_DEBUG
static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
{
} else if (ret_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) {
/* pass */
+ } else if (ret_info->type_hint == IS_VOID) {
+ zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
} else {
/* Use strict check to verify return value of internal function */
zend_verify_internal_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), zend_zval_type_name(ret), "");
} else if (ret_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) {
/* pass */
+ /* There would be a check here for the IS_VOID type hint, which
+ * would trigger an error because a value had been returned.
+ * However, zend_compile.c already does a compile-time check
+ * that bans `return ...;` within a void function. Thus we can skip
+ * this part of the runtime check for non-internal functions.
+ */
} else if (UNEXPECTED(!zend_verify_scalar_type_hint(ret_info->type_hint, ret, ZEND_RET_USES_STRICT_TYPES()))) {
zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), zend_zval_type_name(ret), "");
}
char *need_msg;
zend_class_entry *ce;
- if (ret_info->type_hint) {
+ if (ret_info->type_hint && EXPECTED(ret_info->type_hint != IS_VOID)) {
if (ret_info->class_name) {
if (EXPECTED(*cache_slot)) {
ce = (zend_class_entry*)*cache_slot;
/* fake types */
#define _IS_BOOL 13
#define IS_CALLABLE 14
+#define IS_VOID 18
/* internal types */
#define IS_INDIRECT 15