skip to content

In Laravel Pint's pint.json, how do you pick a preset, override individual rules and exclude files from formatting?

level: middleimportance: should knowfreq 38%

answer

  1. one JSON file in the project root
  2. laravel, per, psr12, symfony, empty
  3. a rules entry replaces, never merges
  4. false switches a preset rule off
  5. exclude, notName, notPath

basics

~20 s

A pint.json in the project root sets "preset" (laravel, per, psr12, symfony or empty), a "rules" object whose entries replace or disable the preset's rules, and "exclude", "notName" or "notPath" to skip folders, name patterns or exact files.

solid answer

~40 s

`pint.json` in the project root is Pint's configuration file. `"preset"` picks the base rule list: `laravel` (the default), `per`, `psr12`, `symfony` or `empty`, the last being a blank slate. `"rules"` takes PHP CS Fixer rule names: `true` enables a rule with its own default options, `false` switches it off, and an object configures it. Each entry replaces the preset's entry for that rule wholesale, so restate every option you want to keep. Files are skipped with `"exclude"` (folders), `"notName"` (file-name patterns) and `"notPath"` (paths), on top of built-in skips such as `vendor` and `storage`. On the command line, `--preset` beats the file's preset, `--config` points at another file, local or `https://`, and `--no-config` ignores it.

code

json · 10 lines
json
{
    "extend": "../shared/pint.json",
    "preset": "laravel",
    "rules": {
        "blank_line_before_statement": {
            "statements": ["continue", "return", "throw"]
        }
    },
    "notPath": ["routes/console.php"]
}

go deeper

for a junior

Recall that pint.json lives in the project root, that "preset" picks one of five presets, and that rules can be switched on or off by name.

for a middle

Explain the shallow merge: a rules entry replaces the preset's entry wholesale, true means the rule's own defaults, and exclude, notName and notPath each match something different.

for a senior

Show how you would share one style across repositories with --config or extend, keep overrides few and documented, and verify with --no-config what the bare preset does.

for a principal

Weigh the cost of every override: each rule you change moves the codebase away from the style the wider Laravel ecosystem reads, so overrides need an owner and a reason.

## What pint.json is for Laravel Pint runs with no configuration, applying its `laravel` preset to every `.php` file it finds. When a team wants something different, it commits a **`pint.json`** file in the project root. The file holds four kinds of setting: - **`preset`**: which built-in rule list to start from; - **`rules`**: per-rule overrides on top of that list; - **finder keys** (`exclude`, `notName`, `notPath`, `in`): which files are inspected; - **`extend`**: another JSON file to inherit settings from. Pint reads the file itself; it does not look for a `.php-cs-fixer.dist.php` file, even though the rules underneath are PHP CS Fixer's. ## Choosing a preset A **preset** is a named, pre-built list of rules. Pint 1.32 ships five: | Preset | What it starts from | |---|---| | `laravel` | Pint's own list, the style Laravel itself is written in (the default) | | `per` | PHP CS Fixer's `@PER-CS` set plus `no_unused_imports` | | `psr12` | PHP CS Fixer's `@PSR12` set plus `no_unused_imports` | | `symfony` | PHP CS Fixer's `@Symfony` set plus `no_unused_imports` | | `empty` | no rules at all | Any other name makes Pint stop with `Preset not found.` The preset can also be given on the command line: `./vendor/bin/pint --preset psr12`. When both are present, **the command-line flag wins** over the file. ## Overriding rules The `rules` object maps a PHP CS Fixer rule name to a value: 1. `true` turns the rule on with that rule's **own default options**. 2. `false` turns off a rule the preset enabled. 3. An object turns the rule on with **exactly those options**. The detail that trips people up is how the two lists combine. Pint merges them **shallowly**, key by key: for a rule named in both places, the `pint.json` entry **replaces** the preset's entry completely. Options are not merged. The `laravel` preset, for example, configures `blank_line_before_statement` with a list of statements (`continue` and `return`). Writing `"blank_line_before_statement": true` does not keep that list; it runs the rule with PHP CS Fixer's defaults. To change one option, copy the whole options object and edit it. ```json { "preset": "laravel", "rules": { "concat_space": { "spacing": "one" }, "not_operator_with_successor_space": false, "simplified_null_return": true }, "exclude": ["app/Legacy"], "notName": ["*.stub.php"] } ``` Here the `laravel` preset's `concat_space` setting (`none`, so `'a'.'b'`) becomes `one` (`'a' . 'b'`), Pint stops enforcing the space after `!` that Laravel style adds, and `simplified_null_return`, which the preset explicitly switches off, is turned on. Two more points: - Pint allows PHP CS Fixer's *risky* rules without an extra flag, so a risky rule listed in `rules` simply runs. - Pint adds its own rules prefixed `Pint/`, such as `Pint/laravel_blade` for Blade templates; they are off until listed here. The `empty` preset exists for teams that want full control: nothing runs except what `rules` lists. ## Excluding files Before `pint.json` is read, Pint already skips `vendor`, `storage`, `node_modules`, `bootstrap/cache`, `build`, dot files, IDE helper files and, unless the Blade rule is on, `*.blade.php`. The file adds to that: - **`exclude`**: directories to skip, such as `"app/Legacy"`; - **`notName`**: file-name patterns, such as `"*-generated.php"`; - **`notPath`**: a specific path, such as `"routes/console.php"`; - **`in`**: directories to inspect instead of the whole project, when no path is passed on the command line. Exclusions also apply to `--dirty` and `--diff` runs: those options pick changed files, and Pint then drops any that its finder would not include. ## Sharing and precedence Several repositories often need the same style. Pint gives three ways to point at shared settings: 1. **`--config`** loads a specific file, for example one inside a company package in `vendor`, or a URL. Only `https://` URLs are accepted; a plain `http://` URL is refused. 2. **`extend`** in `pint.json` names a base file, resolved relative to the `pint.json` that extends it. Local settings override the base. Only one level is allowed: a base that itself extends another file makes Pint throw. 3. **`--no-config`** ignores any `pint.json` and runs the preset untouched, which is useful when checking what the preset alone would do. Keeping `pint.json` small matters. Every override is a place where the project drifts from the style the rest of the Laravel ecosystem uses, so a team should add rules deliberately and write down why.

  • If pint.json sets "blank_line_before_statement": true, does the laravel preset's statement list survive?
    No. Pint merges `rules` over the preset key by key, so the `pint.json` entry replaces the preset's entry. `true` means the rule's own PHP CS Fixer defaults, not the preset's `continue`/`return` list. To tweak it, copy the full options object and edit that.
  • How can several Laravel repositories share one Pint style?
    Put a base `pint.json` somewhere every repository can read, such as a shared package, and either run `pint --config path/to/pint.json` or add `"extend"` to each project's file. Only one level of `extend` is allowed, and remote configs must use `https://`.
  • When would a team choose the empty preset?
    When it wants to own every rule explicitly, for example to adopt a published PHP CS Fixer rule set plus a handful of additions. With `empty`, nothing runs except what the `rules` object lists, so no preset update can change the output behind the team's back.

A preset is a publisher's house style guide, and pint.json rules are editor's notes pinned over single entries. A note does not amend the entry beneath it; it covers it, so anything the old entry said that the note omits is gone.

saying these in an interview costs you the question

  • Pint reads its rules from a .php-cs-fixer.dist.php file in the project.
  • A rule object in pint.json merges into the preset's options for that rule.
  • The per preset is what Pint applies to a Laravel project by default.
  • pint.json must sit in the project root; there is no way to load another file.
  • When pint.json and --preset disagree, the file's preset wins.