Fix phpstan/phpstan#14063: Readonly property modification through clone() is not reported outside allowed scope#5265
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
…de allowed scope - ReadOnlyPropertyAssignRule and ReadOnlyByPhpDocPropertyAssignRule unconditionally skipped all checks when inCloneWith was true, but should still enforce the "assigned outside declaring class" check - Moved the inCloneWith early-exit inside the loop, after the declaring class check, so that only the constructor requirement is skipped for clone-with operations - Added regression test for phpstan/phpstan#14063 - Updated existing clone-with test expectations to include new error cases
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, ['prop' => value])(PHP 8.5 clone-with) from outside the declaring class on a readonly property, PHPStan incorrectly reported no error. In PHP, modifying readonly properties through clone-with is only allowed from within the declaring class scope.Changes
src/Rules/Properties/ReadOnlyPropertyAssignRule.php: Moved theinCloneWithearly-exit from before the declaring class check to after it. This ensures that clone-with from outside the declaring class still reports "Readonly property is assigned outside of its declaring class", while clone-with from inside the class correctly skips the constructor requirement.src/Rules/Properties/ReadOnlyByPhpDocPropertyAssignRule.php: Same fix applied for@readonly(PHPDoc) properties.tests/PHPStan/Rules/Properties/data/bug-14063.php: New regression test reproducing the exact issue scenario.public(set) readonlyproperties andreadonly classpromoted properties.Root cause
Both
ReadOnlyPropertyAssignRuleandReadOnlyByPhpDocPropertyAssignRulehad an unconditional early return when theinCloneWithattribute was set on the property fetch node. This skipped ALL checks, including the scope check that verifies the assignment is happening within the declaring class. The fix moves theinCloneWithcontinue inside the per-property loop, placing it after the declaring class checks but before the constructor checks, so the scope restriction is still enforced while the constructor requirement is correctly relaxed for clone-with operations.Test
Added
tests/PHPStan/Rules/Properties/data/bug-14063.phpwhich reproduces the exact issue: afinal readonly classwith a promoted property, whereclone()with property override is called from global scope. The test verifies that "Readonly property is assigned outside of its declaring class" is now correctly reported. Also updated existing clone-with test expectations for both native readonly and PHPDoc readonly properties.Fixes phpstan/phpstan#14063