loading...

How to setup Git commit hooks for PHP

bdelespierre profile image Benjamin Delespierre ・2 min read

This very simple script will run these checks each time you commit:

  • PHP Internal Linter to check your code for syntax errors
  • PHP Code Sniffer to check for coding standard (PSR-2)
  • PHPStan to analyse the code and find bugs

Overall it takes a few seconds and it analyses only the staged files - those in green when you do a git status.

If a single check fails the error message is displayed and the commit is aborted.

Example:

[1/3] php lint            OK!
[2/3] code sniffer        OK!
[3/3] phpstan             NOK!

 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 ------ --------------------------------------------------------------------- 
  Line   CanSendInvitation.php                                                
 ------ --------------------------------------------------------------------- 
  14     Parameter $lol of method App\Rules\CanSendInvitation::__construct()  
         has invalid typehint type App\Rules\Noob.                            
 ------ --------------------------------------------------------------------- 

 [ERROR] Found 1 error                                                          

Step 1: requirements

You're going to need some packages to validate your code so go ahead and install:

composer install --dev phpstan/phpstan
composer install --dev squizlabs/php_codesniffer

Step 2: create the hook

In .git/hooks/pre-commit write:

#!/usr/bin/env bash

# get bash colors and styles here: 
# http://misc.flogisoft.com/bash/tip_colors_and_formatting
C_RESET='\e[0m'
C_RED='\e[31m'
C_GREEN='\e[32m'
C_YELLOW='\e[33m'

function __run() #(step, name, cmd)
{
    local color output exitcode

    printf "${C_YELLOW}[%s]${C_RESET} %-20s" "$1" "$2"
    output=$(eval "$3" 2>&1)
    exitcode=$?

    if [[ 0 == $exitcode || 130 == $exitcode ]]; then
        echo -e "${C_GREEN}OK!${C_RESET}"
    else
        echo -e "${C_RED}NOK!${C_RESET}\n\n$output"
        exit 1
    fi
}

modified="git diff --diff-filter=M --name-only --cached  | grep \".php$\""
ignore="resources/lang,resources/views,bootstrap/helpers,database/migrations,bin"
phpcs="vendor/bin/phpcs --report=code --colors --report-width=80 --standard=PSR2 --ignore=${ignore}"

__run "1/3" "php lint" "${modified} | xargs -r php -l"
__run "2/3" "code sniffer" "${modified} | xargs -r ${phpcs}"
__run "3/3" "phpstan" "${modified} | xargs -r vendor/bin/phpstan analyse"

The line

ignore="resources/lang,resources/views,bootstrap/helpers,database/migrations,bin"

is project specific. It's the comma separated list of paths to ignore in the PHP Code Sniffer analysis. Typically, you add the paths for which the PSR-2 makes no sense, like configuration or translation files.

You may change it to whatever you want.


My team has been using and refining this script for years and it has saved us countless hours in refactoring. The sooner you detect errors the better.

Our real script includes more tests that are specific to our app like unit & behavioral test to ensure you broke nothing. I'm sure with a little imagination you are capable to adapt the script to your project.

I suggest you play around with it and share what you find out in the comments.


A usual don't forget to like and comment, that keeps me motivated to write more content for you!

Happy coding!

Posted on by:

bdelespierre profile

Benjamin Delespierre

@bdelespierre

CTO at AddWorking, I write Laravel snippets all the time

Discussion

markdown guide