skip to content

Linters & Formatters

Formatters, lint rule engines and static type checkers for each ecosystem, from ESLint and Prettier to ruff, RuboCop and golangci-lint. Asked because the formatter/linter split shapes every CI gate.

on this pageshow

explore

questions

page 1 of 2

What does PHP CS Fixer do to your PHP code, and how do its fix and check commands differ?

level: juniorimportance: must knowfreq 50%

answer

  1. every rule is a fixer
  2. rewrites files in place
  3. check equals fix --dry-run
  4. --diff prints a unified diff
  5. exit bit 8 means files need fixing

basics

~20 s

PHP CS Fixer rewrites PHP source so it matches a configured set of rules; fix changes the files, while check (fix --dry-run) changes nothing and only reports which files would change, exiting with bit 8 set.

solid answer

~40 s

PHP CS Fixer is a **fixer-first** tool: every rule, such as `array_syntax` or `no_unused_imports`, is an automatic transformation of the token stream, grouped into rule sets like `@PER-CS`. `vendor/bin/php-cs-fixer fix` applies the configured rules and writes the files back; it exits 0 even when it changed files, because making those changes was the job. `vendor/bin/php-cs-fixer check`, the shorthand for `fix --dry-run` added in 3.34, runs the same rules without writing and lists the files that would change. Adding `--diff` prints the exact changes as a unified diff. In check or dry-run mode the exit code gains bit 8 when some file needs fixing and bit 4 when a file has invalid syntax, so a pipeline can fail on a non-zero result.

code

bash · 4 lines
bash
vendor/bin/php-cs-fixer check --diff    # show what would change, write nothing
echo $?                                 # 8 when at least one file needs fixing
vendor/bin/php-cs-fixer fix             # apply the changes
echo $?                                 # 0, even though files were modified

go deeper

for a junior

Recall that PHP CS Fixer rewrites code to match rules, that fix writes files and check only reports, and that --diff shows the changes.

for a middle

Explain rules versus rule sets, why fix exits 0 after changing files, and how the check exit code is built from bit flags.

for a senior

Show you know the limits: no report-only rules, no type analysis, risky rules off by default, and how to read combined exit codes in automation.

for a principal

Frame automatic formatting as removing a whole category of review comments, and decide which transformations a team is willing to automate.

## What the tool is **PHP CS Fixer** (Composer package `friendsofphp/php-cs-fixer`, binary `vendor/bin/php-cs-fixer`) is a code **formatter and modernizer** for PHP. It reads PHP files, splits them into tokens, applies a list of **rules**, and writes the transformed code back. Two words are easy to mix up: - **rule** (also called a *fixer*): one transformation, such as `array_syntax` (turn `array()` into `[]`), `no_unused_imports` or `single_quote`. Many rules take options, for example `['syntax' => 'short']`. - **rule set**: a named bundle of rules whose name starts with `@`, such as `@PSR12`, `@PER-CS` or `@PhpCsFixer`. The key design point is that **every rule is a fixer**. PHP CS Fixer does not have a class of report-only checks; if a rule notices something, it can also rewrite it. That makes it fast to adopt, and it also means it has nothing to say about problems that need a human decision, such as a badly named method. ## fix: change the files ```bash vendor/bin/php-cs-fixer fix # use .php-cs-fixer(.dist).php vendor/bin/php-cs-fixer fix src/ -v # one path, list the rules applied per file ``` `fix` applies every configured rule to every file the configuration selects and writes the results. After fixing, it lints each changed file, and if the result would not parse it reports an error rather than keeping broken code. `fix` exits with **0** when it ran successfully, **even if it modified files**. Changing files is its purpose, not a failure. ## check: report, never write `check` was introduced in 3.34 as a shorthand for `fix --dry-run`, and accepts the same options. It runs the same rules in memory and prints the files that **would** change, without touching them. - `--diff` adds a unified diff for each file, which shows exactly what the fixer wants to change. - `--format` chooses the output (`txt`, `json`, `junit`, `checkstyle`, `gitlab`, `xml`; in 3.x the default is `txt`). - `--stop-on-violation` stops after the first file that needs fixing. ## Exit codes are bit flags | Bit | Meaning | When it can appear | |---|---|---| | 0 | OK | always | | 1 | general error, or PHP minimum not met | always | | 4 | some files have invalid syntax | `check` / `fix --dry-run` only | | 8 | some files need fixing | `check` / `fix --dry-run` only | | 16 | configuration error of the application | always | | 32 | configuration error of a rule | always | | 64 | exception raised within the application | always | Because they are bit flags, `12` means "some files need fixing **and** some have invalid syntax". A CI job simply fails on any non-zero value from `check`. ## Everyday workflow 1. Locally, run `fix` before committing, or wire it into the editor. 2. Review the resulting diff like any other change, especially the first time a rule set is adopted. 3. In automation, run `check --diff`, so a failing job shows the exact change the author needs to make. ## Understanding what a run did - `-v` lists, per file, the rules that changed it, which is the quickest way to find the rule behind a surprising edit. - `vendor/bin/php-cs-fixer describe single_quote` prints a rule's description, options, before-and-after examples and whether it is risky. - `vendor/bin/php-cs-fixer describe @PER-CS --expand` lists every rule a set contains, including nested sets. - `vendor/bin/php-cs-fixer list-files` prints only the paths that need fixing, which is handy for scripting. Reading a rule's own description before disabling it usually shows whether an option would fit better than switching it off. ## What it does not do - It does not analyse types or logic; a formatter that knows nothing about what `$user` holds cannot find a null dereference. - It does not report problems it cannot fix, because it has no report-only rules. - By default it does not run **risky** rules, those that may change behaviour, unless the configuration or `--allow-risky=yes` allows them.

  • Why does php-cs-fixer fix exit with 0 after it modified files?
    Because rewriting files is the command's purpose. Only `check` or `fix --dry-run` treat a needed change as a finding and set bit 8. That is why automation that must fail on unformatted code runs `check`, not `fix`.
  • What does the exit code 12 from php-cs-fixer check mean?
    The exit code is built from bit flags: 8 means some files need fixing and 4 means some files have invalid syntax, so 12 means both happened in the same run. The syntax errors must be fixed by hand, because the fixer skips files it cannot parse.

saying these in an interview costs you the question

  • php-cs-fixer only reports problems and never edits files
  • check is a separate engine with different rules from fix
  • fix exits non-zero whenever it changed a file
  • PHP CS Fixer can flag badly named methods it cannot fix
  • Risky rules run by default
open as a page

In PHP_CodeSniffer, what is the difference between the phpcs and phpcbf commands, and which violations can phpcbf fix?

level: juniorimportance: must knowfreq 55%

basics

~20 s

phpcs scans PHP files against a coding standard and reports violations without changing anything; phpcbf reads the same ruleset and rewrites files, fixing only the violations whose sniffs implement an automatic fix and leaving the rest for a human.

open as a page

In PHPStan 2, what are rule levels 0 to 10, and what do the main levels add as you move up?

level: juniorimportance: must knowfreq 58%

basics

~20 s

PHPStan's rule levels are a cumulative strictness dial from 0 (unknown classes, functions, wrong argument counts) to 10 (errors even for implicit mixed); each level adds checks to all lower ones, and max is an alias for the highest.

open as a page

In a .rubocop.yml file, how do you disable a cop, change one of its options and exclude files, and what is a department?

level: juniorimportance: must knowfreq 65%

basics

~20 s

A .rubocop.yml keys each cop by qualified name, such as Layout/LineLength: Enabled: false switches it off, keys like Max tune it, and Exclude skips paths. A department (Lint, Style, Layout, Metrics...) groups cops and can be configured as one.

open as a page

What is Standard Ruby (the standard gem), how do you run it with standardrb or rake standard, and how does it differ from configuring RuboCop yourself?

level: juniorimportance: must knowfreq 55%

basics

~20 s

Standard Ruby is a gem that runs RuboCop with a fixed, non-configurable ruleset. Run standardrb (or rake standard after requiring standard/rake), add --fix for safe corrections; it ignores .rubocop.yml and reads only options from .standard.yml.

open as a page

Which code metrics does detekt's complexity rule set measure, and what are its default limits?

level: middleimportance: must knowfreq 55%

basics

~10 s

detekt's complexity set measures branching and size: CyclomaticComplexMethod and CognitiveComplexMethod per function, LongMethod and LargeClass in lines, NestedBlockDepth, ComplexCondition, LongParameterList, TooManyFunctions. Defaults sit near 60 lines per function and 600 per class.

open as a page

In PHP CS Fixer, how do you configure .php-cs-fixer.dist.php with a Finder and rule sets such as @PER-CS or @PHP8x5Migration?

level: middleimportance: must knowfreq 45%

basics

~20 s

The committed .php-cs-fixer.dist.php is a PHP file that returns a PhpCsFixer\Config whose Finder selects the files and whose setRules() array enables rule sets like @PER-CS or @PHP8x5Migration plus individual rules, which can be tuned or switched off.

open as a page

In a PHP_CodeSniffer phpcs.xml.dist ruleset, how do rule ref, exclude and properties decide which sniffs run and how they behave?

level: middleimportance: must knowfreq 50%

basics

~20 s

A phpcs.xml.dist ruleset pulls in a whole standard, a category, one sniff or one message with rule ref, removes parts of it with exclude, and tunes a sniff's public settings with properties, so the whole team runs the same checks.

open as a page

In PHPStan, how do you generate a baseline, what does each entry record, and how does the baseline shrink as errors get fixed?

level: middleimportance: must knowfreq 50%

basics

~20 s

vendor/bin/phpstan analyse --generate-baseline writes phpstan-baseline.neon, an ignoreErrors list with message, identifier, count and path per file, which you include in phpstan.neon; fixed errors leave entries unmatched, PHPStan reports them, and you regenerate to shrink it.

open as a page

In a phpstan.neon file, what do level, paths, excludePaths and includes do, and how do command-line arguments interact with them?

level: middleimportance: must knowfreq 45%

basics

~20 s

Under parameters, level sets the rule level, paths lists what to analyse and excludePaths removes fnmatch patterns; includes pulls in other config files. A --level or paths given on the command line replace the config values rather than merging with them.

open as a page

In RuboCop, what is the difference between rubocop -a and rubocop -A, and how do a cop's Safe and SafeAutoCorrect settings decide what -a changes?

level: middleimportance: must knowfreq 70%

basics

~20 s

rubocop -a applies only corrections RuboCop marks safe; rubocop -A also applies unsafe ones that may change behaviour. A correction counts as safe only when the cop has both Safe and SafeAutoCorrect true, which is the default.

open as a page

In Ruby code checked by RuboCop, how do rubocop:disable, rubocop:enable and rubocop:todo comments work, and how do you keep them tightly scoped?

level: middleimportance: must knowfreq 65%

basics

~20 s

A # rubocop:disable Cop comment at the end of a line silences that line; on its own line it silences until # rubocop:enable Cop. rubocop:todo is an alias marking a suppression to revisit. -- reason documents why.

open as a page

In RuboCop, how do you share one .rubocop.yml style across many repositories with inherit_gem or inherit_from, and which setting wins on conflict?

level: middleimportance: must knowfreq 50%

basics

~20 s

Publish the shared file in a gem and load it with inherit_gem, or point inherit_from at a path or URL. inherit_gem files load first, then inherit_from files in list order (last wins), and the repository's own settings override both.

open as a page

How do you suppress a single detekt finding in Kotlin source without editing detekt.yml?

level: juniorimportance: should knowfreq 50%

basics

~20 s

Annotate the smallest declaration that contains the finding with Kotlin's @Suppress, passing detekt's rule id — for example @Suppress("MagicNumber") on one function. detekt reuses the standard annotation; a configured alias or the rule set id also works.

open as a page

In ktlint 1.x, how do you suppress a single rule violation for one declaration or file?

level: juniorimportance: should knowfreq 35%

basics

~20 s

Annotate the code with Kotlin's @Suppress using the fully qualified rule id, for example @Suppress("ktlint:standard:function-naming") on the declaration, or @file:Suppress(...) at the top of the file. The old ktlint-disable comments were removed in ktlint 1.0.

open as a page

In PHPStan, what do the PHPDoc types list<T> and array{...} shapes express that PHP's native array type cannot?

level: juniorimportance: should knowfreq 42%

basics

~20 s

Native array only says the value is an array. list<T> means keys 0, 1, 2 with no gaps and values of type T; array{id: int, name?: string} describes each key and its type, optional keys included.

open as a page

How do you make RuboCop run, or autocorrect, just one cop or department with --only, and skip cops with --except?

level: juniorimportance: should knowfreq 40%

basics

~10 s

Pass a comma-separated list: rubocop --only Layout/LineLength,Style/StringLiterals runs just those, --only Lint a department, and --except Metrics runs everything else. Add -a to autocorrect only that selection.

open as a page

Why can ktlintFormat succeed while ktlintCheck still reports violations on the same code?

level: middleimportance: should knowfreq 40%

basics

~20 s

Not every ktlint rule can autocorrect. Rules that need a human decision, such as naming rules and max-line-length, only report; the format task rewrites what it can and leaves those violations in place, so the check task still fails on them.

open as a page

In PHP_CodeSniffer, how do phpcs:ignore, phpcs:disable and phpcs:enable comments differ, and why should they name a sniff code?

level: middleimportance: should knowfreq 45%

basics

~10 s

phpcs:ignore suppresses one line, phpcs:disable suppresses everything until phpcs:enable or the end of the file, and phpcs:ignoreFile skips the file; naming a sniff code limits the suppression so other violations still surface.

open as a page

In PHP_CodeSniffer, how do errors, warnings and severity levels decide which violations a phpcs run reports and fails on?

level: middleimportance: should knowfreq 32%

basics

~20 s

Each PHP_CodeSniffer message is an error or a warning with a severity, 5 by default; phpcs shows it only if that severity reaches the threshold, 5 by default, and every shown message, warnings included, makes the exit code non-zero.

open as a page

In PHPStan, how should a plain string from config become a class-string: narrowed with class_exists() or forced with an inline @var?

level: middleimportance: should knowfreq 30%

basics

~20 s

Narrow it: after if (class_exists($name)) PHPStan treats $name as class-string, and the check also protects runtime. An inline @var is trusted without proof, so the docs call it a last resort and prefer fixing the type at its source.

open as a page

In PHPStan 2, how do you ignore one error with @phpstan-ignore and an identifier, and how does that differ from ignoreErrors in phpstan.neon?

level: middleimportance: should knowfreq 40%

basics

~20 s

Put // @phpstan-ignore argument.type (reason) on or above the line, naming the error identifier; it silences only that error there. ignoreErrors in phpstan.neon matches by message regex or rawMessage, identifier, path and count across files.

open as a page

How does PHPStan's result cache decide what to re-analyse, and how do you keep it effective in CI using tmpDir?

level: middleimportance: should knowfreq 28%

basics

~20 s

PHPStan stores the last result and a file dependency tree in %tmpDir%/resultCache.php and re-analyses only changed files and files that reference their symbols; setting tmpDir inside the workspace lets CI save and restore that cache between runs.

open as a page

In a .rubocop.yml, why can adding one AllCops Exclude pattern make RuboCop inspect vendor/, and how does inherit_mode prevent it?

level: middleimportance: should knowfreq 35%

basics

~10 s

Array settings such as Exclude replace the inherited array, so a one-entry AllCops Exclude drops the default vendor/**/* pattern. inherit_mode: merge: [Exclude] unions the lists instead, globally or for one cop.

open as a page

In RuboCop, what does AllCops TargetRubyVersion change, and where does RuboCop find the version when .rubocop.yml does not set it?

level: middleimportance: should knowfreq 40%

basics

~10 s

TargetRubyVersion tells RuboCop the oldest Ruby the code must run on; it selects the parser grammar and which version-gated cops apply. Unset, RuboCop reads the gemspec, .ruby-version, mise.toml, .tool-versions or Gemfile.lock, else assumes 2.7.

open as a page

In Standard Ruby, what is the difference between standardrb --fix, --fix-unsafely and --no-fix, and how do you make fixing the default?

level: middleimportance: should knowfreq 35%

basics

~10 s

standardrb --fix applies only corrections RuboCop marks safe; --fix-unsafely also applies unsafe ones that can change behaviour. fix: true in .standard.yml makes fixing the default, and --no-fix turns it off for one run.

open as a page

When adopting Standard Ruby on an existing codebase, what does standardrb --generate-todo produce, and how do standard:disable comments work?

level: middleimportance: should knowfreq 30%

basics

~20 s

standardrb --generate-todo writes .standard_todo.yml, mapping each file that has offenses to the cops it violates; Standard then ignores those, warns about them, and still checks everything else. # standard:disable Cop comments silence specific lines like RuboCop's own directives.

open as a page

In a .standard.yml file, what do ignore, default_ignores and ruby_version control, and what does Standard assume when ruby_version is missing?

level: middleimportance: should knowfreq 35%

basics

~20 s

ignore skips paths, or only named cops for a path; default_ignores (true by default) adds Standard's own skips such as vendor/**/, bin/ and db/schema.rb. ruby_version sets RuboCop's target; without it Standard uses the Ruby running standardrb.

open as a page

A detekt rule fires constantly on your codebase — how do you decide between tuning, scoping, and disabling it?

level: seniorimportance: should knowfreq 42%

basics

~20 s

First ask whether the findings are real. If the rule is right but over-broad, narrow it with its own options or an excludes path glob; move the number only if the codebase's idiom genuinely differs; set active: false only when the rule contradicts a deliberate convention.

open as a page

How do you write a custom PHP_CodeSniffer 4.0 sniff, and what do its register() and process() methods do?

level: seniorimportance: should knowfreq 22%

basics

~10 s

A custom sniff is a class implementing PHP_CodeSniffer\Sniffs\Sniff inside a standard's Sniffs/Category folder: register() returns the token types it listens for, and process() inspects each matching token and reports violations through the File object.

open as a page

showing 1–30 of 46