skip to content

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%

answer

  1. every message has a type and a number
  2. default severity 5, threshold 5
  3. -n equals --warning-severity=0
  4. severity 0 in the ruleset switches off
  5. warnings count toward the exit code

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.

solid answer

~40 s

A sniff raises a message as an **error** or a **warning** and may give it a severity; unspecified means 5. `phpcs` displays a message only if its severity is at least the threshold for its type, which defaults to 5 for both. `--severity=N` sets both thresholds, `--error-severity` and `--warning-severity` set one each, and `-n` is shorthand for `--warning-severity=0`, which hides warnings entirely. In the ruleset, `<severity>0</severity>` switches a sniff or message off, a higher or lower number moves it across the threshold, and `<type>warning</type>` demotes an error. Only displayed messages count, and in 4.0 warnings affect the exit code just like errors unless the `ignore_warnings_on_exit` config value is set.

code

xml · 19 lines
xml
<?xml version="1.0"?>
<ruleset name="Acme">
    <rule ref="PSR12"/>

    <!-- Advisory only: shown with --severity=3 or lower -->
    <rule ref="Generic.Commenting.Todo">
        <severity>3</severity>
    </rule>

    <!-- Always blocking, even though the sniff raises a warning -->
    <rule ref="Generic.PHP.RequireStrictTypes.Disabled">
        <type>error</type>
    </rule>

    <!-- Switched off entirely -->
    <rule ref="Generic.Files.LineLength.TooLong">
        <severity>0</severity>
    </rule>
</ruleset>

go deeper

for a junior

Recall that messages are errors or warnings, that -n hides warnings, and that the default severity and threshold are both 5.

for a middle

Explain the at-least-threshold comparison, why a threshold of 0 hides a type, and how severity and type are changed per rule.

for a senior

Show you can design a ruleset where advisory checks stay visible without blocking merges, using type, severity and ignore_warnings_on_exit deliberately.

for a principal

Discuss when a warning tier that never blocks earns its keep, and how to promote advisory checks once the backlog is gone.

## Two attributes on every message Every violation PHP_CodeSniffer reports carries two attributes, set by the sniff that raised it and adjustable in the ruleset: - **type**: `ERROR` or `WARNING`. The sniff chooses by calling `addError()` or `addWarning()` (or their fixable variants). Some sniffs let you choose; `Generic.PHP.ForbiddenFunctions` has a public `error` property, and `Generic.Files.LineLength` raises a warning (`TooLong`) above `lineLimit` and an error (`MaxExceeded`) above `absoluteLineLimit`. - **severity**: an integer. When a sniff passes 0 or nothing, PHP_CodeSniffer uses **5**. ## Thresholds: which messages are displayed The run has a **display threshold** per type. A message is shown only if its severity is **at least** the threshold for its type. Both thresholds default to 5, so default-severity messages appear. | Option | Effect | |---|---| | `--severity=N` | sets both the error and the warning threshold to N | | `--error-severity=N` | sets the error threshold only | | `--warning-severity=N` | sets the warning threshold only | | `-n` | shorthand for `--warning-severity=0` | | `-w` | shows errors and warnings (the default) | A threshold of **0** is special: it hides that type completely rather than showing everything. So `-n` drops every warning, and `--severity=6` hides every default-severity message while still showing any message whose severity was raised to 6 or more. The same values can be stored with `--config-set severity 6` (also `error_severity`, `warning_severity`, `show_warnings`), or put in the ruleset as `<arg name="severity" value="6"/>`. ## Adjusting messages in the ruleset Inside `phpcs.xml.dist`, a `<rule>` can change the attributes of a whole sniff or one message code: 1. `<severity>0</severity>` switches the sniff or message off; this is exactly what a four-part `<exclude>` does internally. 2. `<severity>3</severity>` pushes it below the default threshold, so it is only seen when someone runs with `--severity=3` or lower. That is useful for advisory checks. 3. `<type>warning</type>` turns an error into a warning, and `<type>error</type>` promotes a warning to an error. Changing the type does not change the severity, and changing the severity does not change the type; they are independent levers. ## How this feeds the exit code In PHP_CodeSniffer 4.0 the exit code is computed from the messages that were **actually recorded**, which means those that passed the thresholds and were not suppressed by an annotation: - a warning that is displayed counts, so it makes `phpcs` exit non-zero just like an error; - a warning hidden by `-n` or a threshold is not recorded, so it does not affect the exit code; - setting `ignore_warnings_on_exit` to 1 (`--config-set` or `--runtime-set`) keeps warnings visible in the report but excludes them from the exit code; `ignore_errors_on_exit` does the same for errors. This matters for a pull-request check. If warnings are shown but not meant to block a merge, set `ignore_warnings_on_exit`, rather than hiding them with `-n`, because then developers still see the advice. ## A worked example Suppose a ruleset includes `PSR12` with its line-length settings untouched, sets `Generic.Commenting.Todo` to severity 3, and promotes `Generic.PHP.RequireStrictTypes.Disabled` to an error. A file contains a `TODO` comment, a `declare(strict_types=0);` and one line of 130 characters. - `vendor/bin/phpcs` shows the promoted strict-types **error** and the line-length **warning** (`TooLong`, severity 5); the TODO warning at severity 3 is hidden. Exit code is non-zero. - `vendor/bin/phpcs -n` shows only the error; the warning is neither displayed nor counted. - `vendor/bin/phpcs --severity=3` shows all three messages. - `vendor/bin/phpcs --runtime-set ignore_warnings_on_exit 1` shows the error and the line-length warning, and only the error decides the exit code. ## Choosing between the levers - A check the team does not want at all: exclude it, or give it severity 0. - A check the team wants visible but not blocking: make it a warning and set `ignore_warnings_on_exit`. - A check only worth reading during a cleanup: lower its severity below 5 and run with `--severity=1` when needed. - A check that must always block: keep it an error, or promote a warning with `<type>error</type>`.

  • What is the difference between running phpcs -n and setting ignore_warnings_on_exit?
    `-n` sets the warning threshold to 0, so warnings are neither shown nor recorded and cannot affect the exit code. `ignore_warnings_on_exit` leaves warnings in the report but excludes them when the 4.0 exit code is computed. The second keeps the advice visible to developers while only errors block.
  • Why might a message still appear after you ran phpcs with --severity=6?
    `--severity=6` hides only messages whose severity is below 6. A sniff or ruleset `<severity>` of 6 or more still passes the threshold. A default-severity message, which is 5, disappears.

saying these in an interview costs you the question

  • Warnings never make phpcs exit with a non-zero code
  • A higher --severity value shows more violations
  • --warning-severity=0 shows every warning regardless of severity
  • Changing a rule's type to warning also lowers its severity
  • Severity 0 in the ruleset means maximum priority