Commit 841fe26c11f for php.net

commit 841fe26c11f1cae0bf0ea2aaf6062dd5b68c5bea
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Fri Jul 31 07:56:20 2026 -0400

    Avoid truncation on null bytes in class and function names

    Follow-up to GH-22681. _class_string() and _function_string() still
    formatted the class name and the function name with %s, so
    Reflection*::__toString() cut them at the first NUL. Anonymous class
    names embed one, both directly and inside the name of a closure declared
    in such a class.

    Closes GH-22971

diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 1e2f5d08acf..9f2ca58c56b 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -364,7 +364,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const
 		}
 		smart_str_append_printf(str, "class ");
 	}
-	smart_str_append_printf(str, "%s", ZSTR_VAL(ce->name));
+	smart_str_append(str, ce->name);
 	if (ce->parent) {
 		smart_str_append_printf(str, " extends %s", ZSTR_VAL(ce->parent->name));
 	}
@@ -904,7 +904,8 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent
 	if (fptr->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
 		smart_str_appendc(str, '&');
 	}
-	smart_str_append_printf(str, "%s ] {\n", ZSTR_VAL(fptr->common.function_name));
+	smart_str_append(str, fptr->common.function_name);
+	smart_str_appends(str, " ] {\n");
 	/* The information where a function is declared is only available for user classes */
 	if (fptr->type == ZEND_USER_FUNCTION) {
 		smart_str_append_printf(str, "%s  @@ %s %d - %d\n", indent,
diff --git a/ext/reflection/tests/gh22681/ReflectionClass_name.phpt b/ext/reflection/tests/gh22681/ReflectionClass_name.phpt
new file mode 100644
index 00000000000..c1c24b5c2e5
--- /dev/null
+++ b/ext/reflection/tests/gh22681/ReflectionClass_name.phpt
@@ -0,0 +1,32 @@
+--TEST--
+GH-22681: null bytes in name truncate ReflectionClass::__toString()
+--FILE--
+<?php
+
+$obj = new class {};
+
+$r = new ReflectionClass($obj);
+echo $r;
+var_dump( $r->getName() );
+
+?>
+--EXPECTF--
+Class [ <user> class class@anonymous%0%s ] {
+  @@ %s %d-%d
+
+  - Constants [0] {
+  }
+
+  - Static properties [0] {
+  }
+
+  - Static methods [0] {
+  }
+
+  - Properties [0] {
+  }
+
+  - Methods [0] {
+  }
+}
+string(%d) "class@anonymous%0%s"
diff --git a/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_name.phpt b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_name.phpt
new file mode 100644
index 00000000000..946dd164f3a
--- /dev/null
+++ b/ext/reflection/tests/gh22681/ReflectionFunctionAbstract_name.phpt
@@ -0,0 +1,21 @@
+--TEST--
+GH-22681: null bytes in name truncate ReflectionFunction::__toString()
+--FILE--
+<?php
+
+$obj = new class {
+    public function make(): Closure {
+        return function () {};
+    }
+};
+
+$r = new ReflectionFunction($obj->make());
+echo $r;
+var_dump( $r->getName() );
+
+?>
+--EXPECTF--
+Closure [ <user> public method {closure:class@anonymous%0%s::make():%d} ] {
+  @@ %s %d - %d
+}
+string(%d) "{closure:class@anonymous%0%s::make():%d}"