From 310552a5e2b6bef2af99fda4ca505cb73d16e537 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:44:29 -0700 Subject: [PATCH 01/33] Implement `ReflectionClass::isAnonymous()` using `_class_check_flag()` --- ext/reflection/php_reflection.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 37c45cb02168..d611b5d6bc11 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4362,15 +4362,22 @@ ZEND_METHOD(ReflectionClass, isUserDefined) } /* }}} */ -/* {{{ Returns whether this class is anonymous */ -ZEND_METHOD(ReflectionClass, isAnonymous) +/* {{{ _class_check_flag */ +static void _class_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) { reflection_object *intern; zend_class_entry *ce; ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ce); - RETURN_BOOL(ce->ce_flags & ZEND_ACC_ANON_CLASS); + RETVAL_BOOL(ce->ce_flags & mask); +} +/* }}} */ + +/* {{{ Returns whether this class is anonymous */ +ZEND_METHOD(ReflectionClass, isAnonymous) +{ + _class_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_ANON_CLASS); } /* }}} */ @@ -4880,18 +4887,6 @@ ZEND_METHOD(ReflectionClass, getReflectionConstant) } /* }}} */ -/* {{{ _class_check_flag */ -static void _class_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) -{ - reflection_object *intern; - zend_class_entry *ce; - - ZEND_PARSE_PARAMETERS_NONE(); - GET_REFLECTION_OBJECT_PTR(ce); - RETVAL_BOOL(ce->ce_flags & mask); -} -/* }}} */ - /* {{{ Returns whether this class is instantiable */ ZEND_METHOD(ReflectionClass, isInstantiable) { From dbb4746a44a45b1a56649f3b8fa307b278ec3e91 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 17:05:54 -0700 Subject: [PATCH 02/33] Reflection: use `RETURN_BOOL` in `_class_check_flag()` --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index d611b5d6bc11..bfff775cc43e 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4370,7 +4370,7 @@ static void _class_check_flag(INTERNAL_FUNCTION_PARAMETERS, int mask) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(ce); - RETVAL_BOOL(ce->ce_flags & mask); + RETURN_BOOL(ce->ce_flags & mask); } /* }}} */ From c6a0bd2591049c6c5e085bdf1902955bcb1848aa Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:46:34 -0700 Subject: [PATCH 03/33] Implement `ReflectionClassConstant::isEnumCase()` using existing helper Use the `_class_constant_check_flag()` helper function to simplify things --- ext/reflection/php_reflection.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index bfff775cc43e..8e3113f7a6ec 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4045,12 +4045,7 @@ ZEND_METHOD(ReflectionClassConstant, getAttributes) ZEND_METHOD(ReflectionClassConstant, isEnumCase) { - reflection_object *intern; - zend_class_constant *ref; - - GET_REFLECTION_OBJECT_PTR(ref); - - RETURN_BOOL(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_CLASS_CONST_IS_CASE); + _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_CLASS_CONST_IS_CASE); } ZEND_METHOD(ReflectionClassConstant, isDeprecated) From c3876246cb3c7e775011afbc1344ff79f0e17e24 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:47:41 -0700 Subject: [PATCH 04/33] Implement `ReflectionClassConstant::isDeprecated()` using existing helper Use the `_class_constant_check_flag()` helper function to simplify things --- ext/reflection/php_reflection.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8e3113f7a6ec..f25cd09e067f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4050,14 +4050,7 @@ ZEND_METHOD(ReflectionClassConstant, isEnumCase) ZEND_METHOD(ReflectionClassConstant, isDeprecated) { - reflection_object *intern; - zend_constant *ref; - - ZEND_PARSE_PARAMETERS_NONE(); - - GET_REFLECTION_OBJECT_PTR(ref); - - RETURN_BOOL(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_ACC_DEPRECATED); + _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_DEPRECATED); } /* {{{ reflection_class_object_ctor */ From 456b7b7dc2b8c7774b9a5e609dce8807292d58a5 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:48:07 -0700 Subject: [PATCH 05/33] Fix inline documentation of `ReflectionProperty::getName()` --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index f25cd09e067f..0eb458fa9272 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -5741,7 +5741,7 @@ ZEND_METHOD(ReflectionProperty, __toString) } /* }}} */ -/* {{{ Returns the class' name */ +/* {{{ Returns the property's name */ ZEND_METHOD(ReflectionProperty, getName) { reflection_object *intern; From 5f03270cbdacb678e267535de62a6d33213661c5 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:48:50 -0700 Subject: [PATCH 06/33] Update inline documentation of `ReflectionProperty::setAccessible()` Since PHP 8.1 ReflectionProperty has always allowed access to non-public properties and the method is a no-op. --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0eb458fa9272..6fdf038bd140 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6392,7 +6392,7 @@ ZEND_METHOD(ReflectionProperty, getAttributes) } /* }}} */ -/* {{{ Sets whether non-public properties can be requested */ +/* {{{ No-op; previously controlled whether non-public properties can be requested */ ZEND_METHOD(ReflectionProperty, setAccessible) { bool visible; From 89e0adade198704dd315992865bac8d416997976 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:56:00 -0700 Subject: [PATCH 07/33] Fix inline documentation of `Reflection*::getModifiers()` methods Affected methods: * `ReflectionMethod::getModifiers()` * `ReflectionClassConstant::getModifiers()` * `ReflectionClass::getModifiers()` * `ReflectionProperty::getModifiers()` They were all documented bitfields of "access modifiers", however they all included modifiers unrelated to visibility/access. --- ext/reflection/php_reflection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 6fdf038bd140..be0446e2302a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3735,7 +3735,7 @@ ZEND_METHOD(ReflectionMethod, isDestructor) } /* }}} */ -/* {{{ Returns a bitfield of the access modifiers for this method */ +/* {{{ Returns a bitfield of the modifiers for this method */ ZEND_METHOD(ReflectionMethod, getModifiers) { reflection_object *intern; @@ -3959,7 +3959,7 @@ ZEND_METHOD(ReflectionClassConstant, isFinal) _class_constant_check_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, ZEND_ACC_FINAL); } -/* {{{ Returns a bitfield of the access modifiers for this constant */ +/* {{{ Returns a bitfield of the modifiers for this constant */ ZEND_METHOD(ReflectionClassConstant, getModifiers) { reflection_object *intern; @@ -4965,7 +4965,7 @@ ZEND_METHOD(ReflectionClass, isAbstract) } /* }}} */ -/* {{{ Returns a bitfield of the access modifiers for this class */ +/* {{{ Returns a bitfield of the modifiers for this class */ ZEND_METHOD(ReflectionClass, getModifiers) { reflection_object *intern; @@ -5865,7 +5865,7 @@ ZEND_METHOD(ReflectionProperty, isPromoted) } /* }}} */ -/* {{{ Returns a bitfield of the access modifiers for this property */ +/* {{{ Returns a bitfield of the modifiers for this property */ ZEND_METHOD(ReflectionProperty, getModifiers) { reflection_object *intern; From 6e1d9bcca105ef2d09fcdb7599c940a5c018892b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:57:27 -0700 Subject: [PATCH 08/33] Fix inline documentation of `ReflectionType::allowsNull()` --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index be0446e2302a..37bbc40f08df 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3058,7 +3058,7 @@ ZEND_METHOD(ReflectionParameter, isPromoted) } /* }}} */ -/* {{{ Returns whether parameter MAY be null */ +/* {{{ Returns whether the type MAY be null */ ZEND_METHOD(ReflectionType, allowsNull) { reflection_object *intern; From 1dd00fe3ae562309b7f7f6ac10efb7c83fe82c8d Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 10:58:33 -0700 Subject: [PATCH 09/33] Fix inline documentation of `ReflectionMethod::getClosure()` --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 37bbc40f08df..5d5c8eaeb038 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3364,7 +3364,7 @@ ZEND_METHOD(ReflectionMethod, __toString) } /* }}} */ -/* {{{ Invokes the function */ +/* {{{ Returns a dynamically created closure for the function */ ZEND_METHOD(ReflectionMethod, getClosure) { reflection_object *intern; From 4f905f7b78bd4492d4d996537fa06c4943e07cf5 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 11:03:32 -0700 Subject: [PATCH 10/33] Avoid unnecessary concatenation in `zend_named_reflection_type_to_string()` When a type corresponds to an iterator that may be null, the string representation is known ahead of time to be "?iterable". Use `ZSTR_INIT_LITERAL()` rather than building up a string with `zend_string_concat2()` from a "?" and the "iterable" known string. --- ext/reflection/php_reflection.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 5d5c8eaeb038..8d409a127b81 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3074,11 +3074,10 @@ ZEND_METHOD(ReflectionType, allowsNull) /* For BC with iterable for named types */ static zend_string *zend_named_reflection_type_to_string(zend_type type) { if (ZEND_TYPE_IS_ITERABLE_FALLBACK(type)) { - zend_string *iterable = ZSTR_KNOWN(ZEND_STR_ITERABLE); if (ZEND_TYPE_FULL_MASK(type) & MAY_BE_NULL) { - return zend_string_concat2("?", strlen("?"), ZSTR_VAL(iterable), ZSTR_LEN(iterable)); + return ZSTR_INIT_LITERAL("?iterable", false); } - return iterable; + return ZSTR_KNOWN(ZEND_STR_ITERABLE); } return zend_type_to_string(type); } From 6db4eee2ecbb513501bc725a7867842c55b6473c Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 11:25:32 -0700 Subject: [PATCH 11/33] Reflection: use `true` and `false` for booleans rather than `0`/`1` Update local variables and arguments to * `zend_read_static_property_ex()` * `zend_string_alloc()` * `zend_string_init()` * `zend_string_release_ex()` * `zend_verify_property_type()` * `zend_verify_ref_assignable_zval()` * Object handlers' get_closure() function * `RETURN_ZVAL` (macro) * `ZEND_TYPE_INIT` (macro) * `ZEND_TYPE_INIT_CODE` (macro) --- ext/reflection/php_reflection.c | 52 ++++++++++++++++----------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 8d409a127b81..07a97fe55ad5 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -212,7 +212,7 @@ static void _free_function(zend_function *fptr) /* {{{ */ if (fptr && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_string_release_ex(fptr->internal_function.function_name, 0); + zend_string_release_ex(fptr->internal_function.function_name, false); zend_free_trampoline(fptr); } } @@ -220,7 +220,7 @@ static void _free_function(zend_function *fptr) /* {{{ */ static void reflection_free_property_reference(property_reference *reference) { - zend_string_release_ex(reference->unmangled_name, 0); + zend_string_release_ex(reference->unmangled_name, false); efree(reference); } @@ -567,7 +567,7 @@ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const smart_str_append_printf(str, "%s }\n", indent); smart_str_append_printf(str, "%s}\n", indent); - zend_string_release_ex(sub_indent, 0); + zend_string_release_ex(sub_indent, false); } /* }}} */ @@ -1227,7 +1227,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c smart_str_append_printf(str, "%s }\n", indent); } smart_str_free(&str_classes); - zend_string_release_ex(sub_indent, 0); + zend_string_release_ex(sub_indent, false); } smart_str_append_printf(str, "%s}\n", indent); @@ -1587,7 +1587,7 @@ static void reflection_property_factory(zend_class_entry *ce, zend_string *name, static void reflection_property_factory_str(zend_class_entry *ce, const char *name_str, size_t name_len, zend_property_info *prop, zval *object) { - zend_string *name = zend_string_init(name_str, name_len, 0); + zend_string *name = zend_string_init(name_str, name_len, false); reflection_property_factory(ce, name, prop, object); zend_string_release(name); } @@ -1847,7 +1847,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass) zend_function *closure_func; zend_object *object; if (Z_OBJ_HANDLER(intern->obj, get_closure) - && Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, 1) == SUCCESS + && Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, true) == SUCCESS && closure_func && (called_scope || closure_func->common.scope)) { zend_reflection_class_factory(called_scope ? (zend_class_entry *) called_scope : closure_func->common.scope, return_value); } @@ -2107,7 +2107,7 @@ ZEND_METHOD(ReflectionFunction, invoke) if (!Z_ISUNDEF(intern->obj)) { Z_OBJ_HT(intern->obj)->get_closure( - Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, 0); + Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false); } zend_call_known_fcc(&fcc, &retval, num_args, params, named_params); @@ -2140,7 +2140,7 @@ ZEND_METHOD(ReflectionFunction, invokeArgs) if (!Z_ISUNDEF(intern->obj)) { Z_OBJ_HT(intern->obj)->get_closure( - Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, 0); + Z_OBJ(intern->obj), &fcc.called_scope, &fcc.function_handler, &fcc.object, false); } zend_call_known_fcc(&fcc, &retval, /* num_params */ 0, /* params */ NULL, params); @@ -2460,7 +2460,7 @@ ZEND_METHOD(ReflectionParameter, __construct) struct _zend_arg_info *arg_info; uint32_t num_args; zend_class_entry *ce = NULL; - bool is_closure = 0; + bool is_closure = false; ZEND_PARSE_PARAMETERS_START(2, 2) Z_PARAM_ZVAL(reference) @@ -2554,7 +2554,7 @@ ZEND_METHOD(ReflectionParameter, __construct) if (instanceof_function(ce, zend_ce_closure)) { fptr = (zend_function *)zend_get_closure_method_def(Z_OBJ_P(reference)); Z_ADDREF_P(reference); - is_closure = 1; + is_closure = true; } else if ((fptr = zend_hash_find_ptr(&ce->function_table, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) == NULL) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Method %s::%s() does not exist", ZSTR_VAL(ce->name), ZEND_INVOKE_FUNC_NAME); @@ -2628,7 +2628,7 @@ ZEND_METHOD(ReflectionParameter, __construct) failure: if (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { - zend_string_release_ex(fptr->common.function_name, 0); + zend_string_release_ex(fptr->common.function_name, false); zend_free_trampoline(fptr); } if (is_closure) { @@ -3168,7 +3168,7 @@ ZEND_METHOD(ReflectionUnionType, getTypes) } ZEND_TYPE_LIST_FOREACH_END(); } else if (ZEND_TYPE_HAS_NAME(param->type)) { zend_string *name = ZEND_TYPE_NAME(param->type); - append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, 0, 0)); + append_type(return_value, (zend_type) ZEND_TYPE_INIT_CLASS(name, false, 0)); } type_mask = ZEND_TYPE_PURE_MASK(param->type); @@ -3290,7 +3290,7 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_ } tmp_len = tmp - name; - class_name = zend_string_init(name, tmp_len, 0); + class_name = zend_string_init(name, tmp_len, false); method_name = tmp + 2; method_name_len = ZSTR_LEN(arg1_str) - tmp_len - 2; } @@ -4266,12 +4266,12 @@ ZEND_METHOD(ReflectionClass, setStaticPropertyValue) zend_reference *ref = Z_REF_P(variable_ptr); variable_ptr = Z_REFVAL_P(variable_ptr); - if (!zend_verify_ref_assignable_zval(ref, value, 0)) { + if (!zend_verify_ref_assignable_zval(ref, value, false)) { return; } } - if (ZEND_TYPE_IS_SET(prop_info->type) && !zend_verify_property_type(prop_info, value, 0)) { + if (ZEND_TYPE_IS_SET(prop_info->type) && !zend_verify_property_type(prop_info, value, false)) { return; } @@ -4636,7 +4636,7 @@ ZEND_METHOD(ReflectionClass, getProperty) str_name = ZSTR_VAL(name); if ((tmp = strstr(ZSTR_VAL(name), "::")) != NULL) { classname_len = tmp - ZSTR_VAL(name); - classname = zend_string_init(ZSTR_VAL(name), classname_len, 0); + classname = zend_string_init(ZSTR_VAL(name), classname_len, false); str_name_len = ZSTR_LEN(name) - (classname_len + 2); str_name = tmp + 2; @@ -4645,10 +4645,10 @@ ZEND_METHOD(ReflectionClass, getProperty) if (!EG(exception)) { zend_throw_exception_ex(reflection_exception_ptr, -1, "Class \"%s\" does not exist", ZSTR_VAL(classname)); } - zend_string_release_ex(classname, 0); + zend_string_release_ex(classname, false); RETURN_THROWS(); } - zend_string_release_ex(classname, 0); + zend_string_release_ex(classname, false); 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)); @@ -5299,7 +5299,7 @@ ZEND_METHOD(ReflectionClass, getLazyInitializer) RETURN_NULL(); } - RETURN_ZVAL(zend_lazy_object_get_initializer_zv(object), 1, 0); + RETURN_ZVAL(zend_lazy_object_get_initializer_zv(object), true, false); } /* }}} */ @@ -5437,11 +5437,11 @@ ZEND_METHOD(ReflectionClass, getTraitAliases) break; } } - zend_string_release_ex(lcname, 0); + zend_string_release_ex(lcname, false); ZEND_ASSERT(class_name != NULL); } - mname = zend_string_alloc(ZSTR_LEN(class_name) + ZSTR_LEN(cur_ref->method_name) + 2, 0); + mname = zend_string_alloc(ZSTR_LEN(class_name) + ZSTR_LEN(cur_ref->method_name) + 2, false); snprintf(ZSTR_VAL(mname), ZSTR_LEN(mname) + 1, "%s::%s", ZSTR_VAL(class_name), ZSTR_VAL(cur_ref->method_name)); add_assoc_str_ex(return_value, ZSTR_VAL(ce->trait_aliases[i]->alias), ZSTR_LEN(ce->trait_aliases[i]->alias), mname); } @@ -5894,7 +5894,7 @@ ZEND_METHOD(ReflectionProperty, getValue) GET_REFLECTION_OBJECT_PTR(ref); if (prop_get_flags(ref) & ZEND_ACC_STATIC) { - member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, 0); + member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, false); if (member_p) { RETURN_COPY_DEREF(member_p); } @@ -6304,7 +6304,7 @@ ZEND_METHOD(ReflectionProperty, isInitialized) GET_REFLECTION_OBJECT_PTR(ref); if (prop_get_flags(ref) & ZEND_ACC_STATIC) { - const zval *member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, 1); + const zval *member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, true); if (member_p) { RETURN_BOOL(!Z_ISUNDEF_P(member_p)); } @@ -6437,7 +6437,7 @@ ZEND_METHOD(ReflectionProperty, getSettableType) /* Get-only virtual property can never be written to. */ if (prop->hooks && (prop->flags & ZEND_ACC_VIRTUAL) && !prop->hooks[ZEND_PROPERTY_HOOK_SET]) { - zend_type never_type = ZEND_TYPE_INIT_CODE(IS_NEVER, 0, 0); + zend_type never_type = ZEND_TYPE_INIT_CODE(IS_NEVER, false, 0); reflection_type_factory(never_type, return_value, true); return; } @@ -7142,7 +7142,7 @@ ZEND_METHOD(ReflectionExtension, getDependencies) len += strlen(dep->version) + 1; } - relation = zend_string_alloc(len, 0); + relation = zend_string_alloc(len, false); snprintf(ZSTR_VAL(relation), ZSTR_LEN(relation) + 1, "%s%s%s%s%s", rel_type, dep->rel ? " " : "", @@ -7728,7 +7728,7 @@ ZEND_METHOD(ReflectionEnum, getBackingType) if (ce->enum_backing_type == IS_UNDEF) { RETURN_NULL(); } else { - zend_type type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, 0, 0); + zend_type type = ZEND_TYPE_INIT_CODE(ce->enum_backing_type, false, 0); reflection_type_factory(type, return_value, false); } } From c7bb8988cfe2d122b766b08e8e6afc6dfd6734ac Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 11:33:23 -0700 Subject: [PATCH 12/33] Reflection: convert some integers used for conditions to booleans The previous commit addressed places where `0` or `1` where used in places where a boolean was already expected, such a function arguments declared to be booleans. Now, address some inner implementation details of the reflection extension that use integers for conditions: * `dynam_prop` local variable in `ReflectionProperty::__construct()` * `first` local variable in `_extension_string()` * `is_object` parameter to `reflection_class_object_ctor()` * `variadic` parameter to `reflection_method_invoke()` --- ext/reflection/php_reflection.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 07a97fe55ad5..bcc0fc39d98a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1194,14 +1194,14 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c { zend_function *fptr; - int first = 1; + bool first = true; ZEND_HASH_MAP_FOREACH_PTR(CG(function_table), fptr) { if (fptr->common.type==ZEND_INTERNAL_FUNCTION && fptr->internal_function.module == module) { if (first) { smart_str_appends(str, "\n - Functions {\n"); - first = 0; + first = false; } _function_string(str, fptr, NULL, " "); } @@ -3402,7 +3402,7 @@ ZEND_METHOD(ReflectionMethod, getClosure) /* }}} */ /* {{{ reflection_method_invoke */ -static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic) +static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, bool variadic) { zval retval; zval *params = NULL, *object; @@ -3503,14 +3503,14 @@ static void reflection_method_invoke(INTERNAL_FUNCTION_PARAMETERS, int variadic) /* {{{ Invokes the method. */ ZEND_METHOD(ReflectionMethod, invoke) { - reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } /* }}} */ /* {{{ Invokes the function and pass its arguments as array. */ ZEND_METHOD(ReflectionMethod, invokeArgs) { - reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + reflection_method_invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } /* }}} */ @@ -4053,7 +4053,7 @@ ZEND_METHOD(ReflectionClassConstant, isDeprecated) } /* {{{ reflection_class_object_ctor */ -static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_object) +static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, bool is_object) { zval *object; zend_string *arg_class = NULL; @@ -4100,7 +4100,7 @@ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_ob /* {{{ Constructor. Takes a string or an instance as an argument */ ZEND_METHOD(ReflectionClass, __construct) { - reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); } /* }}} */ @@ -5652,7 +5652,7 @@ ZEND_METHOD(ReflectionClass, getShortName) /* {{{ Constructor. Takes an instance as an argument */ ZEND_METHOD(ReflectionObject, __construct) { - reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1); + reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, true); } /* }}} */ @@ -5662,7 +5662,7 @@ ZEND_METHOD(ReflectionProperty, __construct) zend_string *classname_str; zend_object *classname_obj; zend_string *name; - int dynam_prop = 0; + bool dynam_prop = false; zval *object; reflection_object *intern; zend_class_entry *ce; @@ -5693,10 +5693,10 @@ ZEND_METHOD(ReflectionProperty, __construct) /* Check for dynamic properties */ if (property_info == NULL && classname_obj) { if (zend_hash_exists(classname_obj->handlers->get_properties(classname_obj), name)) { - dynam_prop = 1; + dynam_prop = true; } } - if (dynam_prop == 0) { + if (!dynam_prop) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Property %s::$%s does not exist", ZSTR_VAL(ce->name), ZSTR_VAL(name)); RETURN_THROWS(); } @@ -5706,7 +5706,7 @@ ZEND_METHOD(ReflectionProperty, __construct) zval_ptr_dtor(prop_name); ZVAL_STR_COPY(prop_name, name); /* Note: class name are always interned, no need to destroy them */ - if (dynam_prop == 0) { + if (!dynam_prop) { ZVAL_STR_COPY(reflection_prop_class(object), property_info->ce->name); } else { ZVAL_STR_COPY(reflection_prop_class(object), ce->name); @@ -7624,7 +7624,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance) ZEND_METHOD(ReflectionEnum, __construct) { - reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + reflection_class_object_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU, false); if (EG(exception)) { RETURN_THROWS(); } From 24d68339c80051e34568b7b2955d95111a79162b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 11:44:10 -0700 Subject: [PATCH 13/33] Reflection: remove unneeded indentation support from `_extension_string()` The only caller (`ReflectionExtension::__toString()`) always provided an empty string as the indentation; removing the indentation entirely simplifies the building of the string representation, including replacing some uses of `smart_str_append_printf()` with `smart_str_appends()`, which is more performant. The indentation levels passed to `_extension_ini_string()` and `_extension_class_string()` from within `_extension_string()` are kept for now and will be addressed in follow-up commits. --- ext/reflection/php_reflection.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index bcc0fc39d98a..28e16602a77d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -300,7 +300,7 @@ static void _property_string(smart_str *str, const zend_property_info *prop, con static void _class_const_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent); static void _enum_case_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); +static void _extension_string(smart_str *str, const zend_module_entry *module); static void _zend_extension_string(smart_str *str, const zend_extension *extension, const char *indent); /* {{{ _class_string */ @@ -1110,9 +1110,9 @@ static void _extension_class_string(zend_class_entry *ce, zend_string *key, smar } /* }}} */ -static void _extension_string(smart_str *str, const zend_module_entry *module, const char *indent) /* {{{ */ +static void _extension_string(smart_str *str, const zend_module_entry *module) /* {{{ */ { - smart_str_append_printf(str, "%sExtension [ ", indent); + smart_str_appends(str, "Extension [ "); if (module->type == MODULE_PERSISTENT) { smart_str_appends(str, ""); } @@ -1129,7 +1129,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c smart_str_appends(str, "\n - Dependencies {\n"); while(dep->name) { - smart_str_append_printf(str, "%s Dependency [ %s (", indent, dep->name); + smart_str_append_printf(str, " Dependency [ %s (", dep->name); switch(dep->type) { case MODULE_DEP_REQUIRED: @@ -1155,19 +1155,19 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c smart_str_appends(str, ") ]\n"); dep++; } - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } { smart_str str_ini = {0}; zend_ini_entry *ini_entry; ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), ini_entry) { - _extension_ini_string(ini_entry, &str_ini, indent, module->module_number); + _extension_ini_string(ini_entry, &str_ini, "", module->module_number); } ZEND_HASH_FOREACH_END(); if (smart_str_get_len(&str_ini) > 0) { smart_str_appends(str, "\n - INI {\n"); smart_str_append_smart_str(str, &str_ini); - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } smart_str_free(&str_ini); } @@ -1187,7 +1187,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c if (num_constants) { smart_str_append_printf(str, "\n - Constants [%d] {\n", num_constants); smart_str_append_smart_str(str, &str_constants); - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } smart_str_free(&str_constants); } @@ -1207,30 +1207,28 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c } } ZEND_HASH_FOREACH_END(); if (!first) { - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } } { - zend_string *sub_indent = strpprintf(0, "%s ", indent); smart_str str_classes = {0}; zend_string *key; zend_class_entry *ce; int num_classes = 0; ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(class_table), key, ce) { - _extension_class_string(ce, key, &str_classes, ZSTR_VAL(sub_indent), module, &num_classes); + _extension_class_string(ce, key, &str_classes, " ", module, &num_classes); } ZEND_HASH_FOREACH_END(); if (num_classes) { smart_str_append_printf(str, "\n - Classes [%d] {", num_classes); smart_str_append_smart_str(str, &str_classes); - smart_str_append_printf(str, "%s }\n", indent); + smart_str_appends(str, " }\n"); } smart_str_free(&str_classes); - zend_string_release_ex(sub_indent, false); } - smart_str_append_printf(str, "%s}\n", indent); + smart_str_appends(str, "}\n"); } /* }}} */ @@ -6921,7 +6919,7 @@ ZEND_METHOD(ReflectionExtension, __toString) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(module); - _extension_string(&str, module, ""); + _extension_string(&str, module); RETURN_STR(smart_str_extract(&str)); } /* }}} */ From 00db5d3ad7cf222e5f523f6f83b5defb3df5feac Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 12:11:58 -0700 Subject: [PATCH 14/33] Reflection: remove unneeded indentation support from `_extension_ini_string()` The only caller (`_extension_string()`) always provided `_extension_ini_string()` with the same indentation that it was called with; after the prior commit removed indentation support from `_extension_string()` it became clear that the INI string function also always received an empty string as the indentation. Removing the indentation entirely simplifies the building of the string representation, including replacing a use of `smart_str_append_printf()` with `smart_str_appends()`, which is more performant. --- ext/reflection/php_reflection.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 28e16602a77d..47c5311e2465 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1065,12 +1065,12 @@ static void _property_string(smart_str *str, const zend_property_info *prop, con } /* }}} */ -static void _extension_ini_string(const zend_ini_entry *ini_entry, smart_str *str, const char *indent, int number) /* {{{ */ +static void _extension_ini_string(const zend_ini_entry *ini_entry, smart_str *str, int number) /* {{{ */ { char *comma = ""; if (number == ini_entry->module_number) { - smart_str_append_printf(str, " %sEntry [ %s <", indent, ZSTR_VAL(ini_entry->name)); + smart_str_append_printf(str, " Entry [ %s <", ZSTR_VAL(ini_entry->name)); if (ini_entry->modifiable == ZEND_INI_ALL) { smart_str_appends(str, "ALL"); } else { @@ -1088,11 +1088,11 @@ 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) : ""); + smart_str_append_printf(str, " Current = '%s'\n", ini_entry->value ? ZSTR_VAL(ini_entry->value) : ""); if (ini_entry->modified) { - smart_str_append_printf(str, " %s Default = '%s'\n", indent, ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : ""); + smart_str_append_printf(str, " Default = '%s'\n", ini_entry->orig_value ? ZSTR_VAL(ini_entry->orig_value) : ""); } - smart_str_append_printf(str, " %s}\n", indent); + smart_str_appends(str, " }\n"); } } /* }}} */ @@ -1162,7 +1162,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module) / smart_str str_ini = {0}; zend_ini_entry *ini_entry; ZEND_HASH_MAP_FOREACH_PTR(EG(ini_directives), ini_entry) { - _extension_ini_string(ini_entry, &str_ini, "", module->module_number); + _extension_ini_string(ini_entry, &str_ini, module->module_number); } ZEND_HASH_FOREACH_END(); if (smart_str_get_len(&str_ini) > 0) { smart_str_appends(str, "\n - INI {\n"); From fc4390043ebeae7b6ee89d493222e73ead833b5b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 12:15:29 -0700 Subject: [PATCH 15/33] Reflection: remove unneeded indentation support from `_zend_extension_string()` The only caller (`ReflectionZendExtension::__toString()`) always provided an empty string as the indentation; removing the indentation slightly simplifies the building of the string representation. --- ext/reflection/php_reflection.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 47c5311e2465..2c7690d1fe6f 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -301,7 +301,7 @@ static void _class_const_string(smart_str *str, const zend_string *name, zend_cl static void _enum_case_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); -static void _zend_extension_string(smart_str *str, const zend_extension *extension, const char *indent); +static void _zend_extension_string(smart_str *str, const zend_extension *extension); /* {{{ _class_string */ static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent) @@ -1347,9 +1347,9 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut } /* }}} */ -static void _zend_extension_string(smart_str *str, const zend_extension *extension, const char *indent) /* {{{ */ +static void _zend_extension_string(smart_str *str, const zend_extension *extension) /* {{{ */ { - smart_str_append_printf(str, "%sZend Extension [ %s ", indent, extension->name); + smart_str_append_printf(str, "Zend Extension [ %s ", extension->name); if (extension->version) { smart_str_append_printf(str, "%s ", extension->version); @@ -7230,7 +7230,7 @@ ZEND_METHOD(ReflectionZendExtension, __toString) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(extension); - _zend_extension_string(&str, extension, ""); + _zend_extension_string(&str, extension); RETURN_STR(smart_str_extract(&str)); } /* }}} */ From 8399a447ba280298a8a978f8cadada5c6a8c7801 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 12:45:04 -0700 Subject: [PATCH 16/33] Reflection: access common function fields through `zend_function.common` The `zend_function` union has both an `op_array` member for userland code, and an `internal_function` member for internal code. The elements at the start of each match, and are also made available under the `common` member which only has the fields present in both `op_array` and `internal_function`. For function pointers that have not been checked to determine if they are userland functions or internal functions, access common fields through `common`. While technically there shouldn't be a difference due to the common layout (up through the relevant fields), semantically accessing information about an internal function through `op_array` or about a userland function through `internal_function` does not make sense. --- ext/reflection/php_reflection.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 2c7690d1fe6f..0a670c856eb2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -193,12 +193,12 @@ static inline bool is_closure_invoke(const zend_class_entry *ce, const zend_stri static zend_function *_copy_function(zend_function *fptr) /* {{{ */ { if (fptr - && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) + && (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { zend_function *copy_fptr; copy_fptr = emalloc(sizeof(zend_function)); memcpy(copy_fptr, fptr, sizeof(zend_function)); - copy_fptr->internal_function.function_name = zend_string_copy(fptr->internal_function.function_name); + copy_fptr->common.function_name = zend_string_copy(fptr->common.function_name); return copy_fptr; } else { /* no copy needed */ @@ -210,9 +210,9 @@ static zend_function *_copy_function(zend_function *fptr) /* {{{ */ static void _free_function(zend_function *fptr) /* {{{ */ { if (fptr - && (fptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) + && (fptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) { - zend_string_release_ex(fptr->internal_function.function_name, false); + zend_string_release_ex(fptr->common.function_name, false); zend_free_trampoline(fptr); } } @@ -930,7 +930,7 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze smart_str_appends(str, "function "); } - if (fptr->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) { + if (fptr->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) { smart_str_appendc(str, '&'); } smart_str_append_printf(str, "%s ] {\n", ZSTR_VAL(fptr->common.function_name)); @@ -948,7 +948,7 @@ static void _function_string(smart_str *str, const zend_function *fptr, const ze } _function_parameter_string(str, fptr, ZSTR_VAL(param_indent.s)); smart_str_free(¶m_indent); - if ((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { + if ((fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) { smart_str_append_printf(str, " %s- %s [ ", indent, ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1]) ? "Tentative return" : "Return"); if (ZEND_TYPE_IS_SET(fptr->common.arg_info[-1].type)) { zend_string *type_str = zend_type_to_string(fptr->common.arg_info[-1].type); @@ -2160,7 +2160,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, returnsReference) GET_REFLECTION_OBJECT_PTR(fptr); - RETURN_BOOL((fptr->op_array.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0); + RETURN_BOOL((fptr->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) != 0); } /* }}} */ @@ -2392,8 +2392,8 @@ ZEND_METHOD(ReflectionGenerator, getFunction) zval closure; ZVAL_OBJ(&closure, ZEND_CLOSURE_OBJECT(func)); reflection_function_factory(func, &closure, return_value); - } else if (func->op_array.scope) { - reflection_method_factory(func->op_array.scope, func, NULL, return_value); + } else if (func->common.scope) { + reflection_method_factory(func->common.scope, func, NULL, return_value); } else { reflection_function_factory(func, NULL, return_value); } @@ -3650,7 +3650,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, hasReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - RETVAL_BOOL((fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); + RETVAL_BOOL((fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) && !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); } /* }}} */ @@ -3664,7 +3664,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { + if (!(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { RETURN_NULL(); } @@ -3682,7 +3682,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - RETVAL_BOOL(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); + RETVAL_BOOL(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])); } /* }}} */ @@ -3696,7 +3696,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType) GET_REFLECTION_OBJECT_PTR(fptr); - if (!(fptr->op_array.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { + if (!(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || !ZEND_ARG_TYPE_IS_TENTATIVE(&fptr->common.arg_info[-1])) { RETURN_NULL(); } From ac61d9780bcfca68ce3ab798dd094d6cdeee4d97 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 12:52:00 -0700 Subject: [PATCH 17/33] `ReflectionNamedType::getName()`: inline logic for legacy behavior Instead of a single-use two-line helper function `zend_type_to_string_without_null()`, move the relevant logic into `ReflectionNamedType::getName()` directly. To maintain the behavior of removing the `MAY_BE_NULL` flag from a *copy* of the stored type information, make an explicit copy of the type before the flag is removed. --- ext/reflection/php_reflection.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0a670c856eb2..271a4aae6e0b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3080,11 +3080,6 @@ static zend_string *zend_named_reflection_type_to_string(zend_type type) { return zend_type_to_string(type); } -static zend_string *zend_type_to_string_without_null(zend_type type) { - ZEND_TYPE_FULL_MASK(type) &= ~MAY_BE_NULL; - return zend_named_reflection_type_to_string(type); -} - /* {{{ Return the text of the type hint */ ZEND_METHOD(ReflectionType, __toString) { @@ -3107,10 +3102,12 @@ ZEND_METHOD(ReflectionNamedType, getName) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(param); + // Make a copy so that we don't modify the stored type information + zend_type type = param->type; if (param->legacy_behavior) { - RETURN_STR(zend_type_to_string_without_null(param->type)); + ZEND_TYPE_FULL_MASK(type) &= ~MAY_BE_NULL; } - RETURN_STR(zend_named_reflection_type_to_string(param->type)); + RETURN_STR(zend_named_reflection_type_to_string(type)); } /* }}} */ From b133e1fe598157faac35a6cd3934529688b36821 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:10:34 -0700 Subject: [PATCH 18/33] `ReflectionFunction::__toString()`: stop accessing `intern->ce` For global functions there is no class entry set, the constructor a few lines above explicitly sets `intern->ce = NULL;`, as does `reflection_function_factory()`. Replace the access with `NULL` to avoid suggesting that there might be a class entry to use. --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 271a4aae6e0b..a336ab5b8854 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1767,7 +1767,7 @@ ZEND_METHOD(ReflectionFunction, __toString) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(fptr); - _function_string(&str, fptr, intern->ce, ""); + _function_string(&str, fptr, NULL, ""); RETURN_STR(smart_str_extract(&str)); } /* }}} */ From be1d337ce530179edf4c82be74ab822e33d7606c Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:32:09 -0700 Subject: [PATCH 19/33] `ReflectionFunctionAbstract::getClosureUsedVariables()`: optimize arrays Avoid `array_init(return_value);` in cases where the function does not correspond to a closure or has no static variables, to avoid an unneeded allocation for an array that will be empty. --- ext/reflection/php_reflection.c | 61 +++++++++++++++++---------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a336ab5b8854..69ce3ae8a93d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1862,44 +1862,47 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureUsedVariables) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT(); - array_init(return_value); - if (!Z_ISUNDEF(intern->obj)) { - closure_func = zend_get_closure_method_def(Z_OBJ(intern->obj)); - if (closure_func == NULL || - closure_func->type != ZEND_USER_FUNCTION || - closure_func->op_array.static_variables == NULL) { - return; - } + if (Z_ISUNDEF(intern->obj)) { + RETURN_EMPTY_ARRAY(); + } - const zend_op_array *ops = &closure_func->op_array; + closure_func = zend_get_closure_method_def(Z_OBJ(intern->obj)); + if (closure_func == NULL || + closure_func->type != ZEND_USER_FUNCTION || + closure_func->op_array.static_variables == NULL + ) { + RETURN_EMPTY_ARRAY(); + } - HashTable *static_variables = ZEND_MAP_PTR_GET(ops->static_variables_ptr); + const zend_op_array *ops = &closure_func->op_array; - if (!static_variables) { - return; - } + HashTable *static_variables = ZEND_MAP_PTR_GET(ops->static_variables_ptr); - zend_op *opline = ops->opcodes + ops->num_args; - if (ops->fn_flags & ZEND_ACC_VARIADIC) { - opline++; - } + if (!static_variables) { + RETURN_EMPTY_ARRAY(); + } - for (; opline->opcode == ZEND_BIND_STATIC; opline++) { - if (!(opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) { - continue; - } + array_init(return_value); + zend_op *opline = ops->opcodes + ops->num_args; + if (ops->fn_flags & ZEND_ACC_VARIADIC) { + opline++; + } - Bucket *bucket = (Bucket*) - (((char*)static_variables->arData) + - (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))); + for (; opline->opcode == ZEND_BIND_STATIC; opline++) { + if (!(opline->extended_value & (ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))) { + continue; + } - if (Z_ISUNDEF(bucket->val)) { - continue; - } + Bucket *bucket = (Bucket*) + (((char*)static_variables->arData) + + (opline->extended_value & ~(ZEND_BIND_REF|ZEND_BIND_IMPLICIT|ZEND_BIND_EXPLICIT))); - zend_hash_add_new(Z_ARRVAL_P(return_value), bucket->key, &bucket->val); - Z_TRY_ADDREF(bucket->val); + if (Z_ISUNDEF(bucket->val)) { + continue; } + + zend_hash_add_new(Z_ARRVAL_P(return_value), bucket->key, &bucket->val); + Z_TRY_ADDREF(bucket->val); } } /* }}} */ From f5813be003fec167da4e49615d012023bed30451 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:46:16 -0700 Subject: [PATCH 20/33] `ReflectionClass::getStaticProperties()`: optimize array initialization For classes with no static properties, avoid allocating an array that will be empty. Includes a regression test for the function since it was previously untested, to confirm that even when a class declares no static properties of its own, inherited static properties are still returned. --- ext/reflection/php_reflection.c | 12 +++- .../ReflectionClass_getStaticProperties.phpt | 57 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 ext/reflection/tests/ReflectionClass_getStaticProperties.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 69ce3ae8a93d..3abd95731ca3 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4159,7 +4159,17 @@ ZEND_METHOD(ReflectionClass, getStaticProperties) RETURN_THROWS(); } - if (ce->default_static_members_count && !CE_STATIC_MEMBERS(ce)) { + // default_static_members_count includes inherited static members, see + // zend_do_inheritance_ex(). + // PHP does not support *dynamic* static properties the same way non-static + // properties can be dynamic (i.e. present on an object without having been + // declared on the class). Thus, default_static_members_count will always + // reflect the count of all static members that a class might have. + if (ce->default_static_members_count == 0) { + RETURN_EMPTY_ARRAY(); + } + + if (!CE_STATIC_MEMBERS(ce)) { zend_class_init_statics(ce); } diff --git a/ext/reflection/tests/ReflectionClass_getStaticProperties.phpt b/ext/reflection/tests/ReflectionClass_getStaticProperties.phpt new file mode 100644 index 000000000000..89d894f83989 --- /dev/null +++ b/ext/reflection/tests/ReflectionClass_getStaticProperties.phpt @@ -0,0 +1,57 @@ +--TEST-- +ReflectionClass::getStaticProperties() +--FILE-- +getStaticProperties()); +var_dump($child->getStaticProperties()); + +echo "\nTyped properties initialized\n:"; +Base::$pubTyped = true; +$base->setStaticPropertyValue('privTyped', true); + +var_dump($base->getStaticProperties()); +var_dump($child->getStaticProperties()); +?> +--EXPECT-- +Start: +array(2) { + ["pub"]=> + NULL + ["priv"]=> + NULL +} +array(1) { + ["pub"]=> + NULL +} + +Typed properties initialized +:array(4) { + ["pub"]=> + NULL + ["priv"]=> + NULL + ["pubTyped"]=> + bool(true) + ["privTyped"]=> + bool(true) +} +array(2) { + ["pub"]=> + NULL + ["pubTyped"]=> + bool(true) +} From 48c6e8e304c29a014466f256f14da30851bda767 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:49:54 -0700 Subject: [PATCH 21/33] Fix inline documentation of tentative return methods Both `ReflectionFunctionAbstract::hasTentativeReturnType()` and `ReflectionFunctionAbstract::getTentativeReturnType()` were documented as checking or retrieving the "return type" of a function rather than the "tentative return type". The two are different; update the documentation accordingly. --- ext/reflection/php_reflection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 3abd95731ca3..7a24f084045c 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3672,7 +3672,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getReturnType) } /* }}} */ -/* {{{ Return whether the function has a return type */ +/* {{{ Return whether the function has a tentative return type */ ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType) { reflection_object *intern; @@ -3686,7 +3686,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, hasTentativeReturnType) } /* }}} */ -/* {{{ Returns the return type associated with the function */ +/* {{{ Returns the tentative return type associated with the function */ ZEND_METHOD(ReflectionFunctionAbstract, getTentativeReturnType) { reflection_object *intern; From 8d63d5954addbe843e909424eee729c890b0bbd5 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 13:51:11 -0700 Subject: [PATCH 22/33] `ReflectionFunctionAbstract::getClosureCalledClass()`: remove pointer cast `called_scope` was declared to be a pointer to a `zend_class_entry`, no cast is needed. --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 7a24f084045c..b120e8e4eb9a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1847,7 +1847,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureCalledClass) if (Z_OBJ_HANDLER(intern->obj, get_closure) && Z_OBJ_HANDLER(intern->obj, get_closure)(Z_OBJ(intern->obj), &called_scope, &closure_func, &object, true) == SUCCESS && closure_func && (called_scope || closure_func->common.scope)) { - zend_reflection_class_factory(called_scope ? (zend_class_entry *) called_scope : closure_func->common.scope, return_value); + zend_reflection_class_factory(called_scope ? called_scope : closure_func->common.scope, return_value); } } } From 2335a18b509254a6b0bc9b0a2bc37e380915490e Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:07:44 -0700 Subject: [PATCH 23/33] Reflection: remove `reflection_extension_factory()`, avoid extra lookups `reflection_extension_factory()` was used to initialize a `ReflectionExtension` instance based on the name of the extension. It would look for an extension with the given name in the global `module_registry` and, if found, delegate to `reflection_extension_factory_ex()` to do the actual initialization using the located `zend_module_entry` pointer. However, both callers of `reflection_extension_factory()` already had a `zend_module_entry` pointer! Replace both uses of `reflection_extension_factory()` with `reflection_extension_factory_ex()` using the available pointer, and remove the `reflection_extension_factory()` method. In a follow-up commit the _ex variant will be renamed to remove the suffix. --- ext/reflection/php_reflection.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index b120e8e4eb9a..a683989a487c 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1408,19 +1408,6 @@ static void reflection_extension_factory_ex(zval *object, zend_module_entry *mod } /* }}} */ -/* {{{ reflection_extension_factory */ -static void reflection_extension_factory(zval *object, const char *name_str) -{ - size_t name_len = strlen(name_str); - struct _zend_module_entry *module = zend_hash_str_find_ptr_lc(&module_registry, name_str, name_len); - if (!module) { - return; - } - - reflection_extension_factory_ex(object, module); -} -/* }}} */ - /* {{{ reflection_parameter_factory */ static void reflection_parameter_factory(zend_function *fptr, zval *closure_object, struct _zend_arg_info *arg_info, uint32_t offset, bool required, zval *object) { @@ -2259,7 +2246,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getExtension) internal = (zend_internal_function *)fptr; if (internal->module) { - reflection_extension_factory(return_value, internal->module->name); + reflection_extension_factory_ex(return_value, internal->module); } else { RETURN_NULL(); } @@ -5580,7 +5567,7 @@ ZEND_METHOD(ReflectionClass, getExtension) GET_REFLECTION_OBJECT_PTR(ce); if ((ce->type == ZEND_INTERNAL_CLASS) && ce->info.internal.module) { - reflection_extension_factory(return_value, ce->info.internal.module->name); + reflection_extension_factory_ex(return_value, ce->info.internal.module); } } /* }}} */ From d533f875fb914d281330a0eb4d6ecd22723ecf1f Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:10:33 -0700 Subject: [PATCH 24/33] Reflection: rename `reflection_extension_factory_ex()` After the original `reflection_extension_factory()` was removed in the prior commit, the _ex suffix is no longer needed. Remove it. --- ext/reflection/php_reflection.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index a683989a487c..4a8db77347e4 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1396,8 +1396,8 @@ PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object) } /* }}} */ -/* {{{ reflection_extension_factory_ex */ -static void reflection_extension_factory_ex(zval *object, zend_module_entry *module) +/* {{{ reflection_extension_factory */ +static void reflection_extension_factory(zval *object, zend_module_entry *module) { object_init_ex(object, reflection_extension_ptr); reflection_object *intern = Z_REFLECTION_P(object); @@ -2246,7 +2246,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getExtension) internal = (zend_internal_function *)fptr; if (internal->module) { - reflection_extension_factory_ex(return_value, internal->module); + reflection_extension_factory(return_value, internal->module); } else { RETURN_NULL(); } @@ -5567,7 +5567,7 @@ ZEND_METHOD(ReflectionClass, getExtension) GET_REFLECTION_OBJECT_PTR(ce); if ((ce->type == ZEND_INTERNAL_CLASS) && ce->info.internal.module) { - reflection_extension_factory_ex(return_value, ce->info.internal.module); + reflection_extension_factory(return_value, ce->info.internal.module); } } /* }}} */ @@ -8103,7 +8103,7 @@ static void reflection_constant_find_ext(INTERNAL_FUNCTION_PARAMETERS, bool only if (only_name) { RETURN_STRING(module->name); } - reflection_extension_factory_ex(return_value, module); + reflection_extension_factory(return_value, module); return; } ZEND_HASH_FOREACH_END(); From 841c9addfb6ebf514e4d8e626419ee91da0dcd71 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:13:48 -0700 Subject: [PATCH 25/33] Reflection: improve string release in `reflection_property_factory_str()` Use `zend_string_release_ex()` rather than the generic `zend_string_release()` since we know that the string is not marked as persistent (based on the `false` parameter to `zend_string_init()` two lines earlier). --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 4a8db77347e4..54f420b3cb05 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1574,7 +1574,7 @@ static void reflection_property_factory_str(zend_class_entry *ce, const char *na { zend_string *name = zend_string_init(name_str, name_len, false); reflection_property_factory(ce, name, prop, object); - zend_string_release(name); + zend_string_release_ex(name, false); } /* {{{ reflection_class_constant_factory */ From 2ad966fe06580a0a95c7a93c1109aed4e2d94c8b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:36:15 -0700 Subject: [PATCH 26/33] Simplify `ReflectionProperty::getSettableType()` property handling Once the `property_reference` has been dereferenced to identify the `zend_property_info`, no need to do so again since the result was already stored in a variable. --- ext/reflection/php_reflection.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 54f420b3cb05..7dc65d09cc24 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6426,7 +6426,7 @@ ZEND_METHOD(ReflectionProperty, getSettableType) const zend_property_info *prop = ref->prop; /* Dynamic property is untyped. */ - if (!ref->prop) { + if (!prop) { RETURN_NULL(); } @@ -6448,10 +6448,10 @@ ZEND_METHOD(ReflectionProperty, getSettableType) } /* Fall back to property type */ - if (!ZEND_TYPE_IS_SET(ref->prop->type)) { + if (!ZEND_TYPE_IS_SET(prop->type)) { RETURN_NULL(); } - reflection_type_factory(ref->prop->type, return_value, true); + reflection_type_factory(prop->type, return_value, true); } /* {{{ Returns whether property has a type */ From 230f4fa18a9b6aabfb69b86d9a4327dcc1cb8e51 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 14:38:07 -0700 Subject: [PATCH 27/33] Rename `ReflectionParameter_isDefault.phpt` to reflect actual contents This is testing `ReflectionProperty::isDefault()`, `ReflectionParameter::isDefault()` does not exist. --- ...rameter_isDefault.phpt => ReflectionProperty_isDefault.phpt} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename ext/reflection/tests/{ReflectionParameter_isDefault.phpt => ReflectionProperty_isDefault.phpt} (94%) diff --git a/ext/reflection/tests/ReflectionParameter_isDefault.phpt b/ext/reflection/tests/ReflectionProperty_isDefault.phpt similarity index 94% rename from ext/reflection/tests/ReflectionParameter_isDefault.phpt rename to ext/reflection/tests/ReflectionProperty_isDefault.phpt index 3ab0d980fd04..ebb55a6a0d94 100644 --- a/ext/reflection/tests/ReflectionParameter_isDefault.phpt +++ b/ext/reflection/tests/ReflectionProperty_isDefault.phpt @@ -1,5 +1,5 @@ --TEST-- -ReflectionParameter::isDefault() +ReflectionProperty::isDefault() --FILE-- Date: Thu, 2 Jul 2026 14:40:29 -0700 Subject: [PATCH 28/33] Update `ReflectionMethod_getClosureThis.phpt` to match actual contents The file name was correct, but the test name mentioned `Reflection::getClosureThis()` (which does not exist). Update the test name to say `ReflectionMethod::getClosureThis()`. --- ext/reflection/tests/ReflectionMethod_getClosureThis.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt b/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt index 656fde814d93..c9237e296427 100644 --- a/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt +++ b/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt @@ -1,5 +1,5 @@ --TEST-- -Reflection::getClosureThis() +ReflectionMethod::getClosureThis() --FILE-- Date: Thu, 2 Jul 2026 14:48:05 -0700 Subject: [PATCH 29/33] Fix inline comment in `ReflectionMethod::isConstructor()` "we we looking at" was presumably meant to be "we are looking at". --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 7dc65d09cc24..bcbbf65b110e 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3699,7 +3699,7 @@ ZEND_METHOD(ReflectionMethod, isConstructor) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(mptr); - /* we need to check if the ctor is the ctor of the class level we we + /* we need to check if the ctor is the ctor of the class level we are * looking at since we might be looking at an inherited old style ctor * defined in base class. */ RETURN_BOOL((mptr->common.fn_flags & ZEND_ACC_CTOR) && intern->ce->constructor && intern->ce->constructor->common.scope == mptr->common.scope); From 23bfb228a250788a2de21385b26fe760dd2dd457 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 15:06:55 -0700 Subject: [PATCH 30/33] Reflection: minor whitespace cleanup Consistently indent with tabs, have a space after the `while` keyword, and adjust the lines of a few braces. --- ext/reflection/php_reflection.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index bcbbf65b110e..93c14e0ec72b 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1128,7 +1128,7 @@ static void _extension_string(smart_str *str, const zend_module_entry *module) / smart_str_appends(str, "\n - Dependencies {\n"); - while(dep->name) { + while (dep->name) { smart_str_append_printf(str, " Dependency [ %s (", dep->name); switch(dep->type) { @@ -3751,13 +3751,13 @@ ZEND_METHOD(ReflectionMethod, getDeclaringClass) /* {{{ Returns whether a method has a prototype or not */ ZEND_METHOD(ReflectionMethod, hasPrototype) { - reflection_object *intern; - zend_function *mptr; + reflection_object *intern; + zend_function *mptr; - ZEND_PARSE_PARAMETERS_NONE(); + ZEND_PARSE_PARAMETERS_NONE(); - GET_REFLECTION_OBJECT_PTR(mptr); - RETURN_BOOL(mptr->common.prototype != NULL); + GET_REFLECTION_OBJECT_PTR(mptr); + RETURN_BOOL(mptr->common.prototype != NULL); } /* }}} */ @@ -7099,13 +7099,12 @@ ZEND_METHOD(ReflectionExtension, getDependencies) dep = module->deps; - if (!dep) - { + if (!dep) { RETURN_EMPTY_ARRAY(); } array_init(return_value); - while(dep->name) { + while (dep->name) { zend_string *relation; char *rel_type; size_t len = 0; @@ -7933,9 +7932,7 @@ static zval *_reflection_write_property(zend_object *object, zend_string *name, zend_throw_exception_ex(reflection_exception_ptr, 0, "Cannot set read-only property %s::$%s", ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); return &EG(uninitialized_zval); - } - else - { + } else { return zend_std_write_property(object, name, value, cache_slot); } } From 316332d8a302c0fe6dcbdd2ddff20713e82acab0 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 15:08:49 -0700 Subject: [PATCH 31/33] `ReflectionAttribute::getArguments()`: optimize array initialization For attributes without arguments, avoid allocating an array that will be empty. --- ext/reflection/php_reflection.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 93c14e0ec72b..67111157fb1d 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -7504,6 +7504,10 @@ ZEND_METHOD(ReflectionAttribute, getArguments) ZEND_PARSE_PARAMETERS_NONE(); GET_REFLECTION_OBJECT_PTR(attr); + if (attr->data->argc == 0) { + RETURN_EMPTY_ARRAY(); + } + array_init(return_value); for (i = 0; i < attr->data->argc; i++) { From 7c134cd46ecc96644d713050296b2e956c843dc7 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 15:14:21 -0700 Subject: [PATCH 32/33] `ReflectionClass::get(Reflection)Constants()`: optimize array initialization In both `ReflectionClass::getConstants()` and `ReflectionClass::getReflectionConstants()`, for classes without constants, avoid allocating an array that will be empty. --- ext/reflection/php_reflection.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 67111157fb1d..9fd7b778c349 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4772,8 +4772,13 @@ ZEND_METHOD(ReflectionClass, getConstants) GET_REFLECTION_OBJECT_PTR(ce); + const HashTable *constants_table = CE_CONSTANTS_TABLE(ce); + if (zend_hash_num_elements(constants_table) == 0) { + RETURN_EMPTY_ARRAY(); + } + array_init(return_value); - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), key, constant) { + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(constants_table, key, constant) { if (UNEXPECTED(Z_TYPE(constant->value) == IS_CONSTANT_AST && zend_update_class_constant(constant, key, constant->ce) != SUCCESS)) { RETURN_THROWS(); } @@ -4806,8 +4811,13 @@ ZEND_METHOD(ReflectionClass, getReflectionConstants) GET_REFLECTION_OBJECT_PTR(ce); + const HashTable *constants_table = CE_CONSTANTS_TABLE(ce); + if (zend_hash_num_elements(constants_table) == 0) { + RETURN_EMPTY_ARRAY(); + } + array_init(return_value); - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(CE_CONSTANTS_TABLE(ce), name, constant) { + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(constants_table, name, constant) { if (ZEND_CLASS_CONST_FLAGS(constant) & filter) { zval class_const; reflection_class_constant_factory(name, constant, &class_const); From e3a7f4ba7f86257f99c3b8d0abbdeadcfcbfb210 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 2 Jul 2026 15:15:16 -0700 Subject: [PATCH 33/33] `ReflectionClass::isSubclassOf()`: remove extraneous parentheses --- ext/reflection/php_reflection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 9fd7b778c349..f04bd020b770 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -5504,7 +5504,7 @@ ZEND_METHOD(ReflectionClass, isSubclassOf) GET_REFLECTION_OBJECT_PTR(ce); - RETURN_BOOL((ce != class_ce && instanceof_function(ce, class_ce))); + RETURN_BOOL(ce != class_ce && instanceof_function(ce, class_ce)); } /* }}} */