Commit 70384ffd412 for php.net
commit 70384ffd4122a6b6ab98b4023389d47e24e6a23a
Author: Arnaud Le Blanc <arnaud.lb@gmail.com>
Date: Wed Apr 15 11:41:39 2026 +0200
Expose zend_reflection_property_set_raw_value, zend_reflection_property_set_raw_value_without_lazy_initialization
Closes GH-21763
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index 2f0d016a99c..a467df3b6be 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -118,6 +118,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES
. Added ZEND_CONTAINER_OF().
. The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead
use php_check_open_basedir() directly.
+ . Added zend_reflection_property_set_raw_value_without_lazy_initialization(),
+ zend_reflection_property_set_raw_value() to expose the functionality of
+ ReflectionProperty::setRawValueWithoutLazyInitialization() and
+ ReflectionProperty::setRawValue() to C extensions.
========================
2. Build system changes
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 00085968be7..6c50619a35d 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -6016,10 +6016,10 @@ ZEND_METHOD(ReflectionProperty, setValue)
* property is overridden on 'object' and was not private in 'scope'.
* The effective prop may add hooks or change flags. */
static zend_property_info *reflection_property_get_effective_prop(
- property_reference *ref, zend_class_entry *scope, zend_object *object) {
- zend_property_info *prop = ref->prop;
+ zend_property_info *prop, zend_string *unmangled_name,
+ zend_class_entry *scope, zend_object *object) {
if (scope != object->ce && !(prop && (prop->flags & ZEND_ACC_PRIVATE))) {
- prop = zend_hash_find_ptr(&object->ce->properties_info, ref->unmangled_name);
+ prop = zend_hash_find_ptr(&object->ce->properties_info, unmangled_name);
}
return prop;
}
@@ -6052,8 +6052,8 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
}
}
- zend_property_info *prop = reflection_property_get_effective_prop(ref,
- intern->ce, Z_OBJ_P(object));
+ zend_property_info *prop = reflection_property_get_effective_prop(ref->prop,
+ ref->unmangled_name, intern->ce, Z_OBJ_P(object));
if (UNEXPECTED(prop && (prop->flags & ZEND_ACC_STATIC))) {
zend_throw_exception(reflection_exception_ptr, "May not use getRawValue on static properties", 0);
@@ -6083,13 +6083,15 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
}
}
-static void reflection_property_set_raw_value(zend_property_info *prop,
- zend_string *unmangled_name, void *cache_slot[3], reflection_object *intern,
- zend_object *object, zval *value)
+static void zend_reflection_property_set_raw_value_ex(zend_property_info *prop,
+ zend_string *unmangled_name, void *cache_slot[3],
+ zend_class_entry *scope, zend_object *object, zval *value)
{
+ ZEND_ASSERT(!prop || !(prop->flags & ZEND_ACC_STATIC));
+
if (!prop || !prop->hooks || !prop->hooks[ZEND_PROPERTY_HOOK_SET]) {
const zend_class_entry *old_scope = EG(fake_scope);
- EG(fake_scope) = intern->ce;
+ EG(fake_scope) = scope;
object->handlers->write_property(object, unmangled_name, value, cache_slot);
EG(fake_scope) = old_scope;
} else {
@@ -6098,6 +6100,22 @@ static void reflection_property_set_raw_value(zend_property_info *prop,
}
}
+PHPAPI void zend_reflection_property_set_raw_value(zend_property_info *prop,
+ zend_string *unmangled_name, void *cache_slot[3],
+ zend_class_entry *scope, zend_object *object, zval *value)
+{
+ prop = reflection_property_get_effective_prop(prop,
+ unmangled_name, scope, object);
+
+ if (UNEXPECTED(prop && (prop->flags & ZEND_ACC_STATIC))) {
+ zend_throw_exception(reflection_exception_ptr, "May not use setRawValue on static properties", 0);
+ return;
+ }
+
+ zend_reflection_property_set_raw_value_ex(prop, unmangled_name, cache_slot,
+ scope, object, value);
+}
+
ZEND_METHOD(ReflectionProperty, setRawValue)
{
reflection_object *intern;
@@ -6112,26 +6130,18 @@ ZEND_METHOD(ReflectionProperty, setRawValue)
Z_PARAM_ZVAL(value)
} ZEND_PARSE_PARAMETERS_END();
- zend_property_info *prop = reflection_property_get_effective_prop(ref,
- intern->ce, Z_OBJ_P(object));
-
- if (UNEXPECTED(prop && (prop->flags & ZEND_ACC_STATIC))) {
- zend_throw_exception(reflection_exception_ptr, "May not use setRawValue on static properties", 0);
- RETURN_THROWS();
- }
-
- reflection_property_set_raw_value(prop, ref->unmangled_name,
- ref->cache_slot, intern, Z_OBJ_P(object), value);
+ zend_reflection_property_set_raw_value(ref->prop, ref->unmangled_name,
+ ref->cache_slot, intern->ce, Z_OBJ_P(object), value);
}
static zend_result reflection_property_check_lazy_compatible(
zend_property_info *prop, zend_string *unmangled_name,
- reflection_object *intern, zend_object *object, const char *method)
+ zend_class_entry *scope, zend_object *object, const char *method)
{
if (!prop) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
"Can not use %s on dynamic property %s::$%s",
- method, ZSTR_VAL(intern->ce->name),
+ method, ZSTR_VAL(scope->name),
ZSTR_VAL(unmangled_name));
return FAILURE;
}
@@ -6166,32 +6176,23 @@ static zend_result reflection_property_check_lazy_compatible(
return SUCCESS;
}
-/* {{{ Set property value without triggering initializer while skipping hooks if any */
-ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization)
+PHPAPI void zend_reflection_property_set_raw_value_without_lazy_initialization(
+ zend_property_info *prop, zend_string *unmangled_name,
+ void *cache_slot[3], zend_class_entry *scope,
+ zend_object *object, zval *value)
{
- reflection_object *intern;
- property_reference *ref;
- zend_object *object;
- zval *value;
-
- GET_REFLECTION_OBJECT_PTR(ref);
-
- ZEND_PARSE_PARAMETERS_START(2, 2) {
- Z_PARAM_OBJ_OF_CLASS(object, intern->ce)
- Z_PARAM_ZVAL(value)
- } ZEND_PARSE_PARAMETERS_END();
-
while (zend_object_is_lazy_proxy(object)
&& zend_lazy_object_initialized(object)) {
object = zend_lazy_object_get_instance(object);
}
- zend_property_info *prop = reflection_property_get_effective_prop(ref,
- intern->ce, object);
+ prop = reflection_property_get_effective_prop(prop,
+ unmangled_name, scope, object);
- if (reflection_property_check_lazy_compatible(prop, ref->unmangled_name,
- intern, object, "setRawValueWithoutLazyInitialization") == FAILURE) {
- RETURN_THROWS();
+ if (reflection_property_check_lazy_compatible(prop, unmangled_name,
+ scope, object, "setRawValueWithoutLazyInitialization") == FAILURE) {
+ ZEND_ASSERT(EG(exception));
+ return;
}
zval *var_ptr = OBJ_PROP(object, prop->offset);
@@ -6200,8 +6201,8 @@ ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization)
/* Do not trigger initialization */
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_LAZY;
- reflection_property_set_raw_value(prop, ref->unmangled_name,
- ref->cache_slot, intern, object, value);
+ zend_reflection_property_set_raw_value_ex(prop, unmangled_name,
+ cache_slot, scope, object, value);
/* Mark property as lazy again if an exception prevented update */
if (EG(exception) && prop_was_lazy && Z_TYPE_P(var_ptr) == IS_UNDEF
@@ -6220,6 +6221,26 @@ ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization)
}
}
+/* {{{ Set property value without triggering initializer while skipping hooks if any */
+ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization)
+{
+ reflection_object *intern;
+ property_reference *ref;
+ zend_object *object;
+ zval *value;
+
+ GET_REFLECTION_OBJECT_PTR(ref);
+
+ ZEND_PARSE_PARAMETERS_START(2, 2) {
+ Z_PARAM_OBJ_OF_CLASS(object, intern->ce)
+ Z_PARAM_ZVAL(value)
+ } ZEND_PARSE_PARAMETERS_END();
+
+ zend_reflection_property_set_raw_value_without_lazy_initialization(
+ ref->prop, ref->unmangled_name, ref->cache_slot, intern->ce,
+ object, value);
+}
+
/* {{{ Mark property as non-lazy, and initialize to default value */
ZEND_METHOD(ReflectionProperty, skipLazyInitialization)
{
@@ -6234,7 +6255,7 @@ ZEND_METHOD(ReflectionProperty, skipLazyInitialization)
} ZEND_PARSE_PARAMETERS_END();
if (reflection_property_check_lazy_compatible(ref->prop,
- ref->unmangled_name, intern, object,
+ ref->unmangled_name, intern->ce, object,
"skipLazyInitialization") == FAILURE) {
RETURN_THROWS();
}
@@ -6715,7 +6736,8 @@ ZEND_METHOD(ReflectionProperty, isReadable)
zend_throw_exception(reflection_exception_ptr, "Given object is not an instance of the class this property was declared in", 0);
RETURN_THROWS();
}
- prop = reflection_property_get_effective_prop(ref, intern->ce, obj);
+ prop = reflection_property_get_effective_prop(ref->prop,
+ ref->unmangled_name, intern->ce, obj);
}
zend_class_entry *ce = obj ? obj->ce : intern->ce;
@@ -6826,7 +6848,8 @@ ZEND_METHOD(ReflectionProperty, isWritable)
zend_throw_exception(reflection_exception_ptr, "Given object is not an instance of the class this property was declared in", 0);
RETURN_THROWS();
}
- prop = reflection_property_get_effective_prop(ref, intern->ce, obj);
+ prop = reflection_property_get_effective_prop(ref->prop,
+ ref->unmangled_name, intern->ce, obj);
}
zend_class_entry *ce = obj ? obj->ce : intern->ce;
diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h
index ba03f1d9ac2..51f48b8039c 100644
--- a/ext/reflection/php_reflection.h
+++ b/ext/reflection/php_reflection.h
@@ -50,6 +50,27 @@ extern PHPAPI zend_class_entry *reflection_lazy_object_ptr;
PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object);
+/* Sets the value of a property, bypassing a set hook if defined.
+ * 'prop': The property to set
+ * 'unmangled_name': The name of the property
+ * 'cache_slot': An opaque pointer used as an internal cache. The same
+ * cache_slot can be used again with the same 'unmangled_name' and 'scope'.
+ * Must be zeroed on first use. May be NULL.
+ * 'scope': The scope from which to set the property
+ * 'object': The object to set the value on
+ * 'value': The value to set
+ */
+PHPAPI void zend_reflection_property_set_raw_value(
+ zend_property_info *prop, zend_string *unmangled_name,
+ void *cache_slot[3], zend_class_entry *scope,
+ zend_object *object, zval *value);
+
+/* Same as zend_reflection_property_set_raw_value(), but skips lazy object initialization. */
+PHPAPI void zend_reflection_property_set_raw_value_without_lazy_initialization(
+ zend_property_info *prop, zend_string *unmangled_name,
+ void *cache_slot[3], zend_class_entry *scope,
+ zend_object *object, zval *value);
+
END_EXTERN_C()
#endif /* PHP_REFLECTION_H */