Commit 3a9d86a81bb for php.net
commit 3a9d86a81bbecf867eb8e5d8515e5219d75d14dc
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Fri Jul 31 07:53:05 2026 -0400
Parse qualified reflection names with a length-aware search
ReflectionMethod::__construct(), ReflectionMethod::createFromMethodName()
and ReflectionClass::getProperty() split "Class::member" with strstr(),
which stops at the first NUL. Anonymous class names embed one, so a name
obtained from ReflectionClass::getName() was rejected. Use zend_memnstr()
over the whole string instead.
Closes GH-22970
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 65462bb8652..1e2f5d08acf 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -3327,7 +3327,7 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_
zend_object *orig_obj = NULL;
zend_class_entry *ce = NULL;
zend_string *class_name = NULL;
- char *method_name;
+ const char *method_name;
size_t method_name_len;
char *lcname;
@@ -3370,11 +3370,11 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_
method_name = ZSTR_VAL(arg2_str);
method_name_len = ZSTR_LEN(arg2_str);
} else {
- char *tmp;
+ const char *tmp;
size_t tmp_len;
- char *name = ZSTR_VAL(arg1_str);
+ const char *name = ZSTR_VAL(arg1_str);
- if ((tmp = strstr(name, "::")) == NULL) {
+ if ((tmp = zend_memnstr(name, "::", 2, name + ZSTR_LEN(arg1_str))) == NULL) {
zend_argument_error(reflection_exception_ptr, 1, "must be a valid method name");
RETURN_THROWS();
}
@@ -4759,7 +4759,7 @@ ZEND_METHOD(ReflectionClass, getProperty)
zend_class_entry *ce, *ce2;
zend_property_info *property_info;
zend_string *name, *classname;
- char *tmp, *str_name;
+ const char *tmp, *str_name;
size_t classname_len, str_name_len;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &name) == FAILURE) {
@@ -4781,7 +4781,7 @@ ZEND_METHOD(ReflectionClass, getProperty)
}
}
str_name = ZSTR_VAL(name);
- if ((tmp = strstr(ZSTR_VAL(name), "::")) != NULL) {
+ if ((tmp = zend_memnstr(ZSTR_VAL(name), "::", 2, ZSTR_VAL(name) + ZSTR_LEN(name))) != NULL) {
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);
diff --git a/ext/reflection/tests/ReflectionClass_getProperty_anonymous_class.phpt b/ext/reflection/tests/ReflectionClass_getProperty_anonymous_class.phpt
new file mode 100644
index 00000000000..67169000b4e
--- /dev/null
+++ b/ext/reflection/tests/ReflectionClass_getProperty_anonymous_class.phpt
@@ -0,0 +1,21 @@
+--TEST--
+ReflectionClass::getProperty() with a qualified anonymous class name
+--FILE--
+<?php
+
+$obj = new class {
+ public $p = 42;
+};
+
+$reflector = new ReflectionClass($obj);
+$name = $reflector->getName();
+var_dump(str_contains($name, "\0"));
+
+$p = $reflector->getProperty($name . '::p');
+var_dump($p->getName(), $p->getValue($obj));
+
+?>
+--EXPECT--
+bool(true)
+string(1) "p"
+int(42)
diff --git a/ext/reflection/tests/ReflectionMethod_createFromMethodName_anonymous_class.phpt b/ext/reflection/tests/ReflectionMethod_createFromMethodName_anonymous_class.phpt
new file mode 100644
index 00000000000..a3ddf9bcd81
--- /dev/null
+++ b/ext/reflection/tests/ReflectionMethod_createFromMethodName_anonymous_class.phpt
@@ -0,0 +1,20 @@
+--TEST--
+ReflectionMethod::createFromMethodName() with an anonymous class name
+--FILE--
+<?php
+
+$obj = new class {
+ public function m() { return 42; }
+};
+
+$name = (new ReflectionClass($obj))->getName();
+var_dump(str_contains($name, "\0"));
+
+$m = ReflectionMethod::createFromMethodName($name . '::m');
+var_dump($m->getName(), $m->invoke($obj));
+
+?>
+--EXPECT--
+bool(true)
+string(1) "m"
+int(42)