Category
Regression · php-src-strict · language · asymmetric visibility · pillar 4
Problem
After ReflectionProperty::isFinal() became true for public private(set) (#23068), VM treats the property like a plain final field for all writes. Declaring-class methods can no longer assign; Zend 8.4 allows in-class writes. External writes also use the wrong Error message (final vs private(set)).
Verified 2026-07-25 (PHP_COMPILER_PROFILE=8.4 php bin/vm.php vs php.net / #21526 Zend wording):
| Repro |
Zend 8.4+ |
VM PROFILE=8.4 |
In-class $this->n = $this->n + 1 on public private(set) int $n |
succeeds |
Error: Cannot modify final property U::$n |
Global $t->x = 'z' on public private(set) string $x |
Error: Cannot modify private(set) property T::$x from global scope |
Error: Cannot modify final property T::$x |
Global $t->y = 'z' on public protected(set) string $y |
Cannot modify protected(set) property … from global scope |
same (ok) |
Likely fallout of making asymmetric props isFinal() without keeping the asymmetric set-visibility write path (re-#21526 message work).
php-src reference
PHP implementation target
- Property write lowering / VM object handlers in
lib/ + lib/VM/ — allow writes from declaring class (and protected(set) hierarchy rules); keep redeclaration final
- External deny message must match Zend (
Cannot modify private(set) property … from … scope), not plain final
- PHP-in-PHP; no new
runtime/*.c
Repro
./script/docker-exec.sh -- bash -lc 'cat > /tmp/private_set_write.php <<'"'"'PHP'"'"'
<?php
class U {
public private(set) int $n = 0;
public function bump(): void { $this->n = $this->n + 1; }
}
$u = new U();
$u->bump();
echo $u->n, "\n";
PHP
PHP_COMPILER_PROFILE=8.4 php bin/vm.php /tmp/private_set_write.php'
# Zend: 1
# VM today: Error: Cannot modify final property U::$n
Done when
Category
Regression· php-src-strict · language · asymmetric visibility · pillar 4Problem
After
ReflectionProperty::isFinal()became true forpublic private(set)(#23068), VM treats the property like a plainfinalfield for all writes. Declaring-class methods can no longer assign; Zend 8.4 allows in-class writes. External writes also use the wrongErrormessage (final vsprivate(set)).Verified 2026-07-25 (
PHP_COMPILER_PROFILE=8.4php bin/vm.phpvs php.net / #21526 Zend wording):$this->n = $this->n + 1onpublic private(set) int $nError: Cannot modify final property U::$n$t->x = 'z'onpublic private(set) string $xError: Cannot modify private(set) property T::$x from global scopeError: Cannot modify final property T::$x$t->y = 'z'onpublic protected(set) string $yCannot modify protected(set) property … from global scopeLikely fallout of making asymmetric props
isFinal()without keeping the asymmetric set-visibility write path (re-#21526 message work).php-src reference
Zend/zend_object_handlers.c— asymmetricsetvisibility guard / Error wordingprivate(set)is implicitly final for redeclaration, not for in-class writesPHP implementation target
lib/+lib/VM/— allow writes from declaring class (andprotected(set)hierarchy rules); keep redeclaration finalCannot modify private(set) property … from … scope), not plain finalruntime/*.cRepro
Done when
public private(set)succeed (VM + JIT)private(set)…from global scope), notfinal property.phptundertest/compliance/cases/+ issue repro; php-src-strict; no php-compiler-strict shortcut