diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index a36a1c17ca8..48194ea860f 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 412 Rules Overview +# 413 Rules Overview
@@ -48,7 +48,7 @@ - [Php81](#php81) (11) -- [Php82](#php82) (1) +- [Php82](#php82) (2) - [Privatization](#privatization) (8) @@ -596,10 +596,25 @@ Change multiple null compares to ?? queue ### ConvertStaticPrivateConstantToSelfRector -Replaces static::* access to private constants with self::* on final classes +Replaces static::* access to private constants with self::* + +:wrench: **configure it!** - class: [`Rector\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector`](../rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php) +```php +use Rector\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector; +use Rector\Config\RectorConfig; + +return static function (RectorConfig $rectorConfig): void { + $rectorConfig->ruleWithConfiguration(ConvertStaticPrivateConstantToSelfRector::class, [ + ConvertStaticPrivateConstantToSelfRector::ENABLE_FOR_NON_FINAL_CLASSES => false, + ]); +}; +``` + +↓ + ```diff final class Foo { private const BAR = 'bar'; @@ -6511,6 +6526,21 @@ Decorate read-only class with `readonly` attribute
+### Utf8DecodeEncodeToMbConvertEncodingRector + +Change deprecated utf8_decode and utf8_encode to mb_convert_encoding + +- class: [`Rector\Php82\Rector\FuncCall\Utf8DecodeEncodeToMbConvertEncodingRector`](../rules/Php82/Rector/FuncCall/Utf8DecodeEncodeToMbConvertEncodingRector.php) + +```diff +-utf8_decode($value); +-utf8_encode($value); ++mb_convert_encoding($value, 'ISO-8859-1'); ++mb_convert_encoding($value, 'UTF-8', 'ISO-8859-1'); +``` + +
+ ## Privatization ### ChangeGlobalVariablesToPropertiesRector diff --git a/rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php b/rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php index 52b6154a159..450598b2e37 100644 --- a/rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php +++ b/rules/CodeQuality/Rector/ClassConstFetch/ConvertStaticPrivateConstantToSelfRector.php @@ -8,19 +8,47 @@ use PhpParser\Node\Expr\ClassConstFetch; use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; +use PHPStan\Reflection\ClassReflection; +use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface; use Rector\Core\Rector\AbstractRector; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Rector\Core\Reflection\ReflectionResolver; +use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\CodeQuality\Rector\ClassConstFetch\ConvertStaticPrivateConstantToSelfRector\ConvertStaticPrivateConstantToSelfRectorTest * @see https://3v4l.org/8Y0ba * @see https://phpstan.org/r/11d4c850-1a40-4fae-b665-291f96104d11 */ -final class ConvertStaticPrivateConstantToSelfRector extends AbstractRector +final class ConvertStaticPrivateConstantToSelfRector extends AbstractRector implements AllowEmptyConfigurableRectorInterface { + /** + * @api + * @var string + */ + public const ENABLE_FOR_NON_FINAL_CLASSES = 'enable_for_non_final_classes'; + /** + * @var bool + */ + private $enableForNonFinalClasses = \false; + /** + * @readonly + * @var \Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer + */ + private $familyRelationsAnalyzer; + /** + * @readonly + * @var \Rector\Core\Reflection\ReflectionResolver + */ + private $reflectionResolver; + public function __construct(FamilyRelationsAnalyzer $familyRelationsAnalyzer, ReflectionResolver $reflectionResolver) + { + $this->familyRelationsAnalyzer = $familyRelationsAnalyzer; + $this->reflectionResolver = $reflectionResolver; + } public function getRuleDefinition() : RuleDefinition { - return new RuleDefinition('Replaces static::* access to private constants with self::* on final classes', [new CodeSample(<<<'CODE_SAMPLE' + return new RuleDefinition('Replaces static::* access to private constants with self::*', [new ConfiguredCodeSample(<<<'CODE_SAMPLE' final class Foo { private const BAR = 'bar'; public function run() @@ -38,21 +66,26 @@ final class Foo { } } CODE_SAMPLE -)]); +, [self::ENABLE_FOR_NON_FINAL_CLASSES => \false])]); } public function getNodeTypes() : array { return [ClassConstFetch::class]; } + public function configure(array $configuration) : void + { + $this->enableForNonFinalClasses = $configuration[self::ENABLE_FOR_NON_FINAL_CLASSES] ?? (bool) \current($configuration); + } /** * @param ClassConstFetch $node */ public function refactor(Node $node) : ?ClassConstFetch { - if (!$this->isUsingStatic($node)) { + $class = $this->betterNodeFinder->findParentType($node, Class_::class); + if (!$class instanceof Class_) { return null; } - if (!$this->isPrivateConstant($node)) { + if ($this->shouldBeSkipped($class, $node)) { return null; } $node->class = new Name('self'); @@ -65,25 +98,68 @@ CODE_SAMPLE } return $classConstFetch->class->toString() === 'static'; } - private function isPrivateConstant(ClassConstFetch $classConstFetch) : bool + private function isPrivateConstant(ClassConstFetch $constant, Class_ $class) : bool { - $class = $this->betterNodeFinder->findParentType($classConstFetch, Class_::class); - if (!$class instanceof Class_) { - return \false; - } - if (!$class->isFinal()) { - return \false; - } - $constantName = $classConstFetch->name; - if (!$constantName instanceof Identifier) { + $constantName = $this->getConstantName($constant); + if ($constantName === null) { return \false; } foreach ($class->getConstants() as $classConst) { - if (!$this->nodeNameResolver->isName($classConst, $constantName->toString())) { + if (!$this->nodeNameResolver->isName($classConst, $constantName)) { continue; } return $classConst->isPrivate(); } return \false; } + private function isUsedInPrivateMethod(ClassConstFetch $node) : bool + { + $method = $this->betterNodeFinder->findParentType($node, Node\Stmt\ClassMethod::class); + if (!$method instanceof Node\Stmt\ClassMethod) { + return \false; + } + return $method->flags === Class_::MODIFIER_PRIVATE; + } + private function shouldBeSkipped(Class_ $class, ClassConstFetch $classConstFetch) : bool + { + if (!$this->isUsingStatic($classConstFetch)) { + return \true; + } + if (!$this->isPrivateConstant($classConstFetch, $class)) { + return \true; + } + if ($this->isUsedInPrivateMethod($classConstFetch)) { + return \false; + } + if ($this->enableForNonFinalClasses) { + return $this->isOverwrittenInChildClass($classConstFetch); + } + return !$class->isFinal(); + } + private function isOverwrittenInChildClass(ClassConstFetch $classConstFetch) : bool + { + $constantName = $this->getConstantName($classConstFetch); + if ($constantName === null) { + return \false; + } + $classReflection = $this->reflectionResolver->resolveClassReflection($classConstFetch); + if (!$classReflection instanceof ClassReflection) { + return \false; + } + $childrenClassReflections = $this->familyRelationsAnalyzer->getChildrenOfClassReflection($classReflection); + foreach ($childrenClassReflections as $childrenClassReflection) { + if ($childrenClassReflection->hasConstant($constantName)) { + return \true; + } + } + return \false; + } + private function getConstantName(ClassConstFetch $classConstFetch) : ?string + { + $constantNameIdentifier = $classConstFetch->name; + if (!$constantNameIdentifier instanceof Identifier) { + return null; + } + return $constantNameIdentifier->toString(); + } } diff --git a/src/Application/VersionResolver.php b/src/Application/VersionResolver.php index 9c00313abf0..f1ef1e5d1d8 100644 --- a/src/Application/VersionResolver.php +++ b/src/Application/VersionResolver.php @@ -17,12 +17,12 @@ final class VersionResolver * @api * @var string */ - public const PACKAGE_VERSION = '14e60b06039ff4af2e8d4670390fdcf8c66f5cbe'; + public const PACKAGE_VERSION = '21c9e59aaaae15e1baac98b80b6d3e3be5c5d88d'; /** * @api * @var string */ - public const RELEASE_DATE = '2022-12-17 09:28:50'; + public const RELEASE_DATE = '2022-12-17 09:29:16'; /** * @var int */ diff --git a/vendor/autoload.php b/vendor/autoload.php index f2642bec1d8..b25f3c6f87f 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) { require_once __DIR__ . '/composer/autoload_real.php'; -return ComposerAutoloaderInit1e073b5cc27535a793f501c33ad1391b::getLoader(); +return ComposerAutoloaderInit86de0d5c87da90d3143aeb13739f358e::getLoader(); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index e9294a1732b..f924fdbc95e 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -2,7 +2,7 @@ // autoload_real.php @generated by Composer -class ComposerAutoloaderInit1e073b5cc27535a793f501c33ad1391b +class ComposerAutoloaderInit86de0d5c87da90d3143aeb13739f358e { private static $loader; @@ -22,19 +22,19 @@ class ComposerAutoloaderInit1e073b5cc27535a793f501c33ad1391b return self::$loader; } - spl_autoload_register(array('ComposerAutoloaderInit1e073b5cc27535a793f501c33ad1391b', 'loadClassLoader'), true, true); + spl_autoload_register(array('ComposerAutoloaderInit86de0d5c87da90d3143aeb13739f358e', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); - spl_autoload_unregister(array('ComposerAutoloaderInit1e073b5cc27535a793f501c33ad1391b', 'loadClassLoader')); + spl_autoload_unregister(array('ComposerAutoloaderInit86de0d5c87da90d3143aeb13739f358e', 'loadClassLoader')); require __DIR__ . '/autoload_static.php'; - call_user_func(\Composer\Autoload\ComposerStaticInit1e073b5cc27535a793f501c33ad1391b::getInitializer($loader)); + call_user_func(\Composer\Autoload\ComposerStaticInit86de0d5c87da90d3143aeb13739f358e::getInitializer($loader)); $loader->setClassMapAuthoritative(true); $loader->register(true); - $includeFiles = \Composer\Autoload\ComposerStaticInit1e073b5cc27535a793f501c33ad1391b::$files; + $includeFiles = \Composer\Autoload\ComposerStaticInit86de0d5c87da90d3143aeb13739f358e::$files; foreach ($includeFiles as $fileIdentifier => $file) { - composerRequire1e073b5cc27535a793f501c33ad1391b($fileIdentifier, $file); + composerRequire86de0d5c87da90d3143aeb13739f358e($fileIdentifier, $file); } return $loader; @@ -46,7 +46,7 @@ class ComposerAutoloaderInit1e073b5cc27535a793f501c33ad1391b * @param string $file * @return void */ -function composerRequire1e073b5cc27535a793f501c33ad1391b($fileIdentifier, $file) +function composerRequire86de0d5c87da90d3143aeb13739f358e($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 a47b10a9cf4..90d0c172ab7 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -4,7 +4,7 @@ namespace Composer\Autoload; -class ComposerStaticInit1e073b5cc27535a793f501c33ad1391b +class ComposerStaticInit86de0d5c87da90d3143aeb13739f358e { public static $files = array ( 'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php', @@ -3054,9 +3054,9 @@ class ComposerStaticInit1e073b5cc27535a793f501c33ad1391b public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { - $loader->prefixLengthsPsr4 = ComposerStaticInit1e073b5cc27535a793f501c33ad1391b::$prefixLengthsPsr4; - $loader->prefixDirsPsr4 = ComposerStaticInit1e073b5cc27535a793f501c33ad1391b::$prefixDirsPsr4; - $loader->classMap = ComposerStaticInit1e073b5cc27535a793f501c33ad1391b::$classMap; + $loader->prefixLengthsPsr4 = ComposerStaticInit86de0d5c87da90d3143aeb13739f358e::$prefixLengthsPsr4; + $loader->prefixDirsPsr4 = ComposerStaticInit86de0d5c87da90d3143aeb13739f358e::$prefixDirsPsr4; + $loader->classMap = ComposerStaticInit86de0d5c87da90d3143aeb13739f358e::$classMap; }, null, ClassLoader::class); }