diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php
index 1773ae7c361..88548f6b7f9 100644
--- a/src/Application/VersionResolver.php
+++ b/src/Application/VersionResolver.php
@@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
- public const PACKAGE_VERSION = '8da1bfe2590771077da7ccd297f5df245d3e55c1';
+ public const PACKAGE_VERSION = 'fe8e48de8cc6c962430abab1b1766c05dd12309e';
/**
* @api
* @var string
*/
- public const RELEASE_DATE = '2025-01-09 21:26:38';
+ public const RELEASE_DATE = '2025-01-10 18:33:46';
/**
* @var int
*/
diff --git a/src/NodeAnalyzer/PropertyFetchAnalyzer.php b/src/NodeAnalyzer/PropertyFetchAnalyzer.php
index 0322c664ef0..b24402a2957 100644
--- a/src/NodeAnalyzer/PropertyFetchAnalyzer.php
+++ b/src/NodeAnalyzer/PropertyFetchAnalyzer.php
@@ -20,8 +20,10 @@ use PhpParser\Node\Stmt\Trait_;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ThisType;
+use Rector\DeadCode\NodeAnalyzer\PropertyWriteonlyAnalyzer;
use Rector\Enum\ObjectReference;
use Rector\NodeNameResolver\NodeNameResolver;
+use Rector\NodeNestingScope\ContextAnalyzer;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\PhpParser\AstResolver;
use Rector\PhpParser\Node\BetterNodeFinder;
@@ -49,17 +51,27 @@ final class PropertyFetchAnalyzer
* @readonly
*/
private ReflectionResolver $reflectionResolver;
+ /**
+ * @readonly
+ */
+ private ContextAnalyzer $contextAnalyzer;
+ /**
+ * @readonly
+ */
+ private PropertyWriteonlyAnalyzer $propertyWriteonlyAnalyzer;
/**
* @var string
*/
private const THIS = 'this';
- public function __construct(NodeNameResolver $nodeNameResolver, BetterNodeFinder $betterNodeFinder, AstResolver $astResolver, NodeTypeResolver $nodeTypeResolver, ReflectionResolver $reflectionResolver)
+ public function __construct(NodeNameResolver $nodeNameResolver, BetterNodeFinder $betterNodeFinder, AstResolver $astResolver, NodeTypeResolver $nodeTypeResolver, ReflectionResolver $reflectionResolver, ContextAnalyzer $contextAnalyzer, PropertyWriteonlyAnalyzer $propertyWriteonlyAnalyzer)
{
$this->nodeNameResolver = $nodeNameResolver;
$this->betterNodeFinder = $betterNodeFinder;
$this->astResolver = $astResolver;
$this->nodeTypeResolver = $nodeTypeResolver;
$this->reflectionResolver = $reflectionResolver;
+ $this->contextAnalyzer = $contextAnalyzer;
+ $this->propertyWriteonlyAnalyzer = $propertyWriteonlyAnalyzer;
}
public function isLocalPropertyFetch(Node $node) : bool
{
@@ -102,10 +114,16 @@ final class PropertyFetchAnalyzer
return \true;
}
return (bool) $this->betterNodeFinder->findFirst($trait, function (Node $node) use($propertyName) : bool {
- if (!$node instanceof Assign) {
+ if (!$this->isLocalPropertyFetchName($node, $propertyName)) {
return \false;
}
- return $this->isLocalPropertyFetchName($node->var, $propertyName);
+ /**
+ * @var PropertyFetch|StaticPropertyFetch|NullsafePropertyFetch $node
+ */
+ if ($this->contextAnalyzer->isChangeableContext($node)) {
+ return \true;
+ }
+ return $this->propertyWriteonlyAnalyzer->arePropertyFetchesExclusivelyBeingAssignedTo([$node]);
});
}
/**
diff --git a/src/NodeManipulator/PropertyManipulator.php b/src/NodeManipulator/PropertyManipulator.php
index 8c3a4cb4d20..e825c92c154 100644
--- a/src/NodeManipulator/PropertyManipulator.php
+++ b/src/NodeManipulator/PropertyManipulator.php
@@ -19,6 +19,7 @@ use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\NodeAnalyzer\PropertyFetchAnalyzer;
use Rector\NodeNameResolver\NodeNameResolver;
+use Rector\NodeNestingScope\ContextAnalyzer;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer;
@@ -78,11 +79,15 @@ final class PropertyManipulator
* @readonly
*/
private PropertyFetchAnalyzer $propertyFetchAnalyzer;
+ /**
+ * @readonly
+ */
+ private ContextAnalyzer $contextAnalyzer;
/**
* @var string[]|class-string
[]
*/
private const ALLOWED_NOT_READONLY_CLASS_ANNOTATIONS = ['ApiPlatform\\Core\\Annotation\\ApiResource', 'ApiPlatform\\Metadata\\ApiResource', 'Doctrine\\ORM\\Mapping\\Entity', 'Doctrine\\ORM\\Mapping\\Table', 'Doctrine\\ORM\\Mapping\\MappedSuperclass', 'Doctrine\\ORM\\Mapping\\Embeddable'];
- public function __construct(\Rector\NodeManipulator\AssignManipulator $assignManipulator, BetterNodeFinder $betterNodeFinder, PhpDocInfoFactory $phpDocInfoFactory, PropertyFetchFinder $propertyFetchFinder, NodeNameResolver $nodeNameResolver, PhpAttributeAnalyzer $phpAttributeAnalyzer, NodeTypeResolver $nodeTypeResolver, PromotedPropertyResolver $promotedPropertyResolver, ConstructorAssignDetector $constructorAssignDetector, AstResolver $astResolver, PropertyFetchAnalyzer $propertyFetchAnalyzer)
+ public function __construct(\Rector\NodeManipulator\AssignManipulator $assignManipulator, BetterNodeFinder $betterNodeFinder, PhpDocInfoFactory $phpDocInfoFactory, PropertyFetchFinder $propertyFetchFinder, NodeNameResolver $nodeNameResolver, PhpAttributeAnalyzer $phpAttributeAnalyzer, NodeTypeResolver $nodeTypeResolver, PromotedPropertyResolver $promotedPropertyResolver, ConstructorAssignDetector $constructorAssignDetector, AstResolver $astResolver, PropertyFetchAnalyzer $propertyFetchAnalyzer, ContextAnalyzer $contextAnalyzer)
{
$this->assignManipulator = $assignManipulator;
$this->betterNodeFinder = $betterNodeFinder;
@@ -95,6 +100,7 @@ final class PropertyManipulator
$this->constructorAssignDetector = $constructorAssignDetector;
$this->astResolver = $astResolver;
$this->propertyFetchAnalyzer = $propertyFetchAnalyzer;
+ $this->contextAnalyzer = $contextAnalyzer;
}
/**
* @param \PhpParser\Node\Stmt\Property|\PhpParser\Node\Param $propertyOrParam
@@ -108,7 +114,7 @@ final class PropertyManipulator
$propertyFetches = $this->propertyFetchFinder->findPrivatePropertyFetches($class, $propertyOrParam, $scope);
$classMethod = $class->getMethod(MethodName::CONSTRUCT);
foreach ($propertyFetches as $propertyFetch) {
- if ($this->isChangeableContext($propertyFetch)) {
+ if ($this->contextAnalyzer->isChangeableContext($propertyFetch)) {
return \true;
}
// skip for constructor? it is allowed to set value in constructor method
@@ -192,22 +198,6 @@ final class PropertyManipulator
}
return $this->constructorAssignDetector->isPropertyAssigned($class, $propertyName);
}
- /**
- * @param \PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\StaticPropertyFetch $propertyFetch
- */
- private function isChangeableContext($propertyFetch) : bool
- {
- if ($propertyFetch->getAttribute(AttributeKey::IS_UNSET_VAR, \false)) {
- return \true;
- }
- if ($propertyFetch->getAttribute(AttributeKey::INSIDE_ARRAY_DIM_FETCH, \false)) {
- return \true;
- }
- if ($propertyFetch->getAttribute(AttributeKey::IS_USED_AS_ARG_BY_REF_VALUE, \false) === \true) {
- return \true;
- }
- return $propertyFetch->getAttribute(AttributeKey::IS_INCREMENT_OR_DECREMENT, \false) === \true;
- }
private function hasAllowedNotReadonlyAnnotationOrAttribute(PhpDocInfo $phpDocInfo, Class_ $class) : bool
{
if ($phpDocInfo->hasByAnnotationClasses(self::ALLOWED_NOT_READONLY_CLASS_ANNOTATIONS)) {
diff --git a/src/NodeNestingScope/ContextAnalyzer.php b/src/NodeNestingScope/ContextAnalyzer.php
index a795ff3bded..1a046c8aa96 100644
--- a/src/NodeNestingScope/ContextAnalyzer.php
+++ b/src/NodeNestingScope/ContextAnalyzer.php
@@ -4,6 +4,9 @@ declare (strict_types=1);
namespace Rector\NodeNestingScope;
use PhpParser\Node;
+use PhpParser\Node\Expr\NullsafePropertyFetch;
+use PhpParser\Node\Expr\PropertyFetch;
+use PhpParser\Node\Expr\StaticPropertyFetch;
use Rector\NodeTypeResolver\Node\AttributeKey;
final class ContextAnalyzer
{
@@ -21,4 +24,20 @@ final class ContextAnalyzer
{
return $node->getAttribute(AttributeKey::IS_IN_IF) === \true;
}
+ /**
+ * @param \PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\StaticPropertyFetch|\PhpParser\Node\Expr\NullsafePropertyFetch $propertyFetch
+ */
+ public function isChangeableContext($propertyFetch) : bool
+ {
+ if ($propertyFetch->getAttribute(AttributeKey::IS_UNSET_VAR, \false)) {
+ return \true;
+ }
+ if ($propertyFetch->getAttribute(AttributeKey::INSIDE_ARRAY_DIM_FETCH, \false)) {
+ return \true;
+ }
+ if ($propertyFetch->getAttribute(AttributeKey::IS_USED_AS_ARG_BY_REF_VALUE, \false) === \true) {
+ return \true;
+ }
+ return $propertyFetch->getAttribute(AttributeKey::IS_INCREMENT_OR_DECREMENT, \false) === \true;
+ }
}