From 6a61f6304983f1a7a8c0d5b2c09fe1c40facde21 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 10 Jul 2026 12:52:05 -0700 Subject: [PATCH 1/3] Add regression tests for `Reflection*::__toString()` with null bytes So that when the outputs are fixed the changes can be confirmed in tests --- .../ReflectionClassConstant_doc_comment.phpt | 44 ++++++++++++++++++ .../gh22681/ReflectionClass_doc_comment.phpt | 37 +++++++++++++++ .../gh22681/ReflectionConstant_name.phpt | 15 ++++++ .../ReflectionEnum_case_doc_comment.phpt | 46 +++++++++++++++++++ .../ReflectionExtension_ini_value.phpt | 23 ++++++++++ ...eflectionFunctionAbstract_doc_comment.phpt | 36 +++++++++++++++ .../ReflectionProperty_doc_comment.phpt | 44 ++++++++++++++++++ .../ReflectionProperty_dynamic_name.phpt | 37 +++++++++++++++ 8 files changed, 282 insertions(+) create mode 100644 ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionConstant_name.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt create mode 100644 ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt diff --git a/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt new file mode 100644 index 000000000000..49d646a4a191 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionClassConstant::__toString() +--FILE-- +getDocComment() ); + +echo new ReflectionClass(Demo::class); + +?> +--EXPECTF-- +/** F +Constant [ public bool DEMO ] { 1 } +string(11) "/** F%0oo */" +Class [ class Demo ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [1] { + /** F + Constant [ public bool DEMO ] { 1 } + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Methods [0] { + } +} diff --git a/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt new file mode 100644 index 000000000000..c2da0400dcbd --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionClass::__toString() +--FILE-- +getDocComment() ); + +?> +--EXPECTF-- +/** F +Class [ class Demo ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Methods [0] { + } +} +string(11) "/** F%0oo */" diff --git a/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt new file mode 100644 index 000000000000..a322596fa042 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-22681: null bytes in name truncate ReflectionConstant::__toString() +--FILE-- +getName() ); + +?> +--EXPECTF-- +Constant [ bool F ] { 1 } +string(4) "F%0oo" diff --git a/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt new file mode 100644 index 000000000000..e76c0007c51c --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt @@ -0,0 +1,46 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionEnum::__toString() +--FILE-- +getDocComment() ); +?> +--EXPECTF-- +Class [ final class Demo implements UnitEnum ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [1] { + /** F + Constant [ public Demo C ] { Object } + } + + - Static properties [0] { + } + + - Static methods [1] { + Method [ static public method cases ] { + + - Parameters [0] { + } + - Return [ array ] + } + } + + - Properties [1] { + Property [ public protected(set) readonly string $name ] + } + + - Methods [0] { + } +} +string(11) "/** F%0oo */" diff --git a/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt new file mode 100644 index 000000000000..42777ffd2fd2 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-22681: null bytes in INI values truncate ReflectionExtension::__toString() +--FILE-- +getINIEntries()['arg_separator.output'] ); + +?> +--EXPECTF-- +Entry [ arg_separator.output ] + Current = 'f' + Default = '&' + } + +string(4) "f%0oo" diff --git a/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt new file mode 100644 index 000000000000..b7673de6c567 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionFunctionAbstract::__toString() +--FILE-- +getDocComment() ); + +$r = new ReflectionMethod(Demo::class, 'demo'); +echo $r; +var_dump( $r->getDocComment() ); + +?> +--EXPECTF-- +/** F +Function [ function demo ] { + @@ %s(%d) : eval()'d code %d - %d +} +string(11) "/** F%0oo */" +/** B +Method [ public method demo ] { + @@ %s(%d) : eval()'d code %d - %d +} +string(11) "/** B%0ar */" diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt new file mode 100644 index 000000000000..4f6f7c875e17 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt @@ -0,0 +1,44 @@ +--TEST-- +GH-22681: null bytes in doc comment truncate ReflectionProperty::__toString() +--FILE-- +getDocComment() ); + +echo new ReflectionClass(Demo::class); + +?> +--EXPECTF-- +/** F +Property [ public $prop = NULL ] +string(11) "/** F%0oo */" +Class [ class Demo ] { + @@ %s(%d) : eval()'d code %d-%d + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [1] { + /** F + Property [ public $prop = NULL ] + } + + - Methods [0] { + } +} diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt new file mode 100644 index 000000000000..921a738ea117 --- /dev/null +++ b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-22681: null bytes in name truncate ReflectionProperty::__toString() +--FILE-- + true]; +$r = new ReflectionProperty($obj, "F\0oo"); +echo $r; +var_dump( $r->getDocComment() ); + +echo new ReflectionObject($obj); + +?> +--EXPECT-- +Property [ public $F ] +bool(false) +Object of class [ class stdClass ] { + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Dynamic properties [1] { + Property [ public $F ] + } + + - Methods [0] { + } +} From 0167df4d838da63d20630dbb57f8ce5f7d9db225 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 10 Jul 2026 13:14:49 -0700 Subject: [PATCH 2/3] GH-22681: avoid truncation on null bytes in `Reflection*::__toString()` Given that there is not an unchecked version of `smart_str_append_printf()`, to avoid compiler warnings from using `%S` to format `zend_string` pointers (which the smart string printing machinery supports, but printf does not support) in a few places the format specifier is declared in a variable rather than being specified directly in the `smart_str_append_printf()` call. --- ext/reflection/php_reflection.c | 68 +++++++++++++------ .../ReflectionClassConstant_doc_comment.phpt | 4 +- .../gh22681/ReflectionClass_doc_comment.phpt | 2 +- .../gh22681/ReflectionConstant_name.phpt | 2 +- .../ReflectionEnum_case_doc_comment.phpt | 2 +- .../ReflectionExtension_ini_value.phpt | 2 +- ...eflectionFunctionAbstract_doc_comment.phpt | 4 +- .../ReflectionProperty_doc_comment.phpt | 4 +- .../ReflectionProperty_dynamic_name.phpt | 6 +- 9 files changed, 62 insertions(+), 32 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 661b1eb1f3a4..d73a8100f4bc 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -308,9 +308,9 @@ static zval *reflection_instantiate(zend_class_entry *pce, zval *object) /* {{{ } /* }}} */ -static void _const_string(smart_str *str, const char *name, zval *value, const char *indent); +static void _const_string(smart_str *str, const zend_string *name, zval *value, const char *indent); static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, const char* indent); -static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent); +static void _property_string(smart_str *str, zend_property_info *prop, const zend_string *prop_name, const char* indent); static void _class_const_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent); static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent); static void _extension_string(smart_str *str, const zend_module_entry *module, const char *indent); @@ -324,7 +324,10 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const /* TBD: Repair indenting of doc comment (or is this to be done in the parser?) */ if (ce->doc_comment) { - smart_str_append_printf(str, "%s%s", indent, ZSTR_VAL(ce->doc_comment)); + // %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 = "%s%S"; + smart_str_append_printf(str, format, indent, ce->doc_comment); smart_str_appendc(str, '\n'); } @@ -494,7 +497,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const if (prop_name && ZSTR_LEN(prop_name) && ZSTR_VAL(prop_name)[0]) { /* skip all private and protected properties */ if (!zend_hash_exists(&ce->properties_info, prop_name)) { count++; - _property_string(&prop_str, NULL, ZSTR_VAL(prop_name), ZSTR_VAL(sub_indent)); + _property_string(&prop_str, NULL, prop_name, ZSTR_VAL(sub_indent)); } } } ZEND_HASH_FOREACH_END(); @@ -549,7 +552,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const /* }}} */ /* {{{ _const_string */ -static void _const_string(smart_str *str, const char *name, zval *value, const char *indent) +static void _const_string(smart_str *str, const zend_string *name, zval *value, const char *indent) { const char *type = zend_zval_type_name(value); uint32_t flags = Z_CONSTANT_FLAGS_P(value); @@ -579,7 +582,7 @@ static void _const_string(smart_str *str, const char *name, zval *value, const c smart_str_appends(str, type); smart_str_appendc(str, ' '); - smart_str_appends(str, name); + smart_str_append(str, name); smart_str_appends(str, " ] { "); if (Z_TYPE_P(value) == IS_ARRAY) { @@ -610,7 +613,10 @@ static void _class_const_string(smart_str *str, const zend_string *name, zend_cl const char *type = type_str ? ZSTR_VAL(type_str) : zend_zval_type_name(&c->value); if (c->doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(c->doc_comment)); + // %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 = "%s%S\n"; + smart_str_append_printf(str, format, indent, c->doc_comment); } smart_str_append_printf(str, "%sConstant [ %s%s %s %s ] { ", indent, final, visibility, type, ZSTR_VAL(name)); @@ -826,10 +832,13 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent * What's "wrong" is that any whitespace before the doc comment start is * swallowed, leading to an unaligned comment. */ + // %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 = "%s%S\n"; if (fptr->type == ZEND_USER_FUNCTION && fptr->op_array.doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(fptr->op_array.doc_comment)); + smart_str_append_printf(str, format, indent, fptr->op_array.doc_comment); } else if (fptr->type == ZEND_INTERNAL_FUNCTION && fptr->internal_function.doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(fptr->internal_function.doc_comment)); + smart_str_append_printf(str, format, indent, fptr->internal_function.doc_comment); } smart_str_appendl(str, indent, strlen(indent)); @@ -939,14 +948,19 @@ static zval *property_get_default(zend_property_info *prop_info) { } /* {{{ _property_string */ -static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent) +static void _property_string(smart_str *str, zend_property_info *prop, const zend_string *prop_name, const char* indent) { if (prop && prop->doc_comment) { - smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(prop->doc_comment)); + // %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 = "%s%S\n"; + smart_str_append_printf(str, format, indent, prop->doc_comment); } smart_str_append_printf(str, "%sProperty [ ", indent); if (!prop) { - smart_str_append_printf(str, " public $%s", prop_name); + ZEND_ASSERT(prop_name && "Properties without info must have a name provided"); + smart_str_appends(str, " public $"); + smart_str_append(str, prop_name); } else { if (prop->flags & ZEND_ACC_ABSTRACT) { smart_str_appends(str, "abstract "); @@ -989,11 +1003,15 @@ static void _property_string(smart_str *str, zend_property_info *prop, const cha smart_str_appendc(str, ' '); zend_string_release(type_str); } + smart_str_appendc(str, '$'); if (!prop_name) { const char *class_name; - zend_unmangle_property_name(prop->name, &class_name, &prop_name); + const char *prop_name_cstr; + zend_unmangle_property_name(prop->name, &class_name, &prop_name_cstr); + smart_str_appends(str, prop_name_cstr); + } else { + smart_str_append(str, prop_name); } - smart_str_append_printf(str, "$%s", prop_name); zval *default_value = property_get_default(prop); if (default_value && !Z_ISUNDEF_P(default_value)) { @@ -1031,9 +1049,21 @@ static void _extension_ini_string(const zend_ini_entry *ini_entry, smart_str *st } smart_str_appends(str, "> ]\n"); - smart_str_append_printf(str, " %s Current = '%s'\n", indent, ini_entry->value ? ZSTR_VAL(ini_entry->value) : ""); + if (ini_entry->value) { + // %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 = " %s Current = '%S'\n"; + smart_str_append_printf(str, format, indent, ini_entry->value); + } else { + smart_str_append_printf(str, " %s Current = ''\n", indent); + } if (ini_entry->modified) { - smart_str_append_printf(str, " %s Default = '%s'\n", indent, ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : ""); + if (ini_entry->orig_value) { + const char *format = " %s Default = '%S'\n"; + smart_str_append_printf(str, format, indent, ini_entry->orig_value); + } else { + smart_str_append_printf(str, " %s Default = ''\n", indent); + } } smart_str_append_printf(str, " %s}\n", indent); } @@ -1122,7 +1152,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c ZEND_HASH_MAP_FOREACH_PTR(EG(zend_constants), constant) { if (ZEND_CONSTANT_MODULE_NUMBER(constant) == module->module_number) { - _const_string(&str_constants, ZSTR_VAL(constant->name), &constant->value, " "); + _const_string(&str_constants, constant->name, &constant->value, " "); num_constants++; } } ZEND_HASH_FOREACH_END(); @@ -5894,7 +5924,7 @@ ZEND_METHOD(ReflectionProperty, __toString) RETURN_THROWS(); } GET_REFLECTION_OBJECT_PTR(ref); - _property_string(&str, ref->prop, ZSTR_VAL(ref->unmangled_name), ""); + _property_string(&str, ref->prop, ref->unmangled_name, ""); RETURN_STR(smart_str_extract(&str)); } /* }}} */ @@ -7955,7 +7985,7 @@ ZEND_METHOD(ReflectionConstant, __toString) } GET_REFLECTION_OBJECT_PTR(const_); - _const_string(&str, ZSTR_VAL(const_->name), &const_->value, ""); + _const_string(&str, const_->name, &const_->value, ""); RETURN_STR(smart_str_extract(&str)); } diff --git a/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt index 49d646a4a191..5002f066ff69 100644 --- a/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionClassConstant_doc_comment.phpt @@ -19,14 +19,14 @@ echo new ReflectionClass(Demo::class); ?> --EXPECTF-- -/** F +/** F%0oo */ Constant [ public bool DEMO ] { 1 } string(11) "/** F%0oo */" Class [ class Demo ] { @@ %s(%d) : eval()'d code %d-%d - Constants [1] { - /** F + /** F%0oo */ Constant [ public bool DEMO ] { 1 } } diff --git a/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt index c2da0400dcbd..fc34cd95b1e3 100644 --- a/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionClass_doc_comment.phpt @@ -15,7 +15,7 @@ var_dump( $r->getDocComment() ); ?> --EXPECTF-- -/** F +/** F%0oo */ Class [ class Demo ] { @@ %s(%d) : eval()'d code %d-%d diff --git a/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt index a322596fa042..c43397200c82 100644 --- a/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt +++ b/ext/reflection/tests/gh22681/ReflectionConstant_name.phpt @@ -11,5 +11,5 @@ var_dump( $r->getName() ); ?> --EXPECTF-- -Constant [ bool F ] { 1 } +Constant [ bool F%0oo ] { 1 } string(4) "F%0oo" diff --git a/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt index e76c0007c51c..10afacade8f7 100644 --- a/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionEnum_case_doc_comment.phpt @@ -20,7 +20,7 @@ Class [ final class Demo implements UnitEnum ] { @@ %s(%d) : eval()'d code %d-%d - Constants [1] { - /** F + /** F%0oo */ Constant [ public Demo C ] { Object } } diff --git a/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt index 42777ffd2fd2..f12088b3a9e0 100644 --- a/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt +++ b/ext/reflection/tests/gh22681/ReflectionExtension_ini_value.phpt @@ -16,7 +16,7 @@ var_dump( $r->getINIEntries()['arg_separator.output'] ); ?> --EXPECTF-- Entry [ arg_separator.output ] - Current = 'f' + Current = 'f%0oo' Default = '&' } diff --git a/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt index b7673de6c567..fdd42903c3be 100644 --- a/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_doc_comment.phpt @@ -24,12 +24,12 @@ var_dump( $r->getDocComment() ); ?> --EXPECTF-- -/** F +/** F%0oo */ Function [ function demo ] { @@ %s(%d) : eval()'d code %d - %d } string(11) "/** F%0oo */" -/** B +/** B%0ar */ Method [ public method demo ] { @@ %s(%d) : eval()'d code %d - %d } diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt index 4f6f7c875e17..ca218216b2c3 100644 --- a/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt +++ b/ext/reflection/tests/gh22681/ReflectionProperty_doc_comment.phpt @@ -19,7 +19,7 @@ echo new ReflectionClass(Demo::class); ?> --EXPECTF-- -/** F +/** F%0oo */ Property [ public $prop = NULL ] string(11) "/** F%0oo */" Class [ class Demo ] { @@ -35,7 +35,7 @@ Class [ class Demo ] { } - Properties [1] { - /** F + /** F%0oo */ Property [ public $prop = NULL ] } diff --git a/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt index 921a738ea117..d8cecb0002aa 100644 --- a/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt +++ b/ext/reflection/tests/gh22681/ReflectionProperty_dynamic_name.phpt @@ -11,8 +11,8 @@ var_dump( $r->getDocComment() ); echo new ReflectionObject($obj); ?> ---EXPECT-- -Property [ public $F ] +--EXPECTF-- +Property [ public $F%0oo ] bool(false) Object of class [ class stdClass ] { @@ -29,7 +29,7 @@ Object of class [ class stdClass ] { } - Dynamic properties [1] { - Property [ public $F ] + Property [ public $F%0oo ] } - Methods [0] { From 1e4e671c5214d14f6ffb3edd5fd38cd4330b73d5 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 10 Jul 2026 13:15:29 -0700 Subject: [PATCH 3/3] NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 7324e5c591aa..60a471aecbae 100644 --- a/NEWS +++ b/NEWS @@ -67,6 +67,8 @@ PHP NEWS dynamic properties shadowing a private parent property). (iliaal) . Fixed bug GH-22658 (ReflectionConstant::__toString() with a string value with null bytes truncates output). (DanielEScherzer) + . Fixed bug GH-22681 (Reflection*::__toString() truncates on null bytes). + (DanielEScherzer) - Session: . Fixed bug GH-21314 (Different session garbage collector behavior between