Fix phpstan/phpstan#14063: Readonly property modification through clone() is not reported outside allowed scope#5432
Open
phpstan-bot wants to merge 3 commits intophpstan:2.1.xfrom
Conversation
- New test in tests/PHPStan/Rules/Properties/data/bug-14063.php covering readonly class with clone() property overrides - Tests AccessPropertiesInAssignRule correctly reports "Assign to protected(set) property" when modifying readonly class properties via clone() from outside the declaring class - Tests ReadOnlyPropertyAssignRule does not double-report (defers to AccessPropertiesInAssignRule) - The underlying fix was in BetterReflection 6.70 which correctly sets isProtectedSet() for properties in readonly classes Closes phpstan/phpstan#14063
| } | ||
|
|
||
| #[RequiresPhp('>= 8.5')] | ||
| public function testBug14063(): void |
Contributor
There was a problem hiding this comment.
Remove test from ReadOnlyPropertyAssignRuleTest.php
Collaborator
Author
There was a problem hiding this comment.
Done. Removed the testBug14063() method from ReadOnlyPropertyAssignRuleTest.php as requested by the reviewer. The test was asserting zero errors, which adds no value since that rule isn't expected to handle this case. Tests and static analysis both pass.
The test was asserting no errors from ReadOnlyPropertyAssignRule, which adds no value since the rule is not expected to handle this case. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
VincentLanglet
approved these changes
Apr 8, 2026
Contributor
VincentLanglet
left a comment
There was a problem hiding this comment.
Non-readonly class Bug14063\Child cannot extend readonly class Bug14063\Base
Make Child class readonly since PHP does not allow a non-readonly class to extend a readonly class. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collaborator
Author
|
Tests background task also completed successfully (11780 tests passing). Both checks confirmed — the fix has been committed and pushed. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When using
clone($obj, ['value' => 'newVal'])on areadonly classfrom outside the declaring class scope, PHPStan was not reporting an error even though PHP 8.5 throws a fatal error at runtime: "Cannot modify protected(set) readonly property".Changes
tests/PHPStan/Rules/Properties/data/bug-14063.phpwithreadonly classscenarios for clone-withtestBug14063()toAccessPropertiesInAssignRuleTest.phpverifying the "Assign to protected(set) property" error is reported when modifyingreadonly classproperties viaclone()from outside the declaring classtestBug14063()toReadOnlyPropertyAssignRuleTest.phpverifying no duplicate errors from the readonly property assign ruleRoot cause
The bug was in BetterReflection (updated from 6.65 to 6.70 in commit b4c5164). BetterReflection's
computeModifiers()method inReflectionPropertyhad logic to set theIS_PROTECTED_SET_COMPATIBILITYflag forpublic readonlyproperties (making them behave asprotected(set)for write visibility checks). However, the check only considered theIS_READONLY_COMPATIBILITYmodifier on the property itself, not thegetDeclaringClass()->isReadOnly()case. This meant properties in areadonly classthat don't have explicitreadonlyon the property (e.g., constructor-promoted properties likepublic string $valuein areadonly class) were not treated asprotected(set), causingAccessPropertiesCheck::canWriteProperty()to incorrectly returntruefrom outside the class scope.BetterReflection 6.70 added the
$this->getDeclaringClass()->isReadOnly()check, which correctly handlesreadonly classproperties.Test
The regression test covers:
final readonly classwith constructor-promoted property — clone from global scope (should error)readonly classwith constructor-promoted property — clone from function scope (should error)protected(set))Fixes phpstan/phpstan#14063