初めに
PHPの静的解析ツールでおなじみのPHPStanではユーザ独自のカスタムルールを作成することができます。単純なルールであればサクッと作ることができておすすめなので、今回はPHPStanの簡単なカスタムルールを作ってみようと思います。
説明で使用するPHPStanのバージョンは1.12
系です。
(前提知識)PHPStanのルール
PHPStanで定義されている全てのルールは以下のRuleというInterfaceを継承して作る必要があります。
<?php declare(strict_types = 1);
namespace PHPStan\Rules;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
interface Rule
{
/**
* @phpstan-return class-string<TNodeType>
*/
public function getNodeType(): string;
/**
* @phpstan-param TNodeType $node
* @return (string|RuleError)[] errors
*/
public function processNode(Node $node, Scope $scope): array;
}
インターフェースにはgetNodeType()
とprocessNode()
の二つのメソッドがあり、それぞれ解析対象と解析ルールの役割を担います。
メソッド | 役割 |
---|---|
getNodeType() |
解析する対象を定める |
processNode() |
解析するルールを定める |
getNodeType()
getNodeType()
には解析対象を定義します。ここで返すのはPHP-ParserのNodeを継承したクラスの名前です。PHPStanではNodeを継承したクラスが細かく用意されており、様々な場所を解析対象に定めることができます(このディレクトリにあります)。
processNode()
processNode()
には解析するルールを定義します。返り値のarrayにはエラー時に出力する文章を定義します。エラーがない場合は空配列で問題ありません。
引数の$ode
と$scope
には解析対象の情報が詰められており、これを元に解析でエラーを出すかどうかを決めることができます。
実際に作ってみる
というわけで作ってみましょう。
今回は練習として、「Hoge
クラスのメソッドはget
かpost
で始まらないといけない」というルールを実装してみました(いかにもプロジェクト固有なルールで恐縮ですが、、、)。
Ruleを作る
まずは以下のようなRuleを実装してみました。
<?php
declare(strict_types=1);
namespace Test\rule;
use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
class MethodNameRule implements Rule
{
public function getNodeType(): string
{
return Node\Stmt\ClassMethod::class;
}
/**
* @param Node\Stmt\ClassMethod $node
* @param Scope $scope
* @return string[]
*/
public function processNode(Node $node, Scope $scope): array
{
$className = $scope->getClassReflection()?->getName();
// 対象クラスを A のみに制限
// NOTE: $classNameはnamespaceもついてくるのでstr_ends_withで比較
if (!str_ends_with($className, '\\Hoge')) {
return [];
}
$methodName = $node->name->toString();
// 'get' または 'post' で始まっているかをチェック
if (!preg_match('/^(get|post)/', $methodName)) {
return [sprintf(
'メソッドの名前はgetかpostで始めてください! %s::%s',
$className,
$methodName
)];
}
return [];
}
}
getNodeType()
ではNode\Stmt\ClassMethod
のクラス名をreturnすることで、解析対象をクラスのメソッドだけに絞っています。
また、processNode()
では$scope
が持っているクラス名の情報を利用しHoge
メソッドのみが対象となるように指定します。その後、$node
が持っているメソッド名を正規表現にかけ解析エラーかどうかを判断します。
設定ファイル(.neon)ファイルに記載
作成したルールはphpstan.neonファイル以下のように記載すればOKです。
rules:
- Test\rule\MethodNameRule
ちなみに、ルール自体にコンストラクタなどを定義している場合、rulesではなくServiceなどに定義が必要です。詳しくは公式ドキュメントをご覧ください。
実行してみる
それでは実行してみます。
テスト用に以下のクラスを作成してみました。
<?php
namespace Test\analyseObject;
class Hoge
{
public function getTest(): void {}
public function postTest(): void {}
public function putTest(): void {}
}
解析をかけた結果がこちらです。ちゃんと意図したところでだけエラーがでるのが確認できました!
./vendor/bin/phpstan analyse
Note: Using configuration file C:\path\to\project\phpstan.neon.
9/9 [============================] 100%
------ ------------------------------------------------------------------------------------------
Line analyseObject\Hoge.php
------ ------------------------------------------------------------------------------------------
:10 メソッドの名前はgetかpostで始めてください! Test\analyseObject\Hoge::putTest
✏️ analyseObject\Hoge.php
------ ------------------------------------------------------------------------------------------
終わりに
今回はPHPStanのカスタムルールを作成してみました。
chatGPTにいろいろ聞きながら作ってみましたが、サクッと作れていい感じです。
プロジェクト固有のルールはどこにでもあると思うので、レビューでよく指摘する内容などはルールに起こすのもアリだと思います。
ここまで読んでいただきありがとうございました!
参考