Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ReflectionProperty::getRawValue(), setRawValue(), setRawValueWithoutLazyInitialization() for properties overridden with hooks #17714

Closed
wants to merge 4 commits into from

Conversation

arnaud-lb
Copy link
Member

@arnaud-lb arnaud-lb commented Feb 6, 2025

Fixes GH-17713.

new ReflectionProperty($scope, $propName) keeps a reference to the zend_property_info of $propName declared in $scope. In getRawValue() and related methods, we use this reference to check whether the property is hooked.

However, calling new ReflectionProperty($scope, $propName)->getRawValue($object) is equivalent to the expression $object->$propName from scope $scope (except that it bypasses hooks), and thus may access an overridden property (unless the original is private). This property may have hooks and different flags.

Here I fetch the effective property info before checking for hooks and property flags.

…and flags in ReflectionProperty::getRawValue(), setRawValue(), setRawValueWithoutLazyInitialization()
@arnaud-lb arnaud-lb changed the base branch from master to PHP-8.4 February 6, 2025 11:39
Copy link
Member

@iluuu1994 iluuu1994 left a comment

Choose a reason for hiding this comment

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

LGTM otherwise, if we want to merge it as is. Thanks again!

ext/reflection/php_reflection.c Show resolved Hide resolved
RETURN_THROWS();
}

if (!ref->prop || !ref->prop->hooks || !ref->prop->hooks[ZEND_PROPERTY_HOOK_GET]) {
if (!prop || !prop->hooks || !prop->hooks[ZEND_PROPERTY_HOOK_GET]) {
Copy link
Member

Choose a reason for hiding this comment

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

The reason I originally used zend_read_property_ex() was to avoid replicating its behavior for things like typed uninitialized properties, and readonly properties for write. I suppose that if this gets too tedious, it might be easier to handle these two things, and just access the backing value explicitly. Luckily, prop_info->offset stays the same in child properties, and child properties cannot become virtual, so we could avoid loading the latest property info.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've tried that initially in #17698, but there are other cases to handle, e.g. type checking for writes, and existing code is hard to re-use (e.g. zend_assign_to_typed_prop() can not be called outside of execute_ex due to execute_data being a register and I fear that trying to make it re-usable will lead to performance regressions).

reflection_property_get_effective_prop() turns out to be quite simple, so I think I will leave it like this.

#17698 will revert potential performance regressions in the simple case.

@arnaud-lb arnaud-lb marked this pull request as ready for review February 7, 2025 09:43
@arnaud-lb arnaud-lb closed this in 24b191a Feb 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ReflectionProperty::getRawValue() and related methods may call hooks of overridden properties
2 participants