Utilizing Pre-Commit Hooks in Laravel Projects
Pre-commit hooks are a fantastic way to automate repetitive checks and enforce code quality before changes are committed to version history. They are not language specific and highly adaptable. I, for example, use a pre-commit hook which utilizes Laravel Pint and PHPStan to ensure clean code and that I follow the best practices. In this blog post, I want to show you how pre-commit hooks work and how I implement them in my Laravel projects.
What Are Git Hooks and How To Install Them
Git hooks are scripts that Git executes at specific points in your development workflow. There are many different hooks that one can utilize but the most common ones are pre-commit and post-commit hooks.
When you initialize a git repository, git creates a .git
folder in your project root and also populates
the hooks
folder inside it. Inside the .git/hooks
folder you find a couple of example scripts.
Pre-Commit Hooks
A pre-commit hook runs before a commit is finalized, allowing you to check for issues in the code early. Common use cases for pre-commit hooks include:
- Running linters and formatters.
- Running static code analyzers
- Preventing commits with debug code or broken tests.
- Enforcing team coding standards.
Example Pre-Commit Hook For Laravel Projects
In this example pre-commit hook we will utilize Laravel Pint and Larastan to format the code automatically and check for bugs in the code.
Setup
Before we can start, we need a Laravel project. Be sure you are in the root directory of your Laravel project. Then, run the following commands to install Pint and Larastan:
composer require --dev laravel/pint
composer require --dev larastan/larastan
You can customize both packages by creating a pint.json
and phpstan.neon
file. I recommend customizing
Larastan and setting the phpstan level to a high value (8-10). But for this tutorial, we keep the default
settings.
Writing the bash script
While there are a couple of tools and packages you could use to write pre-commit hooks and link them, I prefer
the manual way. What we will do now is to create a file called pre-commit.sample
in our root directory and then
create a symbolic link to the hook in the .git/hooks
folder. This way, we can add the pre-commit script to our
source control. Let’s start!
Creating The File And Giving Execution Rights
Create the file and give execution rights to the file by writing the following commands in your terminal while you are in the project root directory.
touch pre-commit.sample
chmod +x pre-commit.sample
Including Pint In The Script
Let’s start by writing our pre-commit hook so that it runs Laravel Pint on all staged PHP files. Since Pint is for formatting PHP files it doesn’t really make sense to check other files. After we selected all PHP files from our staged files, the script should run Pint on them and format them properly. Due to this - the files will be changed again. So we need to re-stage the changed files again after running Pint on them. Below you see the code for this.
#!/bin/bash
# Get all staged PHP files.
staged_files=$(git diff --cached --name-only -- '*.php')
# If no PHP file found - print a short message and exit early to proceed with the commit.
if [ -z "$staged_files" ]; then
echo "No PHP files staged for commit."
exit 0
fi
# Run Pint on the staged PHP files.
./vendor/bin/pint $staged_files
# Check if Pint failed. If yes - abort the commit.
if [ $? -ne 0 ]; then
echo "Laravel Pint failed. Commit aborted."
exit 1
fi
# Re-stage the formatted files.
git add $staged_files`
exit 0
Including Larastan In The Script
Including Larastan in the script is very similar to the example above. We run phpstan on the staged files with an increased memory limit for large commits and if any error occurs, print the error and abort the commit. Overwise - proceed like normal with the commit.
#!/bin/bash
# Get all staged PHP files.
staged_files=$(git diff --cached --name-only -- '*.php')
# If no PHP file found - print a short message and exit early to proceed with the commit.
# Run Larastan.
vendor/bin/phpstan analyse $staged_files --memory-limit=1G
# Check if Larastan failed. If yes - abort the commit.
if [ $? -ne 0 ]; then
echo "Larastan found some issues. Commit aborted."
exit 1
fi
# If everything is cool, let the commit proceed.
exit 0
Stitching Both Parts Together
Now it is time to stitch both parts together and create our final script. See the code below:
#!/bin/bash
# Get all staged PHP files.
staged_files=$(git diff --cached --name-only -- '*.php')
# If no PHP file found - print a short message and exit early to proceed with the commit.
if [ -z "$staged_files" ]; then
echo "No PHP files staged for commit."
exit 0
fi
# Run Pint on the staged PHP files.
./vendor/bin/pint $staged_files
# Check if Pint failed. If yes - abort the commit.
if [ $? -ne 0 ]; then
echo "Laravel Pint failed. Commit aborted."
exit 1
fi
# Re-stage the formatted files.
git add $staged_files
# Run Larastan.
vendor/bin/phpstan analyse $staged_files --memory-limit=1G
# Check if Larastan failed. If yes - abort the commit.
if [ $? -ne 0 ]; then
echo "Larastan found some issues. Commit aborted."
exit 1
fi
# If everything is cool, let the commit proceed.
exit 0
Creating A Symbolic Link
The last step is to create a symbolic link between our newly created
bash script and the pre-commit script in the .git/hooks
directory.
Type the following command in your terminal from your project root directoy:
ln -s ln -s ../../pre-commit.sample .git/hooks/pre-commit
Test Things Out
Now, you can add try out the script by committing some changes in your project.
You could add the newly written pre-commit.sample file to your staged files and
commit the changes. If everything works properly, you should get the following
output in the terminal No PHP files staged for commit.
. This is because you just
added the bash script and no PHP files to the staged files. However this output
proves that the script is executable and properly linked.
Utilizing Hooks - Use The TerminalPHPStan
It is important to mention that while the hook is running each time you commit changes to git, you will most likely get no output if you use a GUI tool for Git. In order to get the most out of the hooks it is recommended to run Git through the terminal. This way you always get feedback immediately.
Other Hooks And More Information
If you want to know more about other git hooks and their possibilities, feel free to visit git-scm.com.
Conclusion
Pre-commit hooks are a powerful way to enforce coding standards, catch bugs, and automate essential tasks during the development process. By integrating tools like Laravel Pint and Larastan for Laravel projects, you ensure your code is clean, formatted, and follows best practices. This not only improves your workflow but also enhances the quality and maintainability of your projects. I highly recommend using pre-commit hooks!
I hope this article gave you a good introduction into this topic.
Happy Coding!