Skip to content

Commit 2945c72

Browse files
authored
build/gen_stub: support asymmetric visibility modifiers. (#21773)
Emit ZEND_ACC_PUBLIC_SET / PROTECTED_SET / PRIVATE_SET from the corresponding Modifiers::*_SET flags in generated arginfo, gated to PHP 8.4+ where asymmetric visibility was introduced. Previously private(set) and friends in stubs parsed without error but produced no set-visibility flag. @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. After converting virtual properties from @readonly to private(set), dom_write_property still threw a readonly-modification error. Since the handler replaces zend_std_write_property, the engine's own asymmetric-visibility check is bypassed on the write path, so the DOM handler now raises it explicitly via zend_asymmetric_visibility_property_modification_error() when the caller lacks set access. The readonly error is kept as a fallback.
1 parent b33fee5 commit 2945c72

File tree

7 files changed

+439
-804
lines changed

7 files changed

+439
-804
lines changed

build/gen_stub.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2354,7 +2354,17 @@ protected function getFlagsByPhpVersion(): VersionFlags
23542354
$flags = "ZEND_ACC_PRIVATE";
23552355
}
23562356

2357-
return new VersionFlags([$flags]);
2357+
$versionFlags = new VersionFlags([$flags]);
2358+
2359+
if ($this->flags & Modifiers::PUBLIC_SET) {
2360+
$versionFlags->addForVersionsAbove("ZEND_ACC_PUBLIC_SET", PHP_84_VERSION_ID);
2361+
} elseif ($this->flags & Modifiers::PROTECTED_SET) {
2362+
$versionFlags->addForVersionsAbove("ZEND_ACC_PROTECTED_SET", PHP_84_VERSION_ID);
2363+
} elseif ($this->flags & Modifiers::PRIVATE_SET) {
2364+
$versionFlags->addForVersionsAbove("ZEND_ACC_PRIVATE_SET", PHP_84_VERSION_ID);
2365+
}
2366+
2367+
return $versionFlags;
23582368
}
23592369

23602370
protected function getTypeCode(string $variableLikeName, string &$code): string
@@ -2450,6 +2460,17 @@ protected function addModifiersToFieldSynopsis(DOMDocument $doc, DOMElement $fie
24502460
$fieldsynopsisElement->appendChild(new DOMText("\n$indentation"));
24512461
$fieldsynopsisElement->appendChild($doc->createElement("modifier", "private"));
24522462
}
2463+
2464+
if ($this->flags & Modifiers::PUBLIC_SET) {
2465+
$fieldsynopsisElement->appendChild(new DOMText("\n$indentation"));
2466+
$fieldsynopsisElement->appendChild($doc->createElement("modifier", "public(set)"));
2467+
} elseif ($this->flags & Modifiers::PROTECTED_SET) {
2468+
$fieldsynopsisElement->appendChild(new DOMText("\n$indentation"));
2469+
$fieldsynopsisElement->appendChild($doc->createElement("modifier", "protected(set)"));
2470+
} elseif ($this->flags & Modifiers::PRIVATE_SET) {
2471+
$fieldsynopsisElement->appendChild(new DOMText("\n$indentation"));
2472+
$fieldsynopsisElement->appendChild($doc->createElement("modifier", "private(set)"));
2473+
}
24532474
}
24542475

24552476
}

ext/dom/php_dom.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,6 @@ zval *dom_write_property(zend_object *object, zend_string *name, zval *value, vo
412412
const dom_prop_handler *hnd = dom_get_prop_handler(obj, name, cache_slot);
413413

414414
if (hnd) {
415-
if (UNEXPECTED(!hnd->write_func)) {
416-
zend_readonly_property_modification_error_ex(ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
417-
return &EG(error_zval);
418-
}
419-
420415
zend_property_info *prop = NULL;
421416
if (cache_slot) {
422417
ZEND_ASSERT(*cache_slot == obj->prop_handler);
@@ -429,6 +424,16 @@ zval *dom_write_property(zend_object *object, zend_string *name, zval *value, vo
429424
}
430425
}
431426

427+
if (UNEXPECTED(!hnd->write_func)) {
428+
if (prop && (prop->flags & ZEND_ACC_PPP_SET_MASK) &&
429+
!zend_asymmetric_property_has_set_access(prop)) {
430+
zend_asymmetric_visibility_property_modification_error(prop, "modify");
431+
} else {
432+
zend_readonly_property_modification_error_ex(ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
433+
}
434+
return &EG(error_zval);
435+
}
436+
432437
ZEND_ASSERT(prop && ZEND_TYPE_IS_SET(prop->type));
433438
zval tmp;
434439
ZVAL_COPY(&tmp, value);

0 commit comments

Comments
 (0)