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);
}