Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pint Laravel Package

laravel/pint

Laravel Pint is an opinionated PHP code style fixer for minimalists. Built on PHP-CS-Fixer, it makes it simple to ensure your code style stays clean and consistent, with zero configuration required and an easy-to-use CLI that works in any Laravel or PHP project.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and first run (5 minutes)

composer require laravel/pint --dev
./vendor/bin/pint --test      # survey only: reports violations, changes nothing

--test is the safest first command on an existing codebase — it tells you how large the diff will be before you touch a single file. If the list is huge, do the first full run as an isolated commit (./vendor/bin/pint && git commit -m "chore: apply Pint") and add that SHA to .git-blame-ignore-revs so git blame stays useful.

Where to look first

  • ./vendor/bin/pint --help — the authoritative list of flags for your pinned version. Pint ships frequently; flag behavior has changed across minor releases.
  • The Laravel docs page (laravel.com/docs/pint) — presets, config keys, CI recipes.
  • pint.json at your project root — the single file you will actually maintain.

First real use case: enforce on changed files only

./vendor/bin/pint --dirty     # fixes only files with uncommitted git changes

Wire this into your pre-commit / local loop. It is fast, produces small reviewable diffs, and avoids reformatting files nobody touched. Commit --dirty to your muscle memory before you ever run a whole-tree format.

Minimal pint.json

{
    "preset": "laravel"
}

That is genuinely all most Laravel apps need. Everything below is deviation management.

Implementation Patterns

Daily local loop

./vendor/bin/pint --dirty            # before each commit
./vendor/bin/pint --dirty --diff     # see what it wants to change first
./vendor/bin/pint --dirty --test     # assert clean, non-mutating

--diff pairs well with --test in code review prep: you get a report of violations plus the exact patch Pint would apply, without modifying the working tree.

CI gate

- run: ./vendor/bin/pint --test

--test exits non-zero when violations exist — that is your gate. Note the inverse gotcha: a plain ./vendor/bin/pint (fix mode) exits 0 even when it rewrote files, so it silently passes CI while changing the checkout. If you want "fail if anything was fixed", use --with-exit-status or just use --test. In GitHub Actions, run --test --diff so the failing job prints the patch in the logs.

Composer script (project-wide ergonomics)

"scripts": {
    "lint": "pint --test",
    "fix": "pint"
}

Then composer fix / composer lint. This abstracts away vendor path differences and works identically in Sail, Docker, and local.

Adopting on a legacy codebase (incremental strategy)

  1. Run --test repo-wide, capture the count.
  2. If the diff is enormous, do not run it on everything at once. Start with --dirty enforced in CI on PRs, and format whole-tree directory by directory (./vendor/bin/pint app/Models, app/Http, …) in separate commits.
  3. Or scope Pint via exclude in pint.json and shrink the list over time.

Passing a path argument (./vendor/bin/pint app/Services) is the cheapest way to carve out a formatting beachhead.

Customizing without forking the preset

{
    "preset": "laravel",
    "exclude": ["app/Legacy", "database/migrations/2020_*"],
    "rules": {
        "declare_strict_types": true,
        "final_class": false,
        "Pint/phpdoc_type_annotations_only": true
    }
}

Two important mechanics:

  • preset sets the baseline; rules is a per-rule override layer on top. You are not redefining the preset, only patching it.
  • Rules prefixed Pint/... are Laravel's own custom fixers layered over PHP-CS-Fixer (e.g. Pint/phpdoc_type_annotations_only, added in v1.29). Everything else is a stock PHP-CS-Fixer rule name — look those up in the PHP-CS-Fixer docs, not the Pint docs.

Sharing one style across many repos

Pint supports extends in pint.json so a central config can be the source of truth:

{
    "extends": "https://raw.githubusercontent.com/acme/standards/main/pint.json"
}

Two constraints worth knowing: remote configs must be served over HTTPS (insecure http configs were explicitly rejected in v1.29.2), and local relative paths work too. This is the right pattern for a monorepo or an org with 10+ Laravel services — bump the shared file, let each repo's CI enforce it.

Parallel runs on big repos

./vendor/bin/pint --parallel

Added in v1.23.0 (-p shorthand in v1.24.0) for projects where serial formatting of thousands of files is painful. Use it for full-tree runs; --dirty on a handful of files is already fast enough that parallelism adds noise.

Editor / on-save integration

Two viable patterns:

  • Pint as the only formatter: disable PHP formatting in your IDE, run pint --dirty in a pre-commit hook. One source of truth, zero drift.
  • Pint as the last step: let the IDE format loosely, but always run Pint before commit. Accept that the IDE will occasionally produce a diff Pint immediately reverts — that noise is the cost of dual formatters.

The first is strongly preferable on a team. Mixed IDE formatters produce exactly the "reformat 400 lines" PRs that make people resent tooling.

AI agent workflows

Recent versions add an agent output format that is auto-selected when Pint detects it is being invoked from Claude Code or OpenCode, with machine-parseable output. If you drive Pint from an agent harness or a script that needs structured results, prefer --format=json (or the agent format) over scraping the human-readable table.

Gotchas and Tips

Fix mode exits 0 even when it changed files. The single most common CI mistake: ./vendor/bin/pint in a pipeline never fails. It reformats the ephemeral checkout, discards it, and reports success. Always gate with --test, or opt into --with-exit-status if you actually want fix-and-fail semantics.

--dirty is git-state dependent. It derives its file list from uncommitted changes. In CI this is usually empty — a --dirty step in a pipeline is a no-op that looks like it passes. Also verify behavior in detached-HEAD / shallow-clone environments. Use --dirty locally, --test in CI; do not mix them up.

Stale cache after rule changes. Pint caches which files are already compliant. After editing pint.json, upgrading Pint, or changing presets, a stale cache can mask violations. Run --repair to rebuild it, or delete the cache file. If you have multiple projects sharing a cache location, override it with --cache-file (a matching config key exists too) to avoid cross-contamination.

Rule conflicts are real. Some PHP-CS-Fixer rules fight each other (empty-body braces vs. single-line empty bodies, import ordering vs. fully-qualified strict types). If Pint oscillates — two runs, two different results — you have a conflicting pair. Diagnose by running twice: pint --test twice on a clean tree should both be clean. If not, bisect your rules block.

"preset": "empty" is a trap for the unwary. Setting an empty preset means no rules run unless you list them all explicitly. There was even a historical bug where rules were ignored when empty was specified. Use empty only when you genuinely want to hand-pick every rule; otherwise preset + rules overrides is the correct lever.

Upgrade Pint when you hit flag interactions. Several reported issues were specifically about flags clashing — --parallel combined with --config was broken until v1.27.1, and --parallel dropped verbose output until v1.29.1. If a combination behaves oddly, check the changelog before debugging your config; you may simply be on an older patch.

Errors now go to stderr (v1.29.2). If you parse Pint output in a script, stop assuming everything is on stdout. Route stderr separately or use a structured --format.

Keep it in require-dev. Since v1.26 Composer refuses to install Pint as a non-dev dependency. If you inherited a project with Pint in require, move it — otherwise production installs drag in PHP-CS-Fixer and its transitive tree.

PHP version coupling. Pint is a PHAR/CLI that runs under whatever PHP executes it; its rule set depends on the PHP-CS-Fixer version bundled at that Pint release. If you use PHP 8.4/8.5 syntax (property hooks, asymmetric visibility), pin a Pint version that declares support — 8.5 support was only partial as of v1.26. Formatting a codebase with newer syntax on an older Pint can produce parse errors rather than no-ops.

It is a formatter, not a linter. Pint will not catch unused variables, type errors, or logic problems, and it will happily reformat code that is semantically broken. Pair it with PHPStan/Larastan — the two are complementary, and Pint guarantees the static-analysis diffs stay readable because whitespace churn disappears.

Blade and non-PHP files are out of scope. Pint targets .php. Do not expect it to normalize .blade.php templates; that requires a separate tool.

Extension point: custom fixers. If your team needs a rule Pint does not ship, the path is writing a PHP-CS-Fixer custom fixer and registering it — but that means you are no longer using vanilla Pint, and every upgrade becomes a compatibility exercise. Exhaust rules overrides first; a two-line rules block beats a maintained custom fixer almost every time.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
vento/laravel-airbrake
wfeller/laravel-batch
maco-studios/mage-os-installer
busarm/armie
siberfx/laravel-logger
kfteam1991/dcat-admin
dashed/dashed-laposta
dashed/dashed-ecommerce-montaportal
dashed/dashed-ecommerce-etsy
neohq/string-blade-compiler
log1x/acf-composer
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager