CodeIgniter DevKit で Psalm を使い静的解析する

この記事は CodeIgniter Advent Calendar 2022 - Qiita の22日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

CodeIgniter DevKit で PHPStan を使い静的解析する」 の続きです。

Psalmとは?

PsalmはPHPの静的解析ツールです。

Psalmの設定

DevKitをインストールすれば、Psalmはインストール済みです。

DevKitのインストールについては 「CodeIgniter DevKit で php-cs-fixer を使いコーディングスタイルを修正する」 を参照してください。

vendor/codeigniter4/devkit/src/Template/psalm.xmlvendor/codeigniter4/devkit/src/Template/psalm_autoload.php をプロジェクトルートにコピーします。

そして、コピーした psalm.xml をプロジェクトに合わせて調整します。

<?xml version="1.0"?>
<psalm
    errorLevel="7"
    resolveFromConfigFile="true"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="https://getpsalm.org/schema/config"
    xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
    autoloader="psalm_autoload.php"
    cacheDirectory="build/psalm/"
>
    <projectFiles>
        <directory name="app/" />
        <directory name="tests/" />
        <ignoreFiles>
            <directory name="vendor" />
            <directory name="app/Views" />
        </ignoreFiles>
    </projectFiles>
</psalm>

ここでは、特に変更せずそのままです。

なお、パスの設定が正しいかは必ず確認してください。DevKitのテンプレートのパスはCodeIgniter4のアプリのパスに設定されているため、 ライブラリの場合は少し修正が必要です。

composer.json の設定

実行するためのコマンドを composer.json に追加します。

--- a/composer.json
+++ b/composer.json
@@ -29,7 +29,8 @@
        "cs-fix": "php-cs-fixer fix --ansi --verbose --diff --using-cache=yes",
        "style": "@cs-fix",
        "analyze": [
-           "bash -c \"XDEBUG_MODE=off phpstan analyse\""
+           "bash -c \"XDEBUG_MODE=off phpstan analyse\"",
+           "psalm"
        ],
        "sa": "@analyze"
    },

これで、composer sa でPHPStanの後にPsalmが実行されます。

Psalmの実行

それではPsalmを実行してみましょう。

$ composer sa
...
> psalm
Target PHP version: 7.2 (inferred from composer.json) Extensions enabled:  (unsupported extensions: )
Scanning files...
Deprecation: Psalm stubs for ext-redis loaded using legacy way. Instead, please declare ext-redis as dependency in composer.json or use <enableExtensions> directive in Psalm config.
Analyzing files...

░░░░░░░░░░░E░░░░░░E░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

ERROR: ParseError - app/Config/Events.php:37:25 - Syntax error, unexpected T_STRING, expecting T_PAAMAYIM_NEKUDOTAYIM on line 37 (see https://psalm.dev/173)
        ob_start(static fn ($buffer) => $buffer);


ERROR: UndefinedFunction - app/Config/Events.php:37:25 - Function Config\fn does not exist (see https://psalm.dev/021)
        ob_start(static fn ($buffer) => $buffer);


ERROR: ParseError - app/Config/Events.php:37:38 - Syntax error, unexpected T_DOUBLE_ARROW on line 37 (see https://psalm.dev/173)
        ob_start(static fn ($buffer) => $buffer);


ERROR: MissingFile - app/Config/Routes.php:60:5 - Cannot find file /.../codeigniter4login/app/Config/testing/Routes.php to include (see https://psalm.dev/107)
    require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';


------------------------------
4 errors found
------------------------------
77 other issues found.
You can display them with --show-info=true
------------------------------
Psalm can automatically fix 9 of these issues.
Run Psalm again with 
--alter --issues=MissingReturnType,MissingParamType --dry-run
to see what it can fix.
------------------------------

Checks took 10.81 seconds and used 308.240MB of memory
Psalm was able to infer types for 94.4882% of the codebase
Script psalm handling the analyze event returned with error code 2
Script @analyze was called via sa

エラーが報告されました。

Target PHP version: 7.2 (inferred from composer.json)

対象としているPHPのバージョンが古すぎました。 composer.json の設定を修正します。

--- a/composer.json
+++ b/composer.json
@@ -5,7 +5,7 @@
    "homepage": "https://codeigniter.com",
    "license": "MIT",
    "require": {
-       "php": ">=7.2",
+       "php": ">=7.4",
        "codeigniter4/framework": "^4"
    },
    "require-dev": {

再度、Psalmを実行します。

$ vendor/bin/psalm
Target PHP version: 7.4 (inferred from composer.json) Extensions enabled:  (unsupported extensions: )
Scanning files...
Analyzing files...



ERROR: UndefinedFunction - app/Config/Events.php:37:25 - Function Config\fn does not exist (see https://psalm.dev/021)
        ob_start(static fn ($buffer) => $buffer);


ERROR: MissingFile - app/Config/Routes.php:60:5 - Cannot find file /.../codeigniter4login/app/Config/testing/Routes.php to include (see https://psalm.dev/107)
    require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';


------------------------------
2 errors found
------------------------------
77 other issues found.
You can display them with --show-info=true
------------------------------

Checks took 0.05 seconds and used 7.175MB of memory
No files analyzed
Psalm was able to infer types for 94.4882% of the codebase

エラーが報告されました。

Psalm設定の調整

ERROR: UndefinedFunction - app/Config/Events.php:37:25 - Function Config\fn does not exist (see https://psalm.dev/021) ob_start(static fn ($buffer) => $buffer);

このエラーは正直よくわかりません。Psalmのバグでしょうか? fn は関数ではありませんから。

ERROR: MissingFile - app/Config/Routes.php:60:5 - Cannot find file /.../codeigniter4login/app/Config/testing/Routes.php to include (see https://psalm.dev/107) require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';

こちらはファイルが存在しないというエラーです。このファイルは存在しないので、その通りです。

どちらも対処が難しいので、エラーを抑制します。

ベースラインの作成

ベースラインを作成しましょう。

$ vendor/bin/psalm --set-baseline=psalm-baseline.xml
Target PHP version: 7.4 (inferred from composer.json) Extensions enabled:  (unsupported extensions: )
Scanning files...
Deprecation: Psalm stubs for ext-redis loaded using legacy way. Instead, please declare ext-redis as dependency in composer.json or use <enableExtensions> directive in Psalm config.
Analyzing files...

░░░░░░░░░░░E░░░░░░E░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
Writing error baseline to file...
Baseline saved to psalm-baseline.xml.

ERROR: UndefinedFunction - app/Config/Events.php:37:25 - Function Config\fn does not exist (see https://psalm.dev/021)
        ob_start(static fn ($buffer) => $buffer);


ERROR: MissingFile - app/Config/Routes.php:60:5 - Cannot find file /.../codeigniter4login/app/Config/testing/Routes.php to include (see https://psalm.dev/107)
    require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';


------------------------------
2 errors found
------------------------------
76 other issues found.
You can display them with --show-info=true
------------------------------
Psalm can automatically fix 9 of these issues.
Run Psalm again with 
--alter --issues=MissingReturnType,MissingParamType --dry-run
to see what it can fix.
------------------------------

Checks took 6.62 seconds and used 384.126MB of memory
Psalm was able to infer types for 94.4882% of the codebase

これで、以下の psalm-baseline.xml が作成されました。

<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.4.0@62db5d4f6a7ae0a20f7cc5a4952d730272fc0863">
    <file src="app/Config/Events.php">
        <UndefinedFunction occurrences="1">
            <code>fn ($buffer)</code>
        </UndefinedFunction>
    </file>
    <file src="app/Config/Routes.php">
        <MissingFile occurrences="1">
            <code>require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php'</code>
        </MissingFile>
    </file>
</files>

psalm.xml にも以下が自動的に追加され、ベースラインが使われるようになりました。

--- a/psalm.xml
+++ b/psalm.xml
@@ -7,6 +7,7 @@
     xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
     autoloader="psalm_autoload.php"
     cacheDirectory="build/psalm/"
+    errorBaseline="psalm-baseline.xml"
 >
     <projectFiles>
         <directory name="app/" />

再度、実行してエラーが出ないことを確認しましょう。

$ vendor/bin/psalm
Target PHP version: 7.4 (inferred from composer.json)
Scanning files...
Analyzing files...



------------------------------

       No errors found!       

------------------------------
74 other issues found.
You can display them with --show-info=true
------------------------------

Checks took 0.05 seconds and used 7.023MB of memory
No files analyzed
Psalm was able to infer types for 94.4990% of the codebase

設定が完了し、問題がなければ、Gitでcommitしてください。

まとめ

  • CodeIgniter DevKit はCodeIgniterのライブラリとプロジェクトのための開発用のツールキットです。
  • DevKitにはPsalmが含まれており、簡単に静的解析を実行できます。

この記事は CodeIgniter Advent Calendar 2022 - Qiita の22日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

関連

参考

Tags: codeigniter, codeigniter4, psalm

CodeIgniter DevKit で PHPStan を使い静的解析する

この記事は CodeIgniter Advent Calendar 2022 - Qiita の21日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

CodeIgniter DevKit で Rector を使いコーディングスタイルを修正する」 の続きです。

PHPStanとは?

PHPStanはPHPの静的解析ツールです。

PHPStanの設定

DevKitをインストールすれば、PHPStanはインストール済みです。

DevKitのインストールについては 「CodeIgniter DevKit で php-cs-fixer を使いコーディングスタイルを修正する」 を参照してください。

vendor/codeigniter4/devkit/src/Template/phpstan.neon.dist をプロジェクトルートにコピーします。

そして、コピーした phpstan.neon.dist をプロジェクトに合わせて調整します。

parameters:
    tmpDir: build/phpstan
    level: 5
    paths:
        - app/
        - tests/
    bootstrapFiles:
        - vendor/codeigniter4/framework/system/Test/bootstrap.php
    excludePaths:
        - app/Config/Routes.php
        - app/Views/*
    ignoreErrors:
    universalObjectCratesClasses:
        - CodeIgniter\Entity
        - CodeIgniter\Entity\Entity
        - Faker\Generator
    scanDirectories:
        - vendor/codeigniter4/framework/system/Helpers
    dynamicConstantNames:
        - APP_NAMESPACE
        - CI_DEBUG
        - ENVIRONMENT

ここでは、特に変更せずそのままです。

なお、パスの設定が正しいかは必ず確認してください。DevKitのテンプレートのパスはCodeIgniter4のアプリのパスに設定されているため、ライブラリの場合は少し修正が必要です。

composer.json の設定

実行するためのコマンドを composer.json に追加します。

--- a/composer.json
+++ b/composer.json
@@ -27,7 +27,11 @@
],
"cs": "php-cs-fixer fix --ansi --verbose --dry-run --diff",
"cs-fix": "php-cs-fixer fix --ansi --verbose --diff --using-cache=yes",
-       "style": "@cs-fix"
+       "style": "@cs-fix",
+       "analyze": [
+           "bash -c \"XDEBUG_MODE=off phpstan analyse\""
+       ],
+       "sa": "@analyze"
  },
  "support": {
  "forum": "http://forum.codeigniter.com/",

これで、composer sa でPHPStanが実行されます。

PHPStanの実行

それではPHPStanを実行してみましょう。

$ composer sa
> bash -c "XDEBUG_MODE=off phpstan analyse"
Note: Using configuration file /.../codeigniter4login/phpstan.neon.dist.
 56/56 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 ------ --------------------------------------------------------------------------------------- 
  Line   app/Controllers/Users.php                                                              
 ------ --------------------------------------------------------------------------------------- 
  16     Call to deprecated method getMethod() of class CodeIgniter\HTTP\Request:               
         The $upper functionality will be removed and this will revert to its PSR-7 equivalent  
  68     Call to deprecated method getMethod() of class CodeIgniter\HTTP\Request:               
         The $upper functionality will be removed and this will revert to its PSR-7 equivalent  
  108    Call to deprecated method getMethod() of class CodeIgniter\HTTP\Request:               
         The $upper functionality will be removed and this will revert to its PSR-7 equivalent  
 ------ --------------------------------------------------------------------------------------- 



 [ERROR] Found 3 errors                                                                                                 


Script bash -c "XDEBUG_MODE=off phpstan analyse" handling the analyze event returned with error code 1
Script @analyze was called via sa

エラーが報告されました。

Request::getMethod() の引数 $upper の廃止予定に関する警告です。

PHPStan設定の調整

Call to deprecated method getMethod() of class CodeIgniter\HTTP\Request: The $upper functionality will be removed and this will revert to its PSR-7 equivalent

このエラーはフレームワークのPHPDocコメントが原因なので、 アプリのコードの問題ではありません。ユーザーが修正することはできません。

そこでエラーを抑制します。

ベースラインの作成

ベースラインを作成しましょう。

$ vendor/bin/phpstan --generate-baseline
 ! [NOTE] The Xdebug PHP extension is active, but "--xdebug" is not used. This may slow down performance and the process
 !        will not halt at breakpoints.                                                                                 

Note: Using configuration file /.../codeigniter4login/phpstan.neon.dist.
 56/56 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%



 [OK] Baseline generated with 3 errors.

これで、以下の phpstan-baseline.neon が作成されました。

parameters:
    ignoreErrors:
        -
            message: """
                #^Call to deprecated method getMethod\\(\\) of class CodeIgniter\\\\HTTP\\\\Request\\:
                The \\$upper functionality will be removed and this will revert to its PSR\\-7 equivalent$#
            """
            count: 3
            path: app/Controllers/Users.php

しかし、このエラーは Request::getMethod() を使うたびに発生します。

そこで、以下のように回数を削除して、パスを変更します。

parameters:
    ignoreErrors:
        -
            message: """
                #^Call to deprecated method getMethod\\(\\) of class CodeIgniter\\\\HTTP\\\\Request\\:
                The \\$upper functionality will be removed and this will revert to its PSR\\-7 equivalent$#
            """
            path: app/Controllers/*.php

次にベースラインを使うようにメインの設定ファイルを変更します。

--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -1,3 +1,5 @@
+includes:
+   - phpstan-baseline.neon
 parameters:
    tmpDir: build/phpstan
    level: 5

これで、ベースラインが使われるようになりました。

再度、実行してエラーが出ないことを確認しましょう。

$ composer sa
> bash -c "XDEBUG_MODE=off phpstan analyse"
Note: Using configuration file /.../codeigniter4login/phpstan.neon.dist.
 56/56 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%



 [OK] No errors

設定が完了し、問題がなければ、Gitでcommitしてください。

まとめ

  • CodeIgniter DevKit はCodeIgniterのライブラリとプロジェクトのための開発用のツールキットです。
  • DevKitにはPHPStanが含まれており、簡単に静的解析を実行できます。

CodeIgniter DevKit で Psalm を使い静的解析する」 へ続く。

この記事は CodeIgniter Advent Calendar 2022 - Qiita の21日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

関連

参考

Tags: codeigniter, codeigniter4, phpstan

CodeIgniter DevKit で Rector を使いコードを修正する

この記事は CodeIgniter Advent Calendar 2022 - Qiita の20日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

CodeIgniter DevKit で php-cs-fixer を使いコーディングスタイルを修正する」 の続きです。

Rectorとは?

DevKit には Rector の設定のテンプレートも含まれています。

Rector はコードを自動的にリファクタリングするためのツールです。

Rectorのインストール

DevKit をインストールしても Rector はインストールされないため、ここではプロジェクトにインストールします。

$ composer require rector/rector --dev

バージョン 0.15.1 がインストールされました。

Rector のアップデートに伴いコードが破壊的に変更される可能性がありますので、 composer.json で Rector のバージョンを固定します。

        "rector/rector": "0.15.1"

問題がないことを確認したバージョンの Rector を使ってください。

Rectorの設定

それでは、Rector の設定をします。

vendor/codeigniter4/devkit/src/Template/rector.php をプロジェクトルートにコピーします。

そして、コピーした rector.php をプロジェクトに合わせて調整します。 また、composer cs-fix を実行してコーディングスタイルを整えましょう。

<?php

declare(strict_types=1);

use Rector\CodeQuality\Rector\BooleanAnd\SimplifyEmptyArrayCheckRector;
use Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector;
use Rector\CodeQuality\Rector\Expression\InlineIfToExplicitIfRector;
use Rector\CodeQuality\Rector\For_\ForToForeachRector;
use Rector\CodeQuality\Rector\Foreach_\UnusedForeachValueToArrayKeysRector;
use Rector\CodeQuality\Rector\FuncCall\AddPregQuoteDelimiterRector;
use Rector\CodeQuality\Rector\FuncCall\ChangeArrayPushToArrayAssignRector;
use Rector\CodeQuality\Rector\FuncCall\SimplifyRegexPatternRector;
use Rector\CodeQuality\Rector\FuncCall\SimplifyStrposLowerRector;
use Rector\CodeQuality\Rector\FunctionLike\SimplifyUselessVariableRector;
use Rector\CodeQuality\Rector\If_\CombineIfRector;
use Rector\CodeQuality\Rector\If_\ShortenElseIfRector;
use Rector\CodeQuality\Rector\If_\SimplifyIfElseToTernaryRector;
use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector;
use Rector\CodeQuality\Rector\Ternary\UnnecessaryTernaryExpressionRector;
use Rector\CodingStyle\Rector\ClassMethod\FuncGetArgsToVariadicParamRector;
use Rector\CodingStyle\Rector\ClassMethod\MakeInheritedMethodVisibilitySameAsParentRector;
use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector;
use Rector\Config\RectorConfig;
use Rector\Core\ValueObject\PhpVersion;
use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector;
use Rector\DeadCode\Rector\MethodCall\RemoveEmptyMethodCallRector;
use Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector;
use Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector;
use Rector\EarlyReturn\Rector\If_\RemoveAlwaysElseRector;
use Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Php56\Rector\FunctionLike\AddDefaultValueForUndefinedVariableRector;
use Rector\Php73\Rector\FuncCall\JsonThrowOnErrorRector;
use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector;
use Rector\PHPUnit\Set\PHPUnitSetList;
use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector;
use Rector\PSR4\Rector\FileWithoutNamespace\NormalizeNamespaceByPSR4ComposerAutoloadRector;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;

return static function (RectorConfig $rectorConfig): void {
    $rectorConfig->sets([
        SetList::DEAD_CODE,
        LevelSetList::UP_TO_PHP_74,
        PHPUnitSetList::PHPUNIT_SPECIFIC_METHOD,
        PHPUnitSetList::PHPUNIT_100,
    ]);

    $rectorConfig->parallel();

    // The paths to refactor (can also be supplied with CLI arguments)
    $rectorConfig->paths([
        __DIR__ . '/app/',
        __DIR__ . '/tests/',
    ]);

    // Include Composer's autoload - required for global execution, remove if running locally
    $rectorConfig->autoloadPaths([
        __DIR__ . '/vendor/autoload.php',
    ]);

    // Do you need to include constants, class aliases, or a custom autoloader?
    $rectorConfig->bootstrapFiles([
        realpath(getcwd()) . '/vendor/codeigniter4/framework/system/Test/bootstrap.php',
    ]);

    if (is_file(__DIR__ . '/phpstan.neon.dist')) {
        $rectorConfig->phpstanConfig(__DIR__ . '/phpstan.neon.dist');
    }

    // Set the target version for refactoring
    $rectorConfig->phpVersion(PhpVersion::PHP_74);

    // Auto-import fully qualified class names
    $rectorConfig->importNames();

    // Are there files or rules you need to skip?
    $rectorConfig->skip([
        __DIR__ . '/app/Views',

        JsonThrowOnErrorRector::class,
        StringifyStrNeedlesRector::class,

        // Note: requires php 8
        RemoveUnusedPromotedPropertyRector::class,

        // Ignore tests that might make calls without a result
        RemoveEmptyMethodCallRector::class                    => [
            __DIR__ . '/tests',
        ],

        // Ignore files that should not be namespaced to their folder
        NormalizeNamespaceByPSR4ComposerAutoloadRector::class => [
            __DIR__ . '/app/Helpers',
        ],

        // May load view files directly when detecting classes
        StringClassNameToClassConstantRector::class,

        // May be uninitialized on purpose
        AddDefaultValueForUndefinedVariableRector::class,
    ]);

    // auto import fully qualified class names
    $rectorConfig->importNames();

    $rectorConfig->rule(SimplifyUselessVariableRector::class);
    $rectorConfig->rule(RemoveAlwaysElseRector::class);
    $rectorConfig->rule(CountArrayToEmptyArrayComparisonRector::class);
    $rectorConfig->rule(ForToForeachRector::class);
    $rectorConfig->rule(ChangeNestedForeachIfsToEarlyContinueRector::class);
    $rectorConfig->rule(ChangeIfElseValueAssignToEarlyReturnRector::class);
    $rectorConfig->rule(SimplifyStrposLowerRector::class);
    $rectorConfig->rule(CombineIfRector::class);
    $rectorConfig->rule(SimplifyIfReturnBoolRector::class);
    $rectorConfig->rule(InlineIfToExplicitIfRector::class);
    $rectorConfig->rule(PreparedValueToEarlyReturnRector::class);
    $rectorConfig->rule(ShortenElseIfRector::class);
    $rectorConfig->rule(SimplifyIfElseToTernaryRector::class);
    $rectorConfig->rule(UnusedForeachValueToArrayKeysRector::class);
    $rectorConfig->rule(ChangeArrayPushToArrayAssignRector::class);
    $rectorConfig->rule(UnnecessaryTernaryExpressionRector::class);
    $rectorConfig->rule(AddPregQuoteDelimiterRector::class);
    $rectorConfig->rule(SimplifyRegexPatternRector::class);
    $rectorConfig->rule(FuncGetArgsToVariadicParamRector::class);
    $rectorConfig->rule(MakeInheritedMethodVisibilitySameAsParentRector::class);
    $rectorConfig->rule(SimplifyEmptyArrayCheckRector::class);
    $rectorConfig->rule(NormalizeNamespaceByPSR4ComposerAutoloadRector::class);
    $rectorConfig->rule(StringClassNameToClassConstantRector::class);
    $rectorConfig->rule(PrivatizeFinalClassPropertyRector::class);
    $rectorConfig->rule(CompleteDynamicPropertiesRector::class);
};

ここでは、上記のように修正しました。

なお、パスの設定が正しいかは必ず確認してください。DevKit のテンプレートのパスはCodeIgniter4のアプリのパスに設定されているため、ライブラリの場合は少し修正が必要です。

なお、Rector のルールついては、 https://github.com/rectorphp/rector/blob/main/docs/rector_rules_overview.md に一覧があります。

コードの修正

それでは Rector を実行して、コードを修正してみましょう。

$ vendor/bin/rector
 100/100 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
29 files with changes
=====================

1) app/Config/Filters.php:3

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

 namespace Config;

+use App\Filters\Auth;
+use App\Filters\Noauth;
+use App\Filters\UsersCheck;
 use CodeIgniter\Config\BaseConfig;
 use CodeIgniter\Filters\CSRF;
 use CodeIgniter\Filters\DebugToolbar;
@@ @@
         'honeypot'      => Honeypot::class,
         'invalidchars'  => InvalidChars::class,
         'secureheaders' => SecureHeaders::class,
-        'auth'          => \App\Filters\Auth::class,
-        'noauth'        => \App\Filters\Noauth::class,
-        'userscheck'    => \App\Filters\UsersCheck::class,
+        'auth'          => Auth::class,
+        'noauth'        => Noauth::class,
+        'userscheck'    => UsersCheck::class,
     ];

     /**
    ----------- end diff -----------

2) app/Config/Database.php:85

    ---------- begin diff ----------
@@ @@
         // Ensure that we always set the database group to 'tests' if
         // we are currently running an automated test suite, so that
         // we don't overwrite live data on accident.
-        if (ENVIRONMENT === 'testing') {
-            $this->defaultGroup = 'tests';
-        }
+        $this->defaultGroup = 'tests';
     }
 }
    ----------- end diff -----------

Applied rules:
 * RemoveAlwaysTrueIfConditionRector


3) tests/_support/MigrationTestMigrations/Database/Migrations/2018-01-24-102302_Another_migration.php:3

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

 namespace Tests\Support\MigrationTestMigrations\Database\Migrations;

-class Migration_another_migration extends \CodeIgniter\Database\Migration
+use CodeIgniter\Database\Migration;
+class Migration_another_migration extends Migration
 {
     public function up(): void
     {
    ----------- end diff -----------

...

28) app/Filters/UsersCheck.php:16

    ---------- begin diff ----------
@@ @@
         // we have to redirect the request to the second segment
         $uri = service('uri');
         if ($uri->getSegment(1) === 'users') {
-            if ($uri->getSegment(2) === '') {
-                $segment = '/';
-            } else {
-                $segment = '/' . $uri->getSegment(2);
-            }
+            $segment = $uri->getSegment(2) === '' ? '/' : '/' . $uri->getSegment(2);

             return redirect()->to($segment);
         }
    ----------- end diff -----------

Applied rules:
 * SimplifyIfElseToTernaryRector


29) app/Database/Migrations/20121031100537_add_users.php:3

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

 namespace App\Database\Migrations;

-class AddUsers extends \CodeIgniter\Database\Migration
+use CodeIgniter\Database\Migration;
+class AddUsers extends Migration
 {
     public function up(): void
     {
    ----------- end diff -----------


 [OK] 29 files have been changed by Rector

上記のように修正された差分が表示されました。

Rector設定の修正

しかし、以下のような意図しない破壊的な変更が含まれる可能性があります。

2) app/Config/Database.php:85

    ---------- begin diff ----------
@@ @@
         // Ensure that we always set the database group to 'tests' if
         // we are currently running an automated test suite, so that
         // we don't overwrite live data on accident.
-        if (ENVIRONMENT === 'testing') {
-            $this->defaultGroup = 'tests';
-        }
+        $this->defaultGroup = 'tests';
     }
 }
    ----------- end diff -----------

Applied rules:
 * RemoveAlwaysTrueIfConditionRector

そこで、差分を確認してルールを修正します。

ここでは、特定のルールをスキップするように設定を変更します。

RemoveAlwaysTrueIfConditionRector ルールは app/Config/Database.php に対しては除外します。

--- a/rector.php
+++ b/rector.php
@@ -23,6 +23,7 @@ use Rector\CodingStyle\Rector\FuncCall\CountArrayToEmptyArrayComparisonRector;
 use Rector\Config\RectorConfig;
 use Rector\Core\ValueObject\PhpVersion;
 use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPromotedPropertyRector;
+use Rector\DeadCode\Rector\If_\RemoveAlwaysTrueIfConditionRector;
 use Rector\DeadCode\Rector\MethodCall\RemoveEmptyMethodCallRector;
 use Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector;
 use Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector;
@@ -99,6 +100,10 @@ return static function (RectorConfig $rectorConfig): void {

         // May be uninitialized on purpose
         AddDefaultValueForUndefinedVariableRector::class,
+
+        RemoveAlwaysTrueIfConditionRector::class              => [
+            __DIR__ . '/app/Config/Database.php',
+        ],
     ]);

     // auto import fully qualified class names

また、composer cs-fix を実行してコーディングスタイルを整えましょう。

設定調整が完了し、問題がなければ、Gitでcommitしてください。

試しに実行した結果を GitHub にアップしておきました。

まとめ

  • CodeIgniter DevKit はCodeIgniterのライブラリとプロジェクトのための開発用のツールキットです。
  • DevKit には Rector の設定が含まれており、コードを修正・統一できます。

CodeIgniter DevKit で PHPStan を使い静的解析する」 へ続く。

この記事は CodeIgniter Advent Calendar 2022 - Qiita の20日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

参考

Tags: codeigniter, codeigniter4, rector