every Tech Blog

株式会社エブリーのTech Blogです。

私たちのLaravelプロジェクトにおけるGit hooks設定のご紹介

この記事は every Tech Blog Advent Calendar 2025 の 27 日目の記事です。

はじめに

こんにちは。リテールハブ開発部の清水です。

私たちは小売向けサービスをLaravelで開発しています。
このプロジェクトではGit hooksのpre-commit設定を使用してコミットのタイミングでLaravel Pint, Larastanを呼び出すことでコード品質を整えるための仕組みを使用しています。
この仕組みのベースは、プロジェクト初期に整備されたものを引き継いだもので、今回その内容を見直しながら整理しました。
ちょうど良い機会でしたので、本記事で私たちが使用している設定内容をご紹介いたします。

Git hooksとは?

Git hooksとは、git commit や git push などの Git 操作をきっかけに、自動でスクリプトを実行できる仕組みです。
コミット前にチェック処理を挟むなど、人の操作ミスを防ぐための自動処理を組み込む用途で使われます。
https://git-scm.com/docs/githooks

Laravel Pintとは?

Laravel Pint は、Laravel公式が提供している PHPコードの自動フォーマッターです。
決められたコーディング規約(Laravel / PSR-12 など)に従って、PHPコードの書き方を自動的に統一します。
https://laravel.com/docs/12.x/pint

Larastanとは?

Larastan は、PHP の静的解析ツール PHPStan を Laravel 向けに拡張したツールです。
コードを実行せずに解析し、存在しないプロパティや型の不整合などの問題を事前に検出します。
https://github.com/larastan/larastan

コミットを行った時の処理の流れ

  1. git commitを実行すると、Git hooksのpre-commitフックが起動
  2. コミット対象のPHPファイルを取得
  3. Laravel Pintによるフォーマットチェック
  4. Larastanによる静的解析
  5. 3 or 4のチェックでエラーが検出された場合、コミットを中断
  6. 全てのチェックをパスした場合のみ、コミットが完了

pre-commit設定内容

#!/bin/sh
set -eu

# --- 1) コミット対象(ステージ済み)のPHPファイルだけ拾う ---
php_files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.php$' || true)

# PHPファイルがなければ何もしない
if [ -z "$php_files" ]; then
  exit 0
fi

echo "コミット対象のPHPファイルをチェックしています..."

# --- 2) Pint:フォーマットチェック(修正はしない) ---
echo "Pintでフォーマットをチェックしています..."
if ! echo "$php_files" | xargs ./vendor/bin/pint --test; then
  echo ""
  echo "❌ フォーマットエラーがあります。"
  echo "   以下のコマンドで修正してください:"
  echo "   make lint"
  exit 1
fi
echo "✓ フォーマットチェック OK"

# --- 3) Larastan:静的解析 ---
if echo "$php_files" | grep -qE '^app/'; then
  echo "Larastanで静的解析を実行しています..."
  if ! ./vendor/bin/phpstan analyse --no-progress --memory-limit=1G; then
    echo ""
    echo "❌ 静的解析エラーがあります。"
    echo "   エラーを修正してから再度コミットしてください。"
    exit 1
  fi
  echo "✓ 静的解析 OK"
fi

echo ""
echo "✓ 全てのチェックが完了しました。"
exit 0

Makeコマンドの紹介

コミットのタイミングだけではなく、手動でLaravel Pint, Larastanを実行したい場面もあります。
以下のMakeコマンドで実行できるようにしています。

# コードスタイルチェック&修正
lint:
    @files=$$(git diff --cached --name-only --diff-filter=ACM | grep "\.php$$" | sed "s|^$$(basename $$(pwd))/||"); \
    if [ -n "$$files" ]; then \
        docker compose -f $(COMPOSE_FILE) exec -T $(CONTAINER_PHP) sh -c "./vendor/bin/pint $$files"; \
    else \
        echo "ステージされたPHPファイルがありません"; \
    fi

# コードスタイルチェック
lint-check:
    @files=$$(git diff --cached --name-only --diff-filter=ACM | grep "\.php$$" | sed "s|^$$(basename $$(pwd))/||"); \
    if [ -n "$$files" ]; then \
        docker compose -f $(COMPOSE_FILE) exec -T $(CONTAINER_PHP) sh -c "./vendor/bin/pint --test $$files"; \
    else \
        echo "ステージされたPHPファイルがありません"; \
    fi

# 静的解析
larastan:
    docker compose -f $(COMPOSE_FILE) exec $(CONTAINER_PHP) ./vendor/bin/phpstan analyse --memory-limit=1G

実際にコミットする流れ

Laravel Pint, Larastanで弾かれる内容のコードを作成

<?php

namespace App\Http\Controllers;

// importが名前順になっていない
use Retailapp\Common\Models\User;
use Illuminate\Http\JsonResponse;
use App\Http\Controllers\Controller;

class TestController extends Controller
{
    public function index(): JsonResponse
    {
        $user = User::find(1);
        $name = $user->undefined_property; // 存在しないプロパティを呼び出している

        return response()->json(['name' => $name]);
    }
}

コミットを試みると、フォーマットエラーで弾かれる

% git commit -m '弾かれてほしいコミット'
コミット対象のPHPファイルをチェックしています...

Pintでフォーマットをチェックしています...

  ⨯

  ──────────────────────────────────────────────────────────────────── Laravel  
    FAIL   ............................................. 1 file, 1 style issue  
  ⨯ app/Http/Controllers/TestController.php no_unused_imports, ordered_import…  

❌ フォーマットエラーがあります。
   以下のコマンドで修正してください:
   make lint

pintで自動的に修正

% make lint

  ✓

  ──────────────────────────────────────────────────────────────────── Laravel  
    FIXED   ...................................... 1 file, 1 style issue fixed  
  ✓ app/Http/Controllers/TestController.php no_unused_imports, ordered_import… 

もう一度コミットすると、今度はLarastanで弾かれる

% git commit -m '弾かれてほしいコミット'
コミット対象のPHPファイルをチェックしています...

Pintでフォーマットをチェックしています...

  ──────────────────────────────────────────────────────────────────── Laravel  
    PASS   ............................................................ 1 file  

✓ フォーマットチェック OK
Larastanで静的解析を実行しています...

 ------ -------------------------------------------------------------------------- 
  Line   Http/Controllers/TestController.php                                       
 ------ -------------------------------------------------------------------------- 
  :14    Access to an undefined property                                           
         Retailapp\Common\Models\User::$undefined_property.                        
         💡 Learn more:                                                            
            https://phpstan.org/blog/solving-phpstan-access-to-undefined-property  
 ------ -------------------------------------------------------------------------- 

 [ERROR] Found 1 error                                                          
                                                                                
❌ 静的解析エラーがあります。
   エラーを修正してから再度コミットしてください。

Larastanに違反する部分を修正

<?php

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Retailapp\Common\Models\User;

class TestController extends Controller
{
    public function index(): JsonResponse
    {
        $user = User::find(1);
        $name = $user->name; // 修正

        return response()->json(['name' => $name]);
    }
}

コミット成功

% git commit -m '通ってほしいコミット'
コミット対象のPHPファイルをチェックしています...

Pintでフォーマットをチェックしています...

  .

  ──────────────────────────────────────────────────────────────────── Laravel  
    PASS   ............................................................ 1 file  

✓ フォーマットチェック OK
Larastanで静的解析を実行しています..
                                                                                
 [OK] No errors                                                                 
                                                                                
✓ 静的解析 OK

✓ 全てのチェックが完了しました。

おわりに

本記事では、私たちの Laravel プロジェクトで使用している Git hookのpre-commit 設定と、その中で Laravel Pint・Larastan をどのように組み込んでいるかをご紹介しました。
同様の仕組みを検討されている方の参考になれば幸いです。

最後までお読みいただきましてありがとうございました。