Coder's Drupal ruleset includes sniffs from PHP_CodeSniffer's own standards (Generic, PEAR,
PSR2, Squiz and Zend) and from the Slevomat Coding Standard. The VariableAnalysis sniff is part of
the DrupalPractice ruleset, on the DrupalPractice standard page.
Mago's no-redundant-method-override reports only an override without parameters, and only when the parent call writes the method name in the same case. It compares a spread argument with a variadic parameter the wrong way round, so it misses an override that passes its parameters through, and reports one that spreads an array parameter, such as parent::foo(...$items).
Mago's rule reports a body without braces, with no fix. The formatter doesn't add braces. It moves the body to a line of its own. The rule also reports a loop or if whose body is a lone ;, such as while (next($items));, which Coder skips.
OldStyleCall: A call such as parent::Base() in a constructor. mago analyze reports the call that fails on PHP 8, when the parent doesn't have a method of that name. A call to a parent method of that name that exists works, and drupal/old-style-constructor reports that method where it's declared, unless the parent class also has __construct(). A parent class outside the scanned paths isn't reported.
With interface-name = { psr = true }. Coder ignores case in the suffix, so it accepts Foointerface; Mago wants Interface. Mago also reports an interface name that isn't in class case, which Coder reports under Drupal.NamingConventions.ValidClassName.
With trait-name = { psr = true }. Coder ignores case in the suffix, so it accepts Footrait; Mago wants Trait. Mago also reports a trait name that isn't in class case, which Coder reports under Drupal.NamingConventions.ValidClassName.
ClassConstantNotUpperCase: Mago's constant-name also reports an upper-case name with a leading, trailing or doubled underscore, such as A__B, which Coder accepts.
phpcs takes the deprecated functions from the running PHP, and the analyzer from Mago's stubs. Mago's stubs for strptime(), zip_open(), zip_close() and six zip_entry_*() functions don't have a deprecation, so the analyzer doesn't report them. The analyzer also reports a function marked @deprecated in the codebase, and a call through a variable that holds a function name.
Found: Coder reports <? only when PHP's short_open_tag is on. Mago always reports it. Mago's fix doesn't write a space after <?php, so <?echo 1; becomes <?phpecho 1;, which doesn't parse.
PossibleFound: Mago always reads <? as an opening tag, so a <? in HTML text that isn't PHP code gives parse errors.
IncorrectSingle: The formatter can break a long expression right after echo, print, include, include_once, require or require_once, which Coder reports. It also puts new alone on its line before an anonymous class with attributes.
Mago also reports a typed parameter with a null default before a required one, such as int $a = NULL, $b. Coder skips it, because before PHP 8 that was how a type allowed NULL. PHP 8.4 deprecates it. Coder also skips ?int $a = NULL, $b, which PHP 8.1 deprecates.
BreakNotNewLine: A keyword inside an expression, as in $x or exit();, stays where it is. phpcs reads it as the end of the case and keeps reporting it.
WrongOpener: case 1: {. Mago reports the braces as a redundant block.
WrongOpenercase: case 1;. Coder also reports case 1 ?>, which PHP accepts. Mago reports a parse error there and leaves that switch out. The other rules still report on the rest of the file, but mago lint --fix and mago format don't change a file with a parse error.
WrongOpenerdefault: default;. Coder also reports default ?>, which PHP accepts. Mago reports a parse error there and leaves that switch out. The other rules still report on the rest of the file, but mago lint --fix and mago format don't change a file with a parse error.
KeySpecified: An array over several lines whose first entry doesn't have a key and a later one has. Not ported. Core turns the code off, and the written standard's own array example, ['hello', 'world', 'foo' => 'bar'], mixes the two. Coder also takes the spread of an array element before a key, as in [...$x['k'], 'k' => 1], for an entry without a key.
NoKeySpecified: An array over several lines with an entry without a key after one with a key. Not ported. Core turns the code off, and the written standard's own array example, ['hello', 'world', 'foo' => 'bar'], mixes the two.
With file-name on and both rules excluding *Test.php and *TestBase.php under tests/<dir>/, as the configuration does and Coder's ruleset does. Mago reports at warning level, Coder at error. Core's own config runs Coder 8, whose sniff checks only the first class-like of a .php file.
file-name checks only a file with one class-like declaration. In a file with several, single-class-per-file reports every declaration after the first, whatever its name.
SpaceBeforeColonCase: The formatter removes the space before the colon. Coder also reports case 1: { under this code, and phpcbf doesn't fix it. Mago's no-redundant-block reports the braces, and doesn't have a fix either.
SpaceBeforeColonDefault: The formatter removes the space before the colon. Coder also reports default: { under this code, and phpcbf doesn't fix it. Mago's no-redundant-block reports the braces, and doesn't have a fix either.
WrongOpenerCase: case 1 ?>, which PHP accepts. Mago reports a parse error there and leaves that switch out. The other rules still report on the rest of the file, but mago lint --fix and mago format don't change a file with a parse error.
WrongOpenerDefault: default ?>, which PHP accepts. Mago reports a parse error there and leaves that switch out. The other rules still report on the rest of the file, but mago lint --fix and mago format don't change a file with a parse error.
With check-name-casing = true. Reported without a fix. The analyzer skips a first-class callable such as STRLEN(...). It also reports a call to a function of the codebase whose case differs from the declaration, where Coder checks only PHP's own functions.
Unreachable: A break; after return is useless-control-flow. The analyzer also reports code after an if and else that both exit, and code after an exit on the same line, which Coder skips.
AfterLast: The formatter removes the blank line between a function and the end of a block, such as if (!function_exists()) or a loop, which Coder reports.
Before: A function on the same line as a statement is reported without a fix. mago format splits the line, and the next mago lint --fix adds the blank line.
BeforeFirst: The formatter removes the blank line between the start of a block, such as if (!function_exists()) or a loop, and a function, which Coder reports.
The formatter removes a closing tag at the end of the file. When the tag ends the last statement, as in $a = 1 ?>, drupal/closing-tag-statement reports it and its fix writes the ;. Coder's ruleset skips *.tpl.php files, and so does that rule. Mago's rule and the formatter check them too.
drupal/doc-type-namespace reports an import that only a docblock type uses by its short name. An import named only in the text of a comment, or only by its full name in a docblock, isn't reported.
Mago also reports each name in a group import and each name after the first in a comma list. Coder skips those. Mago also reports an import whose namespace is written in another case, which Coder skips.
The fix of Mago's no-redundant-use doesn't remove the last name of a group import. A group with two names from the file's own namespace, such as use Drupal\mymod\{First, Second};, keeps Second, and both tools keep reporting it.
Core's phpcs.xml.dist runs four Slevomat sniffs left out of Coder's rulesets, and gives
ForbiddenComments one more pattern. They apply to core, and to a project that copies core's
configuration.
Core forbids @encode, @inheritDoc, @inheritdoc and @returns. drupal/doc-comment reports a bare @inheritdoc that opens a docblock, as Coder does. Nothing reports the other tags, or a bare @inheritdoc below a summary.