Fixes GH-21768: ext/reflection: assertion failure on internal virtual p…#21769
Fixes GH-21768: ext/reflection: assertion failure on internal virtual p…#21769devnexen wants to merge 2 commits intophp:masterfrom
Conversation
|
So this solves the assertion failure. But I wonder what this new reflection method actually tests... I.e. the following code will throw: <?php
$dom = new DOMDocument;
$dom->doctype = null; // Error!but it claims this property is writable: <?php
$rc = new ReflectionClass($dom);
var_dump($rc->getProperty('doctype')->isWritable(null)); // bool(true) |
|
Fair point, returning true for read-only props like doctype is misleading. I'm not sure what the right call is here. Would it make more sense to fix this on the DOM side, giving these @virtual properties proper get-only hooks so reflection can answer correctly, rather than working As a narrow alternative for the crash-fix, we could throw ReflectionException whenever the property is ZEND_ACC_VIRTUAL without hooks, something like "Cannot Happy to go either way, or hold off on this PR if you'd rather sort it at the DOM level. |
|
The properties of dom that aren't writable are marked as readonly. This isn't really right as some dom manipulations can change those properties indirectly anyway. This is a leftover from back in the days. |
@readonly on DOM property stubs was documentation only and did not translate to any runtime flag, so reflection reported the properties as writable while the write_property handler threw on assignment. Declaring them public private(set) lets the engine reject external writes via the normal visibility check and lets ReflectionProperty:: isWritable() answer honestly. Also drops @readonly from the three non-virtual HTMLCollection $children properties (Element, DocumentFragment, Document) and applies private(set) there as well. Depends on the assertion removal from phpGH-21769 for isReadable() to stop aborting on these properties.
| if (prop->flags & ZEND_ACC_VIRTUAL) { | ||
| ZEND_ASSERT(prop->hooks); | ||
| if (!prop->hooks[ZEND_PROPERTY_HOOK_SET]) { | ||
| if (prop->hooks && !prop->hooks[ZEND_PROPERTY_HOOK_SET]) { |
There was a problem hiding this comment.
I think the prop->hooks should be moved to the if (prop->flags & ZEND_ACC_VIRTUAL) { check, and then adjusting the prop->flags & ZEND_ACC_READONLY to return false for @readonly internal properties. I think it's safe to assume they will always be initialized internally. That should work for the existing cases like in ext-date.
There was a problem hiding this comment.
Thanks, that's a cleaner shape. Two questions before I go that way.
ndossche suggested on the same PR that DOM's formerly @readonly @virtual properties should be redeclared as public private(set) instead, and I prototyped that on a
companion branch (https://github.com/php/php-src/pull/XXXX if I open it, otherwise local for now). With private(set), isWritable(null) already returns false for
DOMDocument::$doctype without touching reflection, because the existing visibility check at ZEND_ACC_PPP_SET_MASK fires first.
Your approach and hers overlap in intent, so I'd rather pick one direction than merge both. Do you have a preference?
The second question is whether the @readonly doc tag should start emitting ZEND_ACC_READONLY for internal virtual properties in gen_stub.php, since currently it's
documentation only. Without that, your short-circuit wouldn't fire on DOM because the flag isn't set. If we go your way, I'd add that to gen_stub.php alongside the
reflection change, but it would also apply the flag to a few non-virtual HTMLCollection $children cases, which needs a look to confirm the runtime readonly check
doesn't surprise anything.
And yes, I'll add Fixes GH-21768 to the PR body, sorry about the missing link.
There was a problem hiding this comment.
There are legitimate uses of virtual+readonly, even if dom is not one. So I think both should be fixed.
There was a problem hiding this comment.
I don't know the historical reason for @readonly and whether they can just be converted to readonly. But if possible that sounds reasonable. (Edit: I guess a bit weird for virtual properties.)
…ual properties. ReflectionProperty::isReadable() and isWritable() asserted prop->hooks being set whenever ZEND_ACC_VIRTUAL is set. Internal classes such as DOMDocument declare virtual properties backed by the object read/write handlers without hooks, tripping the assertion. Assertion introduced in e4f727d.
…able(). Follow-up to review feedback from @iluuu1994 on phpGH-21769. Gate the ZEND_ACC_VIRTUAL branch on prop->hooks being set, and short-circuit virtual properties that also carry ZEND_ACC_READONLY to RETURN_FALSE (no backing slot, treat as always initialized). Covers legitimate virtual+readonly uses outside of DOM.
…roperties.
ReflectionProperty::isReadable() and isWritable() asserted prop->hooks being set whenever ZEND_ACC_VIRTUAL is set. Internal classes such as DOMDocument declare virtual properties backed by the object read/write handlers without hooks, tripping the assertion.
Assertion introduced in e4f727d.