🥊
Lefthook で PHPStan と PHP-CS-Fixer と PHPUnit を使う
はじめに
PHP の開発でよく使う以下のツールを Lefthook で動かしてみたので備忘録。
環境に依存したくなかったので Docker を使って実行してみる。
前提
セットアップ
あらかじめ lefthook.yml
があるディレクトリ内で以下を実行しておく必要がある。
% lefthook install
動作確認をしたディレクトリ構成は以下。
├── .git
├── .php-cs-fixer.dist.php
├── lefthook.yml
├── phpstan.dist.neon
├── phpunit.dist.xml
├── src
│ └── sample.php
└── tests
└── SampleTest.php
Lefthook
- 辞書順にコマンドが実行されるのでコマンド名の先頭に数字をつけておく
- tags - コマンド指定用のタグ
- {staged_files} - ステージングされたファイルを指定
- stage_fixed: true - 修正を自動でステージングする
lefthook.yml
pre-commit:
commands:
1_phpstan:
tags: [dev, scan]
glob: "src/*.php"
run: docker run -it --rm -v $(pwd):/app
ghcr.io/phpstan/phpstan:2-php8.3 analyse {staged_files}
2_php-cs-fixer:
tags: [dev, format]
glob: "src/*.php"
stage_fixed: true
run: docker run -it --rm -v $(pwd):/code
ghcr.io/php-cs-fixer/php-cs-fixer:3-php8.3 fix {staged_files}
3_phpunit:
tags: [dev, test]
glob: "{tests,src}/*.php"
run: docker run -it --rm -v $(pwd):/app
ghcr.io/jitesoft/phpunit:8.3
PHPStan
phpstan.neon
parameters:
level: 7
PHP-CS-Fixer
.php-cs-fixer.dist.php
<?php
declare(strict_types=1);
return (new PhpCsFixer\Config())
// ↓並列実行できる場合の設定
->setParallelConfig(PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect())
->setRiskyAllowed(true)
->setRules([
'@PSR12' => true,
'strict_param' => true,
'return_assignment' => true,
'array_syntax' => ['syntax' => 'short'],
])
;
PHPUnit
phpunit.dist.xml
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
failOnRisky="true"
failOnWarning="true"
colors="true"
>
<testsuites>
<testsuite name="default">
<directory>tests</directory>
</testsuite>
</testsuites>
<source ignoreIndirectDeprecations="true" restrictNotices="true" restrictWarnings="true">
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
動作確認用の PHP のコード
src/sample.php
<?php
declare(strict_types=1);
function hoge(int $a, int $b){
if(in_array($b, [1, 2, 3, 4, 5])){
return $a + $b;
}
return $a;
}
echo hoge(5, 10) . PHP_EOL;
echo hoge('5', 10) . PHP_EOL;
tests/SampleTest.php
<?php
declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SampleTest extends TestCase
{
public function testSample(): void
{
$this->assertSame('3', 1 + 2);
}
}
動作確認
% git add src/sample.php
% git commit
╭───────────────────────────────────────╮
│ 🥊 lefthook v1.10.8 hook: pre-commit │
╰───────────────────────────────────────╯
┃ 1_phpstan ❯
Note: Using configuration file /app/phpstan.dist.neon.
1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
------ -------------------------------------------------------------
Line sample.php
------ -------------------------------------------------------------
4 Function hoge() has no return type specified.
🪪 missingType.return
13 Parameter #1 $a of function hoge expects int, string given.
🪪 argument.type
------ -------------------------------------------------------------
[ERROR] Found 2 errors
┃ 2_php-cs-fixer ❯
PHP CS Fixer 3.66.0 Persian Successor by Fabien Potencier, Dariusz Ruminski and contributors.
PHP runtime: 8.3.7
Running analysis on 7 cores with 10 files per process.
Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!
Loaded config default from "/code/.php-cs-fixer.dist.php".
Using cache file ".php-cs-fixer.cache".
1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
1) src/sample.php
Fixed 1 of 1 files in 0.005 seconds, 16.00 MB memory used
┃ 3_phpunit ❯
PHPUnit 11.5.3 by Sebastian Bergmann and contributors.
Runtime: PHP 8.3.16
Configuration: /app/phpunit.dist.xml
F 1 / 1 (100%)
Time: 00:00.002, Memory: 25.36 MB
There was 1 failure:
1) SampleTest::testSample
Failed asserting that 3 is identical to '3'.
/app/tests/SampleTest.php:10
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
────────────────────────────────────
summary: (done in 1.55 seconds)
✔️ 2_php-cs-fixer
🥊 1_phpstan
🥊 3_phpunit
fix された PHP ファイル。
<?php
declare(strict_types=1);
function hoge(int $a, int $b)
{
if (in_array($b, [1, 2, 3, 4, 5], true)) {
return $a + $b;
}
return $a;
}
echo hoge(5, 10) . PHP_EOL;
echo hoge('5', 10) . PHP_EOL;
特定のコマンドをスキップする
LEFTHOOK_EXCLUDE
変数にコマンド名やタグを指定することで特定のコマンドをスキップできる。
コマンド名を指定した場合。
% LEFTHOOK_EXCLUDE=phpunit git commit
タグを指定した場合。
% LEFTHOOK_EXCLUDE=dev git commit
所感
- 絵文字が右手 😅
-
lefthook install
の実行を忘れそうなので工夫が必要 -
{staged_files}
指定は良き - PHP-CS-Fixer で修正分を勝手にステージングされるのは微妙だった
- 修正を確認してからステージングしたいところ
- PHPUnit はエクステンションとかの依存もあるので開発用のコンテナで実行した方が良さそう
- それを言い出したら他のツールも同様か
- コミットのたびに全テスト実行するのはしんどいので PHPUnit は手動でもよいかな
- CI サーバでの担保は必要
おまけ
並列実行の設定をしないまま PHP-CS-Fixer を実行すると以下のメッセージが表示される。
You can enable parallel runner and speed up the analysis! Please see usage docs for more information.
で、並列実行を設定すると以下のメッセージが表示される。
Parallel runner is an experimental feature and may be unstable, use it at your own risk. Feedback highly appreciated!
いや、まぁそうなんだろうけど、なんか理不尽。。。
環境
% sw_vers
ProductName: macOS
ProductVersion: 15.2
BuildVersion: 24C101
% docker --version
Docker version 27.4.0, build bde2b89
% lefthook version
1.10.8
Discussion