前回紹介したmatch式Constructor Property Promotion のように新しい記法が導入されることでボイラープレートが駆逐されコードがより簡潔に読みやすくなります。しかし、長年運用されているプロジェクトなどでは書き換え必要な箇所が多く、導入するのに及び腰になってしまいがちです。Rectorを使うことでそんな億劫な作業が一瞬で完了するかもしれませんよ。

Rectorとは?

About Rectorより

「Rector」とは、どんなPHPプロジェクトでも実行可能なPHPツールであり、瞬時にアップグレードや自動リファクタリングを実行できます。

という事で、まさにPHP8から導入されたsyntaxで書き換えたい、という私の要求にマッチしています。それらを自動で行なってくれるなら大助かりです。

現時点(2023/04/22)で ver 0.15.23 が最新で実行にはPHP7.2以上が必要となっています。

どんなツールか早速使って体感してみましょう。

インストール

新しく作成したLaravelのprojectにてインストールしました。

composer require rector/rector --dev

すると vendor/bin/rector が追加されます。

vendor/bin/rector --version
>> Rector 0.15.25

設定ファイルを追加

次に設定ファイルを(rector.php)プロジェクトに追加します。

vendor/bin/rector init

// rector.php を作成するか問われるので yes とタイプ
No "rector.php" config found. Should we generate it for you? [yes]:
> yes

[OK] The config is added now. Re-run command to make Rector do the work! 

プロジェクトのルートディレクトリに rector.php が作成されました。
デフォルトでは以下の状態です。

<?php

declare(strict_types=1);

use Rector\CodeQuality\Rector\Class_\InlineConstructorDefaultToPropertyRector;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;

return static function (RectorConfig $rectorConfig): void {
    $rectorConfig->paths([
        __DIR__ . '/app',
        __DIR__ . '/bootstrap',
        __DIR__ . '/config',
        __DIR__ . '/lang',
        __DIR__ . '/public',
        __DIR__ . '/resources',
        __DIR__ . '/routes',
        __DIR__ . '/tests',
    ]);

    // register a single rule
    $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);

    // define sets of rules
    //    $rectorConfig->sets([
    //        LevelSetList::UP_TO_PHP_80
    //    ]);
};

リファクタリングしたいパスは

$rectorConfig->paths([...]);

にて指定できます。

また、適用したい変更をruleとして指定します。

$rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);

// 複数のruleをまとめて指定するならrules
$rectorConfig->rules([
    InlineConstructorDefaultToPropertyRector::class,
    InlineIfToExplicitIfRector::class
]);

Rectorで扱うリファクタリングは主に記法の統一なので、
「こういう時はこう書きましょう」というルールを定めるイメージですね。

Rules Overview にて指定可能なルールがまとめられていますので、ご確認を。

実践

前回紹介した Constructor Property Promotion について
リファクタリング可能か試してみましょう。

まず、以前の記事で使用した例と同じ Person.php を app/Models/ 配下に作成します。

<?php

namespace App\Models;

class Person
{
    public string $name;
    public int $age;
    public string $sex;
 
    public function __construct(string $name, int $age, string $sex)
    {
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;
    }
}

次に、rector.php を以下のように書き換え設定します。

<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector;

return static function (RectorConfig $rectorConfig): void {

    // app 配下のみを対象にする
    $rectorConfig->paths([
        __DIR__ . '/app',
    ]);

    // Constructor Property Promotion を適用するruleを追加
    $rectorConfig->rule(ClassPropertyAssignToConstructorPromotionRector::class);
};

Constructor Property Promotion に書き換えるruleはこちらにありました。

準備が整ったので書き換えてみましょう。
まずは--dry-runを付けて実行します。

vendor/bin/rector process --dry-run

--dry-runではコード自体は変更されません。
変更前後の差分が表示されるので、それを見て意図通りの変更が適用されるか事前確認します。

1 file with changes
===================

1) app/Models/Person.php:3

    ---------- begin diff ----------
@@ @@

 class Person
 {
-    public string $name;
-    public int $age;
-    public string $sex;
- 
-    public function __construct(string $name, int $age, string $sex)
+    public function __construct(public string $name, public int $age, public string $sex)
     {
-        $this->name = $name;
-        $this->age = $age;
-        $this->sex = $sex;
     }
 }
    ----------- end diff -----------

Applied rules:
 * ClassPropertyAssignToConstructorPromotionRector (https://wiki.php.net/rfc/constructor_promotion https://github.com/php/php-src/pull/5291)



 [OK] 1 file would have changed (dry-run) by Rector      

意図通りの変更が施されていますね。
問題無かったので今度は--dry-runオプションを省いて実行します。

vendor/bin/rector process

Person.php がリファクタリングされ以下となりました。

<?php

namespace App\Models;

class Person
{
    public function __construct(public string $name, public int $age, public string $sex)
    {
    }
}

まとめ

長くなってしまったのでRector導入編、ということで一旦ここで切ります。
今回の記事では掘り下げられなかったSet Listsについては次回以降の記事で紹介できればと思います。

今回は解説用なので1ファイルのみのリファクタリングとなりましたが、
大きいプロジェクトであれば数十数百のファイルにコマンド一発で変更が適用されるので
記法を置換する際にはとても便利なツールだと思います。

また、今回は古い記法を置換する目的で触ってみましたが、
例えばチームで開発している際に以前に紹介されたpintなどと同様に
コードスタイルを統一する目的でも活用できるのでは、と思います。

By hikaru