From 2c9581b70cab1d7e377b75ac7357be16c8e3829f Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Fri, 31 Jul 2026 07:53:05 -0400 Subject: [PATCH] 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 --- ext/reflection/php_reflection.c | 12 +++++------ ...tionClass_getProperty_anonymous_class.phpt | 21 +++++++++++++++++++ ..._createFromMethodName_anonymous_class.phpt | 20 ++++++++++++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 ext/reflection/tests/ReflectionClass_getProperty_anonymous_class.phpt create mode 100644 ext/reflection/tests/ReflectionMethod_createFromMethodName_anonymous_class.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0945c9574c62..b9665a4b23d5 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(); } @@ -4747,7 +4747,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) { @@ -4769,7 +4769,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 000000000000..67169000b4e0 --- /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-- +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 000000000000..a3ddf9bcd81b --- /dev/null +++ b/ext/reflection/tests/ReflectionMethod_createFromMethodName_anonymous_class.phpt @@ -0,0 +1,20 @@ +--TEST-- +ReflectionMethod::createFromMethodName() with an anonymous class name +--FILE-- +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)