弊社で毎月開催し、PHPエンジニアの間で好評いただいているPHP TechCafe。
2022年5月のイベントでは「静的解析」について語り合いました。
弊社のメンバーが事前にまとめてきた静的解析ツールやその使い方の情報にしたがって、他の参加者に意見を頂いて語り合いながら静的解析について学びました。
今回はその内容についてレポートします。
rakus.connpass.com
静的解析とは
Wikipediaからの引用が紹介されました。
コンピュータのソフトウェアの解析手法の一種であり、ファイルを実行することなく解析を行うこと。
ソースコードに対して行われることが多く、少数ながらオブジェクトコードに対して行う場合もある。
具体例として、PHPのソースコードに対してプログラムを実行せずに解析し、問題点を検出するイメージです。
一般的な使い方
今回は以下の二つを挙げて、紹介されました。
編集中コードの検証
IDEを利用し、コーディング中に静的解析を行うことをイメージして紹介されました。
例として「PHP文法の誤りを表示」「不適切コードの指摘」「コード補完」を挙げられていました。
その際に、次のような様々な意見がありました。
- PHPerならPhpStormを使用していることが多いため、恩恵は受けていると思う。
- コードを書いている際に、下に赤い線で表示されている。
- コンパイルしない言語で作る場合、実行時エラーとなる前に気づけるのは、非常に心強い味方となる。
CIに組み込む
コードを定期的/自動的に静的解析を行うことをイメージして紹介されました。
具体例としては、以下の説明がありました。
- GitにPushした際に自動的に解析を行い、不適切なコードやルール違反を指摘する。
- 静的解析の実行をGit hooksに組み込んでいる場合もある。
- GitにPushし、編集したコードで警告が検出されれば、Pushを拒否するようにも出来る。
- PhpStormでコミットする際に、チェックを走らせる事や、自分で選んだコード解析ツールを走らせる事もできる。
メリットとして、以下のような事が紹介されました。
- コードレビューの観点が明確になる事で、より有益な話に時間を使えるようになる。
- 人に指摘されるより気が楽である。
実行時の型検査について
参加者から「実行時の型検査」について考えを聞く質問があり、大変盛り上がりました。
(以下が質問と合わせて貼られたリンクになります。)
https://stitcher.io/blog/we-dont-need-runtime-type-checks
- この記事はPHPの現場で紹介された。
これについては、次のような様々な意見がありました。
- 自社内でもレビュアーによって意見が割れている。
- Docコメントだけでなく、メソッドの戻り値の型を記載して欲しい人。
- 両方記載は面倒くさいため、言語機能としてサポートされている型はメソッドの戻り値の型を記載し、サポートされていない型はDocコメントに記載する人。
- レビューコスト下げるために静的解析を入れているが、意見が割れるため返ってコストが上がってしまっている。
- これにはPHPの歴史的背景もある。
- 元々はメソッドの戻り値に型指定ができないため、Docコメントへ記載し、静的解析でチェックする事でしのいできた。
- メソッドの戻り値に型指定できるようになった今、「Docコメントで静的解析によるチェック」、「言語仕様として縛りを入れる」のどちらが良いのか?
最終的には以下の方針が挙げられました。
- 今から新規作成の場合は、言語仕様でもチェックした方が良い。
- 既存処理への修正の場合、元々静的解析チェックのみで統一されていた箇所は、そのまま「Docコメントに戻り値を記載」で統一でも良い。
- しかし、Docコメントはただの文字列であるため、言語仕様で縛れるように少しづつリファクタリングしていく方が良い。
PHPDocを書かないと型が通らなくなる?
また、質問者様の考えは以下であったようです。
- 原則、メソッド戻り値の型宣言は記載する。
- Docコメントはジェネリックスのような必要と判断した場合のみ記載する。
- 上記以外の基準は、PHPStanでレベルを上げた際に、Docコメントを記載しないとエラーと判定された場合に記載する。
これについては、次のような様々な意見がありました。
- PHPStanでしかチェックできないものも多いので、今後方針に悩む箇所が発生しそうだ。
- PHPStanでレベルを上げる事によりエラーとなる場合は、他のツールを併用する事で、少しづつPHPStanでメンテする範囲を緩める方法もある。
- PHP言語の型指定が表現豊かではないので、表現豊かにすると実行時にそれだけ解析して処理が重くなるため、Docコメントの方に書いておいたほうがPHP全体の利益になる。
- 今は言語仕様が過渡期であるため、PHPの利点(途中でエラー中断せず最後まで実行)を残すためにノーチェックに出来るオプションが希望されている。
- メソッド戻り値の型宣言をCS-Fixerで一括で記載する方法もある。
- 自動置き換えによる信頼性を疑問視しているので、検出だけ担当するのは良さそう。
- テストコードが存在すれば品質は担保できる。
最終的には、チーム皆で話し合って方針を決めて行くのが大事だとまとめられました。
テストコードを静的解析の対象に入れるか?
話の流れから「CIとかで解析を回すときにテストコードは解析対象に入れるか否か」のアンケートが取られました。
このアンケートには、次のような様々な意見がありました。
- テストコードを解析するメリットが分からない。
- プロジェクト内でPHPの有名なライブラリを確認したが、解析対象になっているものが多かった。
- 変更部分のみテストできる機能があれば便利である。
- CS-Fixerを使用しているが、PHPStanは面倒なので除外にすることが多い。
- 解析の対象に入れてデメリットは特にないため、基本方針として解析の対象としている。
議論の余地はあるようですが、解析対象として入れるほうが優勢でありました。
PSRとの関係性
PSRとはPHP-FIGが策定しているPHPの標準規約集です。
規約は各ルールがほぼ独立しており、好きな規約のみを選んで採用することができます。
www.php-fig.org
- PSR1とか2とか12とかはコーディング規約に関するものなので取り入れている静的解析ツールも多いのではないか
- PSR2は現在では非推奨だが、昔からの名残で残っているものもある
代表的なPHP静的解析ツール
PHPMD
コードの複雑性を検知してくれるものです。
具体例を挙げると、バグになりそうなコード、最適ではないコード、複雑な表現、未使用のパラメータ・メソッド・プロパティなどを検出してくれるツールです。
これについては、次のような様々な意見がありました。
- インストール方法は色々あり、最近だとComposerが便利。直接GtiHubから落とすことも出来る。
- コマンドで実行した場合、PHPMDで検出対象のファイルやディレクトリを指定、結果のフォーマット選択、ルールセット指定の手順である。
- ルールセットは、コードサイズや、設計関連ルール、ネーミング、未使用コード検出などのルールが予め定義されている。
- xmlファイルを配置し、必要なルールのみ適用、または、不要なルールを削除し、個別ルール(最大値や最小値など)へ変更することも可能である。
実行例
[root@vg110 core]# vendor/phpmd/phpmd/src/bin/phpmd test.php text codesize,controversial,design,naming,unusedcode /usr/local/vanguardDev/vanguard/vg-back/core/test.php:5 CamelCaseMethodName The method test_function is not named in camelCase. /usr/local/vanguardDev/vanguard/vg-back/core/test.php:5 CamelCaseParameterName The parameter $param_string is not named in camelCase. /usr/local/vanguardDev/vanguard/vg-back/core/test.php:5 CamelCaseVariableName The variable $param_string is not named in camelCase.
PHP_CodeSniffer (phpcs)
コーディング規約の違反を検出するライブラリです。
PSRやPEARなどの様々なコーディング規約を指定して検査を実施してくれます。
これについては、次のような様々な意見がありました。
- 独自の規約を追加することも可能である。
- インストール方法は色々あり、ComposerやGitHubからなど、好みの方法で導入できる。
- コーディング規約の違反を自動で修正するphpcbfも付属している。
- 全て修正する事は出来ず、修正出来た物も目視チェックは必要である。
- 完全に信用する訳ではなく、phpcsの指摘があるファイルをユーザに通知し、修正は手動で行う運用をしている。
- phpcbfは結構正確だが、phpcsに問題がある。
- 改行やインデントについて、コード上は正常であっても、CS-Fixerがエラーと判定するケースがたまにある。
実行例
$ phpcs /path/to/code/myfile.php FILE: /path/to/code/myfile.php -------------------------------------------------------------------------------- FOUND 5 ERROR(S) AND 1 WARNING(S) AFFECTING 5 LINE(S) -------------------------------------------------------------------------------- 2 | ERROR | Missing file doc comment 20 | ERROR | PHP keywords must be lowercase; expected "false" but found | | "FALSE" 47 | ERROR | Line not indented correctly; expected 4 spaces but found 1 47 | WARNING | Equals sign not aligned with surrounding assignments 51 | ERROR | Missing function doc comment 88 | ERROR | Line not indented correctly; expected 9 spaces but found 6 --------------------------------------------------------------------------------
コーディング規約の指定
# インストールされているコーディング規約を確認 $ phpcs -i The installed coding standards are MySource, PEAR, PSR1, PSR12, PSR2, Squiz and Zend # コーディング規約を指定して実行(デフォルトはPEAR) $ phpcs --standard=PSR12 /path/to/code/myfile.inc # デフォルトのコーディング規約を変更 $ phpcs --config-set default_standard PSR12
規約のカスタマイズ
- phpcs.xml ファイルを設置して独自のコーディング規約を設定できる。
<?xml version="1.0"?> <ruleset name="Custom Standard"> <rule ref="PSR12"> <!-- "PSR12" の中で除外するルール --> <exclude name="Generic.Files.LineLength"/> </rule> <!-- 追加するルール --> <rule ref="PEAR.WhiteSpace.ObjectOperatorIndent"/> <!-- 除外するファイル・ディレクトリ --> <exclude-pattern>node_modules/</exclude-pattern> </ruleset>
PHPStan
こちらも同じくComposerやDockerから利用可能です。
PHARからもダウンロード可能です。
ただし、Composerが無いとPHPStanの拡張機能は使えません。
解析の厳密さはオプションで変更可能で、PHPStanの場合はレベル0から9で設定可能です。
- レベル0が一番ゆるく、レベル9が最も厳密となっている
- レベル0は基本的なチェックで、PSRと同等のチェックレベル
- レベル9では、混合型(mixed)についても厳密であるレベル(混合型(mixed)は、別の混合型(mixed)に渡す操作しか許可されない)になる
PHPStanの公式に記載がありますが、以下の理由から使用方法はレベル0から順番に対応していくのが正攻法のようです。
- 初めからレベル9を設定すると、エラー件数が膨大となり、何から手を付けたらいいか分からなくる。
- 上位のレベルでエラーを検知した際は、下位のレベルで検知されるエラーも合わせて修正を行わないと、エラーは解消されない
歴史の長いプロジェクトでは、レベル0の設定でも多くのエラーが検出されます。
全ての検出されたエラー対応が困難な場合は、ベースラインの設定を推奨します。
ベースラインを設定することにより、以下のようなことが出来ることが紹介されました。
- ある時点で検出したエラー内容を基準とし、それ以降の静的解析の実行で、基準としたエラー内容を報告しないように出来る。
- 既存コードで検出されているエラーを除外出来るため、新しいコードと変更されたコードで検知されたエラーのみに注力することが出来る。
- カスタムルールも設定できるようになっており、
phpstan-rules
とphpstan-strict-rules
を使用して拡張することが出来る。
これについては、次のような様々な意見がありました。
- レベル5からレベル6の間が深い河である
- レベル6は「タイプヒントの欠落を表示」であるため、クリア難易度が高い
- ベースラインの概念があることは有難い
- 別のツールでPHPStanのベースラインと同様の事を行ったが、非常に辛かった
- ベースラインの概念は他ツールでは、Psalmにもある
- サイボウズさんでは「ベースラインを自動実行する」取り組みを行っていて面白そう(※)
※ 情報元の紹介はありませんでしたが、記事化にあたって調べたところ参考になるスライドがありました。
ツールはPHPStanではないですが、適応出来そうです。
効果的な静的解析の CI導入パターンを求めて / Great static analysis with CI - Speaker Deck
Psalm
こちらもPHPStanと同様で、厳密さをレベルで調節できます。
ただし、レベルの強弱がPHPStanと逆になっており、レベル1が最も厳しく、レベル8が一番ゆるい設定になっています。
良い点として、次のように紹介されました。
- 検知された違反ルールがなぜ不適切なのか理由が解説されるため、納得感を得やすくなっている。
- これがなんで悪いの? と思うときがあるが、理解ができると直すモチベーションも得られるのではないか。
例
PhpStorm
編集中に解析を行い、危険な箇所に対して下線で示すか、ハイライトしてくれます。
解析方法はPHPファイルをPhpStormで開くだけです。
- 文法の誤りをハイライトし、重要度によってハイライト部分の色が変更されるため見やすい。
- プロファイルの編集で解析内容を編集でき、任意の解析を追加する事もできる。
- 他の解析ツールをインストールし使用すことも可能。
PHPコマンドによる文法チェック
少し本筋とは違いますが、「静的解析を調べる際に、php -l
で文法チェックができることを見つけた。」と紹介されました。
そのため、文法の間違いについては、静的解析ツールを使用せずにエラー検知する事が出来ます。
これについては、次のような様々な意見がありました。
php -l
で静的解析できることを初めて知った。- 静的解析かと言われると疑問がある。一応解析は行っているので意味は合っている?
- 全ファイルを対象に
php -l
を実行したことが何度かある。
編集後記
以上、静的解析の概要と代表的なツールについて取り上げました。
PHP TechCafeでは今まで取り上げることがなかった話題でしたので、主催者/参加者ともに盛り上がりを見せていました!
今後も静的解析の活用方法や、新たなニュースに着目していきたいと思います!
「PHP TechCafe」では今後もPHPに関する様々なテーマのイベントを企画していきます。
皆さまのご参加をお待ちしております。
connpass.com
エンジニア中途採用サイト
ラクスでは、エンジニア・デザイナーの中途採用を積極的に行っております!
ご興味ありましたら是非ご確認をお願いします。
https://career-recruit.rakus.co.jp/career_engineer/カジュアル面談お申込みフォーム
どの職種に応募すれば良いかわからないという方は、カジュアル面談も随時行っております。
以下フォームよりお申込みください。
rakus.hubspotpagebuilder.comラクスDevelopers登録フォーム
https://career-recruit.rakus.co.jp/career_engineer/form_rakusdev/イベント情報
会社の雰囲気を知りたい方は、毎週開催しているイベントにご参加ください!
◆TECH PLAY
techplay.jp
◆connpass
rakus.connpass.com