diff --git a/Zend/tests/anon/anon_class_name_in_abstract_info.phpt b/Zend/tests/anon/anon_class_name_in_abstract_info.phpt new file mode 100644 index 000000000000..9c633ded0656 --- /dev/null +++ b/Zend/tests/anon/anon_class_name_in_abstract_info.phpt @@ -0,0 +1,8 @@ +--TEST-- +Anonymous class name is not truncated in the abstract method list +--FILE-- + +--EXPECTF-- +Fatal error: Class class@anonymous%0%s must implement 1 abstract method (class@anonymous%0%s::$p::get) in %s on line %d diff --git a/Zend/tests/anon/anon_class_name_in_errors.phpt b/Zend/tests/anon/anon_class_name_in_errors.phpt new file mode 100644 index 000000000000..be7f271283c2 --- /dev/null +++ b/Zend/tests/anon/anon_class_name_in_errors.phpt @@ -0,0 +1,60 @@ +--TEST-- +Anonymous class names are not truncated in runtime error messages +--FILE-- +f(); } + public function setPrivateSet() { $a = new A; $a->p = 1; } + public function destroy() { new B; } +}; + +$v = new class extends ArrayObject { + public int $virtual { get => 1; } +}; + +foreach ([ + fn() => $o->priv(), + fn() => $o->callPriv(), + fn() => $o->setPrivateSet(), + fn() => $o->destroy(), + fn() => $o->ro = 2, + fn() => $o->ret(), + fn() => $o->args(), + fn() => $v->__unserialize([0, [], ['virtual' => 2]]), +] as $fn) { + try { + $fn(); + } catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; + } +} + +$o->byRef(...new ArrayIterator([1])); + +?> +--EXPECTF-- +Error: Call to private method class@anonymous%0%s::priv() from global scope +Error: Call to private method A::f() from scope class@anonymous%0%s +Error: Cannot modify private(set) property A::$p from scope class@anonymous%0%s +Error: Call to private B::__destruct() from scope class@anonymous%0%s +Error: Cannot modify readonly property class@anonymous%0%s::$ro +TypeError: class@anonymous%0%s::ret(): Return value must be of type int, string returned +ArgumentCountError: Too few arguments to function class@anonymous%0%s::args(), 0 passed in %s on line %d and exactly 1 expected +Error: Cannot unserialize value for virtual property ArrayObject@anonymous%0%s::$virtual + +Warning: Cannot pass by-reference argument 1 of class@anonymous%0%s::byRef() by unpacking a Traversable, passing by-value instead in %s on line %d diff --git a/Zend/tests/anon/anon_class_name_in_inheritance_error.phpt b/Zend/tests/anon/anon_class_name_in_inheritance_error.phpt new file mode 100644 index 000000000000..cf5b8ffd9e19 --- /dev/null +++ b/Zend/tests/anon/anon_class_name_in_inheritance_error.phpt @@ -0,0 +1,9 @@ +--TEST-- +Anonymous class name is not truncated in method inheritance errors +--FILE-- + +--EXPECTF-- +Fatal error: Access level to A@anonymous%0%s::f() must be public (as in class A) in %s on line %d diff --git a/Zend/tests/attributes/override/020.phpt b/Zend/tests/attributes/override/020.phpt index 0b6161b8feb9..b96cdabf7170 100644 --- a/Zend/tests/attributes/override/020.phpt +++ b/Zend/tests/attributes/override/020.phpt @@ -18,4 +18,4 @@ echo "Done"; ?> --EXPECTF-- -Fatal error: I@anonymous::c() has #[\Override] attribute, but no matching parent method exists in %s on line %d +Fatal error: I@anonymous%0%s::c() has #[\Override] attribute, but no matching parent method exists in %s on line %d diff --git a/Zend/tests/object_types/return_type_in_class.phpt b/Zend/tests/object_types/return_type_in_class.phpt index 3e441073737e..632e94d2589d 100644 --- a/Zend/tests/object_types/return_type_in_class.phpt +++ b/Zend/tests/object_types/return_type_in_class.phpt @@ -19,7 +19,7 @@ $three = new class extends Two { $three->a(); ?> --EXPECTF-- -Fatal error: Uncaught TypeError: Two@anonymous::a(): Return value must be of type object, int returned in %s:%d +Fatal error: Uncaught TypeError: Two@anonymous%0%s::a(): Return value must be of type object, int returned in %s:%d Stack trace: #0 %s(%d): Two@anonymous->a() #1 {main} diff --git a/Zend/tests/object_types/return_type_inheritance_in_class.phpt b/Zend/tests/object_types/return_type_inheritance_in_class.phpt index 6e2dbac55cf2..518aea3e97e3 100644 --- a/Zend/tests/object_types/return_type_inheritance_in_class.phpt +++ b/Zend/tests/object_types/return_type_inheritance_in_class.phpt @@ -19,7 +19,7 @@ $three = new class extends Two { $three->a(); ?> --EXPECTF-- -Fatal error: Uncaught TypeError: Two@anonymous::a(): Return value must be of type object, int returned in %s:%d +Fatal error: Uncaught TypeError: Two@anonymous%0%s::a(): Return value must be of type object, int returned in %s:%d Stack trace: #0 %s(%d): Two@anonymous->a() #1 {main} diff --git a/Zend/tests/object_types/return_type_inheritance_in_interface.phpt b/Zend/tests/object_types/return_type_inheritance_in_interface.phpt index 8bb5ad1616e9..9f5b43f900c1 100644 --- a/Zend/tests/object_types/return_type_inheritance_in_interface.phpt +++ b/Zend/tests/object_types/return_type_inheritance_in_interface.phpt @@ -19,7 +19,7 @@ $three = new class implements Two { $three->a(); ?> --EXPECTF-- -Fatal error: Uncaught TypeError: Two@anonymous::a(): Return value must be of type object, int returned in %s:%d +Fatal error: Uncaught TypeError: Two@anonymous%0%s::a(): Return value must be of type object, int returned in %s:%d Stack trace: #0 %s(%d): Two@anonymous->a() #1 {main} diff --git a/Zend/tests/readonly_classes/gh10377.phpt b/Zend/tests/readonly_classes/gh10377.phpt index faa4ae330bf7..c31c875f7188 100644 --- a/Zend/tests/readonly_classes/gh10377.phpt +++ b/Zend/tests/readonly_classes/gh10377.phpt @@ -34,9 +34,9 @@ try { var_dump($anon->field); ?> ---EXPECT-- +--EXPECTF-- int(2) -Error: Cannot modify readonly property class@anonymous::$field +Error: Cannot modify readonly property class@anonymous%0%s::$field int(2) int(2) int(123) diff --git a/Zend/tests/type_declarations/union_types/anonymous_class.phpt b/Zend/tests/type_declarations/union_types/anonymous_class.phpt index 0fd4e379a3c0..4ae0f77c06de 100644 --- a/Zend/tests/type_declarations/union_types/anonymous_class.phpt +++ b/Zend/tests/type_declarations/union_types/anonymous_class.phpt @@ -27,4 +27,4 @@ try { ?> --EXPECTF-- TypeError: class@anonymous%0%s::testParam(): Argument #1 ($a) must be of type class@anonymous|string, null given, called in %s on line %d -TypeError: class@anonymous::test(): Return value must be of type class@anonymous|string, stdClass returned +TypeError: class@anonymous%0%s::test(): Return value must be of type class@anonymous|string, stdClass returned diff --git a/Zend/zend_API.c b/Zend/zend_API.c index e43b3abf9aed..e96202f99723 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1786,7 +1786,7 @@ ZEND_API void object_properties_load(zend_object *object, const HashTable *prope /* Mimick unserialize behaviour for virtual properties. */ if (UNEXPECTED(property_info->flags & ZEND_ACC_VIRTUAL)) { - zend_throw_error(NULL, "Cannot unserialize value for virtual property %s::$%s", ZSTR_VAL(object->ce->name), zend_get_unmangled_property_name(property_info->name)); + zend_throw_error(NULL, "Cannot unserialize value for virtual property %pS::$%s", object->ce->name, zend_get_unmangled_property_name(property_info->name)); return; } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index e929bc41a03d..4d6dd55e54e8 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -707,18 +707,8 @@ static zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_throw_non_object_erro static zend_never_inline ZEND_COLD void zend_verify_type_error_common( const zend_function *zf, const zend_arg_info *arg_info, const zval *value, - const char **fname, const char **fsep, const char **fclass, zend_string **need_msg, const char **given_kind) { - *fname = ZSTR_VAL(zf->common.function_name); - if (zf->common.scope) { - *fsep = "::"; - *fclass = ZSTR_VAL(zf->common.scope->name); - } else { - *fsep = ""; - *fclass = ""; - } - *need_msg = zend_type_to_string_resolved(arg_info->type, zf->common.scope); if (value) { @@ -732,12 +722,10 @@ ZEND_API zend_never_inline ZEND_COLD void zend_verify_arg_error( const zend_function *zf, const zend_arg_info *arg_info, uint32_t arg_num, const zval *value) { const zend_execute_data *ptr = EG(current_execute_data)->prev_execute_data; - const char *fname, *fsep, *fclass; zend_string *need_msg; const char *given_msg; - zend_verify_type_error_common( - zf, arg_info, value, &fname, &fsep, &fclass, &need_msg, &given_msg); + zend_verify_type_error_common(zf, arg_info, value, &need_msg, &given_msg); ZEND_ASSERT(zf->common.type == ZEND_USER_FUNCTION && "Arginfo verification is not performed for internal functions"); @@ -930,8 +918,8 @@ zend_never_inline ZEND_COLD void zend_match_unhandled_error(const zval *value) ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_readonly_property_modification_error( const zend_property_info *info) { - zend_readonly_property_modification_error_ex( - ZSTR_VAL(info->ce->name), zend_get_unmangled_property_name(info->name)); + zend_throw_error(NULL, "Cannot modify readonly property %pS::$%s", + info->ce->name, zend_get_unmangled_property_name(info->name)); } ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_readonly_property_modification_error_ex( @@ -978,12 +966,12 @@ ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_asymmetric_visibili } } - zend_throw_error(NULL, "Cannot %s %s property %pS::$%pS from %s%s", + zend_throw_error(NULL, "Cannot %s %s property %pS::$%pS from %s%pS", operation, visibility, prop_info->ce->name, prop_info->name, - scope ? "scope " : "global scope", scope ? ZSTR_VAL(scope->name) : ""); + scope ? "scope " : "global scope", scope ? scope->name : ZSTR_EMPTY_ALLOC()); } static const zend_class_entry *resolve_single_class_type(zend_string *name, const zend_class_entry *self_ce) { @@ -1431,8 +1419,8 @@ ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(c const zend_execute_data *ptr = EX(prev_execute_data); if (ptr && ptr->func && ZEND_USER_CODE(ptr->func->common.type)) { - zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %s%s%pS(), %d passed in %pS on line %d and %s %d expected", - EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "", + zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %pS%s%pS(), %d passed in %pS on line %d and %s %d expected", + EX(func)->common.scope ? EX(func)->common.scope->name : ZSTR_EMPTY_ALLOC(), EX(func)->common.scope ? "::" : "", EX(func)->common.function_name, EX_NUM_ARGS(), @@ -1441,8 +1429,8 @@ ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(c EX(func)->common.required_num_args == EX(func)->common.num_args ? "exactly" : "at least", EX(func)->common.required_num_args); } else { - zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %s%s%pS(), %d passed and %s %d expected", - EX(func)->common.scope ? ZSTR_VAL(EX(func)->common.scope->name) : "", + zend_throw_error(zend_ce_argument_count_error, "Too few arguments to function %pS%s%pS(), %d passed and %s %d expected", + EX(func)->common.scope ? EX(func)->common.scope->name : ZSTR_EMPTY_ALLOC(), EX(func)->common.scope ? "::" : "", EX(func)->common.function_name, EX_NUM_ARGS(), @@ -1454,17 +1442,17 @@ ZEND_API zend_never_inline ZEND_COLD void ZEND_FASTCALL zend_missing_arg_error(c ZEND_API zend_never_inline ZEND_COLD void zend_verify_return_error(const zend_function *zf, const zval *value) { const zend_arg_info *arg_info = &zf->common.arg_info[-1]; - const char *fname, *fsep, *fclass; + zend_string *func_name = get_function_or_method_name(zf); zend_string *need_msg; const char *given_msg; - zend_verify_type_error_common( - zf, arg_info, value, &fname, &fsep, &fclass, &need_msg, &given_msg); + zend_verify_type_error_common(zf, arg_info, value, &need_msg, &given_msg); - zend_type_error("%s%s%s(): Return value must be of type %pS, %s returned", - fclass, fsep, fname, need_msg, given_msg); + zend_type_error("%pS(): Return value must be of type %pS, %s returned", + func_name, need_msg, given_msg); zend_string_release(need_msg); + zend_string_release(func_name); } ZEND_API zend_never_inline ZEND_COLD void zend_verify_never_error(const zend_function *zf) @@ -1481,33 +1469,24 @@ ZEND_API zend_never_inline ZEND_COLD void zend_verify_never_error(const zend_fun static zend_never_inline ZEND_COLD void zend_verify_internal_return_error(const zend_function *zf, const zval *value) { const zend_arg_info *arg_info = &zf->common.arg_info[-1]; - const char *fname, *fsep, *fclass; + zend_string *func_name = get_function_or_method_name(zf); zend_string *need_msg; const char *given_msg; - zend_verify_type_error_common( - zf, arg_info, value, &fname, &fsep, &fclass, &need_msg, &given_msg); + zend_verify_type_error_common(zf, arg_info, value, &need_msg, &given_msg); - zend_error_noreturn(E_CORE_ERROR, "%s%s%s(): Return value must be of type %pS, %s returned", - fclass, fsep, fname, need_msg, given_msg); + zend_error_noreturn(E_CORE_ERROR, "%pS(): Return value must be of type %pS, %s returned", + func_name, need_msg, given_msg); } static zend_never_inline 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; + zend_string *func_name = get_function_or_method_name(zf); - if (zf->common.scope) { - fsep = "::"; - fclass = ZSTR_VAL(zf->common.scope->name); - } else { - fsep = ""; - fclass = ""; - } + zend_type_error("%pS() must not return a value, %s%s returned", + func_name, returned_msg, returned_kind); - zend_type_error("%s%s%s() must not return a value, %s%s returned", - fclass, fsep, fname, returned_msg, returned_kind); + zend_string_release(func_name); } ZEND_API bool zend_verify_internal_return_type(const zend_function *zf, zval *ret) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 5a11a5d8bd8b..dd4fbb8e5868 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1153,8 +1153,8 @@ static inheritance_status do_inheritance_check_on_method( return INHERITANCE_ERROR; } zend_error_at_noreturn(E_COMPILE_ERROR, func_filename(child), func_lineno(child), - "Cannot override final method %s::%pS()", - ZEND_FN_SCOPE_NAME(parent), child->common.function_name); + "Cannot override final method %pS::%pS()", + parent->common.scope->name, child->common.function_name); } child_flags = child->common.fn_flags; @@ -1167,12 +1167,12 @@ static inheritance_status do_inheritance_check_on_method( } if (child_flags & ZEND_ACC_STATIC) { zend_error_at_noreturn(E_COMPILE_ERROR, func_filename(child), func_lineno(child), - "Cannot make non static method %s::%pS() static in class %s", - ZEND_FN_SCOPE_NAME(parent), child->common.function_name, ZEND_FN_SCOPE_NAME(child)); + "Cannot make non static method %pS::%pS() static in class %pS", + parent->common.scope->name, child->common.function_name, child->common.scope->name); } else { zend_error_at_noreturn(E_COMPILE_ERROR, func_filename(child), func_lineno(child), - "Cannot make static method %s::%pS() non static in class %s", - ZEND_FN_SCOPE_NAME(parent), child->common.function_name, ZEND_FN_SCOPE_NAME(child)); + "Cannot make static method %pS::%pS() non static in class %pS", + parent->common.scope->name, child->common.function_name, child->common.scope->name); } } @@ -1183,8 +1183,8 @@ static inheritance_status do_inheritance_check_on_method( return INHERITANCE_ERROR; } zend_error_at_noreturn(E_COMPILE_ERROR, func_filename(child), func_lineno(child), - "Cannot make non abstract method %s::%pS() abstract in class %s", - ZEND_FN_SCOPE_NAME(parent), child->common.function_name, ZEND_FN_SCOPE_NAME(child)); + "Cannot make non abstract method %pS::%pS() abstract in class %pS", + parent->common.scope->name, child->common.function_name, child->common.scope->name); } if ((flags & ZEND_INHERITANCE_SET_CHILD_CHANGED) @@ -1218,8 +1218,8 @@ static inheritance_status do_inheritance_check_on_method( return INHERITANCE_ERROR; } zend_error_at_noreturn(E_COMPILE_ERROR, func_filename(child), func_lineno(child), - "Access level to %s::%pS() must be %s (as in class %s)%s", - ZEND_FN_SCOPE_NAME(child), child->common.function_name, zend_visibility_string(parent_flags), ZEND_FN_SCOPE_NAME(parent), (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker"); + "Access level to %pS::%pS() must be %s (as in class %pS)%s", + child->common.scope->name, child->common.function_name, zend_visibility_string(parent_flags), parent->common.scope->name, (parent_flags&ZEND_ACC_PUBLIC) ? "" : " or weaker"); } if (flags & ZEND_INHERITANCE_CHECK_PROTO) { @@ -2318,8 +2318,8 @@ void zend_inheritance_check_override(const zend_class_entry *ce) zend_error_at_noreturn( E_COMPILE_ERROR, f->op_array.filename, f->op_array.line_start, - "%s::%pS() has #[\\Override] attribute, but no matching parent method exists", - ZEND_FN_SCOPE_NAME(f), f->common.function_name); + "%pS::%pS() has #[\\Override] attribute, but no matching parent method exists", + f->common.scope->name, f->common.function_name); } } ZEND_HASH_FOREACH_END(); @@ -2348,8 +2348,8 @@ void zend_inheritance_check_override(const zend_class_entry *ce) zend_error_at_noreturn( E_COMPILE_ERROR, f->op_array.filename, f->op_array.line_start, - "%s::%pS() has #[\\Override] attribute, but no matching parent method exists", - ZEND_FN_SCOPE_NAME(f), f->common.function_name); + "%pS::%pS() has #[\\Override] attribute, but no matching parent method exists", + f->common.scope->name, f->common.function_name); } } } @@ -3064,9 +3064,9 @@ ZEND_API void zend_class_use_internal_traits(zend_class_entry *class_entry, int } #define MAX_ABSTRACT_INFO_CNT 3 -#define MAX_ABSTRACT_INFO_FMT "%s%s%s%s" +#define MAX_ABSTRACT_INFO_FMT "%pS%s%s%s" #define DISPLAY_ABSTRACT_FN(idx) \ - ai.afn[idx] ? ZEND_FN_SCOPE_NAME(ai.afn[idx]) : "", \ + ai.afn[idx] ? ai.afn[idx]->common.scope->name : ZSTR_EMPTY_ALLOC(), \ ai.afn[idx] ? "::" : "", \ ai.afn[idx] ? ZSTR_VAL(ai.afn[idx]->common.function_name) : "", \ ai.afn[idx] && ai.afn[idx + 1] ? ", " : (ai.afn[idx] && ai.cnt > MAX_ABSTRACT_INFO_CNT ? ", ..." : "") diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index a537f6b2a3ea..0019d27a5a0b 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1959,10 +1959,10 @@ ZEND_API zend_function *zend_get_property_hook_trampoline( ZEND_API ZEND_COLD zend_never_inline void zend_bad_method_call(const zend_function *fbc, const zend_string *method_name, const zend_class_entry *scope) /* {{{ */ { - zend_throw_error(NULL, "Call to %s method %s::%pS() from %s%s", - zend_visibility_string(fbc->common.fn_flags), ZEND_FN_SCOPE_NAME(fbc), method_name, + zend_throw_error(NULL, "Call to %s method %pS::%pS() from %s%pS", + zend_visibility_string(fbc->common.fn_flags), fbc->common.scope->name, method_name, scope ? "scope " : "global scope", - scope ? ZSTR_VAL(scope->name) : "" + scope ? scope->name : ZSTR_EMPTY_ALLOC() ); } /* }}} */ diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 447956752fa2..a1655662c16c 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -129,10 +129,10 @@ ZEND_API void zend_objects_destroy_object(zend_object *object) ZEND_ASSERT(!(destructor->common.fn_flags & ZEND_ACC_PUBLIC)); if (!zend_check_method_accessible(destructor, scope)) { zend_throw_error(NULL, - "Call to %s %pS::__destruct() from %s%s", + "Call to %s %pS::__destruct() from %s%pS", zend_visibility_string(destructor->common.fn_flags), object->ce->name, scope ? "scope " : "global scope", - scope ? ZSTR_VAL(scope->name) : "" + scope ? scope->name : ZSTR_EMPTY_ALLOC() ); return; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 351c67f2ab9f..1596838195bc 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -5460,9 +5460,9 @@ ZEND_VM_C_LABEL(send_again): if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { zend_error( - E_WARNING, "Cannot pass by-reference argument %d of %s%s%pS()" + E_WARNING, "Cannot pass by-reference argument %d of %pS%s%pS()" " by unpacking a Traversable, passing by-value instead", arg_num, - EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "", + EX(call)->func->common.scope ? EX(call)->func->common.scope->name : ZSTR_EMPTY_ALLOC(), EX(call)->func->common.scope ? "::" : "", EX(call)->func->common.function_name ); @@ -5486,9 +5486,9 @@ ZEND_VM_C_LABEL(send_again): if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { zend_error( - E_WARNING, "Cannot pass by-reference argument %d of %s%s%pS()" + E_WARNING, "Cannot pass by-reference argument %d of %pS%s%pS()" " by unpacking a Traversable, passing by-value instead", arg_num, - EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "", + EX(call)->func->common.scope ? EX(call)->func->common.scope->name : ZSTR_EMPTY_ALLOC(), EX(call)->func->common.scope ? "::" : "", EX(call)->func->common.function_name ); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 96405697644f..b196dcc01f07 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -2586,9 +2586,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_SEND_UNPACK_S if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { zend_error( - E_WARNING, "Cannot pass by-reference argument %d of %s%s%pS()" + E_WARNING, "Cannot pass by-reference argument %d of %pS%s%pS()" " by unpacking a Traversable, passing by-value instead", arg_num, - EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "", + EX(call)->func->common.scope ? EX(call)->func->common.scope->name : ZSTR_EMPTY_ALLOC(), EX(call)->func->common.scope ? "::" : "", EX(call)->func->common.function_name ); @@ -2612,9 +2612,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_SEND_UNPACK_S if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { zend_error( - E_WARNING, "Cannot pass by-reference argument %d of %s%s%pS()" + E_WARNING, "Cannot pass by-reference argument %d of %pS%s%pS()" " by unpacking a Traversable, passing by-value instead", arg_num, - EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "", + EX(call)->func->common.scope ? EX(call)->func->common.scope->name : ZSTR_EMPTY_ALLOC(), EX(call)->func->common.scope ? "::" : "", EX(call)->func->common.function_name ); @@ -55588,9 +55588,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_SEND_UNPACK_SPEC_T if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { zend_error( - E_WARNING, "Cannot pass by-reference argument %d of %s%s%pS()" + E_WARNING, "Cannot pass by-reference argument %d of %pS%s%pS()" " by unpacking a Traversable, passing by-value instead", arg_num, - EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "", + EX(call)->func->common.scope ? EX(call)->func->common.scope->name : ZSTR_EMPTY_ALLOC(), EX(call)->func->common.scope ? "::" : "", EX(call)->func->common.function_name ); @@ -55614,9 +55614,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_SEND_UNPACK_SPEC_T if (ARG_MUST_BE_SENT_BY_REF(EX(call)->func, arg_num)) { zend_error( - E_WARNING, "Cannot pass by-reference argument %d of %s%s%pS()" + E_WARNING, "Cannot pass by-reference argument %d of %pS%s%pS()" " by unpacking a Traversable, passing by-value instead", arg_num, - EX(call)->func->common.scope ? ZSTR_VAL(EX(call)->func->common.scope->name) : "", + EX(call)->func->common.scope ? EX(call)->func->common.scope->name : ZSTR_EMPTY_ALLOC(), EX(call)->func->common.scope ? "::" : "", EX(call)->func->common.function_name ); diff --git a/ext/dom/tests/dom_anon_class_name_in_errors.phpt b/ext/dom/tests/dom_anon_class_name_in_errors.phpt new file mode 100644 index 000000000000..29884c8ff9d5 --- /dev/null +++ b/ext/dom/tests/dom_anon_class_name_in_errors.phpt @@ -0,0 +1,26 @@ +--TEST-- +Anonymous class names are not truncated in DOM error messages +--EXTENSIONS-- +dom +--FILE-- +__construct($GLOBALS['doc']); + } catch (Error $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; + } +} + +$doc = new DOMDocument(); +$doc->loadXML(''); + +$xpath = new class($doc) extends DOMXPath {}; +$xpath->registerNamespace('php', 'http://php.net/xpath'); +$xpath->registerPhpFunctions(); +$xpath->evaluate('string(php:function("reconstruct"))'); + +?> +--EXPECTF-- +Error: Cannot call DOMXPath@anonymous%0%s::__construct() while an XPath evaluation is in progress diff --git a/ext/dom/xpath.c b/ext/dom/xpath.c index e3c5e48f6cd5..48c4126abbcf 100644 --- a/ext/dom/xpath.c +++ b/ext/dom/xpath.c @@ -165,8 +165,8 @@ static void dom_xpath_construct(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry * dom_xpath_object *intern = Z_XPATHOBJ_P(ZEND_THIS); if (UNEXPECTED(intern->evaluation_depth > 0)) { - zend_throw_error(NULL, "Cannot call %s::__construct() while an XPath evaluation is in progress", - ZSTR_VAL(Z_OBJCE_P(ZEND_THIS)->name)); + zend_throw_error(NULL, "Cannot call %pS::__construct() while an XPath evaluation is in progress", + Z_OBJCE_P(ZEND_THIS)->name); RETURN_THROWS(); } diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index d547b9b3e904..aea0e23e4a54 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -199,7 +199,7 @@ static zval *xmlreader_write_property(zend_object *object, zend_string *name, zv xmlreader_prop_handler *hnd = xmlreader_get_prop_handler(name, cache_slot); if (hnd != NULL) { - zend_readonly_property_modification_error_ex(ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); + zend_throw_error(NULL, "Cannot modify readonly property %pS::$%pS", object->ce->name, name); } else { value = zend_std_write_property(object, name, value, cache_slot); } diff --git a/ext/xmlreader/tests/xmlreader_anon_class_name_in_errors.phpt b/ext/xmlreader/tests/xmlreader_anon_class_name_in_errors.phpt new file mode 100644 index 000000000000..70ade87100be --- /dev/null +++ b/ext/xmlreader/tests/xmlreader_anon_class_name_in_errors.phpt @@ -0,0 +1,17 @@ +--TEST-- +Anonymous class names are not truncated in XMLReader error messages +--EXTENSIONS-- +xmlreader +--FILE-- +nodeType = 1; +} catch (Error $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECTF-- +Error: Cannot modify readonly property XMLReader@anonymous%0%s::$nodeType diff --git a/main/main.c b/main/main.c index 43c5fa087c52..815edf52daee 100644 --- a/main/main.c +++ b/main/main.c @@ -78,6 +78,7 @@ #include "zend_observer.h" #include "zend_system_id.h" #include "zend_smart_string.h" +#include "zend_smart_str.h" #include "php_content_types.h" #include "php_ticks.h" @@ -949,6 +950,24 @@ PHPAPI bool php_get_module_initialized(void) } /* }}} */ +/* Log sinks take C strings, so escape NUL bytes instead of truncating at them. */ +static zend_string *php_escape_nul_for_log(zend_string *message) +{ + if (!memchr(ZSTR_VAL(message), '\0', ZSTR_LEN(message))) { + return zend_string_copy(message); + } + + smart_str buf = {0}; + for (size_t i = 0; i < ZSTR_LEN(message); i++) { + if (ZSTR_VAL(message)[i] == '\0') { + smart_str_appendl(&buf, "\\0", 2); + } else { + smart_str_appendc(&buf, ZSTR_VAL(message)[i]); + } + } + return smart_str_extract(&buf); +} + /* {{{ php_log_err_with_severity */ PHPAPI ZEND_COLD void php_log_err_with_severity(const char *log_message, int syslog_type_int) { @@ -1438,19 +1457,21 @@ static ZEND_COLD void php_error_cb(int orig_type, zend_string *error_filename, c if (PG(log_errors) || (!module_initialized && (!PG(display_startup_errors) || !PG(display_errors)))) { char *log_buffer; + zend_string *log_message = php_escape_nul_for_log(message); #ifdef PHP_WIN32 if (type == E_CORE_ERROR || type == E_CORE_WARNING) { - syslog(LOG_ALERT, "PHP %s: %s (%s)", error_type_str, ZSTR_VAL(message), GetCommandLine()); + syslog(LOG_ALERT, "PHP %s: %s (%s)", error_type_str, ZSTR_VAL(log_message), GetCommandLine()); } #endif - spprintf(&log_buffer, 0, "PHP %s: %s in %s on line %" PRIu32 "%s%s", error_type_str, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno, ZSTR_LEN(backtrace) ? "\nStack trace:\n" : "", ZSTR_VAL(backtrace)); + spprintf(&log_buffer, 0, "PHP %s: %s in %s on line %" PRIu32 "%s%s", error_type_str, ZSTR_VAL(log_message), ZSTR_VAL(error_filename), error_lineno, ZSTR_LEN(backtrace) ? "\nStack trace:\n" : "", ZSTR_VAL(backtrace)); + zend_string_release(log_message); php_log_err_with_severity(log_buffer, syslog_type_int); efree(log_buffer); } if (PG(display_errors) && ((module_initialized && !PG(during_request_startup)) || (PG(display_startup_errors)))) { if (PG(xmlrpc_errors)) { - php_printf("faultCode" ZEND_LONG_FMT "faultString%s:%s in %s on line %" PRIu32 "%s%s", PG(xmlrpc_error_number), error_type_str, ZSTR_VAL(message), ZSTR_VAL(error_filename), error_lineno, ZSTR_LEN(backtrace) ? "\nStack trace:\n" : "", ZSTR_VAL(backtrace)); + php_printf("faultCode" ZEND_LONG_FMT "faultString%s:%pS in %s on line %" PRIu32 "%s%s", PG(xmlrpc_error_number), error_type_str, message, ZSTR_VAL(error_filename), error_lineno, ZSTR_LEN(backtrace) ? "\nStack trace:\n" : "", ZSTR_VAL(backtrace)); } else { const char *prepend_string = zend_ini_string_literal("error_prepend_string"); const char *append_string = zend_ini_string_literal("error_append_string"); @@ -1458,7 +1479,7 @@ static ZEND_COLD void php_error_cb(int orig_type, zend_string *error_filename, c if (PG(html_errors)) { if (type == E_ERROR || type == E_PARSE) { zend_string *buf = escape_html(ZSTR_VAL(message), ZSTR_LEN(message)); - php_printf("%s
\n%s: %s in %s on line %" PRIu32 "
\n%s", STR_PRINT(prepend_string), error_type_str, ZSTR_VAL(buf), ZSTR_VAL(error_filename), error_lineno, STR_PRINT(append_string)); + php_printf("%s
\n%s: %pS in %s on line %" PRIu32 "
\n%s", STR_PRINT(prepend_string), error_type_str, buf, ZSTR_VAL(error_filename), error_lineno, STR_PRINT(append_string)); zend_string_free(buf); } else { php_printf_unchecked("%s
\n%s: %S in %s on line %" PRIu32 "
%s%s\n%s", STR_PRINT(prepend_string), error_type_str, message, ZSTR_VAL(error_filename), error_lineno, ZSTR_LEN(backtrace) ? "\nStack trace:\n" : "", ZSTR_VAL(backtrace), STR_PRINT(append_string)); diff --git a/tests/basic/errorlog_nul_byte.phpt b/tests/basic/errorlog_nul_byte.phpt new file mode 100644 index 000000000000..b53f1cee6871 --- /dev/null +++ b/tests/basic/errorlog_nul_byte.phpt @@ -0,0 +1,20 @@ +--TEST-- +NUL bytes in error messages are escaped in the error log +--INI-- +log_errors=1 +display_errors=0 +error_log={PWD}/errorlog_nul_byte.log +--FILE-- +dyn = 1; +trigger_error("foo\0bar", E_USER_WARNING); +echo file_get_contents(__DIR__ . '/errorlog_nul_byte.log'); +?> +--CLEAN-- + +--EXPECTF-- +[%s] PHP Deprecated: Creation of dynamic property class@anonymous\0%s::$dyn is deprecated in %s on line %d +[%s] PHP Warning: foo\0bar in %s on line %d diff --git a/tests/basic/html_errors_nul_byte.phpt b/tests/basic/html_errors_nul_byte.phpt new file mode 100644 index 000000000000..2f9582aa28e9 --- /dev/null +++ b/tests/basic/html_errors_nul_byte.phpt @@ -0,0 +1,11 @@ +--TEST-- +NUL bytes in fatal error messages are not truncated with html_errors +--INI-- +html_errors=1 +--FILE-- + +--EXPECTF-- +
+Fatal error: Class class@anonymous%0%s must implement 1 abstract method (class@anonymous%0%s::$p::get) in %s on line %d
diff --git a/tests/basic/xmlrpc_errors_nul_byte.phpt b/tests/basic/xmlrpc_errors_nul_byte.phpt new file mode 100644 index 000000000000..750a28e8f8b3 --- /dev/null +++ b/tests/basic/xmlrpc_errors_nul_byte.phpt @@ -0,0 +1,10 @@ +--TEST-- +NUL bytes in error messages are not truncated with xmlrpc_errors +--INI-- +xmlrpc_errors=1 +--FILE-- + +--EXPECTF-- +faultCode0faultStringFatal error:Class class@anonymous%0%s must implement 1 abstract method (class@anonymous%0%s::$p::get) in %s on line %d