Larastan, Rector, and Pint — Your Automated Code Quality Team
Series: Every Laravel Project Should Have These Building Blocks
Part: 25 of 35 Level: Intermediate Prerequisites: GitHub Actions CI
What You’ll Learn
- What Larastan, Rector, and Pint each do
- How to configure each tool for a real Laravel project
- How they work together in the development workflow
- The pre-commit hook that runs them automatically
- How to handle false positives and exceptions
The Three Tools and What They Do
These three tools are the quality pipeline that every project in this series runs:
| Tool | What It Does | When to Run |
|---|---|---|
| Pint | Auto-formats PHP code to PSR-12 | Every commit (pre-commit hook) |
| Rector | Upgrades code automatically (PHP 8 features, deprecated patterns) | Every commit (pre-commit hook) |
| Larastan | Static analysis — catches type errors, missing methods, wrong return types | CI (quality.yml) |
Think of them as: Pint is the formatter, Rector is the refactorer, Larastan is the reviewer.
Laravel Pint
Pint is Laravel’s official PHP formatter, built on PHP-CS-Fixer. It enforces PSR-12 and Laravel conventions automatically:
// pint.json
{
"preset": "laravel",
"rules": {
"ordered_imports": {
"sort_algorithm": "alpha"
},
"no_unused_imports": true,
"blank_line_after_namespace": true,
"array_syntax": {
"syntax": "short"
},
"not_operator_with_successor_space": false,
"single_trait_insert_per_statement": true
}
}
Run it:
# Format all files
vendor/bin/pint
# Format only changed files (fast — for pre-commit hook)
vendor/bin/pint --dirty
# Check only (for CI — fails if any changes would be made)
vendor/bin/pint --test
# Format a specific file
vendor/bin/pint app/Models/User.php
You never manually debate tabs vs. spaces or brace placement again. Pint makes those decisions for you, consistently.
Rector
Rector automates refactoring. It knows about PHP upgrades, Laravel deprecated patterns, and code quality improvements. When you upgrade PHP from 8.0 to 8.2, Rector rewrites your code to use the new features:
// rector.php
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return RectorConfig::configure()
->withPaths([
__DIR__ . '/app',
__DIR__ . '/tests',
])
->withSets([
LevelSetList::UP_TO_PHP_84, // Use PHP 8.4 syntax
SetList::CODE_QUALITY, // General code quality improvements
SetList::DEAD_CODE, // Remove dead code
SetList::EARLY_RETURN, // Replace else with early return
])
->withImportNames()
->withSkip([
// Skip specific files or rules if needed
// \Rector\Php81\Rector\ClassConst\ReadonlyPropertyRector::class => [
// __DIR__ . '/app/Models', // skip models
// ],
]);
Run it:
# Preview what would change (dry run)
vendor/bin/rector --dry-run
# Apply changes
vendor/bin/rector
# Apply only to changed files
vendor/bin/rector --dry-run $(git diff --name-only --cached | grep '\.php$')
Examples of what Rector rewrites automatically:
array_push($arr, $item)→$arr[] = $item- Old-style constructors → constructor property promotion
strpos() !== false→str_contains()null !== $x→$x !== null- Adding
declare(strict_types=1)to files that are missing it
Larastan
Larastan is PHPStan configured for Laravel. It understands Eloquent relationships, facades, helpers, and the container — things that regular PHPStan doesn’t.
# phpstan.neon
includes:
- vendor/larastan/larastan/extension.neon
parameters:
paths:
- app/
level: 5
# Laravel-specific ignores
ignoreErrors:
# Ignore dynamic properties on stdClass (from JSON decodes)
- '#Access to an undefined property stdClass::#'
# Exclude generated files and vendor
excludePaths:
- vendor
- bootstrap/cache
- storage
Run it:
vendor/bin/phpstan analyse
vendor/bin/phpstan analyse --memory-limit=512M # for large codebases
What Larastan Catches
- Return type mismatches — method says it returns
Userbut sometimes returnsnull - Undefined methods — calling a method that doesn’t exist on an Eloquent model
- Wrong argument types — passing
stringwhereintis expected - Dead code — code after a return statement
- Missing properties — accessing
$user->fullNamewhen there’s no accessor or property
Level 5 is the Right Starting Point
Larastan has levels 0–10. Level 5 catches the most useful errors without excessive noise. Below 5 misses important type errors. Above 5 requires perfect PHPDoc annotations everywhere, which isn’t practical for most app codebases.
The Pre-Commit Hook
The pre-commit hook runs Rector and Pint automatically on every git commit. It only processes files you’ve changed:
# .hooks/pre-commit
#!/usr/bin/env bash
set -e
# Get list of staged PHP files
PHP_FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.php$' || true)
if [ -z "$PHP_FILES" ]; then
exit 0
fi
echo "Running Rector on staged files..."
echo "$PHP_FILES" | xargs vendor/bin/rector --clear-cache 2>/dev/null || true
echo "Running Pint on staged files..."
echo "$PHP_FILES" | xargs vendor/bin/pint 2>/dev/null || true
# Re-add any files that Rector/Pint modified
echo "$PHP_FILES" | xargs git add
Install the hook:
# composer.json scripts
"scripts": {
"post-install-cmd": [
"chmod +x .hooks/pre-commit",
"git config core.hooksPath .hooks"
],
"post-update-cmd": [
"chmod +x .hooks/pre-commit",
"git config core.hooksPath .hooks"
]
}
After composer install, the hook is automatically active. Any PHP file you commit gets formatted by Pint and refactored by Rector before the commit is created.
Composer Scripts for Easy Running
Add shortcuts to composer.json:
{
"scripts": {
"analyse": "vendor/bin/phpstan analyse --memory-limit=512M",
"format": "vendor/bin/pint",
"refactor": "vendor/bin/rector",
"quality": [
"@refactor",
"@format",
"@analyse"
]
}
}
Usage:
composer quality # run all three: Rector → Pint → Larastan
composer analyse # Larastan only
composer format # Pint only
composer refactor # Rector only
Before submitting a PR: composer quality. If everything passes, you’re ready.
Key Takeaways
- Pint auto-formats code style. Run on every commit via pre-commit hook — never manually debate formatting again.
- Rector upgrades code automatically: PHP version features, deprecated patterns, early returns, dead code. Same pre-commit hook.
- Larastan catches type errors and undefined methods that would only surface at runtime otherwise. Start at level 5.
- The pre-commit hook runs Rector + Pint only on staged PHP files — it’s fast (under 2 seconds for most commits).
composer quality= Rector + Pint + Larastan. Run it before pushing. The CI mirrors it.
Tips and Gotchas
⚠️ Warning: Don’t set Larastan to level 9 on an existing codebase. You’ll get hundreds of errors and likely give up. Start at level 3, fix the issues, then bump to 5, then 8. Incremental strictness is sustainable; big-bang strictness is not.
💡 Tip: Add a
rector.phpbaseline with only the rules you’re actively enforcing. A Rector config that enables every rule will make massive changes to existing code all at once. Start withLevelSetList::UP_TO_PHP_82and a handful of targeted rules.
🔥 Expert Note: The pre-commit hook (running Rector + Pint on staged files) is more valuable than CI for formatting. CI tells you after the push; the hook fixes it before the commit. Most formatting issues never reach CI because they’re fixed automatically.
Further Reading
- Larastan — PHPStan extension for Laravel
- PHPStan — the underlying static analysis tool
- Rector — automated PHP refactoring and upgrades
- Laravel Pint — official Laravel code style fixer (wraps PHP-CS-Fixer)
- PHP-CS-Fixer — the underlying formatter Pint uses