PHPStanのレベルを変更することなく引数の型検査ルールを追加した話

  • URLをコピーしました!

皆さんこんにちは、開発本部でインターンをしている石田です。

今回はバックエンド側で導入しているPHPStanというツールにレベルを変更することなくルールを追加した話とその方法についてお話します。

目次

PHPStanとは

PHPStanはPHPのコードに対して実行時エラーや問題となるような部分をコードを実行せずに検査できるツールです。設定をすることでエディタ上で警告を行ったり、CIで解析を行って警告を出すことができるようになり、人間がプログラム上の問題を見落としてしまうことを防げます。

レベル設定について

PHPStanでは0 ~ 9までの範囲で検査レベルの設定ができます。現在弊社ではレベル3に設定しており、エディタとCI上で利用しています。

関数やメソッドの引数の型に対する厳格な検査を有効化したい

弊社ではPHPStanをレベル3で運用していたので、PHPStanによる引数の型に対する検査は行われていませんでした。ただ、PHPStanを利用しなくとも、基本的な引数の型に対する検査はPhpStormが行ってくれたり、コードレビューにて見つかるため大きく問題になることはありませんでした。

しかし、PhpStormによる検査は基本的なもの限定で、少し複雑になるだけで警告してくれなくなります。

以下の例を見てください。echoNameという関数がstring型を引数として要求しています。しかし実際に利用している部分では stringとnullの両方を取りうるstring|null型となる変数を与えています。

<?php declare(strict_types=1);

function echoName(string $name): void
{
    echo $name;
}

$hoge = rand(0, 1) ? null : "hoge";

echoName($hoge);

このプログラムを実行すると2分の1程度の確率でTypeErrorを引き起こします。しかし、このようなプログラムはPhpStormでは警告されません。PHPStanの場合、レベル8以上に設定していると警告が行われますがそれ以下のレベルでは警告が行われません。

このようなコードが発生したときに警告が行われないため、TypeErrorを発生させるコードがあっても気付けないパターンが存在するという危険性がありました。

レベルを上げることによる弊害

上記のような場合に警告を出すようにするにはレベル8以上が必要ということですが、一気に8までレベルを上げると警告が多くなり、開発に支障をきたす可能性があります。特に弊社は昔に書かれたレガシーコードが大量に存在しています。そのためリファクタリングしないと解決しないような問題が発生したり、それを無理やり解決するために// @phpstan-ignore 等で握りつぶしてしまうという問題が多発する可能性があります。このような状況になるとPHPStanが「Member of Your Team」ではなくなってしまいます。

このようなレベルとコード品質の問題に直面し、導入したい検査を導入できない状況に困っている方も多いのではないかと思います。

レベルを変更せずにルールを追加する方法

先に結論からお話すると、現在のレベル3から変更することなく先程の「関数やメソッドの引数の型に対する厳格な検査」を有効化するには以下のオプションをPHPStanのconfigに記載することで実現できます。

checkFunctionArgumentTypes: true
checkUnionTypes: true
checkNullables: true

このオプションを付けることでレベル3でもレベル0でも関数の引数に対する高度な検査が行えるようになります。

オプションの意味としては以下の通りです。

  • checkFunctionArgumentTypes : 関数やメソッドの引数の型に対するチェックを有効化 (本来はレベル5以降で適応)
  • checkUnionTypes : ユニオン型の部分的な間違いに対するチェックを有効化 (本来はレベル7以降で適応)
  • checkNullables : Nullableな変数やプロパティ等に対するチェックを有効化 (本来はレベル8以降で適応)

もし関数やメソッドの引数の型に対するチェックだけを有効化したければcheckFunctionArgumentTypesのオプションを付けるだけでも有効化できます。今回は更に厳密なチェックを行いたいのでcheckUnionTypescheckNullablesのオプションをつけました。これらのオプションはPHPStanのドキュメントには載っていないため知らない方も多いと思います。

このような方法を使い、現在弊社ではレベル3の状態で「関数やメソッドの引数の型に対する厳格な検査」を有効にしてPHPStanを利用しています。

なぜレベルを変更しなくてもルールを追加できるのか

上記のような方法でなぜレベルに関係なくルールを追加できるのか経緯から説明します。

前述したように、関数に対する引数の型の厳格な検査を導入したいと考えていました。しかし、レベルを8まで変更するのは難しいということで、レベルを変更せずに特定のチェックだけを追加する方法がないかGitHubでソースコードを確認したところ、PHPStanのConfigをまとめたディレクトリがあることを発見しました。このディレクトリの中を見てみるとそれぞれのレベルに対応したConfigファイルが配置されていました。

該当部分 : https://github.com/phpstan/phpstan-src/tree/1.12.x/conf

Configファイルの中を見ると、1つ下のレベルのConfigをincludesで読み込み、新たなルールを追加していることがわかります。そのため、PHPStanのレベルを設定するとそれに応じたConfigファイル読み込まれ、そのConfigからこれまでのレベルの設定がすべて読み込まれるという仕組みになっていることがわかります。

このことから、レベルを変更しなくても自分が導入したいルールに該当するオプションを探し、自分でConfigに追加すれば検査項目を追加することができるのではないかと考え、実際に試してみたところ、レベルに関係なく自由に検査内容を追加できることに気づきました。

他のルールでも同じ手法が使える

上記で説明した方法は引数の型に対するチェックだけではなくその他の検査項目でも同様に利用可能です。「PHPStanを導入したいけど1や2で追加されるチェック項目が多すぎて大変。でもレベル2で追加される特定の検査だけは導入したい…」というような悩みがある場合に、レベル2に対応するConfigの中から該当の項目を探し、設定ファイルに書き込むことで導入ができます。

こうすることで、1や2で追加される検査項目を大量に抱えることなく任意の検査項目のみを追加することができます。

注意点

しかし、上記の手法には注意点があります。それは各オプションに関係性があることです。関数の引数の型検査に関するオプションを例にあげると関係性は以下のようになると考えられます。

関数やメソッドの引数の型に対する検査を有効化した状態でなければ引数のユニオン型に対する検査を有効化できません。そして、その2つが有効化されていないとNullableに対する検査も有効になりません。というより有効化しても根本となる「関数やメソッドの引数の型に対する検査」が有効化されていないので意味をなさないというわけです。

このような関連性が他のオプションにも存在している可能性が十分にあります。PHPStanのレベルはこのような部分も考慮されて構成されているので、個別にルールを追加する場合はそれぞれのルールの関連性に注意が必要です。

紹介した手法は正規の手法か

前述した方法が正しい手段かというと必ずしもそうではないと言えます。まず上記で説明した方法はドキュメントに記載されていません。PHPStanのドキュメントがそこまで充実していないという話もありますが、意図的に載せていない可能性もあります。

PHPStanの開発者であるオンドレイ氏は、上記で説明した方法に関連したissueにて以下のようにコメントしています。

So you’re trying to skip level 7? Why? I recommend you not to set checkNullables manually, but simply follow on level 7 after completing level 6 and then go to level 8 after you complete level 7. You can also take advantage of the baseline: https://phpstan.org/user-guide/baseline

翻訳) レベル7をスキップしようとしているのですか?なぜ? 私はcheckNullablesを手動で設定するのではなく、レベル6を完了した後にレベル7を進め、それを完了したらレベル8に進むことをお勧めします。また、ベースラインを活用することもできます https://phpstan.org/user-guide/baseline

オンドレイ氏のコメントやPHPStanのレベルがわざわざ用意されていることから、レベルは低いレベルから順番に上げられていくことが推奨されているとも受け取れます。下地をある程度作ってからでないと開発に支障をきたす可能性もありますし、下地がある方が検査も効果的にできるとも感じます。

これらのことから今回紹介した手法が必ずしも良い結果を生むとは限らないのでご注意ください。

おわりに

ここまで読んで頂きありがとうございました。PHPStanに関して同じような悩みを持つ方にこの記事が参考になれば幸いです。

  • URLをコピーしました!

この記事を書いた人

目次