[php-src] master: Expose zend_reflection_property_set_raw_value, zend_reflection_property_set_raw_value_without_lazy_initialization

Arnaud Le Blanc <[email protected]>
Newsgroups gmane.comp.php.cvs.general
Message-ID <[email protected]>
Author: Arnaud Le Blanc (arnaud-lb)
Date: 2026-06-26T14:23:48+02:00

Commit: https://github.com/php/php-src/commit/70384ffd4122a6b6ab98b4023389d47e24e6a23a
Raw diff: https://github.com/php/php-src/commit/70384ffd4122a6b6ab98b4023389d47e24e6a23a.diff

Expose zend_reflection_property_set_raw_value, zend_reflection_property_set_raw_value_without_lazy_initialization

Closes GH-21763

Changed paths:
  M  UPGRADING.INTERNALS
  M  ext/reflection/php_reflection.c
  M  ext/reflection/php_reflection.h


Diff:

diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index 2f0d016a99cd..a467df3b6be5 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 00085968be79..6c50619a35d8 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 ba03f1d9ac2d..51f48b8039cc 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 */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.