[PHP-CVS] [php-src] master: Merge branch 'PHP-8.4' into PHP-8.5
[email protected] (Daniel Scherzer) Mon, 3 Aug 2026 17:47:21 +0000
| Newsgroups | php.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: Daniel Scherzer (DanielEScherzer)
Date: 2026-08-03T10:22:50-07:00
Commit: https://github.com/php/php-src/commit/74a1f21ebdfb7458943527813db8a72e878d9dea
Raw diff: https://github.com/php/php-src/commit/74a1f21ebdfb7458943527813db8a72e878d9dea.diff
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
NEWS for PHP 8.4
GH-22905: avoid truncation on null bytes in reflection exceptions
Reflection: Add regression tests for error messages with null bytes
Changed paths:
A ext/reflection/tests/gh22905/ReflectionClassConstant_construct_class.phpt
A ext/reflection/tests/gh22905/ReflectionClassConstant_construct_constant.phpt
A ext/reflection/tests/gh22905/ReflectionClass_construct.phpt
A ext/reflection/tests/gh22905/ReflectionClass_getMethod.phpt
A ext/reflection/tests/gh22905/ReflectionClass_getProperty.phpt
A ext/reflection/tests/gh22905/ReflectionClass_getProperty_qualified_base.phpt
A ext/reflection/tests/gh22905/ReflectionClass_getProperty_qualified_nobase.phpt
A ext/reflection/tests/gh22905/ReflectionClass_getStaticPropertyValue.phpt
A ext/reflection/tests/gh22905/ReflectionClass_implementsInterface.phpt
A ext/reflection/tests/gh22905/ReflectionClass_isSubclassOf.phpt
A ext/reflection/tests/gh22905/ReflectionClass_setStaticPropertyValue.phpt
A ext/reflection/tests/gh22905/ReflectionConstant_construct.phpt
A ext/reflection/tests/gh22905/ReflectionEnum_getCase.phpt
A ext/reflection/tests/gh22905/ReflectionExtension_construct.phpt
A ext/reflection/tests/gh22905/ReflectionFunction_construct.phpt
A ext/reflection/tests/gh22905/ReflectionMethod_construct_class.phpt
A ext/reflection/tests/gh22905/ReflectionMethod_construct_method.phpt
A ext/reflection/tests/gh22905/ReflectionMethod_create_method.phpt
A ext/reflection/tests/gh22905/ReflectionParameter_construct_class.phpt
A ext/reflection/tests/gh22905/ReflectionParameter_construct_method.phpt
A ext/reflection/tests/gh22905/ReflectionParameter_construct_string.phpt
A ext/reflection/tests/gh22905/ReflectionProperty_construct_class.phpt
A ext/reflection/tests/gh22905/ReflectionProperty_construct_property.phpt
A ext/reflection/tests/gh22905/ReflectionProperty_setRawValueWithoutLazyInitialization.phpt
A ext/reflection/tests/gh22905/ReflectionProperty_skipLazyInitialization.phpt
A ext/reflection/tests/gh22905/ReflectionZendExtension_construct.phpt
A ext/reflection/tests/gh22905/Reflection_getAttributes.phpt
M Zend/zend.c
M ext/reflection/php_reflection.c
Diff:
diff --git a/Zend/zend.c b/Zend/zend.c
index 59a1a1a3ba88..e58566541dd2 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -1816,7 +1816,6 @@ ZEND_API void zend_free_recorded_errors(void)
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format, ...) /* {{{ */
{
va_list va;
- char *message = NULL;
if (!exception_ce) {
exception_ce = zend_ce_error;
@@ -1828,16 +1827,20 @@ ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const c
}
va_start(va, format);
- zend_vspprintf(&message, 0, format, va);
+ zend_string *message = zend_vstrpprintf(0, format, va);
//TODO: we can't convert compile-time errors to exceptions yet???
if (EG(current_execute_data) && !CG(in_compilation)) {
- zend_throw_exception(exception_ce, message, 0);
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ // Use "%S" so that the message can contain null bytes.
+ const char *format = "%S";
+ zend_throw_exception_ex(exception_ce, 0, format, message);
} else {
- zend_error_noreturn(E_ERROR, "%s", message);
+ zend_error_noreturn(E_ERROR, "%s", ZSTR_VAL(message));
}
- efree(message);
+ zend_string_release(message);
va_end(va);
}
/* }}} */
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 9780a0368cf5..9aa6a908627f 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -1382,7 +1382,10 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut
if (name && (flags & REFLECTION_ATTRIBUTE_IS_INSTANCEOF)) {
if (NULL == (base = zend_lookup_class(name))) {
if (!EG(exception)) {
- zend_throw_error(NULL, "Class \"%s\" not found", ZSTR_VAL(name));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Class \"%S\" not found";
+ zend_throw_error(NULL, format, name);
}
RETURN_THROWS();
@@ -1802,8 +1805,11 @@ ZEND_METHOD(ReflectionFunction, __construct)
}
if (fptr == NULL) {
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Function %S() does not exist";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Function %s() does not exist", ZSTR_VAL(fname));
+ format, fname);
RETURN_THROWS();
}
}
@@ -2566,8 +2572,11 @@ ZEND_METHOD(ReflectionParameter, __construct)
zend_string_release(lcname);
}
if (!fptr) {
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Function %S() does not exist";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Function %s() does not exist", Z_STRVAL_P(reference));
+ format, Z_STR_P(reference));
RETURN_THROWS();
}
ce = fptr->common.scope;
@@ -2594,8 +2603,11 @@ ZEND_METHOD(ReflectionParameter, __construct)
return;
}
if ((ce = zend_lookup_class(name)) == NULL) {
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Class \"%S\" does not exist";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Class \"%s\" does not exist", ZSTR_VAL(name));
+ format, name);
zend_string_release(name);
RETURN_THROWS();
}
@@ -2614,8 +2626,11 @@ ZEND_METHOD(ReflectionParameter, __construct)
/* nothing to do. don't set is_closure since is the invoke handler,
not the closure itself */
} else if ((fptr = zend_hash_find_ptr(&ce->function_table, lcname)) == NULL) {
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Method %S::%S() does not exist";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Method %s::%s() does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ format, ce->name, name);
zend_string_release(name);
zend_string_release(lcname);
RETURN_THROWS();
@@ -3380,7 +3395,10 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_
if (class_name) {
if ((ce = zend_lookup_class(class_name)) == NULL) {
if (!EG(exception)) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Class \"%s\" does not exist", ZSTR_VAL(class_name));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Class \"%S\" does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, class_name);
}
zend_string_release(class_name);
RETURN_THROWS();
@@ -3409,8 +3427,16 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_
/* do nothing, mptr already set */
} else if ((mptr = zend_hash_str_find_ptr(&ce->function_table, lcname, method_name_len)) == NULL) {
efree(lcname);
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Method %S::%S() does not exist";
+ ALLOCA_FLAG(use_heap);
+ zend_string *method_name_zstr;
+ ZSTR_ALLOCA_INIT(method_name_zstr, method_name, method_name_len, use_heap);
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Method %s::%s() does not exist", ZSTR_VAL(ce->name), method_name);
+ format, ce->name, method_name_zstr);
+ ZSTR_ALLOCA_FREE(method_name_zstr, use_heap);
+
RETURN_THROWS();
}
efree(lcname);
@@ -3893,7 +3919,10 @@ ZEND_METHOD(ReflectionClassConstant, __construct)
ce = classname_obj->ce;
} else {
if ((ce = zend_lookup_class(classname_str)) == NULL) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Class \"%s\" does not exist", ZSTR_VAL(classname_str));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Class \"%S\" does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, classname_str);
RETURN_THROWS();
}
}
@@ -3902,7 +3931,10 @@ ZEND_METHOD(ReflectionClassConstant, __construct)
intern = Z_REFLECTION_P(object);
if ((constant = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), constname)) == NULL) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant %s::%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(constname));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Constant %S::%S does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, ce->name, constname);
RETURN_THROWS();
}
@@ -4168,7 +4200,10 @@ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_ob
} else {
if ((ce = zend_lookup_class(arg_class)) == NULL) {
if (!EG(exception)) {
- zend_throw_exception_ex(reflection_exception_ptr, -1, "Class \"%s\" does not exist", ZSTR_VAL(arg_class));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Class \"%S\" does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, -1, format, arg_class);
}
RETURN_THROWS();
}
@@ -4310,8 +4345,11 @@ ZEND_METHOD(ReflectionClass, getStaticPropertyValue)
zend_throw_error(NULL,
"Typed property %s::$%s must not be accessed before initialization", ZSTR_VAL(ce->name), ZSTR_VAL(name));
} else {
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Property %S::$%S does not exist";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Property %s::$%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ format, ce->name, name);
}
}
/* }}} */
@@ -4340,8 +4378,11 @@ ZEND_METHOD(ReflectionClass, setStaticPropertyValue)
EG(fake_scope) = old_scope;
if (!variable_ptr) {
zend_clear_exception();
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Class %S does not have a property named %S";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Class %s does not have a property named %s", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ format, ce->name, name);
RETURN_THROWS();
}
@@ -4583,8 +4624,11 @@ ZEND_METHOD(ReflectionClass, getMethod)
} else if ((mptr = zend_hash_find_ptr(&ce->function_table, lc_name)) != NULL) {
reflection_method_factory(ce, mptr, NULL, return_value);
} else {
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Method %S::%S() does not exist";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Method %s::%s() does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ format, ce->name, name);
}
zend_string_release(lc_name);
}
@@ -4710,7 +4754,9 @@ ZEND_METHOD(ReflectionClass, getProperty)
}
}
str_name = ZSTR_VAL(name);
+ bool fully_qualified = false;
if ((tmp = zend_memnstr(ZSTR_VAL(name), "::", 2, ZSTR_VAL(name) + ZSTR_LEN(name))) != NULL) {
+ fully_qualified = true;
classname_len = tmp - ZSTR_VAL(name);
classname = zend_string_init(ZSTR_VAL(name), classname_len, 0);
str_name_len = ZSTR_LEN(name) - (classname_len + 2);
@@ -4727,7 +4773,15 @@ ZEND_METHOD(ReflectionClass, getProperty)
zend_string_release_ex(classname, 0);
if (!instanceof_function(ce, ce2)) {
- zend_throw_exception_ex(reflection_exception_ptr, -1, "Fully qualified property name %s::$%s does not specify a base class of %s", ZSTR_VAL(ce2->name), str_name, ZSTR_VAL(ce->name));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Fully qualified property name %S::$%S does not specify a base class of %S";
+ ALLOCA_FLAG(use_heap);
+ zend_string *prop_name_zstr;
+ ZSTR_ALLOCA_INIT(prop_name_zstr, str_name, str_name_len, use_heap);
+ zend_throw_exception_ex(reflection_exception_ptr, -1, format, ce2->name, prop_name_zstr, ce->name);
+ ZSTR_ALLOCA_FREE(prop_name_zstr, use_heap);
+
RETURN_THROWS();
}
ce = ce2;
@@ -4740,7 +4794,19 @@ ZEND_METHOD(ReflectionClass, getProperty)
return;
}
}
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Property %s::$%s does not exist", ZSTR_VAL(ce->name), str_name);
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Property %S::$%S does not exist";
+ // Can only use the existing `name` string if it wasn't fully qualified
+ if (fully_qualified) {
+ ALLOCA_FLAG(use_heap);
+ zend_string *prop_name_zstr;
+ ZSTR_ALLOCA_INIT(prop_name_zstr, str_name, str_name_len, use_heap);
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, ce->name, prop_name_zstr);
+ ZSTR_ALLOCA_FREE(prop_name_zstr, use_heap);
+ } else {
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, ce->name, name);
+ }
}
/* }}} */
@@ -5584,7 +5650,10 @@ ZEND_METHOD(ReflectionClass, isSubclassOf)
class_ce = argument->ptr;
} else {
if ((class_ce = zend_lookup_class(class_str)) == NULL) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Class \"%s\" does not exist", ZSTR_VAL(class_str));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Class \"%S\" does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, class_str);
RETURN_THROWS();
}
}
@@ -5617,7 +5686,10 @@ ZEND_METHOD(ReflectionClass, implementsInterface)
interface_ce = argument->ptr;
} else {
if ((interface_ce = zend_lookup_class(interface_str)) == NULL) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Interface \"%s\" does not exist", ZSTR_VAL(interface_str));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Interface \"%S\" does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, interface_str);
RETURN_THROWS();
}
}
@@ -5773,7 +5845,10 @@ ZEND_METHOD(ReflectionProperty, __construct)
ce = classname_obj->ce;
} else {
if ((ce = zend_lookup_class(classname_str)) == NULL) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Class \"%s\" does not exist", ZSTR_VAL(classname_str));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Class \"%S\" does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, classname_str);
RETURN_THROWS();
}
}
@@ -5789,7 +5864,10 @@ ZEND_METHOD(ReflectionProperty, __construct)
}
}
if (dynam_prop == 0) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Property %s::$%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Property %S::$%S does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, ce->name, name);
RETURN_THROWS();
}
}
@@ -6199,13 +6277,17 @@ static zend_result reflection_property_check_lazy_compatible(
zend_property_info *prop, zend_string *unmangled_name,
reflection_object *intern, zend_object *object, const char *method)
{
- if (!prop) {
+ if (!prop) {
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Can not use %s on dynamic property %S::$%S";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Can not use %s on dynamic property %s::$%s",
- method, ZSTR_VAL(intern->ce->name),
- ZSTR_VAL(unmangled_name));
+ format,
+ method, intern->ce->name,
+ unmangled_name);
return FAILURE;
}
+ // Non-dynamic properties cannot have null bytes so %s is fine
if (prop->flags & ZEND_ACC_STATIC) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -6729,22 +6811,24 @@ ZEND_METHOD(ReflectionExtension, __construct)
char *lcname;
reflection_object *intern;
zend_module_entry *module;
- char *name_str;
- size_t name_len;
+ zend_string *name_zstr;
ALLOCA_FLAG(use_heap)
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name_str, &name_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name_zstr) == FAILURE) {
RETURN_THROWS();
}
object = ZEND_THIS;
intern = Z_REFLECTION_P(object);
- lcname = do_alloca(name_len + 1, use_heap);
- zend_str_tolower_copy(lcname, name_str, name_len);
- if ((module = zend_hash_str_find_ptr(&module_registry, lcname, name_len)) == NULL) {
+ lcname = do_alloca(ZSTR_LEN(name_zstr) + 1, use_heap);
+ zend_str_tolower_copy(lcname, ZSTR_VAL(name_zstr), ZSTR_LEN(name_zstr));
+ if ((module = zend_hash_str_find_ptr(&module_registry, lcname, ZSTR_LEN(name_zstr))) == NULL) {
free_alloca(lcname, use_heap);
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Extension \"%S\" does not exist";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Extension \"%s\" does not exist", name_str);
+ format, name_zstr);
RETURN_THROWS();
}
free_alloca(lcname, use_heap);
@@ -7045,20 +7129,22 @@ ZEND_METHOD(ReflectionZendExtension, __construct)
zval *object;
reflection_object *intern;
zend_extension *extension;
- char *name_str;
- size_t name_len;
+ zend_string *name_zstr;
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name_str, &name_len) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name_zstr) == FAILURE) {
RETURN_THROWS();
}
object = ZEND_THIS;
intern = Z_REFLECTION_P(object);
- extension = zend_get_extension(name_str);
+ extension = zend_get_extension(ZSTR_VAL(name_zstr));
if (!extension) {
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Zend Extension \"%S\" does not exist";
zend_throw_exception_ex(reflection_exception_ptr, 0,
- "Zend Extension \"%s\" does not exist", name_str);
+ format, name_zstr);
RETURN_THROWS();
}
ZVAL_STRING(reflection_prop_name(object), extension->name);
@@ -7519,7 +7605,10 @@ ZEND_METHOD(ReflectionEnum, getCase)
zend_class_constant *constant = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), name);
if (constant == NULL) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Case %s::%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(name));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Case %S::%S does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, ce->name, name);
RETURN_THROWS();
}
if (!(ZEND_CLASS_CONST_FLAGS(constant) & ZEND_CLASS_CONST_IS_CASE)) {
@@ -7819,7 +7908,10 @@ ZEND_METHOD(ReflectionConstant, __construct)
zend_constant *const_ = zend_get_constant_ptr(lc_name);
zend_string_release_ex(lc_name, /* persistent */ false);
if (!const_) {
- zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant \"%s\" does not exist", ZSTR_VAL(name));
+ // %S is used for zend_string pointers by smart str printing, but normally
+ // is for wide character strings and so compilers complain if this is inline
+ const char *format = "Constant \"%S\" does not exist";
+ zend_throw_exception_ex(reflection_exception_ptr, 0, format, name);
RETURN_THROWS();
}
diff --git a/ext/reflection/tests/gh22905/ReflectionClassConstant_construct_class.phpt b/ext/reflection/tests/gh22905/ReflectionClassConstant_construct_class.phpt
new file mode 100644
index 000000000000..cb8ce925e851
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClassConstant_construct_class.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionClassConstant::__construct() error messages (class name)
+--FILE--
+<?php
+
+new ReflectionClassConstant("foo\0bar", "");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Class "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClassConstant->__construct('foo\x00bar', '')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClassConstant_construct_constant.phpt b/ext/reflection/tests/gh22905/ReflectionClassConstant_construct_constant.phpt
new file mode 100644
index 000000000000..537204a781b4
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClassConstant_construct_constant.phpt
@@ -0,0 +1,15 @@
+--TEST--
+GH-22905: null bytes in ReflectionClassConstant::__construct() error messages (constant name)
+--FILE--
+<?php
+
+class Demo {}
+new ReflectionClassConstant(Demo::class, "foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Constant Demo::foo%0bar does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClassConstant->__construct('Demo', 'foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClass_construct.phpt b/ext/reflection/tests/gh22905/ReflectionClass_construct.phpt
new file mode 100644
index 000000000000..f4575993c631
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClass_construct.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionClass::__construct() error messages
+--FILE--
+<?php
+
+new ReflectionClass("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Class "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->__construct('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClass_getMethod.phpt b/ext/reflection/tests/gh22905/ReflectionClass_getMethod.phpt
new file mode 100644
index 000000000000..27bfbd9072a8
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClass_getMethod.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in ReflectionClass::getMethod() error messages
+--FILE--
+<?php
+
+class Demo {}
+$r = new ReflectionClass(Demo::class);
+$r->getMethod("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Method Demo::foo%0bar() does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->getMethod('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClass_getProperty.phpt b/ext/reflection/tests/gh22905/ReflectionClass_getProperty.phpt
new file mode 100644
index 000000000000..d989daebdebb
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClass_getProperty.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in ReflectionClass::getProperty() error messages
+--FILE--
+<?php
+
+class Demo {}
+$r = new ReflectionClass(Demo::class);
+$r->getProperty("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Property Demo::$foo%0bar does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->getProperty('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClass_getProperty_qualified_base.phpt b/ext/reflection/tests/gh22905/ReflectionClass_getProperty_qualified_base.phpt
new file mode 100644
index 000000000000..650e9ad189e2
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClass_getProperty_qualified_base.phpt
@@ -0,0 +1,17 @@
+--TEST--
+GH-22905: null bytes in ReflectionClass::getProperty() (fully qualified, base class) error messages
+--FILE--
+<?php
+
+class Base {}
+class Demo extends Base {}
+$r = new ReflectionClass(Demo::class);
+$r->getProperty("Base::foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Property Base::$foo%0bar does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->getProperty('Base::foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClass_getProperty_qualified_nobase.phpt b/ext/reflection/tests/gh22905/ReflectionClass_getProperty_qualified_nobase.phpt
new file mode 100644
index 000000000000..561a2afc585d
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClass_getProperty_qualified_nobase.phpt
@@ -0,0 +1,17 @@
+--TEST--
+GH-22905: null bytes in ReflectionClass::getProperty() (fully qualified, non-base class) error messages
+--FILE--
+<?php
+
+class Base {}
+class Demo {}
+$r = new ReflectionClass(Demo::class);
+$r->getProperty("Base::foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Fully qualified property name Base::$foo%0bar does not specify a base class of Demo in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->getProperty('Base::foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClass_getStaticPropertyValue.phpt b/ext/reflection/tests/gh22905/ReflectionClass_getStaticPropertyValue.phpt
new file mode 100644
index 000000000000..7ded20bedd46
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClass_getStaticPropertyValue.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in ReflectionClass::getStaticPropertyValue() error messages
+--FILE--
+<?php
+
+class Demo {}
+$r = new ReflectionClass(Demo::class);
+$r->getStaticPropertyValue("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Property Demo::$foo%0bar does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->getStaticPropertyValue('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClass_implementsInterface.phpt b/ext/reflection/tests/gh22905/ReflectionClass_implementsInterface.phpt
new file mode 100644
index 000000000000..b516907dc51e
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClass_implementsInterface.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in ReflectionClass::implementsInterface() error messages
+--FILE--
+<?php
+
+class Demo {}
+$r = new ReflectionClass(Demo::class);
+$r->implementsInterface("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Interface "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->implementsInterface('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClass_isSubclassOf.phpt b/ext/reflection/tests/gh22905/ReflectionClass_isSubclassOf.phpt
new file mode 100644
index 000000000000..915426824f3f
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClass_isSubclassOf.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in ReflectionClass::isSubclassOf() error messages
+--FILE--
+<?php
+
+class Demo {}
+$r = new ReflectionClass(Demo::class);
+$r->isSubclassOf("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Class "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->isSubclassOf('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionClass_setStaticPropertyValue.phpt b/ext/reflection/tests/gh22905/ReflectionClass_setStaticPropertyValue.phpt
new file mode 100644
index 000000000000..c450f41cb6ed
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionClass_setStaticPropertyValue.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in ReflectionClass::setStaticPropertyValue() error messages
+--FILE--
+<?php
+
+class Demo {}
+$r = new ReflectionClass(Demo::class);
+$r->setStaticPropertyValue("foo\0bar", 123);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Class Demo does not have a property named foo%0bar in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->setStaticPropertyValue('foo\x00bar', 123)
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionConstant_construct.phpt b/ext/reflection/tests/gh22905/ReflectionConstant_construct.phpt
new file mode 100644
index 000000000000..62b6024d83d7
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionConstant_construct.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionConstant::__construct() error messages
+--FILE--
+<?php
+
+new ReflectionConstant("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Constant "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionConstant->__construct('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionEnum_getCase.phpt b/ext/reflection/tests/gh22905/ReflectionEnum_getCase.phpt
new file mode 100644
index 000000000000..3c50da112ef0
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionEnum_getCase.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in ReflectionEnum::getCase() error messages
+--FILE--
+<?php
+
+enum Demo {}
+$r = new ReflectionEnum(Demo::class);
+$r->getCase("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Case Demo::foo%0bar does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionEnum->getCase('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionExtension_construct.phpt b/ext/reflection/tests/gh22905/ReflectionExtension_construct.phpt
new file mode 100644
index 000000000000..c1980087edbb
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionExtension_construct.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionExtension::__construct() error messages
+--FILE--
+<?php
+
+new ReflectionExtension("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Extension "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionExtension->__construct('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionFunction_construct.phpt b/ext/reflection/tests/gh22905/ReflectionFunction_construct.phpt
new file mode 100644
index 000000000000..a145473c3485
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionFunction_construct.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionFunction::__construct() error messages
+--FILE--
+<?php
+
+new ReflectionFunction("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Function foo%0bar() does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionFunction->__construct('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionMethod_construct_class.phpt b/ext/reflection/tests/gh22905/ReflectionMethod_construct_class.phpt
new file mode 100644
index 000000000000..fcc39c1209bd
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionMethod_construct_class.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionMethod::__construct() error messages (class name)
+--FILE--
+<?php
+
+new ReflectionMethod("foo\0bar", "");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Class "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionMethod->__construct('foo\x00bar', '')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionMethod_construct_method.phpt b/ext/reflection/tests/gh22905/ReflectionMethod_construct_method.phpt
new file mode 100644
index 000000000000..1a4ba03cf2d2
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionMethod_construct_method.phpt
@@ -0,0 +1,15 @@
+--TEST--
+GH-22905: null bytes in ReflectionMethod::__construct() error messages (method name)
+--FILE--
+<?php
+
+class Demo {}
+new ReflectionMethod(Demo::class, "foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Method Demo::foo%0bar() does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionMethod->__construct('Demo', 'foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionMethod_create_method.phpt b/ext/reflection/tests/gh22905/ReflectionMethod_create_method.phpt
new file mode 100644
index 000000000000..137106559dcd
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionMethod_create_method.phpt
@@ -0,0 +1,15 @@
+--TEST--
+GH-22905: null bytes in ReflectionMethod::createFromMethodName() error messages (method name)
+--FILE--
+<?php
+
+class Demo {}
+ReflectionMethod::createFromMethodName("Demo::foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Method Demo::foo%0bar() does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionMethod::createFromMethodName('Demo::foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionParameter_construct_class.phpt b/ext/reflection/tests/gh22905/ReflectionParameter_construct_class.phpt
new file mode 100644
index 000000000000..6b2b599a1109
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionParameter_construct_class.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionParameter::__construct() error messages (array class name)
+--FILE--
+<?php
+
+new ReflectionParameter(["foo\0bar", ""], 0);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Class "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionParameter->__construct(Array, 0)
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionParameter_construct_method.phpt b/ext/reflection/tests/gh22905/ReflectionParameter_construct_method.phpt
new file mode 100644
index 000000000000..2158724dd451
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionParameter_construct_method.phpt
@@ -0,0 +1,15 @@
+--TEST--
+GH-22905: null bytes in ReflectionParameter::__construct() error messages (array method name)
+--FILE--
+<?php
+
+class Demo {}
+new ReflectionParameter([Demo::class, "foo\0bar"], 0);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Method Demo::foo%0bar() does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionParameter->__construct(Array, 0)
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionParameter_construct_string.phpt b/ext/reflection/tests/gh22905/ReflectionParameter_construct_string.phpt
new file mode 100644
index 000000000000..2ac9b8b763d2
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionParameter_construct_string.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionParameter::__construct() error messages (string function)
+--FILE--
+<?php
+
+new ReflectionParameter("foo\0bar", 0);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Function foo%0bar() does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionParameter->__construct('foo\x00bar', 0)
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionProperty_construct_class.phpt b/ext/reflection/tests/gh22905/ReflectionProperty_construct_class.phpt
new file mode 100644
index 000000000000..a5a785e908ea
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionProperty_construct_class.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionProperty::__construct() error messages (class name)
+--FILE--
+<?php
+
+new ReflectionProperty("foo\0bar", "");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Class "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionProperty->__construct('foo\x00bar', '')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionProperty_construct_property.phpt b/ext/reflection/tests/gh22905/ReflectionProperty_construct_property.phpt
new file mode 100644
index 000000000000..d18f926ecbff
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionProperty_construct_property.phpt
@@ -0,0 +1,15 @@
+--TEST--
+GH-22905: null bytes in ReflectionProperty::__construct() error messages (property name)
+--FILE--
+<?php
+
+class Demo {}
+new ReflectionProperty(Demo::class, "foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Property Demo::$foo%0bar does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionProperty->__construct('Demo', 'foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionProperty_setRawValueWithoutLazyInitialization.phpt b/ext/reflection/tests/gh22905/ReflectionProperty_setRawValueWithoutLazyInitialization.phpt
new file mode 100644
index 000000000000..d2d609bcf287
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionProperty_setRawValueWithoutLazyInitialization.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in ReflectionProperty::setRawValueWithoutLazyInitialization() error messages
+--FILE--
+<?php
+
+$o = (object)["foo\0bar" => "baz"];
+$r = new ReflectionProperty($o, "foo\0bar");
+$r->setRawValueWithoutLazyInitialization($o, 123);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Can not use setRawValueWithoutLazyInitialization on dynamic property stdClass::$foo%0bar in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionProperty->setRawValueWithoutLazyInitialization(Object(stdClass), 123)
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionProperty_skipLazyInitialization.phpt b/ext/reflection/tests/gh22905/ReflectionProperty_skipLazyInitialization.phpt
new file mode 100644
index 000000000000..49bad02ab4d7
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionProperty_skipLazyInitialization.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in ReflectionProperty::skipLazyInitialization() error messages
+--FILE--
+<?php
+
+$o = (object)["foo\0bar" => "baz"];
+$r = new ReflectionProperty($o, "foo\0bar");
+$r->skipLazyInitialization($o);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Can not use skipLazyInitialization on dynamic property stdClass::$foo%0bar in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionProperty->skipLazyInitialization(Object(stdClass))
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/ReflectionZendExtension_construct.phpt b/ext/reflection/tests/gh22905/ReflectionZendExtension_construct.phpt
new file mode 100644
index 000000000000..5722b6b08d8f
--- /dev/null
+++ b/ext/reflection/tests/gh22905/ReflectionZendExtension_construct.phpt
@@ -0,0 +1,14 @@
+--TEST--
+GH-22905: null bytes in ReflectionZendExtension::__construct() error messages
+--FILE--
+<?php
+
+new ReflectionZendExtension("foo\0bar");
+
+?>
+--EXPECTF--
+Fatal error: Uncaught ReflectionException: Zend Extension "foo%0bar" does not exist in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionZendExtension->__construct('foo\x00bar')
+#1 {main}
+ thrown in %s on line %d
diff --git a/ext/reflection/tests/gh22905/Reflection_getAttributes.phpt b/ext/reflection/tests/gh22905/Reflection_getAttributes.phpt
new file mode 100644
index 000000000000..ea3ec411629f
--- /dev/null
+++ b/ext/reflection/tests/gh22905/Reflection_getAttributes.phpt
@@ -0,0 +1,16 @@
+--TEST--
+GH-22905: null bytes in Reflection*::getAttributes() error messages
+--FILE--
+<?php
+
+class Demo {}
+$r = new ReflectionClass(Demo::class);
+$r->getAttributes("foo\0bar", ReflectionAttribute::IS_INSTANCEOF);
+
+?>
+--EXPECTF--
+Fatal error: Uncaught Error: Class "foo%0bar" not found in %s:%d
+Stack trace:
+#0 %s(%d): ReflectionClass->getAttributes('foo\x00bar', 2)
+#1 {main}
+ thrown in %s on line %d