Level Up Your Code Style With Laravel Pint Configuration Tips
Level Up Your Code Style With Laravel Pint Configuration Tips
If you've been on Laravel long enough to remember arguing with your team about whether to use tabs or spaces (spaces, obviously), Laravel Pint is the answer to prayers you gave up praying. It's opinionated, it's fast, and it's zero-config out of the box.
But "zero-config" doesn't mean "no config options." Once you dig into Pint's configuration, there's a solid set of tricks that make it fit perfectly into any team's workflow. Let's walk through the best ones.
The Basics: What Pint Actually Is
Laravel Pint is a PHP code style fixer built on top of PHP-CS-Fixer. It ships with every new Laravel project and has over 124 million Packagist downloads. If you're not already using it, add it:
composer require laravel/pint --dev
Then run it:
./vendor/bin/pint
That's it for basic use. But let's make it actually useful.
1. Understanding Presets
Pint ships with five presets that apply different rule sets:
{
"preset": "laravel"
}
The available presets are laravel (default), psr12, per, symfony, and empty.
laravel follows the official Laravel coding style. psr12 follows the PSR-12 extended coding style
standard. per follows the PHP Evolving Recommendation standard. empty applies no rules at all -- a
clean slate for building your own rule set from scratch.
For most Laravel projects, stick with laravel. For packages you want to be framework-agnostic,
psr12 or per is a better fit.
2. Excluding Files and Directories
Not every file in your project should be touched by Pint. Generated files, vendor code, and legacy modules you haven't modernized yet are all valid exclusions.
{
"preset": "laravel",
"exclude": [
"bootstrap/cache",
"storage"
],
"notPath": [
"app/Legacy"
],
"notName": [
"*Stub.php"
]
}
exclude removes entire directories. notPath excludes files matching a path pattern. notName
excludes files matching a filename pattern. These can save you from Pint helpfully "fixing" files
that should stay exactly as they are.
3. The --test Flag for CI Pipelines
Running Pint in your CI pipeline? You don't want it auto-fixing files -- you want it to fail the build if code style violations exist.
./vendor/bin/pint --test
With --test, Pint inspects your code and returns a non-zero exit code if violations are found, without
making any changes. Plug this into your GitHub Actions workflow:
- name: Check code style
run: ./vendor/bin/pint --test
Developers who push unformatted code get an instant, clear signal. No more "style fix" commits polluting your git history.
4. The --dirty Flag for Speed During Development
Running Pint across your entire codebase on every save is slower than it needs to be. The --dirty flag
tells Pint to only fix files that have uncommitted changes according to Git:
./vendor/bin/pint --dirty
This is the ideal flag for local development. Pair it with a Git pre-commit hook so Pint only touches what you're actually changing:
# .git/hooks/pre-commit
./vendor/bin/pint --dirty
git add -u
5. Overriding Individual Rules
The Laravel preset is opinionated, and occasionally it's opinionated in a way that doesn't match your
team's preferences. You can override individual rules in pint.json:
{
"preset": "laravel",
"rules": {
"single_line_comment_style": false,
"php_unit_test_class_requires_covers": false,
"ordered_imports": {
"sort_algorithm": "alpha"
}
}
}
Setting a rule to false disables it. Passing an array lets you configure its options. The full list
of available PHP-CS-Fixer rules is vast -- Pint exposes all of them.
6. Run Pint in Parallel (Experimental)
On large codebases, Pint can take a while. The experimental --parallel flag lets it process multiple
files simultaneously:
./vendor/bin/pint --parallel
You can also cap the number of processes:
./vendor/bin/pint --parallel --max-processes=4
Marked as experimental, but works well in practice for most projects.
Putting It All Together
A reasonable pint.json for a mid-sized Laravel project might look like this:
{
"preset": "laravel",
"exclude": [
"bootstrap/cache",
"storage"
],
"notPath": [
"app/Legacy"
],
"rules": {
"single_line_comment_style": false
}
}
And your CI job runs ./vendor/bin/pint --test, while your local pre-commit hook runs
./vendor/bin/pint --dirty. Clean code, minimal friction.
Sources: