Skip to content

Regression: public private(set) in-class writes throw "Cannot modify final property" — Zend allows (re-#21526/#23068, Zend/zend_object_handlers.c) #23110

Description

@PurHur

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

  • In-class / same-class method writes to public private(set) succeed (VM + JIT)
  • External write Error message matches Zend (private(set) … from global scope), not final property
  • Child class cannot redeclare the property (still final for override)
  • Compliance .phpt under test/compliance/cases/ + issue repro; php-src-strict; no php-compiler-strict shortcut

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:vmVirtual machinebugSomething isn't workingimplementation-readySpec complete: repro, php-src ref, done-when — safe for workers to claimphase-2:languagePhase 2 – language features

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions