diff --git a/rules/Php81/Rector/Property/ReadOnlyPropertyRector.php b/rules/Php81/Rector/Property/ReadOnlyPropertyRector.php index da3a5953b9e..79c72b08015 100644 --- a/rules/Php81/Rector/Property/ReadOnlyPropertyRector.php +++ b/rules/Php81/Rector/Property/ReadOnlyPropertyRector.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Property; +use Rector\Core\NodeManipulator\PropertyFetchAssignManipulator; use Rector\Core\NodeManipulator\PropertyManipulator; use Rector\Core\Rector\AbstractRector; use Rector\Core\ValueObject\PhpVersionFeature; @@ -28,14 +29,20 @@ final class ReadOnlyPropertyRector extends \Rector\Core\Rector\AbstractRector im * @var \Rector\Core\NodeManipulator\PropertyManipulator */ private $propertyManipulator; + /** + * @readonly + * @var \Rector\Core\NodeManipulator\PropertyFetchAssignManipulator + */ + private $propertyFetchAssignManipulator; /** * @readonly * @var \Rector\Privatization\NodeManipulator\VisibilityManipulator */ private $visibilityManipulator; - public function __construct(\Rector\Core\NodeManipulator\PropertyManipulator $propertyManipulator, \Rector\Privatization\NodeManipulator\VisibilityManipulator $visibilityManipulator) + public function __construct(\Rector\Core\NodeManipulator\PropertyManipulator $propertyManipulator, \Rector\Core\NodeManipulator\PropertyFetchAssignManipulator $propertyFetchAssignManipulator, \Rector\Privatization\NodeManipulator\VisibilityManipulator $visibilityManipulator) { $this->propertyManipulator = $propertyManipulator; + $this->propertyFetchAssignManipulator = $propertyFetchAssignManipulator; $this->visibilityManipulator = $visibilityManipulator; } public function getRuleDefinition() : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition @@ -112,6 +119,9 @@ CODE_SAMPLE if ($property->isStatic()) { return null; } + if ($this->propertyFetchAssignManipulator->isAssignedMultipleTimesInConstructor($property)) { + return null; + } $this->visibilityManipulator->makeReadonly($property); $attributeGroups = $property->attrGroups; if ($attributeGroups !== []) { diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index 44a7a454fab..d693815762b 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -16,11 +16,11 @@ final class VersionResolver /** * @var string */ - public const PACKAGE_VERSION = '996caa3f1f7c92c3b811cf51eb2fb887bb27d230'; + public const PACKAGE_VERSION = 'c7ff21825ec2c480bba1f09f09d2d650b37de5b4'; /** * @var string */ - public const RELEASE_DATE = '2022-04-17 14:01:44'; + public const RELEASE_DATE = '2022-04-17 22:17:24'; /** * @var string */ diff --git a/src/NodeManipulator/PropertyFetchAssignManipulator.php b/src/NodeManipulator/PropertyFetchAssignManipulator.php index 4fd2893e5b3..f360d9a3681 100644 --- a/src/NodeManipulator/PropertyFetchAssignManipulator.php +++ b/src/NodeManipulator/PropertyFetchAssignManipulator.php @@ -7,6 +7,13 @@ use PhpParser\Node; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Stmt\ClassLike; +use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Property; +use PhpParser\NodeTraverser; +use Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer; +use Rector\Core\PhpParser\Node\BetterNodeFinder; +use Rector\Core\ValueObject\MethodName; use Rector\NodeNameResolver\NodeNameResolver; use RectorPrefix20220417\Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; final class PropertyFetchAssignManipulator @@ -21,10 +28,53 @@ final class PropertyFetchAssignManipulator * @var \Rector\NodeNameResolver\NodeNameResolver */ private $nodeNameResolver; - public function __construct(\RectorPrefix20220417\Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser $simpleCallableNodeTraverser, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver) + /** + * @readonly + * @var \Rector\Core\PhpParser\Node\BetterNodeFinder + */ + private $betterNodeFinder; + /** + * @readonly + * @var \Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer + */ + private $propertyFetchAnalyzer; + public function __construct(\RectorPrefix20220417\Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser $simpleCallableNodeTraverser, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder, \Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer $propertyFetchAnalyzer) { $this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser; $this->nodeNameResolver = $nodeNameResolver; + $this->betterNodeFinder = $betterNodeFinder; + $this->propertyFetchAnalyzer = $propertyFetchAnalyzer; + } + public function isAssignedMultipleTimesInConstructor(\PhpParser\Node\Stmt\Property $property) : bool + { + $classLike = $this->betterNodeFinder->findParentType($property, \PhpParser\Node\Stmt\ClassLike::class); + if (!$classLike instanceof \PhpParser\Node\Stmt\ClassLike) { + return \false; + } + $classMethod = $classLike->getMethod(\Rector\Core\ValueObject\MethodName::CONSTRUCT); + if (!$classMethod instanceof \PhpParser\Node\Stmt\ClassMethod) { + return \false; + } + $count = 0; + $propertyName = $this->nodeNameResolver->getName($property); + $this->simpleCallableNodeTraverser->traverseNodesWithCallable((array) $classMethod->getStmts(), function (\PhpParser\Node $node) use($propertyName, $classLike, &$count) : ?int { + if (!$node instanceof \PhpParser\Node\Expr\Assign) { + return null; + } + if (!$this->propertyFetchAnalyzer->isLocalPropertyFetchName($node->var, $propertyName)) { + return null; + } + $parentClassLike = $this->betterNodeFinder->findParentType($node, \PhpParser\Node\Stmt\ClassLike::class); + if ($parentClassLike !== $classLike) { + return null; + } + ++$count; + if ($count === 2) { + return \PhpParser\NodeTraverser::STOP_TRAVERSAL; + } + return null; + }); + return $count === 2; } /** * @return string[] diff --git a/vendor/autoload.php b/vendor/autoload.php index a3b5b23207d..a646b9db5c3 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -9,4 +9,4 @@ if (PHP_VERSION_ID < 50600) { require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3::getLoader(); +return ComposerAutoloaderInit38105230ef14f0ae3fc27a020c0e613e::getLoader(); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index 56f086ccb69..68c047ffc3c 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3 +class ComposerAutoloaderInit38105230ef14f0ae3fc27a020c0e613e { private static $loader; @@ -22,19 +22,19 @@ class ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3 return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit38105230ef14f0ae3fc27a020c0e613e', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit38105230ef14f0ae3fc27a020c0e613e', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit3c58f3adbe619e0ef433a8501fb18de3::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit38105230ef14f0ae3fc27a020c0e613e::getInitializer($loader)); $loader->setClassMapAuthoritative(true); $loader->register(true); - $includeFiles = \Composer\Autoload\ComposerStaticInit3c58f3adbe619e0ef433a8501fb18de3::$files; + $includeFiles = \Composer\Autoload\ComposerStaticInit38105230ef14f0ae3fc27a020c0e613e::$files; foreach ($includeFiles as $fileIdentifier => $file) { - composerRequire3c58f3adbe619e0ef433a8501fb18de3($fileIdentifier, $file); + composerRequire38105230ef14f0ae3fc27a020c0e613e($fileIdentifier, $file); } return $loader; @@ -46,7 +46,7 @@ class ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3 * @param string $file * @return void */ -function composerRequire3c58f3adbe619e0ef433a8501fb18de3($fileIdentifier, $file) +function composerRequire38105230ef14f0ae3fc27a020c0e613e($fileIdentifier, $file) { if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index bd7c28d85c2..fab5410e4cc 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInit3c58f3adbe619e0ef433a8501fb18de3 +class ComposerStaticInit38105230ef14f0ae3fc27a020c0e613e { public static $files = array ( '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', @@ -3865,9 +3865,9 @@ class ComposerStaticInit3c58f3adbe619e0ef433a8501fb18de3 public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit3c58f3adbe619e0ef433a8501fb18de3::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit3c58f3adbe619e0ef433a8501fb18de3::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit3c58f3adbe619e0ef433a8501fb18de3::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit38105230ef14f0ae3fc27a020c0e613e::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit38105230ef14f0ae3fc27a020c0e613e::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit38105230ef14f0ae3fc27a020c0e613e::$classMap; }, null, ClassLoader::class); } diff --git a/vendor/scoper-autoload.php b/vendor/scoper-autoload.php index 3a37ff0577d..d72862e81e0 100644 --- a/vendor/scoper-autoload.php +++ b/vendor/scoper-autoload.php @@ -9,8 +9,8 @@ $loader = require_once __DIR__.'/autoload.php'; if (!class_exists('AutoloadIncluder', false) && !interface_exists('AutoloadIncluder', false) && !trait_exists('AutoloadIncluder', false)) { spl_autoload_call('RectorPrefix20220417\AutoloadIncluder'); } -if (!class_exists('ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3', false) && !interface_exists('ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3', false) && !trait_exists('ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3', false)) { - spl_autoload_call('RectorPrefix20220417\ComposerAutoloaderInit3c58f3adbe619e0ef433a8501fb18de3'); +if (!class_exists('ComposerAutoloaderInit38105230ef14f0ae3fc27a020c0e613e', false) && !interface_exists('ComposerAutoloaderInit38105230ef14f0ae3fc27a020c0e613e', false) && !trait_exists('ComposerAutoloaderInit38105230ef14f0ae3fc27a020c0e613e', false)) { + spl_autoload_call('RectorPrefix20220417\ComposerAutoloaderInit38105230ef14f0ae3fc27a020c0e613e'); } if (!class_exists('Helmich\TypoScriptParser\Parser\AST\Statement', false) && !interface_exists('Helmich\TypoScriptParser\Parser\AST\Statement', false) && !trait_exists('Helmich\TypoScriptParser\Parser\AST\Statement', false)) { spl_autoload_call('RectorPrefix20220417\Helmich\TypoScriptParser\Parser\AST\Statement'); @@ -59,9 +59,9 @@ if (!function_exists('print_node')) { return \RectorPrefix20220417\print_node(...func_get_args()); } } -if (!function_exists('composerRequire3c58f3adbe619e0ef433a8501fb18de3')) { - function composerRequire3c58f3adbe619e0ef433a8501fb18de3() { - return \RectorPrefix20220417\composerRequire3c58f3adbe619e0ef433a8501fb18de3(...func_get_args()); +if (!function_exists('composerRequire38105230ef14f0ae3fc27a020c0e613e')) { + function composerRequire38105230ef14f0ae3fc27a020c0e613e() { + return \RectorPrefix20220417\composerRequire38105230ef14f0ae3fc27a020c0e613e(...func_get_args()); } } if (!function_exists('scanPath')) {