Merge branch 'PHP-7.1'

This commit is contained in:
Nikita Popov 2016-08-11 18:45:42 +02:00
commit d4bbbc4aa6
7 changed files with 391 additions and 57 deletions

View file

@ -18,7 +18,7 @@ new bar(new \stdclass);
--EXPECTF-- --EXPECTF--
NULL NULL
Fatal error: Uncaught TypeError: Argument 1 passed to foo\bar::__construct() must be of the type array, object given, called in %s on line %d and defined in %s:%d Fatal error: Uncaught TypeError: Argument 1 passed to foo\bar::__construct() must be of the type array or null, object given, called in %s on line %d and defined in %s:%d
Stack trace: Stack trace:
#0 %s(%d): foo\bar->__construct(Object(stdClass)) #0 %s(%d): foo\bar->__construct(Object(stdClass))
#1 {main} #1 {main}

View file

@ -30,7 +30,7 @@ object(foo\test)#%d (0) {
} }
NULL NULL
Fatal error: Uncaught TypeError: Argument 1 passed to foo\bar::__construct() must implement interface foo\foo, instance of stdClass given, called in %s on line %d and defined in %s:%d Fatal error: Uncaught TypeError: Argument 1 passed to foo\bar::__construct() must implement interface foo\foo or be null, instance of stdClass given, called in %s on line %d and defined in %s:%d
Stack trace: Stack trace:
#0 %s(%d): foo\bar->__construct(Object(stdClass)) #0 %s(%d): foo\bar->__construct(Object(stdClass))
#1 {main} #1 {main}

View file

@ -16,7 +16,7 @@ foo(0);
ok ok
ok ok
Fatal error: Uncaught TypeError: Return value of foo() must be of the type array, integer returned in %s030.php:3 Fatal error: Uncaught TypeError: Return value of foo() must be of the type array or null, integer returned in %s030.php:3
Stack trace: Stack trace:
#0 %s030.php(10): foo(0) #0 %s030.php(10): foo(0)
#1 {main} #1 {main}

View file

@ -0,0 +1,317 @@
--TEST--
Test "or null"/"or be null" in type-checking errors for userland functions
--FILE--
<?php
// This should test every branch in zend_execute.c's `zend_verify_arg_type`, `zend_verify_return_type` and `zend_verify_missing_return_type` functions which produces an "or null"/"or be null" part in its error message
function unloadedClass(?I\Dont\Exist $param) {}
try {
unloadedClass(new \StdClass);
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
class RealClass {}
interface RealInterface {}
function loadedClass(?RealClass $param) {}
function loadedInterface(?RealInterface $param) {}
try {
loadedClass(new \StdClass);
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
try {
loadedInterface(new \StdClass);
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
try {
unloadedClass(1);
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
try {
loadedClass(1);
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
try {
loadedInterface(1);
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function callableF(?callable $param) {}
try {
callableF(1);
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function iterableF(?iterable $param) {}
try {
iterableF(1);
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function intF(?int $param) {}
try {
intF(new StdClass);
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnUnloadedClass(): ?I\Dont\Exist {
return new \StdClass;
}
try {
returnUnloadedClass();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnLoadedClass(): ?RealClass {
return new \StdClass;
}
try {
returnLoadedClass();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnLoadedInterface(): ?RealInterface {
return new \StdClass;
}
try {
returnLoadedInterface();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnUnloadedClassScalar(): ?I\Dont\Exist {
return 1;
}
try {
returnUnloadedClassScalar();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnLoadedClassScalar(): ?RealClass {
return 1;
}
try {
returnLoadedClassScalar();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnLoadedInterfaceScalar(): ?RealInterface {
return 1;
}
try {
returnLoadedInterfaceScalar();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnCallable(): ?callable {
return 1;
}
try {
returnCallable();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnIterable(): ?iterable {
return 1;
}
try {
returnIterable();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnInt(): ?int {
return new \StdClass;
}
try {
returnInt();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnMissingUnloadedClass(): ?I\Dont\Exist {
}
try {
returnMissingUnloadedClass();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnMissingLoadedClass(): ?RealClass {
}
try {
returnMissingLoadedClass();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnMissingLoadedInterface(): ?RealInterface {
}
try {
returnMissingLoadedInterface();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnMissingCallable(): ?callable {
}
try {
returnMissingCallable();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnMissingIterable(): ?iterable {
}
try {
returnMissingIterable();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
function returnMissingInt(): ?int {
}
try {
returnMissingInt();
} catch (\TypeError $e) {
echo $e, PHP_EOL;
}
?>
--EXPECTF--
TypeError: Argument 1 passed to unloadedClass() must be an instance of I\Dont\Exist or null, instance of stdClass given, called in %s on line 8 and defined in %s:5
Stack trace:
#0 %s(8): unloadedClass(Object(stdClass))
#1 {main}
TypeError: Argument 1 passed to loadedClass() must be an instance of RealClass or null, instance of stdClass given, called in %s on line 20 and defined in %s:16
Stack trace:
#0 %s(20): loadedClass(Object(stdClass))
#1 {main}
TypeError: Argument 1 passed to loadedInterface() must implement interface RealInterface or be null, instance of stdClass given, called in %s on line 26 and defined in %s:17
Stack trace:
#0 %s(26): loadedInterface(Object(stdClass))
#1 {main}
TypeError: Argument 1 passed to unloadedClass() must be an instance of I\Dont\Exist or null, integer given, called in %s on line 32 and defined in %s:5
Stack trace:
#0 %s(32): unloadedClass(1)
#1 {main}
TypeError: Argument 1 passed to loadedClass() must be an instance of RealClass or null, integer given, called in %s on line 38 and defined in %s:16
Stack trace:
#0 %s(38): loadedClass(1)
#1 {main}
TypeError: Argument 1 passed to loadedInterface() must implement interface RealInterface or be null, integer given, called in %s on line 44 and defined in %s:17
Stack trace:
#0 %s(44): loadedInterface(1)
#1 {main}
TypeError: Argument 1 passed to callableF() must be callable or null, integer given, called in %s on line 52 and defined in %s:49
Stack trace:
#0 %s(52): callableF(1)
#1 {main}
TypeError: Argument 1 passed to iterableF() must be iterable or null, integer given, called in %s on line 60 and defined in %s:57
Stack trace:
#0 %s(60): iterableF(1)
#1 {main}
TypeError: Argument 1 passed to intF() must be of the type integer or null, object given, called in %s on line 68 and defined in %s:65
Stack trace:
#0 %s(68): intF(Object(stdClass))
#1 {main}
TypeError: Return value of returnUnloadedClass() must be an instance of I\Dont\Exist or null, instance of stdClass returned in %s:74
Stack trace:
#0 %s(78): returnUnloadedClass()
#1 {main}
TypeError: Return value of returnLoadedClass() must be an instance of RealClass or null, instance of stdClass returned in %s:84
Stack trace:
#0 %s(88): returnLoadedClass()
#1 {main}
TypeError: Return value of returnLoadedInterface() must implement interface RealInterface or be null, instance of stdClass returned in %s:94
Stack trace:
#0 %s(98): returnLoadedInterface()
#1 {main}
TypeError: Return value of returnUnloadedClassScalar() must be an instance of I\Dont\Exist or null, integer returned in %s:104
Stack trace:
#0 %s(108): returnUnloadedClassScalar()
#1 {main}
TypeError: Return value of returnLoadedClassScalar() must be an instance of RealClass or null, integer returned in %s:114
Stack trace:
#0 %s(118): returnLoadedClassScalar()
#1 {main}
TypeError: Return value of returnLoadedInterfaceScalar() must implement interface RealInterface or be null, integer returned in %s:124
Stack trace:
#0 %s(128): returnLoadedInterfaceScalar()
#1 {main}
TypeError: Return value of returnCallable() must be callable or null, integer returned in %s:134
Stack trace:
#0 %s(138): returnCallable()
#1 {main}
TypeError: Return value of returnIterable() must be iterable or null, integer returned in %s:144
Stack trace:
#0 %s(148): returnIterable()
#1 {main}
TypeError: Return value of returnInt() must be of the type integer or null, object returned in %s:154
Stack trace:
#0 %s(158): returnInt()
#1 {main}
TypeError: Return value of returnMissingUnloadedClass() must be an instance of I\Dont\Exist or null, none returned in %s:164
Stack trace:
#0 %s(167): returnMissingUnloadedClass()
#1 {main}
TypeError: Return value of returnMissingLoadedClass() must be an instance of RealClass or null, none returned in %s:173
Stack trace:
#0 %s(176): returnMissingLoadedClass()
#1 {main}
TypeError: Return value of returnMissingLoadedInterface() must implement interface RealInterface or be null, none returned in %s:182
Stack trace:
#0 %s(185): returnMissingLoadedInterface()
#1 {main}
TypeError: Return value of returnMissingCallable() must be callable or null, none returned in %s:191
Stack trace:
#0 %s(194): returnMissingCallable()
#1 {main}
TypeError: Return value of returnMissingIterable() must be iterable or null, none returned in %s:200
Stack trace:
#0 %s(203): returnMissingIterable()
#1 {main}
TypeError: Return value of returnMissingInt() must be of the type integer or null, none returned in %s:209
Stack trace:
#0 %s(212): returnMissingInt()
#1 {main}

View file

@ -597,7 +597,7 @@ static inline int make_real_object(zval *object)
return 1; return 1;
} }
static char * zend_verify_internal_arg_class_kind(const zend_internal_arg_info *cur_arg_info, char **class_name, zend_class_entry **pce) static char * zend_verify_internal_arg_class_kind(const zend_internal_arg_info *cur_arg_info, char **class_name, zend_class_entry **pce, char **need_or_null)
{ {
zend_string *key; zend_string *key;
ALLOCA_FLAG(use_heap); ALLOCA_FLAG(use_heap);
@ -608,8 +608,10 @@ static char * zend_verify_internal_arg_class_kind(const zend_internal_arg_info *
*class_name = (*pce) ? ZSTR_VAL((*pce)->name) : (char*)cur_arg_info->class_name; *class_name = (*pce) ? ZSTR_VAL((*pce)->name) : (char*)cur_arg_info->class_name;
if (*pce && (*pce)->ce_flags & ZEND_ACC_INTERFACE) { if (*pce && (*pce)->ce_flags & ZEND_ACC_INTERFACE) {
*need_or_null = " or be null";
return "implement interface "; return "implement interface ";
} else { } else {
*need_or_null = " or null";
return "be an instance of "; return "be an instance of ";
} }
} }
@ -619,7 +621,7 @@ static zend_always_inline zend_class_entry* zend_verify_arg_class_kind(const zen
return zend_fetch_class(cur_arg_info->class_name, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD)); return zend_fetch_class(cur_arg_info->class_name, (ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_NO_AUTOLOAD));
} }
static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *given_msg, const char *given_kind) static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t arg_num, const char *need_msg, const char *need_kind, const char *need_or_null, const char *given_msg, const char *given_kind)
{ {
zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data;
const char *fname = ZSTR_VAL(zf->common.function_name); const char *fname = ZSTR_VAL(zf->common.function_name);
@ -636,14 +638,14 @@ static ZEND_COLD void zend_verify_arg_error(const zend_function *zf, uint32_t ar
if (zf->common.type == ZEND_USER_FUNCTION) { if (zf->common.type == ZEND_USER_FUNCTION) {
if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) { if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) {
zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given, called in %s on line %d", zend_type_error("Argument %d passed to %s%s%s() must %s%s%s, %s%s given, called in %s on line %d",
arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind, arg_num, fclass, fsep, fname, need_msg, need_kind, need_or_null, given_msg, given_kind,
ZSTR_VAL(ptr->func->op_array.filename), ptr->opline->lineno); ZSTR_VAL(ptr->func->op_array.filename), ptr->opline->lineno);
} else { } else {
zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); zend_type_error("Argument %d passed to %s%s%s() must %s%s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, need_or_null, given_msg, given_kind);
} }
} else { } else {
zend_type_error("Argument %d passed to %s%s%s() must %s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, given_msg, given_kind); zend_type_error("Argument %d passed to %s%s%s() must %s%s%s, %s%s given", arg_num, fclass, fsep, fname, need_msg, need_kind, need_or_null, given_msg, given_kind);
} }
} }
@ -728,7 +730,7 @@ static zend_bool zend_verify_scalar_type_hint(zend_uchar type_hint, zval *arg, z
static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg) static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zval *arg)
{ {
zend_internal_arg_info *cur_arg_info; zend_internal_arg_info *cur_arg_info;
char *need_msg, *class_name; char *need_msg, *need_or_null, *class_name;
zend_class_entry *ce; zend_class_entry *ce;
if (EXPECTED(arg_num <= zf->internal_function.num_args)) { if (EXPECTED(arg_num <= zf->internal_function.num_args)) {
@ -743,32 +745,32 @@ static int zend_verify_internal_arg_type(zend_function *zf, uint32_t arg_num, zv
ZVAL_DEREF(arg); ZVAL_DEREF(arg);
if (EXPECTED(cur_arg_info->type_hint == Z_TYPE_P(arg))) { if (EXPECTED(cur_arg_info->type_hint == Z_TYPE_P(arg))) {
if (cur_arg_info->class_name) { if (cur_arg_info->class_name) {
need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce); need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce, &need_or_null);
if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) { if (!ce || !instanceof_function(Z_OBJCE_P(arg), ce)) {
zend_verify_arg_error(zf, arg_num, need_msg, class_name, "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name)); zend_verify_arg_error(zf, arg_num, need_msg, class_name, (cur_arg_info->allow_null ? need_or_null : ""), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
return 0; return 0;
} }
} }
} else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) { } else if (Z_TYPE_P(arg) != IS_NULL || !cur_arg_info->allow_null) {
if (cur_arg_info->class_name) { if (cur_arg_info->class_name) {
need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce); need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info*)cur_arg_info, &class_name, &ce, &need_or_null);
zend_verify_arg_error(zf, arg_num, need_msg, class_name, zend_zval_type_name(arg), ""); zend_verify_arg_error(zf, arg_num, need_msg, class_name, (cur_arg_info->allow_null ? need_or_null : ""), zend_zval_type_name(arg), "");
return 0; return 0;
} else if (cur_arg_info->type_hint == IS_CALLABLE) { } else if (cur_arg_info->type_hint == IS_CALLABLE) {
if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) { if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) {
zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), ""); zend_verify_arg_error(zf, arg_num, "be callable", "", (cur_arg_info->allow_null ? " or null" : ""), zend_zval_type_name(arg), "");
return 0; return 0;
} }
} else if (cur_arg_info->type_hint == IS_ITERABLE) { } else if (cur_arg_info->type_hint == IS_ITERABLE) {
if (!zend_is_iterable(arg)) { if (!zend_is_iterable(arg)) {
zend_verify_arg_error(zf, arg_num, "be iterable", "", zend_zval_type_name(arg), ""); zend_verify_arg_error(zf, arg_num, "be iterable", "", (cur_arg_info->allow_null ? " or null" : ""), zend_zval_type_name(arg), "");
return 0; return 0;
} }
} else if (cur_arg_info->type_hint == _IS_BOOL && } else if (cur_arg_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
/* pass */ /* pass */
} else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) { } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data))))) {
zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), ""); zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), (cur_arg_info->allow_null ? " or null" : ""), zend_zval_type_name(arg), "");
return 0; return 0;
} }
} }
@ -796,7 +798,7 @@ static zend_never_inline int zend_verify_internal_arg_types(zend_function *fbc,
static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot) static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t arg_num, zval *arg, zval *default_value, void **cache_slot)
{ {
zend_arg_info *cur_arg_info; zend_arg_info *cur_arg_info;
char *need_msg; char *need_msg, *need_or_null;
zend_class_entry *ce; zend_class_entry *ce;
if (EXPECTED(arg_num <= zf->common.num_args)) { if (EXPECTED(arg_num <= zf->common.num_args)) {
@ -816,7 +818,7 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
} else { } else {
ce = zend_verify_arg_class_kind(cur_arg_info); ce = zend_verify_arg_class_kind(cur_arg_info);
if (UNEXPECTED(!ce)) { if (UNEXPECTED(!ce)) {
zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name)); zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), (cur_arg_info->allow_null ? " or null" : ""), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
return 0; return 0;
} }
*cache_slot = (void*)ce; *cache_slot = (void*)ce;
@ -825,7 +827,10 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
need_msg = need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ? (ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of "; "implement interface " : "be an instance of ";
zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name)); need_or_null =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
" or be null" : " or null";
zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), (cur_arg_info->allow_null ? need_or_null : ""), "instance of ", ZSTR_VAL(Z_OBJCE_P(arg)->name));
return 0; return 0;
} }
} }
@ -837,7 +842,7 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
ce = zend_verify_arg_class_kind(cur_arg_info); ce = zend_verify_arg_class_kind(cur_arg_info);
if (UNEXPECTED(!ce)) { if (UNEXPECTED(!ce)) {
ZEND_ASSERT(Z_TYPE_P(arg) != IS_OBJECT); ZEND_ASSERT(Z_TYPE_P(arg) != IS_OBJECT);
zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), "", zend_zval_type_name(arg)); zend_verify_arg_error(zf, arg_num, "be an instance of ", ZSTR_VAL(cur_arg_info->class_name), (cur_arg_info->allow_null ? " or null" : ""), "", zend_zval_type_name(arg));
return 0; return 0;
} }
*cache_slot = (void*)ce; *cache_slot = (void*)ce;
@ -845,23 +850,26 @@ static zend_always_inline int zend_verify_arg_type(zend_function *zf, uint32_t a
need_msg = need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ? (ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of "; "implement interface " : "be an instance of ";
zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), zend_zval_type_name(arg), ""); need_or_null =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
" or be null" : " or null";
zend_verify_arg_error(zf, arg_num, need_msg, ZSTR_VAL(ce->name), (cur_arg_info->allow_null ? need_or_null : ""), zend_zval_type_name(arg), "");
return 0; return 0;
} else if (cur_arg_info->type_hint == IS_CALLABLE) { } else if (cur_arg_info->type_hint == IS_CALLABLE) {
if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) { if (!zend_is_callable(arg, IS_CALLABLE_CHECK_SILENT, NULL)) {
zend_verify_arg_error(zf, arg_num, "be callable", "", zend_zval_type_name(arg), ""); zend_verify_arg_error(zf, arg_num, "be callable", "", (cur_arg_info->allow_null ? " or null" : ""), zend_zval_type_name(arg), "");
return 0; return 0;
} }
} else if (cur_arg_info->type_hint == IS_ITERABLE) { } else if (cur_arg_info->type_hint == IS_ITERABLE) {
if (!zend_is_iterable(arg)) { if (!zend_is_iterable(arg)) {
zend_verify_arg_error(zf, arg_num, "be iterable", "", zend_zval_type_name(arg), ""); zend_verify_arg_error(zf, arg_num, "be iterable", "", (cur_arg_info->allow_null ? " or null" : ""), zend_zval_type_name(arg), "");
return 0; return 0;
} }
} else if (cur_arg_info->type_hint == _IS_BOOL && } else if (cur_arg_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) { EXPECTED(Z_TYPE_P(arg) == IS_FALSE || Z_TYPE_P(arg) == IS_TRUE)) {
/* pass */ /* pass */
} else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_ARG_USES_STRICT_TYPES()))) { } else if (UNEXPECTED(!zend_verify_scalar_type_hint(cur_arg_info->type_hint, arg, ZEND_ARG_USES_STRICT_TYPES()))) {
zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), zend_zval_type_name(arg), ""); zend_verify_arg_error(zf, arg_num, "be of the type ", zend_get_type_by_const(cur_arg_info->type_hint), (cur_arg_info->allow_null ? " or null" : ""), zend_zval_type_name(arg), "");
return 0; return 0;
} }
} }
@ -894,7 +902,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(zend_execute_data *
} }
} }
static ZEND_COLD void zend_verify_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind) static ZEND_COLD void zend_verify_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *need_or_null, const char *returned_msg, const char *returned_kind)
{ {
const char *fname = ZSTR_VAL(zf->common.function_name); const char *fname = ZSTR_VAL(zf->common.function_name);
const char *fsep; const char *fsep;
@ -908,12 +916,12 @@ static ZEND_COLD void zend_verify_return_error(const zend_function *zf, const ch
fclass = ""; fclass = "";
} }
zend_type_error("Return value of %s%s%s() must %s%s, %s%s returned", zend_type_error("Return value of %s%s%s() must %s%s%s, %s%s returned",
fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind); fclass, fsep, fname, need_msg, need_kind, need_or_null, returned_msg, returned_kind);
} }
#if ZEND_DEBUG #if ZEND_DEBUG
static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *returned_msg, const char *returned_kind) static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, const char *need_msg, const char *need_kind, const char *need_or_null, const char *returned_msg, const char *returned_kind)
{ {
const char *fname = ZSTR_VAL(zf->common.function_name); const char *fname = ZSTR_VAL(zf->common.function_name);
const char *fsep; const char *fsep;
@ -927,8 +935,8 @@ static ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf,
fclass = ""; fclass = "";
} }
zend_error_noreturn(E_CORE_ERROR, "Return value of %s%s%s() must %s%s, %s%s returned", zend_error_noreturn(E_CORE_ERROR, "Return value of %s%s%s() must %s%s%s, %s%s returned",
fclass, fsep, fname, need_msg, need_kind, returned_msg, returned_kind); fclass, fsep, fname, need_msg, need_kind, need_or_null, 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) static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, const char *returned_msg, const char *returned_kind)
@ -952,31 +960,31 @@ static ZEND_COLD void zend_verify_void_return_error(const zend_function *zf, con
static int zend_verify_internal_return_type(zend_function *zf, zval *ret) static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
{ {
zend_arg_info *ret_info = zf->common.arg_info - 1; zend_arg_info *ret_info = zf->common.arg_info - 1;
char *need_msg, *class_name; char *need_msg, *need_or_null, *class_name;
zend_class_entry *ce; zend_class_entry *ce;
if (ret_info->type_hint) { if (ret_info->type_hint) {
if (EXPECTED(ret_info->type_hint == Z_TYPE_P(ret))) { if (EXPECTED(ret_info->type_hint == Z_TYPE_P(ret))) {
if (ret_info->class_name) { if (ret_info->class_name) {
need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce, &need_or_null);
if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) { if (!ce || !instanceof_function(Z_OBJCE_P(ret), ce)) {
zend_verify_internal_return_error(zf, need_msg, class_name, "instance of ", ZSTR_VAL(Z_OBJCE_P(ret)->name)); zend_verify_internal_return_error(zf, need_msg, class_name, (ret_info->allow_null ? need_or_null : ""), "instance of ", ZSTR_VAL(Z_OBJCE_P(ret)->name));
return 0; return 0;
} }
} }
} else if (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null) { } else if (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null) {
if (ret_info->class_name) { if (ret_info->class_name) {
need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce); need_msg = zend_verify_internal_arg_class_kind((zend_internal_arg_info *)ret_info, &class_name, &ce, &need_or_null);
zend_verify_internal_return_error(zf, need_msg, class_name, zend_zval_type_name(ret), ""); zend_verify_internal_return_error(zf, need_msg, class_name, (ret_info->allow_null ? need_or_null : ""), zend_zval_type_name(ret), "");
} else if (ret_info->type_hint == IS_CALLABLE) { } else if (ret_info->type_hint == IS_CALLABLE) {
if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) {
zend_verify_internal_return_error(zf, "be callable", "", zend_zval_type_name(ret), ""); zend_verify_internal_return_error(zf, "be callable", "", (ret_info->allow_null ? " or null" : ""), zend_zval_type_name(ret), "");
return 0; return 0;
} }
} else if (ret_info->type_hint == IS_ITERABLE) { } else if (ret_info->type_hint == IS_ITERABLE) {
if (!zend_is_iterable(ret) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) { if (!zend_is_iterable(ret) && (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null)) {
zend_verify_internal_return_error(zf, "be iterable", "", zend_zval_type_name(ret), ""); zend_verify_internal_return_error(zf, "be iterable", "", (ret_info->allow_null ? " or null" : ""), zend_zval_type_name(ret), "");
return 0; return 0;
} }
} else if (ret_info->type_hint == _IS_BOOL && } else if (ret_info->type_hint == _IS_BOOL &&
@ -986,7 +994,7 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
zend_verify_void_return_error(zf, zend_zval_type_name(ret), ""); zend_verify_void_return_error(zf, zend_zval_type_name(ret), "");
} else { } else {
/* Use strict check to verify return value of internal function */ /* 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), ""); zend_verify_internal_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), (ret_info->allow_null ? " or null" : ""), zend_zval_type_name(ret), "");
return 0; return 0;
} }
} }
@ -998,7 +1006,7 @@ static int zend_verify_internal_return_type(zend_function *zf, zval *ret)
static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *ret, void **cache_slot) static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *ret, void **cache_slot)
{ {
zend_arg_info *ret_info = zf->common.arg_info - 1; zend_arg_info *ret_info = zf->common.arg_info - 1;
char *need_msg; char *need_msg, *need_or_null;
zend_class_entry *ce; zend_class_entry *ce;
if (ret_info->type_hint) { if (ret_info->type_hint) {
@ -1009,7 +1017,7 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
} else { } else {
ce = zend_verify_arg_class_kind(ret_info); ce = zend_verify_arg_class_kind(ret_info);
if (UNEXPECTED(!ce)) { if (UNEXPECTED(!ce)) {
zend_verify_return_error(zf, "be an instance of ", ZSTR_VAL(ret_info->class_name), "instance of ", ZSTR_VAL(Z_OBJCE_P(ret)->name)); zend_verify_return_error(zf, "be an instance of ", ZSTR_VAL(ret_info->class_name), (ret_info->allow_null ? " or null" : ""), "instance of ", ZSTR_VAL(Z_OBJCE_P(ret)->name));
return; return;
} }
*cache_slot = (void*)ce; *cache_slot = (void*)ce;
@ -1018,7 +1026,10 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
need_msg = need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ? (ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of "; "implement interface " : "be an instance of ";
zend_verify_return_error(zf, need_msg, ZSTR_VAL(ce->name), "instance of ", ZSTR_VAL(Z_OBJCE_P(ret)->name)); need_or_null =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
" or be null" : " or null";
zend_verify_return_error(zf, need_msg, ZSTR_VAL(ce->name), (ret_info->allow_null ? need_or_null : ""), "instance of ", ZSTR_VAL(Z_OBJCE_P(ret)->name));
} }
} }
} else if (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null) { } else if (Z_TYPE_P(ret) != IS_NULL || !ret_info->allow_null) {
@ -1028,7 +1039,7 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
} else { } else {
ce = zend_verify_arg_class_kind(ret_info); ce = zend_verify_arg_class_kind(ret_info);
if (UNEXPECTED(!ce)) { if (UNEXPECTED(!ce)) {
zend_verify_return_error(zf, "be an instance of ", ZSTR_VAL(ret_info->class_name), zend_zval_type_name(ret), ""); zend_verify_return_error(zf, "be an instance of ", ZSTR_VAL(ret_info->class_name), (ret_info->allow_null ? " or null" : ""), zend_zval_type_name(ret), "");
return; return;
} }
*cache_slot = (void*)ce; *cache_slot = (void*)ce;
@ -1036,14 +1047,17 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
need_msg = need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ? (ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of "; "implement interface " : "be an instance of ";
zend_verify_return_error(zf, need_msg, ZSTR_VAL(ce->name), zend_zval_type_name(ret), ""); need_or_null =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
" or be null" : " or null";
zend_verify_return_error(zf, need_msg, ZSTR_VAL(ce->name), (ret_info->allow_null ? need_or_null : ""), zend_zval_type_name(ret), "");
} else if (ret_info->type_hint == IS_CALLABLE) { } else if (ret_info->type_hint == IS_CALLABLE) {
if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL)) { if (!zend_is_callable(ret, IS_CALLABLE_CHECK_SILENT, NULL)) {
zend_verify_return_error(zf, "be callable", "", zend_zval_type_name(ret), ""); zend_verify_return_error(zf, "be callable", "", (ret_info->allow_null ? " or null" : ""), zend_zval_type_name(ret), "");
} }
} else if (ret_info->type_hint == IS_ITERABLE) { } else if (ret_info->type_hint == IS_ITERABLE) {
if (!zend_is_iterable(ret)) { if (!zend_is_iterable(ret)) {
zend_verify_return_error(zf, "be iterable", "", zend_zval_type_name(ret), ""); zend_verify_return_error(zf, "be iterable", "", (ret_info->allow_null ? " or null" : ""), zend_zval_type_name(ret), "");
} }
} else if (ret_info->type_hint == _IS_BOOL && } else if (ret_info->type_hint == _IS_BOOL &&
EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) { EXPECTED(Z_TYPE_P(ret) == IS_FALSE || Z_TYPE_P(ret) == IS_TRUE)) {
@ -1055,7 +1069,7 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
* this part of the runtime check for non-internal functions. * 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()))) { } 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), ""); zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), (ret_info->allow_null ? " or null" : ""), zend_zval_type_name(ret), "");
} }
} }
} }
@ -1064,7 +1078,7 @@ static zend_always_inline void zend_verify_return_type(zend_function *zf, zval *
static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **cache_slot) static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **cache_slot)
{ {
zend_arg_info *ret_info = zf->common.arg_info - 1; zend_arg_info *ret_info = zf->common.arg_info - 1;
char *need_msg; char *need_msg, *need_or_null;
zend_class_entry *ce; zend_class_entry *ce;
if (ret_info->type_hint && EXPECTED(ret_info->type_hint != IS_VOID)) { if (ret_info->type_hint && EXPECTED(ret_info->type_hint != IS_VOID)) {
@ -1074,7 +1088,7 @@ static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **c
} else { } else {
ce = zend_verify_arg_class_kind(ret_info); ce = zend_verify_arg_class_kind(ret_info);
if (UNEXPECTED(!ce)) { if (UNEXPECTED(!ce)) {
zend_verify_return_error(zf, "be an instance of ", ZSTR_VAL(ret_info->class_name), "none", ""); zend_verify_return_error(zf, "be an instance of ", ZSTR_VAL(ret_info->class_name), (ret_info->allow_null ? " or null" : ""), "none", "");
return 0; return 0;
} }
*cache_slot = (void*)ce; *cache_slot = (void*)ce;
@ -1082,14 +1096,17 @@ static ZEND_COLD int zend_verify_missing_return_type(zend_function *zf, void **c
need_msg = need_msg =
(ce->ce_flags & ZEND_ACC_INTERFACE) ? (ce->ce_flags & ZEND_ACC_INTERFACE) ?
"implement interface " : "be an instance of "; "implement interface " : "be an instance of ";
zend_verify_return_error(zf, need_msg, ZSTR_VAL(ce->name), "none", ""); need_or_null =
(ce->ce_flags & ZEND_ACC_INTERFACE) ?
" or be null" : " or null";
zend_verify_return_error(zf, need_msg, ZSTR_VAL(ce->name), (ret_info->allow_null ? need_or_null : ""), "none", "");
return 0; return 0;
} else if (ret_info->type_hint == IS_CALLABLE) { } else if (ret_info->type_hint == IS_CALLABLE) {
zend_verify_return_error(zf, "be callable", "", "none", ""); zend_verify_return_error(zf, "be callable", "", (ret_info->allow_null ? " or null" : ""), "none", "");
} else if (ret_info->type_hint == IS_ITERABLE) { } else if (ret_info->type_hint == IS_ITERABLE) {
zend_verify_return_error(zf, "be iterable", "", "none", ""); zend_verify_return_error(zf, "be iterable", "", (ret_info->allow_null ? " or null" : ""), "none", "");
} else { } else {
zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), "none", ""); zend_verify_return_error(zf, "be of the type ", zend_get_type_by_const(ret_info->type_hint), (ret_info->allow_null ? " or null" : ""), "none", "");
} }
return 0; return 0;
} }

View file

@ -78,7 +78,7 @@ int(5)
int(6) int(6)
int(4) int(4)
===ERRORS=== ===ERRORS===
Error: Argument 3 passed to iterator_apply() must be of the type array, integer given Error: Argument 3 passed to iterator_apply() must be of the type array or null, integer given
Error: iterator_apply() expects parameter 2 to be a valid callback, function 'non_existing_function' not found or invalid function name Error: iterator_apply() expects parameter 2 to be a valid callback, function 'non_existing_function' not found or invalid function name
NULL NULL
Error: iterator_apply() expects at most 3 parameters, 4 given Error: iterator_apply() expects at most 3 parameters, 4 given

View file

@ -152,7 +152,7 @@ Ensure type hints are enforced for functions invoked as callbacks.
0: Argument 1 passed to f1() must be an instance of A, integer given%s(%d) 0: Argument 1 passed to f1() must be an instance of A, integer given%s(%d)
in f1; in f1;
0: Argument 1 passed to f2() must be an instance of A, integer given%s(%d) 0: Argument 1 passed to f2() must be an instance of A or null, integer given%s(%d)
in f2; in f2;
in f2; in f2;
@ -163,7 +163,7 @@ in f2;
0: Argument 1 passed to C::f1() must be an instance of A, integer given%s(%d) 0: Argument 1 passed to C::f1() must be an instance of A, integer given%s(%d)
in C::f1 (static); in C::f1 (static);
0: Argument 1 passed to C::f2() must be an instance of A, integer given%s(%d) 0: Argument 1 passed to C::f2() must be an instance of A or null, integer given%s(%d)
in C::f2 (static); in C::f2 (static);
in C::f2 (static); in C::f2 (static);
@ -174,7 +174,7 @@ in C::f2 (static);
0: Argument 1 passed to D::f1() must be an instance of A, integer given%s(%d) 0: Argument 1 passed to D::f1() must be an instance of A, integer given%s(%d)
in C::f1 (instance); in C::f1 (instance);
0: Argument 1 passed to D::f2() must be an instance of A, integer given%s(%d) 0: Argument 1 passed to D::f2() must be an instance of A or null, integer given%s(%d)
in C::f2 (instance); in C::f2 (instance);
in C::f2 (instance); in C::f2 (instance);