From 8b4be1f01cbbadd4aa0b16ca27e9403db7e97506 Mon Sep 17 00:00:00 2001 From: Benedikt Franke Date: Fri, 10 Jan 2025 09:31:28 +0100 Subject: [PATCH] Add UnaliasCollectionMethodsRector --- composer.json | 1 + config/sets/laravel-collection.php | 2 + docs/rector_rules_overview.md | 20 ++++- .../UnaliasCollectionMethodsRector.php | 81 +++++++++++++++++++ stubs/Illuminate/Support/Enumerable.php | 26 ++++++ .../config/configured_rule.php | 1 - .../Fixture/fixture.php.inc | 23 ++++++ .../UnaliasCollectionMethodsRectorTest.php | 31 +++++++ .../config/configured_rule.php | 11 +++ 9 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 src/Rector/MethodCall/UnaliasCollectionMethodsRector.php create mode 100644 tests/Rector/MethodCall/UnaliasCollectionMethodsRector/Fixture/fixture.php.inc create mode 100644 tests/Rector/MethodCall/UnaliasCollectionMethodsRector/UnaliasCollectionMethodsRectorTest.php create mode 100644 tests/Rector/MethodCall/UnaliasCollectionMethodsRector/config/configured_rule.php diff --git a/composer.json b/composer.json index 18b40c31..5988682b 100644 --- a/composer.json +++ b/composer.json @@ -33,6 +33,7 @@ }, "scripts": { "phpstan": "vendor/bin/phpstan analyse --ansi", + "test": "vendor/bin/phpunit tests", "lint": "vendor/bin/duster lint", "fix": "vendor/bin/duster fix", "rector-dry-run": "vendor/bin/rector process --dry-run --ansi", diff --git a/config/sets/laravel-collection.php b/config/sets/laravel-collection.php index c7618c90..7b28ff95 100644 --- a/config/sets/laravel-collection.php +++ b/config/sets/laravel-collection.php @@ -5,9 +5,11 @@ use Rector\Config\RectorConfig; use RectorLaravel\Rector\BooleanNot\AvoidNegatedCollectionContainsOrDoesntContainRector; use RectorLaravel\Rector\MethodCall\AvoidNegatedCollectionFilterOrRejectRector; +use RectorLaravel\Rector\MethodCall\UnaliasCollectionMethodsRector; return static function (RectorConfig $rectorConfig): void { $rectorConfig->import(__DIR__ . '/../config.php'); $rectorConfig->rule(AvoidNegatedCollectionContainsOrDoesntContainRector::class); $rectorConfig->rule(AvoidNegatedCollectionFilterOrRejectRector::class); + $rectorConfig->rule(UnaliasCollectionMethodsRector::class); }; diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index a60ee127..27ce4039 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 76 Rules Overview +# 77 Rules Overview ## AbortIfRector @@ -1445,6 +1445,24 @@ Automatically type hints your tappable closures
+## UnaliasCollectionMethodsRector + +Use the base collection methods instead of their aliases. + +- class: [`RectorLaravel\Rector\MethodCall\UnaliasCollectionMethodsRector`](../src/Rector/MethodCall/UnaliasCollectionMethodsRector.php) + +```diff + use Illuminate\Support\Collection; + + $collection = new Collection([0, 1, null, -1]); +-$collection->average(); +-$collection->some(fn (?int $number): bool => is_null($number)); ++$collection->avg(); ++$collection->contains(fn (?int $number): bool => is_null($number)); +``` + +
+ ## UnifyModelDatesWithCastsRector Unify Model `$dates` property with `$casts` diff --git a/src/Rector/MethodCall/UnaliasCollectionMethodsRector.php b/src/Rector/MethodCall/UnaliasCollectionMethodsRector.php new file mode 100644 index 00000000..7a7507e2 --- /dev/null +++ b/src/Rector/MethodCall/UnaliasCollectionMethodsRector.php @@ -0,0 +1,81 @@ +average(); +$collection->some(fn (?int $number): bool => is_null($number)); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use Illuminate\Support\Collection; + +$collection = new Collection([0, 1, null, -1]); +$collection->avg(); +$collection->contains(fn (?int $number): bool => is_null($number)); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [MethodCall::class]; + } + + /** + * @param MethodCall $node + */ + public function refactor(Node $node): ?Node + { + return $this->updateMethodCall($node); + } + + private function updateMethodCall(MethodCall $methodCall): ?MethodCall + { + if (! $this->isObjectType($methodCall->var, new ObjectType('Illuminate\Support\Enumerable'))) { + return null; + } + + $name = $methodCall->name; + if ($this->isName($name, 'some')) { + $replacement = 'contains'; + } elseif ($this->isName($name, 'average')) { + $replacement = 'avg'; + } else { + return null; + } + + $methodCall->name = new Identifier($replacement); + + return $methodCall; + } +} diff --git a/stubs/Illuminate/Support/Enumerable.php b/stubs/Illuminate/Support/Enumerable.php index 505f77e6..61cc1e67 100644 --- a/stubs/Illuminate/Support/Enumerable.php +++ b/stubs/Illuminate/Support/Enumerable.php @@ -18,6 +18,32 @@ */ interface Enumerable { + /** + * Alias for the "avg" method. + * + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null + */ + public function average($callback = null); + + /** + * Get the average value of a given key. + * + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null + */ + public function avg($callback = null); + + /** + * Alias for the "contains" method. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function some($key, $operator = null, $value = null); + /** * Determine if an item exists in the enumerable. * diff --git a/tests/Rector/MethodCall/AvoidNegatedCollectionFilterOrRejectRector/config/configured_rule.php b/tests/Rector/MethodCall/AvoidNegatedCollectionFilterOrRejectRector/config/configured_rule.php index de648d42..51699f47 100644 --- a/tests/Rector/MethodCall/AvoidNegatedCollectionFilterOrRejectRector/config/configured_rule.php +++ b/tests/Rector/MethodCall/AvoidNegatedCollectionFilterOrRejectRector/config/configured_rule.php @@ -7,6 +7,5 @@ return static function (RectorConfig $rectorConfig): void { $rectorConfig->import(__DIR__ . '/../../../../../config/config.php'); - $rectorConfig->rule(AvoidNegatedCollectionFilterOrRejectRector::class); }; diff --git a/tests/Rector/MethodCall/UnaliasCollectionMethodsRector/Fixture/fixture.php.inc b/tests/Rector/MethodCall/UnaliasCollectionMethodsRector/Fixture/fixture.php.inc new file mode 100644 index 00000000..45b487fc --- /dev/null +++ b/tests/Rector/MethodCall/UnaliasCollectionMethodsRector/Fixture/fixture.php.inc @@ -0,0 +1,23 @@ +average(); +$collection->some(fn (?int $number): bool => is_null($number)); + +?> +----- +avg(); +$collection->contains(fn (?int $number): bool => is_null($number)); + +?> diff --git a/tests/Rector/MethodCall/UnaliasCollectionMethodsRector/UnaliasCollectionMethodsRectorTest.php b/tests/Rector/MethodCall/UnaliasCollectionMethodsRector/UnaliasCollectionMethodsRectorTest.php new file mode 100644 index 00000000..40cbdd07 --- /dev/null +++ b/tests/Rector/MethodCall/UnaliasCollectionMethodsRector/UnaliasCollectionMethodsRectorTest.php @@ -0,0 +1,31 @@ +doTestFile($filePath); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Rector/MethodCall/UnaliasCollectionMethodsRector/config/configured_rule.php b/tests/Rector/MethodCall/UnaliasCollectionMethodsRector/config/configured_rule.php new file mode 100644 index 00000000..a03355ce --- /dev/null +++ b/tests/Rector/MethodCall/UnaliasCollectionMethodsRector/config/configured_rule.php @@ -0,0 +1,11 @@ +import(__DIR__ . '/../../../../../config/config.php'); + $rectorConfig->rule(UnaliasCollectionMethodsRector::class); +};