Larastan, Rector, and Pint — Your Automated Code Quality Team

Larastan, Rector, and Pint — Your Automated Code Quality Team

Reading Time: 4 minutes

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:

ToolWhat It DoesWhen to Run
PintAuto-formats PHP code to PSR-12Every commit (pre-commit hook)
RectorUpgrades code automatically (PHP 8 features, deprecated patterns)Every commit (pre-commit hook)
LarastanStatic analysis — catches type errors, missing methods, wrong return typesCI (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

  1. Return type mismatches — method says it returns User but sometimes returns null
  2. Undefined methods — calling a method that doesn’t exist on an Eloquent model
  3. Wrong argument types — passing string where int is expected
  4. Dead code — code after a return statement
  5. Missing properties — accessing $user->fullName when 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.php baseline 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 with LevelSetList::UP_TO_PHP_82 and 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

← GitHub Actions CI | Next: AI in Laravel Development →

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.