Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 72 additions & 49 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -6013,11 +6013,11 @@ ZEND_METHOD(ReflectionProperty, setValue)
* 'scope' on 'object'. The result may be different from 'ref->prop' when the
* 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;
static const zend_property_info *reflection_property_get_effective_prop(
const zend_property_info *prop, zend_string *unmangled_name,
const 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;
}
Expand Down Expand Up @@ -6050,8 +6050,8 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
}
}

zend_property_info *prop = reflection_property_get_effective_prop(ref,
intern->ce, Z_OBJ_P(object));
const 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);
Expand Down Expand Up @@ -6081,13 +6081,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(const zend_property_info *prop,
zend_string *unmangled_name, void *cache_slot[3],
const 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 {
Expand All @@ -6096,6 +6098,22 @@ static void reflection_property_set_raw_value(zend_property_info *prop,
}
}

PHPAPI void zend_reflection_property_set_raw_value(const zend_property_info *prop,
zend_string *unmangled_name, void *cache_slot[3],
const 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;
Expand All @@ -6110,26 +6128,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)
const zend_property_info *prop, zend_string *unmangled_name,
const 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;
}
Expand Down Expand Up @@ -6164,32 +6174,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(
const zend_property_info *prop, zend_string *unmangled_name,
void *cache_slot[3], const 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);
Expand All @@ -6198,8 +6199,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
Expand All @@ -6218,6 +6219,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)
{
Expand All @@ -6232,7 +6253,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();
}
Expand Down Expand Up @@ -6703,7 +6724,7 @@ ZEND_METHOD(ReflectionProperty, isReadable)

GET_REFLECTION_OBJECT_PTR(ref);

zend_property_info *prop = ref->prop;
const zend_property_info *prop = ref->prop;
if (prop && obj) {
if (prop->flags & ZEND_ACC_STATIC) {
zend_throw_exception(reflection_exception_ptr, "null is expected as object argument for static properties", 0);
Expand All @@ -6713,7 +6734,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;
Expand Down Expand Up @@ -6809,7 +6831,7 @@ ZEND_METHOD(ReflectionProperty, isWritable)

GET_REFLECTION_OBJECT_PTR(ref);

zend_property_info *prop = ref->prop;
const zend_property_info *prop = ref->prop;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you want to split up the marking of things as const into a different patch that one should be easier to review and merge while this is still being discussed

if (prop && obj) {
if (prop->flags & ZEND_ACC_STATIC) {
zend_throw_exception(reflection_exception_ptr, "null is expected as object argument for static properties", 0);
Expand All @@ -6819,7 +6841,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;
Expand Down
15 changes: 15 additions & 0 deletions ext/reflection/php_reflection.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,21 @@ 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.
* '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. */
PHPAPI void zend_reflection_property_set_raw_value(
const zend_property_info *prop, zend_string *unmangled_name,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a bit more documentation? I assume

  • zend_object *object is what object we are setting a property on
  • zend_property_info *prop is the property to set
  • zval *value is the value to set

What is the unmangled_name here - if it is the same as the property name, why is a separate parameter needed? The unmangled name could already be retrieved from zend_get_unmangled_property_name(prop-> name)

What does the scope represent - since this is reflection, it doesn't matter what the calling scope is, visibility gets bypassed. Is it the class of the object? Then is it already available as object->ce, so why is a separate parameter needed

Internally reflection might pass the unmangled name and the class entry separately because they are already available and for performance, but that shouldn't be required from external callers

void *cache_slot[3], const 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(
const zend_property_info *prop, zend_string *unmangled_name,
void *cache_slot[3], const zend_class_entry *scope,
zend_object *object, zval *value);

END_EXTERN_C()

#endif /* PHP_REFLECTION_H */
Loading