Migrating from PHP_CodeSniffer and Slevomat
How to migrate from PHP_CodeSniffer and Slevomat to DressCode: converting phpcs.xml to a DressCode
configuration, check and fix in place of the phpcs and phpcbf commands,
different exit codes, and an overview of what matches what.
Before You Start
Go through the common steps first: you can leave the code and the comments as
they are, import converts the configuration, and migrate-suppressions rewrites the comments. This page
adds what is specific to PHP_CodeSniffer. And one piece of good news right away: phpcs.xml is XML, so
import reads it even after PHP_CodeSniffer and Slevomat have been uninstalled.
Converting the Configuration
dresscode import phpcs.xml dresscode.neon
The PSR1, PSR2 and PSR12 sets are translated to the psr12 standard.
Individual sniffs (rules of PHP_CodeSniffer) are translated to decisions, together with their property from
<properties> where it has a counterpart: Generic.Files.LineLength is file.longLines and its
absoluteLineLimit becomes the value of file.lineLength;
SlevomatCodingStandard.Namespaces.UnusedUses is imports.unused, which always counts a name in a doc comment as
used, as with the searchAnnotations property; Squiz.WhiteSpace.OperatorSpacing is
spacing.binaryOperator. In DressCode there is one line length for everything, so import also takes it
from SlevomatCodingStandard.Files.LineLength, and from RequireMultiLineMethodSignature and
RequireMultiLineCondition of Slevomat, where it uses one less than their minLineLength; when the values
differ, import keeps the smallest one and tells you so.
Excluding a sniff inside a set (<exclude name="…"/>) and silencing it with
<severity>0</severity> are both carried over by import as the value keep for
the decision that matches the sniff. But when the same decision is also covered by another sniff, import leaves the
decision alone and prints a warning; the same happens when you exclude one message of a sniff
(Generic.PHP.ForbiddenFunctions.Found), because a decision cannot be narrowed to one message. A sniff with no
counterpart is listed on standard error; typically these are sniffs about doc comments from the Squiz.Commenting
group, and sniffs that check the same thing as another sniff, only in different words.
The parts of phpcs.xml that are not rules, which you rewrite by hand:
<file>and<exclude-pattern>go to thepathsandexcludePathskeys; the patterns are described in Configuration. An<exclude-pattern>inside a<rule>belongs in an override that gives the decision the valuekeepfor those paths.importwarns you about<exclude-pattern>, but not about<file>.<arg name="extensions">goes to thefileExtensionskey.<config name="php_version">is not needed; DressCode reads the target version fromcomposer.json.<arg name="tab-width">goes to the decisionindentation.tabWidth; the indentation is set by the standard, or by the decisionindentation.unit.
Slevomat sniffs that DressCode covers are translated by name, like any others. Its helper classes and the
installed_paths setting are not needed here.
Sections Instead of Sniffs
PHP_CodeSniffer builds one area of style from sniffs by different authors, and each one has its own vocabulary. Blank lines in
PSR-12, with one line between methods, none after the opening brace of a class and of a block, and one before return,
take nine sniffs:
<rule ref="PSR2.Namespaces.NamespaceDeclaration"/>
<rule ref="PSR2.Namespaces.UseDeclaration"/>
<rule ref="SlevomatCodingStandard.Classes.EmptyLinesAroundClassBraces">
<properties>
<property name="linesCountAfterOpeningBrace" value="0"/>
<property name="linesCountBeforeClosingBrace" value="0"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Classes.ClassMemberSpacing">
<properties>
<property name="linesCountBetweenMembers" value="1"/>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Classes.MethodSpacing">
<properties>
<property name="minLinesCount" value="1"/>
<property name="maxLinesCount" value="1"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.FunctionOpeningBraceSpace"/>
<rule ref="PSR2.Methods.FunctionClosingBrace"/>
<rule ref="Squiz.WhiteSpace.ControlStructureSpacing"/>
<rule ref="SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing">
<properties>
<property name="jumpStatements" type="array">
<element value="return"/>
</property>
<property name="linesCountBefore" value="1"/>
<property name="linesCountBeforeFirst" value="0"/>
<property name="linesCountAfter" value="0"/>
<property name="linesCountAfterLast" value="0"/>
</properties>
</rule>
In DressCode it is one section, blankLines, with six
values whose keys say where the blank lines go:
blankLines:
afterNamespace: 1
afterImports: 1
aroundMethods: 1
bodyStart: 0
bodyEnd: 0
beforeStatement:
return: 1
Both configurations do the same thing with the same file. The difference is in what you need to know to write and read them:
with sniffs, which sniff checks which place and what its property is called; here, only how many blank lines you want where. The
whole section, including ranges like 1-2 for “one or two”, is described on its page.
Commands
| PHP_CodeSniffer | DressCode |
|---|---|
phpcs src |
dresscode check src |
phpcbf src |
dresscode fix src |
phpcs --standard=PSR12 src |
dresscode check --use psr12 src |
phpcs --sniffs=SlevomatCodingStandard.Namespaces.UnusedUses src |
dresscode check --only imports.unused src |
phpcs --exclude=SlevomatCodingStandard.Namespaces.UnusedUses src |
dresscode check --set imports.unused=keep src |
phpcs -e |
dresscode catalogue or dresscode config |
phpcs --report=checkstyle |
dresscode check -f checkstyle |
phpcs --report=json |
dresscode check -f json |
phpcs --parallel=8 |
dresscode check --jobs 8 |
phpcs --cache |
the cache is on by itself; --no-cache bypasses it |
phpcs -p |
progress is shown in the terminal by itself |
PHP_CodeSniffer uses its exit code to tell whether the violations found can be fixed, and phpcbf has its own
scale. DressCode returns 0 for clean, 1 for violations, 2 for a failure on some file and
3 for an error on the command line or in the configuration; the summary of check and the
json format tell you how many violations remain after a fix. A script that evaluates the exit code needs one
change.
What Is Different
- One tool for checking and fixing.
phpcsandphpcbfwere two commands that sometimes disagreed, because a fix could reveal more violations. Herefixis the same ascheckplus writing the files, and whatfixdoes not fix, it reports just likecheck. - The message tells you what the fix can change. The messages of both tools give the expected and the found value; the
difference is elsewhere. Slevomat has the
StrictCallsniff, and for twoin_array()calls without$strictit writes the same thing twice:Strict parameter missing in in_array() call.DressCode with types from PHPStan can tell that the first call searches for anintin alist<int>, and addstruethere by itself; in the second call the value searched for ismixed, so it only reports the fix with the sentenceRisky because a strict search no longer finds a value of another type.The same output also shows things that no standard shipped with PHP_CodeSniffer has a sniff for: an overriding method whose parameter is named differently than in the parent, so a call with a named argument fails with an error, and a call to a method the library marked as deprecated, together with the library's advice on what to write instead. The full output is shown in Getting Started. - PSR-12 even where
phpcbfonly reports.phpcbfreports missing constant visibility, which PSR-12 requires, but cannot add it; DressCode adds it. In Laravel, afterphpcbfDressCode still finds 772 places, see the comparison. - You can also try a decision that the standard does not have. In PHP_CodeSniffer,
--sniffscan only choose from the sniffs the selected standard contains.dresscode check --set path=value --only path --diffgives a decision a value for one run and shows only its changes. The name of a set or a section in--only, on the other hand, only narrows the run to the decisions the configuration makes, so it does not turn on anything the project left atkeepon purpose, see Command Line. - There are no sniff priorities. The rules run again and again until the code settles; see why.
- The comments
phpcs:ignore,phpcs:disable,phpcs:enable,phpcs:ignoreFileand@phpcsSuppresskeep working, even with the sniff names, which DressCode translates to decisions. The only difference is thatphpcs:ignoreon a line of its own covers the whole next statement, not just one line, see the common steps. Rewriting them todresscode:*is one command, and it is optional. - The baseline is built in. In PHP_CodeSniffer it has been discussed for years and is solved by third-party add-ons. Here
the
dresscode baselinecommand records it, and it contains only whatfixcannot fix, so it does not get in the way of fixes; see Baseline. - A custom sniff is not carried over but rewritten; written against the tree, it usually shrinks to a fraction of its length. The guide is on a separate page.