こんにちは。Fusicの@hkusaba です。
Fusic Advent Calendar 2023 の2日目ですね。
今回は、rectorとrector-laravelを使用して、PHP・Laravelをアップデートしたお話をします。
弊社内で行っている開発合宿で触る機会がありました!いい会社です。
はじめに
rectorとは何ぞや
公式がこちらです。
rectorには2つの特徴があります。 (Google翻訳です。
- 即時アップグレード
Rector は、PHP 5.3 から 8.2 へのアップグレードと、 Symfony、PHPUnit、Doctrineなどの主要なオープンソース プロジェクトをサポートするようになりました。手間をかけずに常に最新の PHP とフレームワークを使用したいですか?
Rector を使用して即時アップグレードを処理します。- 自動リファクタリング
必要なコード品質はあるものの、チーム内の新しい開発者と一緒にそれを維持するのに苦労していませんか? 上級開発者全員が寝ているときでも、スマートなコードレビューを見たいですか?
Rector を CI に追加すると、コードが継続的にリファクタリングされ、コードの品質が高く保たれます。
自動リファクタリングを設定する方法については、ブログ投稿をお読みください。
PHPやSymfony、PHPunit、さらには、Laravelにも対応しています。
例えば、下記の記述のみで、PHP8.2とLaravel10にアップデートできると思うと、わくわくしませんか?
<?php
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use RectorLaravel\Set\LaravelLevelSetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([
__DIR__ . '/app',
__DIR__ . '/config',
__DIR__ . '/database',
]);
$rectorConfig->sets([
LevelSetList::UP_TO_PHP_82,
LaravelLevelSetList::UP_TO_LARAVEL_100,
]);
};
そこでやってみることにしました!
やってみよう
はじめ方は、だいたい公式のReadmeと同じです。やっていきましょう。
# rectorとlaravel-rectorを導入
composer require rector/rector --dev
composer require driftingly/rector-laravel --dev
# rectorの設定ファイルを、vendorディレクトリやcomposer.jsonと同じ階層のディレクトリに生成
# 内容は、公式Readmeのもの、もしくは、記事上部のrector.phpの内容でOKです
vi rector.php
# 設定を書き終えたらdry-run or 実行
# $rectorConfig->paths() で対象を指定していない場合、appなどの対象を指定する
# 指定している場合は書かなくてOK
vendor/bin/rector process app --dry-run
やってみた
vendor/bin/rector process --dry-run
すると、下記のようになります。
1) app/fugafuga/hogehoge/HogeHoge.php:8
---------- begin diff ----------
@@ @@
class HogeHoge
{
/**
- * slackService
- *
- * @var SlackService $slackService
- */
- public SlackService $slackService;
-
- /**
* __construct
*
- * @param SlackService $slackService
* @return void
*/
- public function __construct(SlackService $slackService)
+ public function __construct(
+ /**
+ * slackService
+ */
+ public SlackService $slackService
+ )
{
- $this->slackService = $slackService;
}
/**
* Handle the event.
*
- * @param HogeHoge $event
* @return void
*/
public function handle(HogeHoge $event): void
----------- end diff -----------
Applied rules:
* ClassPropertyAssignToConstructorPromotionRector (https://wiki.php.net/rfc/constructor_promotion https://github.com/php/php-src/pull/5291)
* MixedTypeRector
変更対象になったファイルのdiffと、適用されたルールがズラッと表示されます。
このケースでは、PHP8.0で導入されたConstructor Property Promotion
が適用されました。
コメントが引数に入るのが惜しいですが、これでも全然動きます。
元となるRFCを表示してくれるので、仮にわからないルールの適用でも安心です。
また、MixedTypeRector
によって、handle(HogeHoge $event)
は引数の型が明示なので、PHPDocからその旨が消されています。冗長な記述がスッキリしましたね。
ルールの詳細
rectorやrector-laravelのルールの詳細は、この辺りから確認できます。
exampleもあって、見やすいですね。
ほかにも、下記のようなルールが適用されました。
- ? array_merge($rules, $this->hogehoge())
+ ? [...$rules, ...$this->hogehoge()]
----------- end diff -----------
Applied rules:
* LongArrayToShortArrayRector
* ArraySpreadInsteadOfArrayMergeRector (https://wiki.php.net/rfc/spread_operator_for_array)
14) app/Providers/RouteServiceProvider.php:16
---------- begin diff ----------
@@ @@
*
* @var string
*/
- public const HOME = '/hoge';
+ final public const HOME = '/hoge';
/**
* The controller namespace for the application.
@@ @@
*/
protected function configureRateLimiting(): void
{
- RateLimiter::for('api', function (Request $request) {
- return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
- });
+ RateLimiter::for('api', fn(Request $request) => Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()));
}
}
----------- end diff -----------
Applied rules:
* ClosureToArrowFunctionRector (https://wiki.php.net/rfc/arrow_functions_v2)
* FinalizePublicClassConstantRector (https://php.watch/versions/8.1/final-class-const)
少し地味な変更ですが、直近のPHPの更新に適応するように、いい感じにリファクタされました。
地味な変更の理由の1つとして、今回はPHP8.0->8.2 とLaravel8.x->10.x と比較的差分が少なかったことも考えられます。
個人的に驚いた点としては、細かい調整を必要とせず、リファクタされた時点で動くことでした。
一方で、コメントのズレは調整してプルリクエスト出したいですし、自身のニーズにマッチしないルールはskipするなど、チューニングの余地があります。
アップグレードのルールセットについて
ルールセットのLevelSetList::UP_TO_PHP_82
が何しているのか気になったので、調べました。
このように、PHP8.2のルールセットと、PHP8.1へのアップデートのルールセットが設定されている状態となっています。UP_TO_PHP_81
の中も同様に、8.1のルールセットと、8.0へのアップデートのルールセットが設定されています。
そのため、UP_TO_PHP_82
を設定すれば、下位バージョンのアップデートルールセットも再帰的に設定されるので、最新版のアップデートのルールセットのみを設定する形で大丈夫ですね。
return static function (RectorConfig $rectorConfig) : void {
$rectorConfig->sets([SetList::PHP_82, LevelSetList::UP_TO_PHP_81]);
// parameter must be defined after import, to override imported param version
$rectorConfig->phpVersion(PhpVersion::PHP_82);
};
rector-laravel も同様です。
Laravelアップデートについて
rectorとは別件で、Laravelのアップデートに関して、つまりどころがあったので記載します。
ほかのライブラリをアップデートしようとしたときに、 composer update –with-all-dependencies
が依存関係から通らないという現象です。
これに関しては、8.x->9.xなどバージョンごとに、公式のアップグレードガイドに詳細が書いてあるので、こちらを確認・実行するようにしましょう。
https://laravel.com/docs/9.x/upgrade#updating-dependencies
https://laravel.com/docs/10.x/upgrade#updating-dependencies
最後に
Qiitaでrectorの記事が5個しか無くて驚きました。
ほかには @yamii 様の記事が参考になります。
rectorを使ってみた感想としては、地味ながらも、最新のPHPの記法にリファクタしてくれることを魅力に感じました。よければ、皆さんも使ってみてください。
今回は諸般の都合上、PHP8.0->8.2 とLaravel8.x->10.x にて実施しました。下位バージョンにおける動作はまだ見れていないのですが、もし使ってみた感想があれば、コメントに書いていただけるとありがたいです。
LGTMいただけると励みになります。みなさん、2024年もよいお年を!