From b2626a4873211031ce282a219df4bcd6901a0891 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 1 Jan 2025 19:15:13 +0700 Subject: [PATCH 1/5] [DowngradePhp82] Add DowngradeIteratorCountToArrayRector --- ...owngradeIteratorCountToArrayRectorTest.php | 28 +++++ .../Fixture/possible_not_traversable.php.inc | 27 ++++ .../skip_already_has_object_type.php.inc | 11 ++ .../Fixture/skip_already_traversable.php.inc | 12 ++ .../config/configured_rule.php | 10 ++ .../DowngradeIteratorCountToArrayRector.php | 118 ++++++++++++++++++ 6 files changed, 206 insertions(+) create mode 100644 rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/DowngradeIteratorCountToArrayRectorTest.php create mode 100644 rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/Fixture/possible_not_traversable.php.inc create mode 100644 rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/Fixture/skip_already_has_object_type.php.inc create mode 100644 rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/Fixture/skip_already_traversable.php.inc create mode 100644 rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/config/configured_rule.php create mode 100644 rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php diff --git a/rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/DowngradeIteratorCountToArrayRectorTest.php b/rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/DowngradeIteratorCountToArrayRectorTest.php new file mode 100644 index 00000000..c64d2a55 --- /dev/null +++ b/rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/DowngradeIteratorCountToArrayRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + 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/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/Fixture/possible_not_traversable.php.inc b/rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/Fixture/possible_not_traversable.php.inc new file mode 100644 index 00000000..50326ac7 --- /dev/null +++ b/rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/Fixture/possible_not_traversable.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/Fixture/skip_already_has_object_type.php.inc b/rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/Fixture/skip_already_has_object_type.php.inc new file mode 100644 index 00000000..dadee299 --- /dev/null +++ b/rules-tests/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector/Fixture/skip_already_has_object_type.php.inc @@ -0,0 +1,11 @@ +rule(DowngradeIteratorCountToArrayRector::class); +}; diff --git a/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php b/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php new file mode 100644 index 00000000..a7720ad5 --- /dev/null +++ b/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php @@ -0,0 +1,118 @@ +> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition( + 'Ensure pass Traversable instance before use in iterator_count() and iterator_to_array()', + [ + new CodeSample( + <<<'CODE_SAMPLE' +function test(array|Traversable $data) { + $c = iterator_count($data); + $v = iterator_to_array($data); +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +function test(array|Traversable $data) { + $c = iterator_count(is_array($data) ? new ArrayIterator($data) : $data); + $v = iterator_to_array(is_array($data) ? new ArrayIterator($data) : $data); +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isNames($node, ['iterator_count', 'iterator_to_array'])) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + $args = $node->getArgs(); + if ($this->argsAnalyzer->hasNamedArg($args)) { + return null; + } + + if (! isset($args[0])) { + return null; + } + + $type = $this->nodeTypeResolver->getType($args[0]->value); + if ($this->shouldSkip($type, $args[0]->value)) { + return null; + } + + $firstValue = $node->args[0]->value; + $node->args[0]->value = new Ternary( + $this->nodeFactory->createFuncCall('is_array', [new Arg($firstValue)]), + new New_(new FullyQualified('ArrayIterator'), [new Arg($firstValue)]), + $firstValue + ); + + return $node; + } + + private function shouldSkip(Type $type, Expr $expr): bool + { + // only array type + if ($type->isArray()->yes()) { + return false; + } + + if ($type instanceof UnionType) { + // possibly already transformed + return $expr instanceof Ternary; + } + + // already has object type check + return $type->isObject()->yes(); + } +} From c8b39ed570e644900a74afd73c402365910ffae5 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 1 Jan 2025 19:18:25 +0700 Subject: [PATCH 2/5] cs fix --- .../Rector/FuncCall/DowngradeIteratorCountToArrayRector.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php b/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php index a7720ad5..a80291b2 100644 --- a/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php +++ b/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php @@ -113,6 +113,7 @@ private function shouldSkip(Type $type, Expr $expr): bool } // already has object type check - return $type->isObject()->yes(); + return $type->isObject() + ->yes(); } } From 55165c0928f5fb637e3172f47e4715f788b12145 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 1 Jan 2025 19:21:40 +0700 Subject: [PATCH 3/5] Fix phpstan --- .../Rector/FuncCall/DowngradeIteratorCountToArrayRector.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php b/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php index a80291b2..d580f711 100644 --- a/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php +++ b/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php @@ -17,6 +17,7 @@ use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @changelog https://www.php.net/manual/en/migration82.other-changes.php#migration82.other-changes.functions.spl @@ -90,6 +91,8 @@ public function refactor(Node $node): ?Node return null; } + Assert::isInstanceOf($node->args[0], Arg::class); + $firstValue = $node->args[0]->value; $node->args[0]->value = new Ternary( $this->nodeFactory->createFuncCall('is_array', [new Arg($firstValue)]), From ed7aabfc91a64373221d61018e52051ab422b7f0 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 1 Jan 2025 19:23:28 +0700 Subject: [PATCH 4/5] Fix phpstan --- config/set/downgrade-php82.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/set/downgrade-php82.php b/config/set/downgrade-php82.php index f2c4e5f0..c4b78c20 100644 --- a/config/set/downgrade-php82.php +++ b/config/set/downgrade-php82.php @@ -5,12 +5,14 @@ use Rector\Config\RectorConfig; use Rector\ValueObject\PhpVersion; use Rector\DowngradePhp82\Rector\Class_\DowngradeReadonlyClassRector; +use Rector\DowngradePhp82\Rector\FuncCall\DowngradeIteratorCountToArrayRector; use Rector\DowngradePhp82\Rector\FunctionLike\DowngradeStandaloneNullTrueFalseReturnTypeRector; return static function (RectorConfig $rectorConfig): void { $rectorConfig->phpVersion(PhpVersion::PHP_81); $rectorConfig->rules([ DowngradeReadonlyClassRector::class, - DowngradeStandaloneNullTrueFalseReturnTypeRector::class + DowngradeStandaloneNullTrueFalseReturnTypeRector::class, + DowngradeIteratorCountToArrayRector::class, ]); }; From c73c21035c2df5478a18f32dba22f15c53cc6443 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Wed, 1 Jan 2025 19:26:53 +0700 Subject: [PATCH 5/5] fix --- .../DowngradeIteratorCountToArrayRector.php | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php b/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php index d580f711..25c45763 100644 --- a/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php +++ b/rules/DowngradePhp82/Rector/FuncCall/DowngradeIteratorCountToArrayRector.php @@ -6,13 +6,11 @@ use PhpParser\Node; use PhpParser\Node\Arg; -use PhpParser\Node\Expr; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Name\FullyQualified; use PHPStan\Type\Type; -use PHPStan\Type\UnionType; use Rector\NodeAnalyzer\ArgsAnalyzer; use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; @@ -87,7 +85,7 @@ public function refactor(Node $node): ?Node } $type = $this->nodeTypeResolver->getType($args[0]->value); - if ($this->shouldSkip($type, $args[0]->value)) { + if ($this->shouldSkip($type)) { return null; } @@ -103,19 +101,12 @@ public function refactor(Node $node): ?Node return $node; } - private function shouldSkip(Type $type, Expr $expr): bool + private function shouldSkip(Type $type): bool { - // only array type if ($type->isArray()->yes()) { return false; } - if ($type instanceof UnionType) { - // possibly already transformed - return $expr instanceof Ternary; - } - - // already has object type check return $type->isObject() ->yes(); }