Why You *Really* Need a Pre-Commit Script for Your PHP Projects cover image

Why You *Really* Need a Pre-Commit Script for Your PHP Projects

Scott Keck-Warren • June 9, 2025

Despite what we tell others we’ve all pushed something to production we probably shouldn’t have. A missing semicolon, a rogue var_dump(), and even an unused use statement or two just sitting there like it owns the place. These little mistakes are SO easy to miss but can cause huge problems for us and our subscribers. By using a pre-commit script we can reduce if not completely eliminate some of these issues.

What’s a Pre-Commit Script?

A pre-commit script is one of the hooks that Git gives us to interrupt its lifecycle and add your own logic. Specifically, a pre-commit script runs before you’re allowed to create a commit to see if the code is still in a valid state. It provides you with one last line of defense to catch simple problems early.

In a PHP-based project, this is a great opportunity to run static code analysis (SCA) tools like:

It's easy to run these tools on our files before we create the commit but it's even easy to be lazy and skip them so we want to make sure we automate these using make and a very simple pre-commit script.

Using a "Makefile" to Organize Your Pre-commit Tasks

We've discussed make and "Makefiles" in "What is the make Command in Linux?" but as a quick refresher:

make is a build automation tool. It reads instructions from a file called "Makefile" and runs the commands you and your team define there. While it originated in the C programming world to compile and link source code, at its core make can be used as a general-purpose task runner. If you write a "Makefile" with steps to run PHPUnit, lint code, or compile SCSS, make will happily do that for you.

The important piece is that it "can be used as a general-purpose task runner". I use make to run my SCA tools locally and include them in every project I set up. Below is the current "Makefile" from https://github.com/warren5236/ScottsValueObjects which tends to be the starting point for when I roll out checks for my teams:

## PreCommit Section
CHANGED_PHP_FILES = git diff --diff-filter=AM --staged --name-only src tests scripts | grep ".php"
pre-commit: lint_changed phpcs_changed phpstan_changed rector_changed

lint_changed:
    ${CHANGED_PHP_FILES} | xargs vendor/bin/parallel-lint

phpcs_changed:
    ${CHANGED_PHP_FILES} | xargs vendor/bin/phpcs --standard=phpcs.xml

phpstan_changed:
    ${CHANGED_PHP_FILES} | xargs vendor/bin/phpstan analyze  --memory-limit 1G

rector_changed:
    ${CHANGED_PHP_FILES} | xargs vendor/bin/rector process --dry-run

## Full Static Code Analysis Section
phpcbf:
    vendor/bin/phpcbf --standard=phpcs.xml src tests scripts

phpstan:
    vendor/bin/phpstan analyze  --memory-limit 1G -l9 src tests

This allows us to run make pre-commit at the command line and it will run a variety of SCA tools on just the files that we have staged for the commit (that's what git diff --diff-filter=AM --staged --name-only src tests scripts | grep ".php") is responsible for.

Feel free to use the above file as a starting point for your project and add or remove pieces that you need.

Installing the Git Pre-Commit Hook

The missing piece is that we still don't have a pre-commit script we just have a "Makefile" that does the work for us but we still need to "wire it up" so it's part of the flow.

To do that create a file at .git/hooks/pre-commit with the following:

#!/bin/sh
make pre-commit

Then make it executable:

chmod +x .git/hooks/pre-commit

To add some additional robustness (and make sure it gets installed consistently) you can also write the minimal pre-commit script from above into "scripts/pre-commit" and then install it using the "post-install-cmd" section of your "composer.json" scripts:

"scripts": {
    "post-install-cmd": [
        "cp ./scripts/pre-commit .git/hooks/",
        "chmod +x .git/hooks/pre-commit"
    ]
}

Now every time you run git commit, your pre-commit script will run make pre-commit and perform checks on your code before it can be committed.

% git commit -a
git diff --diff-filter=AM --staged --name-only src tests scripts | grep ".php" | xargs vendor/bin/phpcs --standard=phpcs.xml

FILE: /Users/scottkeck-warren/Projects/ScottsValueObjects/src/Location/City.php
-------------------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
-------------------------------------------------------------------------------
 22 | ERROR | [x] Whitespace found at end of line
-------------------------------------------------------------------------------
PHPCBF CAN FIX THE 1 MARKED SNIFF VIOLATIONS AUTOMATICALLY
-------------------------------------------------------------------------------

If you haven’t already set this up in your PHP project, now’s the perfect time. Your teammates (and your future self) will thank you.

This was originally published at https://phpdeveloperstv.substack.com/p/why-you-really-need-a-pre-commit