はじめに
弁護士ドットコム Advent Calendar 2021
記念すべき1日目🎉(2年連続)
本記事はあくまでも、Laravel MeetUp Okinawa でお話しした内容を記事にしたものになります。
本題
突然ですが、レビューでこんな事を言われた/言った経験はないだろうか。
「インデント!!これもう指摘するの3回目!!」
「ここの変数未定義だし、ここも型合ってないよ!!」
……………
…………………………
………………………………………
指摘された側
はその場では反省して、気をつける事だろう。
しかし、所詮は人間。同じミスはしてしまう。
指摘する側
も同じことを注意するのは面白いものではないし、
何よりロジックや設計のレビューに集中してレビューしたい。
静的解析と自動整形を導入してみない?
そんな時に 静的解析
と 自動整形
の導入を検討してみよう。
静的解析(PHPStan)
プログラムコードを実行せずにドキュメントやソースコードなどのチェックによって誤りや脆弱性を検出するテスト手法。
それらをPHPで提供されているのが静的解析ツール(今回はPHPStan)です。
今回はLaravel専用のPHPStanのラッパーライブラリLarastanを使用します。
静的解析とは
- 仕様
- プログラムを実行せずに解析される(ただしPHPstanは一部実行する)
- 未定義の変数/メソッド/Class/プロパティなどを検出
- その他PHPDocの構文不備や分岐のチェックも
- メリット
- 静的に解析される箇所は網羅的にテストができる
- テストを書く必要がないのでコストが少ない
- 変数の未定義など細かいエラーが事前に分かるのでコードレビューの手間が省ける
- デメリット
- 無限ループなどは検出できない
PHPDoc
PHPDocに引数や戻り値に型を宣言することで(実際の型宣言ではない)色々なメリットがあります。
型を強くしていきたいけど、型宣言はちょっとハードル高いなぁ...という時に導入すると良いでしょう!
- コードの見通しが良くなる
- IDEの補完が効くようになる
- 静的解析ができる
- arrayの中身を指定できる(array)
- 型宣言本格導入前に気軽に導入できる
/**
* プロフィール情報を取得する
*
* @param string|null $name
* @param integer $age
* @return string|null
*/
public function getProfile($name = null, $age)
{
if (!$name) {
return null;
}
return "名前: {$name} 年齢: {$age}";
}
Levelや範囲などの設定
静的解析のレベルはプロジェクトによって柔軟に設定することができます。
このレベル設定によって、0から初めて徐々にレベルを上げることでより厳密にプロジェクトを静的解析していくことが可能です。
- includes
- 設定ファイルの読み込み
- paths
- 対象ファイル
- level
- 静的解析のレベル 0 ~ 8(max)
- excludePaths
- 無視ファイル
includes:
- ./vendor/nunomaduro/larastan/extension.neon
parameters:
paths:
- app/
- database/
- tests/
level: max
excludePaths:
- ./*/*/FileToBeExcluded.php
checkMissingIterableValueType: false
使い方
ライブラリをプロジェクトに入れたら以下のようなコマンドで実行できます。
./vendor/bin/phpstan analyze
自動整形(PHP-CS-Fixer)
コーディング規約を設定した基準沿って自動でコーディングを統一する機能。
その機能をPHPで提供されているのが PHP-CS-Fixer (他にもPHP_CodeSnifferなどがある) です。
PHP-CS-Fixer では公式で PSR-1 や PSR-2 他にも Symfonyコミュニティ の標準 などが用意されていて、それらを使うことも自身でカスタマイズして使用することができる。
また VSCode でプラグインも用意されており、保存時に自動整形することも可能です。
カスタマイズ
自動整形のルールは独自でカスタマイズも可能です。
ver3.0.0 からはphpファイルになりました。
- Finder::create()
- 対象ファイル
- setRiskyAllowed
- リスクが高い変更の許可
- setRules
- カスタマイズするルール設定を記述
<?php declare(strict_types=1);
$finder = PhpCsFixer\Finder::create()
->in([
__DIR__ . '/app',
__DIR__ . '/config',
__DIR__ . '/database/factories',
__DIR__ . '/database/seeders',
__DIR__ . '/routes',
__DIR__ . '/tests',
]);
$config = new PhpCsFixer\Config();
return $config
->setRiskyAllowed(true)
->setRules([
'@PhpCsFixer:risky' => true,
'array_syntax' => ['syntax' => 'short'],
'blank_line_after_opening_tag' => true,
'cast_spaces' => ['space' => 'single'],
])
->setFinder($finder);
使い方
ライブラリをプロジェクトに入れたら以下のようなコマンドで実行できます。
整形箇所のピックアップ(実際に整形はされない)
./vendor/bin/php-cs-fixer fix -v --diff --dry-run
自動整形を適用する
./vendor/bin/php-cs-fixer fix -v --diff
静的解析
と 自動整形
を導入できました!
おめでとうございます。
これによって実装者はレビューを出す前に細かなエラーに気付いて修正する事ができます!
これでお互い細かなレビューを指摘する/される必要がなくなリました!やったね!
ただもっと楽がしたい!
静的解析
は 自動整形
を導入できたけど、ローカルで実行し忘れたままレビューに投げちゃった(ToT)
なんて事もあるでしょう。
そういう時は CI で自動に実行するようにしましょう。
CI(Github Actions)
CIとは、Continuous Integrationの略で、継続的インテグレーションと呼ばれていて、
開発、テスト、リリースのサイクルが継続的に行われていく様子の事であり、それらを円滑に使用出来る様にしたものがCIツールです。
今回は Github Actions を使用しますが、CIのツールは Github Actions だけでなく、複数のツールが存在しています(Circle CIなど)
Github Actions を使うメリットは無料(privateリポジトリは月2,000時間まで)という点と、普段利用している Github内のプロジェクトに .github/workflows/ ディレクトリを用意して設定ファイルを書くだけで利用できる手軽さにあります。(個人的見解)
他にも公式が用意しているテンプレートを簡単に使用することもできます。
設定方法
- /.github/workflows/ にyaml形式で記入する
- on: CIが走るタイミングを決める
- jobs: stepsの集まり
- steps: jobを構成するAction
- などなど
ここまで学んだ静的解析と自動整形をCIで設定してみる
PHPStan
マスターに対してプルリクエスト、マージが行われたタイミングでテストが走るように設定。
/.github/workflows/larastan.yml
PHP-CS-Fixer
こちらもLarastanと同様のトリガーでテストを実行します。
今回は
dry-run
で差分を出力までしかしていません。
自動整形を適用したい場合は一番下の run を./vendor/bin/php-cs-fixer fix -v --diff
に差し替えてください。
/.github/workflows/php-cs-fixer.yml
CIであえて落ちる実装
あえてテストが落ちるように実装して、CIが走るようにプルリクを作ってみました。
<?php declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ExampleController extends Controller
{
/**
* @param int $id
* @return int
*/
public function index(string $id): int
{
echo $test;
$array = array();
return $id;
}
}
プルリクを作成してCIが動いているのを見てみる
先ほどの実装でプルリクを作ってみた。
Github Actions を使っていると、Merge pull request の上にテストが走っているのが分かる。
テストが落ちると下の図のように、どのテストが落ちていて、Merge pull request が不活性になっているのが確認できる。
静的解析のテスト結果
確認すると以下の内容で、怒られているのが確認できる。
- 引数の型不一致
- 戻り値の型不一致
- 未定義の変数の使用
自動整形のテスト結果
確認すると以下の内容で、怒られているのが確認できる。
- declareの改行
- namespaceの改行
- array()の使用
修正して再度プッシュ
先ほどの内容を修正して、再度プッシュしてみました。
そうするとテストが全部通って、Merge pull requestが活性化しているのが確認できました。
これでプルリクでレビュワーをアサインする前に自動的に細かいレビューで指摘されることがなくなりました!やったね!
最後に
これまで学んだ内容は以下のリポジトリに公開していますので、ご自由にお使いください。
明日は @hkano さんの OpenSearch を使ってみる
です!