From 5b59464b0a17c34b1f492898660e25cedb02841b Mon Sep 17 00:00:00 2001 From: NickSdot Date: Wed, 23 Jul 2025 09:29:18 +0700 Subject: [PATCH 1/2] feat: added `RemoveDeprecatedReflectionSetAccessibleCallsRector` --- config/set/php81.php | 2 + .../Fixture/fixture.php.inc | 43 +++++++++ ...ReflectionSetAccessibleCallsRectorTest.php | 30 +++++++ .../config/configured_rule.php | 13 +++ ...moveReflectionSetAccessibleCallsRector.php | 88 +++++++++++++++++++ 5 files changed, 176 insertions(+) create mode 100644 rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/Fixture/fixture.php.inc create mode 100644 rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php create mode 100644 rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/config/configured_rule.php create mode 100644 rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php diff --git a/config/set/php81.php b/config/set/php81.php index 17cbdcee746..b87051387c8 100644 --- a/config/set/php81.php +++ b/config/set/php81.php @@ -8,6 +8,7 @@ use Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector; use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector; use Rector\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector; +use Rector\Php81\Rector\MethodCall\RemoveReflectionSetAccessibleCallsRector; use Rector\Php81\Rector\MethodCall\SpatieEnumMethodCallToEnumConstRector; use Rector\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector; use Rector\Php81\Rector\Property\ReadOnlyPropertyRector; @@ -24,5 +25,6 @@ SpatieEnumMethodCallToEnumConstRector::class, NullToStrictStringFuncCallArgRector::class, FirstClassCallableRector::class, + RemoveReflectionSetAccessibleCallsRector::class, ]); }; diff --git a/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/Fixture/fixture.php.inc b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..c5ed1800193 --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/Fixture/fixture.php.inc @@ -0,0 +1,43 @@ +setAccessible(true); + $value = $reflectionProperty->getValue($this); + + $reflectionMethod = new ReflectionMethod($this, 'privateMethod'); + $reflectionMethod->setAccessible(false); + $reflectionMethod->invoke($this); + } +} + +?> +----- +getValue($this); + + $reflectionMethod = new ReflectionMethod($this, 'privateMethod'); + $reflectionMethod->invoke($this); + } +} + +?> diff --git a/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php new file mode 100644 index 00000000000..c90d31c68ff --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php @@ -0,0 +1,30 @@ +doTestFile($filePath); + } + + /** + * @return \Iterator> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/config/configured_rule.php b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/config/configured_rule.php new file mode 100644 index 00000000000..c15b8139eea --- /dev/null +++ b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(RemoveReflectionSetAccessibleCallsRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_85); +}; diff --git a/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php b/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php new file mode 100644 index 00000000000..e81c9b47bcd --- /dev/null +++ b/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php @@ -0,0 +1,88 @@ +> + */ + public function getNodeTypes(): array + { + return [Expression::class]; + } + + /** + * @param Expression $node + */ + public function refactor(Node $node): ?int + { + if ($node->expr instanceof MethodCall === false) { + return null; + } + + $methodCall = $node->expr; + + if ($this->isName($methodCall->name, 'setAccessible') === false) { + return null; + } + + if ($this->isObjectType($methodCall->var, new ObjectType('ReflectionProperty')) === true + || $this->isObjectType($methodCall->var, new ObjectType('ReflectionMethod')) === true + ) { + return NodeVisitor::REMOVE_NODE; + } + + return null; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Remove Reflection::setAccessible() calls', [ + new CodeSample( + <<<'CODE_SAMPLE' +$reflectionProperty = new ReflectionProperty($object, 'property'); +$reflectionProperty->setAccessible(true); +$value = $reflectionProperty->getValue($object); + +$reflectionMethod = new ReflectionMethod($object, 'method'); +$reflectionMethod->setAccessible(false); +$reflectionMethod->invoke($object); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +$reflectionProperty = new ReflectionProperty($object, 'property'); +$value = $reflectionProperty->getValue($object); + +$reflectionMethod = new ReflectionMethod($object, 'method'); +$reflectionMethod->invoke($object); +CODE_SAMPLE + ), + ]); + } + + public function provideMinPhpVersion(): int + { + return PhpVersion::PHP_81; + } +} From 4ed3b1057a99c0b63b4345b91bd002a51da6129e Mon Sep 17 00:00:00 2001 From: NickSdot Date: Wed, 23 Jul 2025 15:49:05 +0700 Subject: [PATCH 2/2] chore: ran rector in preparation for downgrade testing --- ...ReflectionSetAccessibleCallsRectorTest.php | 5 +- ...FromCallableToFirstClassCallableRector.php | 79 +++++++++++-------- ...moveReflectionSetAccessibleCallsRector.php | 4 +- src/Util/Reflection/PrivatesAccessor.php | 7 +- 4 files changed, 50 insertions(+), 45 deletions(-) diff --git a/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php index c90d31c68ff..b41e85eda5c 100644 --- a/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php +++ b/rules-tests/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector/RemoveReflectionSetAccessibleCallsRectorTest.php @@ -4,6 +4,7 @@ namespace Rector\Tests\Php81\Rector\MethodCall\RemoveReflectionSetAccessibleCallsRector; +use Iterator; use PHPUnit\Framework\Attributes\DataProvider; use Rector\Testing\PHPUnit\AbstractRectorTestCase; @@ -16,9 +17,9 @@ public function testRule(string $filePath): void } /** - * @return \Iterator> + * @return Iterator> */ - public static function provideData(): \Iterator + public static function provideData(): Iterator { return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); } diff --git a/rules/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector.php b/rules/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector.php index 047707a7803..de20cdee8b3 100644 --- a/rules/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector.php +++ b/rules/CodingStyle/Rector/FuncCall/ClosureFromCallableToFirstClassCallableRector.php @@ -4,6 +4,19 @@ namespace Rector\CodingStyle\Rector\FuncCall; +use PhpParser\Node\Expr\StaticCall; +use PhpParser\Node\Arg; +use PhpParser\Node\Scalar\String_; +use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\VariadicPlaceholder; +use PhpParser\Node\Expr\Array_; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\MethodCall; +use PhpParser\Node\Name\FullyQualified; +use PhpParser\Node\Expr\ClassConstFetch; +use PhpParser\Node\Expr; +use PhpParser\Node\Name; +use PhpParser\Node\Identifier; use PhpParser\Node; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; @@ -26,9 +39,9 @@ public function getRuleDefinition(): RuleDefinition 'Change `Closure::fromCallable()` to first class callable syntax', [ new CodeSample('Closure::fromCallable([$obj, \'method\']);', '$obj->method(...);'), - new CodeSample('Closure::fromCallable(\'trim\');', 'trim(...);'), + new CodeSample("Closure::fromCallable('trim');", 'trim(...);'), new CodeSample( - 'Closure::fromCallable([\'SomeClass\', \'staticMethod\']);', + "Closure::fromCallable(['SomeClass', 'staticMethod']);", 'SomeClass::staticMethod(...);' ), ] @@ -41,11 +54,11 @@ public function getRuleDefinition(): RuleDefinition */ public function getNodeTypes(): array { - return [Node\Expr\StaticCall::class]; + return [StaticCall::class]; } /** - * @param Node\Expr\StaticCall $node + * @param StaticCall $node */ public function refactor(Node $node): ?Node { @@ -54,52 +67,52 @@ public function refactor(Node $node): ?Node } $arg = $node->args[0]; - if (! $arg instanceof Node\Arg) { + if (! $arg instanceof Arg) { return null; } - if ($arg->value instanceof Node\Scalar\String_) { - return new Node\Expr\FuncCall( + if ($arg->value instanceof String_) { + return new FuncCall( $this->toFullyQualified($arg->value->value), - [new Node\VariadicPlaceholder()], + [new VariadicPlaceholder()], ); } - if ($arg->value instanceof Node\Expr\Array_) { + if ($arg->value instanceof Array_) { if ( ! array_key_exists(0, $arg->value->items) || ! array_key_exists(1, $arg->value->items) - || ! $arg->value->items[1]->value instanceof Node\Scalar\String_ + || ! $arg->value->items[1]->value instanceof String_ ) { return null; } - if ($arg->value->items[0]->value instanceof Node\Expr\Variable) { - return new Node\Expr\MethodCall( + if ($arg->value->items[0]->value instanceof Variable) { + return new MethodCall( $arg->value->items[0]->value, $arg->value->items[1]->value->value, - [new Node\VariadicPlaceholder()], + [new VariadicPlaceholder()], ); } - if ($arg->value->items[0]->value instanceof Node\Scalar\String_) { - $classNode = new Node\Name\FullyQualified($arg->value->items[0]->value->value); - } elseif ($arg->value->items[0]->value instanceof Node\Expr\ClassConstFetch) { - if ($arg->value->items[0]->value->class instanceof Node\Expr) { + if ($arg->value->items[0]->value instanceof String_) { + $classNode = new FullyQualified($arg->value->items[0]->value->value); + } elseif ($arg->value->items[0]->value instanceof ClassConstFetch) { + if ($arg->value->items[0]->value->class instanceof Expr) { return null; } - $classNode = new Node\Name\FullyQualified($arg->value->items[0]->value->class->name); - } elseif ($arg->value->items[0]->value instanceof Node\Name\FullyQualified) { - $classNode = new Node\Name\FullyQualified($arg->value->items[0]->value->name); + $classNode = new FullyQualified($arg->value->items[0]->value->class->name); + } elseif ($arg->value->items[0]->value instanceof FullyQualified) { + $classNode = new FullyQualified($arg->value->items[0]->value->name); } else { return null; } - return new Node\Expr\StaticCall( + return new StaticCall( $classNode, $arg->value->items[1]->value->value, - [new Node\VariadicPlaceholder()], + [new VariadicPlaceholder()], ); } @@ -111,37 +124,33 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::FIRST_CLASS_CALLABLE_SYNTAX; } - public function shouldSkip(Node\Expr\StaticCall $node): bool + public function shouldSkip(StaticCall $staticCall): bool { - if (! $node->class instanceof Node\Name) { + if (! $staticCall->class instanceof Name) { return true; } - if (! $this->isName($node->class, 'Closure')) { + if (! $this->isName($staticCall->class, 'Closure')) { return true; } - if (! $node->name instanceof Node\Identifier || $node->name->name !== 'fromCallable') { + if (! $staticCall->name instanceof Identifier || $staticCall->name->name !== 'fromCallable') { return true; } - if ($node->isFirstClassCallable()) { + if ($staticCall->isFirstClassCallable()) { return true; } - $args = $node->getArgs(); - if (count($args) !== 1) { - return true; - } - - return false; + $args = $staticCall->getArgs(); + return count($args) !== 1; } - public function toFullyQualified(string $functionName): Node\Name\FullyQualified + public function toFullyQualified(string $functionName): FullyQualified { // in case there's already a \ prefix, remove it $functionName = ltrim($functionName, '\\'); - return new Node\Name\FullyQualified($functionName); + return new FullyQualified($functionName); } } diff --git a/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php b/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php index e81c9b47bcd..a8d5e05abe4 100644 --- a/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php +++ b/rules/Php81/Rector/MethodCall/RemoveReflectionSetAccessibleCallsRector.php @@ -47,8 +47,8 @@ public function refactor(Node $node): ?int return null; } - if ($this->isObjectType($methodCall->var, new ObjectType('ReflectionProperty')) === true - || $this->isObjectType($methodCall->var, new ObjectType('ReflectionMethod')) === true + if ($this->isObjectType($methodCall->var, new ObjectType('ReflectionProperty')) + || $this->isObjectType($methodCall->var, new ObjectType('ReflectionMethod')) ) { return NodeVisitor::REMOVE_NODE; } diff --git a/src/Util/Reflection/PrivatesAccessor.php b/src/Util/Reflection/PrivatesAccessor.php index 0a67594c557..3d7512277ce 100644 --- a/src/Util/Reflection/PrivatesAccessor.php +++ b/src/Util/Reflection/PrivatesAccessor.php @@ -47,7 +47,6 @@ public function callPrivateMethod(object|string $object, string $methodName, arr public function getPrivateProperty(object $object, string $propertyName): mixed { $reflectionProperty = $this->resolvePropertyReflection($object, $propertyName); - $reflectionProperty->setAccessible(true); return $reflectionProperty->getValue($object); } @@ -55,17 +54,13 @@ public function getPrivateProperty(object $object, string $propertyName): mixed public function setPrivateProperty(object $object, string $propertyName, mixed $value): void { $reflectionProperty = $this->resolvePropertyReflection($object, $propertyName); - $reflectionProperty->setAccessible(true); $reflectionProperty->setValue($object, $value); } private function createAccessibleMethodReflection(object $object, string $methodName): ReflectionMethod { - $reflectionMethod = new ReflectionMethod($object, $methodName); - $reflectionMethod->setAccessible(true); - - return $reflectionMethod; + return new ReflectionMethod($object, $methodName); } private function resolvePropertyReflection(object $object, string $propertyName): ReflectionProperty