diff --git a/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/on_get_methods.php.inc b/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/on_get_methods.php.inc new file mode 100644 index 00000000..c89c7679 --- /dev/null +++ b/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/on_get_methods.php.inc @@ -0,0 +1,36 @@ +getMethods() as $reflectionMethod) { + $reflectionMethod->invoke($object); + } + } +} + +?> +----- +getMethods() as $reflectionMethod) { + if (PHP_VERSION_ID < 80100) { + $reflectionMethod->setAccessible(true); + } + $reflectionMethod->invoke($object); + } + } +} + +?> diff --git a/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/on_get_properties.php.inc b/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/on_get_properties.php.inc new file mode 100644 index 00000000..11fa6c57 --- /dev/null +++ b/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/on_get_properties.php.inc @@ -0,0 +1,36 @@ +getProperties() as $reflectionProperty) { + echo $reflectionProperty->getValue($object); + } + } +} + +?> +----- +getProperties() as $reflectionProperty) { + if (PHP_VERSION_ID < 80100) { + $reflectionProperty->setAccessible(true); + } + echo $reflectionProperty->getValue($object); + } + } +} + +?> diff --git a/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/skip_already_accessible_in_foreach.php.inc b/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/skip_already_accessible_in_foreach.php.inc new file mode 100644 index 00000000..e3f48330 --- /dev/null +++ b/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/skip_already_accessible_in_foreach.php.inc @@ -0,0 +1,17 @@ +getProperties() as $reflectionProperty) { + if (PHP_VERSION_ID < 80100) { + $reflectionProperty->setAccessible(true); + } + echo $reflectionProperty->getValue($object); + } + } +} diff --git a/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/skip_non_reflection_get_properties.php.inc b/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/skip_non_reflection_get_properties.php.inc new file mode 100644 index 00000000..11f5577d --- /dev/null +++ b/rules-tests/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector/Fixture/skip_non_reflection_get_properties.php.inc @@ -0,0 +1,22 @@ +getProperties() as $property) { + echo $property; + } + } +} diff --git a/rules/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector.php b/rules/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector.php index 232a4113..e518efe8 100644 --- a/rules/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector.php +++ b/rules/DowngradePhp81/Rector/StmtsAwareInterface/DowngradeSetAccessibleReflectionPropertyRector.php @@ -16,8 +16,10 @@ use PhpParser\Node\Scalar\Int_; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Foreach_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; +use PHPStan\Type\ObjectType; use Rector\Naming\Naming\VariableNaming; use Rector\PhpParser\Enum\NodeGroup; use Rector\PHPStan\ScopeFetcher; @@ -69,6 +71,38 @@ public function run($object) return $reflectionProperty->getValue($object); } } +CODE_SAMPLE + ), + + new CodeSample( + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run($object) + { + $reflectionObject = new ReflectionObject($object); + foreach ($reflectionObject->getProperties() as $reflectionProperty) { + echo $reflectionProperty->getValue($object); + } + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +class SomeClass +{ + public function run($object) + { + $reflectionObject = new ReflectionObject($object); + foreach ($reflectionObject->getProperties() as $reflectionProperty) { + if (PHP_VERSION_ID < 80100) { + $reflectionProperty->setAccessible(true); + } + echo $reflectionProperty->getValue($object); + } + } +} CODE_SAMPLE ), @@ -96,6 +130,14 @@ public function refactor(Node $node): ?Node $hasChanged = false; foreach ($node->stmts as $key => $stmt) { + if ($stmt instanceof Foreach_) { + if ($this->refactorForeach($stmt)) { + $hasChanged = true; + } + + continue; + } + if (! $stmt instanceof Expression && ! $stmt instanceof Return_) { continue; } @@ -158,6 +200,45 @@ public function refactor(Node $node): ?Node return null; } + private function refactorForeach(Foreach_ $foreach): bool + { + if (! $foreach->valueVar instanceof Variable) { + return false; + } + + if (! $this->isReflectionMembersCall($foreach->expr)) { + return false; + } + + $firstStmt = $foreach->stmts[0] ?? null; + if ($this->isSetAccessibleMethodCall($firstStmt) || $this->isSetAccessibleIfMethodCall($firstStmt)) { + return false; + } + + array_unshift($foreach->stmts, $this->createSetAccessibleExpression($foreach->valueVar)); + + return true; + } + + private function isReflectionMembersCall(Expr $expr): bool + { + if (! $expr instanceof MethodCall) { + return false; + } + + if (! $this->isNames($expr->name, ['getProperties', 'getMethods'])) { + return false; + } + + $callerType = $this->nodeTypeResolver->getType($expr->var); + if (! $callerType instanceof ObjectType) { + return false; + } + + return $callerType->isInstanceOf('ReflectionClass') + ->yes(); + } + private function createSetAccessibleExpression(Expr $expr): If_ { $args = [$this->nodeFactory->createArg($this->nodeFactory->createTrue())];