A Configuration Measured from Your Code
You do not have to write your first configuration: dresscode init in the root of the project
measures the code you already have and tailors a dresscode.neon to fit it. It does not guess. It measures the value
of each decision with the rule that will later check it, counting the places the rule reports, so what was measured and what is
checked cannot drift apart.
dresscode init
DRESS|CODE 1.3.0 looking at /var/www/shop to write its `dresscode.neon`
Project 13 PHP files in git, acme/shop
Excluded fixtures test data
src/Generated generated
not measured: 1 file git does not track, which the first fix takes too
PHP 8.2 and newer, from `composer.json`
Standard perCs (PER Coding Style), the nearest to your code by the files a fix changes, 13 under psr12; `--use` names another
Measured your code departs from it in 3 decisions over 13 files; perCs as it is, with no decision below `use`, would rewrite 13 of them
As written indentation.unit: tab your code, 92% of 13 files
blankLines.beforeDeclaration: 2 your code, all 13 places
braces.emptyBody.function: expanded your code, all 26 places
Checked all 13 files: a fix changes 1 file, 1 of them for the indentation, the line endings or the long lines the rest of the code has
Your call `dresscode init --use nette` measures against nette instead, where there is no `dresscode.neon` yet
Written `dresscode.neon`
Next `dresscode fix` rewrites 1 file in place (`dresscode check --diff` shows the changes first); until then `check` reports them
First, init finds the files of the project: what Git tracks or, without Git, the autoload paths from
composer.json together with the directories src, tests, test,
app, lib, bin, cron and www* that actually exist in the root. It
leaves out test data (the directories fixtures, Fixtures and expected), files that their
header or linguist-generated in .gitattributes marks as generated by a tool, tests in the PHPT format,
what Git ignores, and files that do not parse, because nobody writes any of these by hand. It writes every such file or directory
into the excludePaths key together with the reason, so that every later check leaves it out too; the
Excluded line lists them. A file Git does not track yet but does not ignore either is not measured; the
not measured line counts it all the same, because the first fix will process it too.
Then it chooses the standard: it checks the code against each standard and takes the one under which a fix would change the
fewest files, and the Standard line says so. It counts files, not decisions, because a difference across the whole
project weighs more than a difference in a single place. When two standards are close, within a tenth, the one named in the
configuration of the project's formatter wins. The framework the project is built on does not decide: an application built on
Nette or Symfony need not follow its style. When the formatter names none, init takes perCs if it is
among the close ones, and in a terminal it asks whether you would prefer another of them; if you pick one, it measures against
that one. Outside a terminal, instead of asking, it prints a Your call line with the command that measures against
the other one. The psr12 standard gives way to perCs, its successor, unless the formatter names it or a
fix under perCs would change more than 1.5 times as many files: PSR-12 says nothing about the newer constructs of
PHP, so code written to PER Coding Style 3.1 departs from it in fewer
decisions. A project formatted by Laravel Pint gets the standard of its preset, and a standard given with the --use
option is taken as it is. Sets given with further --use options are written after the standard, so
dresscode init --use nette --use modernizations writes both names into the use key.
Then it measures the decisions in which the code departs from the standard, each one separately and with each of its values. It
measures the indentation, the line length and the line endings first, because everything else depends on them. A project of up to
a thousand files and 8 MB is measured in full. A larger one is measured on a sample split evenly among its directories, so that
one large directory does not outweigh the others; the Sample line says how many files each directory contributed.
A value that at least 70% of the places agree on is written together with what it rests on
(your code, 92% of 13 files). Where the code writes the same thing in several ways and none of them prevails,
init writes keep, which means “do not check”, with a not checked: comment and what it
found in the code; the Unchecked line says how many such decisions there are.
A directory whose files write the indentation or one of the file.* decisions differently from the rest of the
project (in at least five files and in nine tenths of them) gets a Differs line with the override that would let it keep its
own style. Only you can write it, though: init proposes one style for the whole project, and an override is your
decision.
Differs tests: indentation.unit 4 spaces in all 6 files; an override for `tests` would keep it, none is written
Finally, it runs the whole configuration over all files. The first fix after init should not rewrite
half of the project, so there is a budget: the fix may change 1% of the files, but never fewer than 2 or more than 20; the
--budget option sets a different number. When the fixes together would change more files, init leaves
some decisions unchecked until the rest fits the budget, and asks you about them, showing a place in the code that each one
concerns. The indentation, the line length and the line endings are never left unchecked, because the other decisions depend on
them; fix corrects the files that write them differently from the rest of the project regardless of the budget, and
the Checked line counts them separately. Your answers do not count against the budget either. The findings that no
rule fixes belong in the baseline; the same line
says how many there are.
Standard nette (the Nette Coding Standard), as given
Measured your code departs from it in 4 decisions over 13 files; nette as it is, with no decision below `use`, would rewrite 13 of them
As written file.strictTypes.position: nextLine your code, all 13 files
Checked all 13 files: a fix changes 1 file, 1 of them for the indentation, the line endings or the long lines the rest of the code has
1 more decision is left as your code has it, as the fixes together would change more than 2 files, the indentation, the line endings, the long lines and your answers aside (`--budget` raises the limit)
Check `literals.quotes: single`? A fix rewrites 3 files, more than the budget allows.
e.g. src/Cart.php:18 A plain string must be written with single quotes.
[1] don't check it (recommended)
[2] single everywhere, a fix rewrites 3 files
Your choice [1/2]
You answer with the number in the square brackets. The recommended answer always comes first, as number 1, and pressing Enter
accepts it; it leaves your code as it is, so if you are not sure, you cannot break anything. The answers that change the code show
how many files the fix would rewrite. When your answers change the configuration, init checks it again, so that the
Checked line also holds for what you chose. If the budget then leaves another decision unchecked, it asks about that
one as well and says that your answers caused it. The questions of one round are numbered, (2/3), so you know how
many are left.
What does init ask about? About the decisions the budget left unchecked, and about the standard when two are
close, as described above. It also asks about a value the configuration of the project's formatter contradicts, and about names
that do not use the case the standard requires and share a prefix or a suffix, such as classes ending with an underscore; a
pattern in naming.except can let them pass.
It asks about at most five decisions, the costliest first.
init does not ask whether to take the standard as it is; the Measured line only shows what that would
cost: here nette would rewrite all 13 files, mostly because of a single decision, as your code always puts
declare(strict_types=1) in a different place than nette does. A difference like that is often a trace
of a standard that has changed since. If you want to follow it, delete the decision file.strictTypes.position from
dresscode.neon and run fix; otherwise the value init measured from your code stays.
Without a terminal, with the --yes option or with --format json, init asks nothing and
takes the recommended answers, so it writes the same file as if you had answered every question by pressing Enter. It then prints
each question on a Your call line, in the form of a dresscode.neon line or a command that gives a
different answer:
Your call `literals.quotes: single` in `dresscode.neon` checks it too; its fix changes 3 files
You can also give an answer in advance: dresscode init --set literals.quotes=single writes the value regardless of
what the code says, and the budget never leaves it unchecked. The path and the value are checked against the catalogue before
measuring starts, so you learn about a typo before the measuring, not after it.
With the --dry-run option, init prints the configuration below the report and writes nothing.
--jobs limits how many processors the measuring uses, and --jobs 1 measures in a single process. With
--format json, it prints a single document for a tool or an AI agent instead of the text: the version of the format
in version, the root of the project in root, the scope in scope, the sample in
sample, the standard in standard with the number of files a fix would change under each one, the values
in values with their comments and origins, the directories of the Differs line as regions,
the check over all files in verification, the Your call lines as instructions, the files a
rule failed in as failed, the configuration in configuration, and the file it was written to in
written; when a configuration already exists, also what has moved since it was written, in shifts.
The written file shows what every value rests on, right next to it:
# made to measure by `dresscode init`; run it again to see what has moved since, it writes nothing over this file
use: perCs # PER Coding Style, of the standards the one a fix changes the fewest files of your code under
paths: [src, tests]
excludePaths:
- fixtures # test data
- src/Generated # generated
# the namespaces of the scope declare no function and no constant, so an unqualified name in a namespace
# is resolved for certain; one declared later is reported until it is listed
nameResolution: certain
indentation:
unit: tab # your code, 92% of 13 files
braces:
emptyBody:
function: expanded # your code, all 26 places
blankLines:
beforeDeclaration: 2 # your code, all 13 places
Besides the decisions, init also writes what the namespaces declare. Here what matters is not the majority but
completeness, so it goes through all files, finds the functions and constants declared in namespaces, writes their lists into the
namespaces key and adds nameResolution: certain. The page Functions and Constants in Namespaces describes what these keys
mean. If it finds phpstan/phpstan among the installed packages, it also writes typeAnalysis: phpstan with a comment on what the types
bring. init never writes the fixRisky key: permission for fixes that may change the behavior of the code
cannot be measured from the code.
If the project has PHP_CodeSniffer, PHP CS Fixer or Laravel Pint, init reads its configuration but never runs it
without your consent. It picks the configuration the same way as migrate config, choosing the one the project
actually runs, and in a terminal it asks when it cannot tell. From that configuration it takes the paths the formatter checks, the
files it leaves out, and the rules turned off in only part of the project, for which it writes an override. It compares the rules of
the formatter with what the code actually follows, and where they contradict each other, it asks which value to write. The
configuration of PHP CS Fixer, however, is PHP code: init reads the paths of its Finder from the code and lists under
the Formatter line those it could not read, but the rules can only be learned by running the configuration, so in a
terminal init first asks whether it may. To use a different configuration from the one it picked, name it with the
--formatter option; naming it also gives your consent to read its rules, even when it is PHP. At the end it says
where dresscode check can replace the formatter, whether it compared the rules of the formatter with the code or did
not read them at all, and that dresscode migrate config translates them instead of measuring the code; either way,
the rules of the formatter are not carried over into the written file. It also says how many phpcs suppression comments
dresscode migrate suppressions would rewrite.
At most two commands remain, and init prints them itself: dresscode fix fixes the files that depart
from the written configuration, and dresscode baseline records the places that no rule fixes, so that
check passes.
Before it writes the file, init loads it as a configuration and checks that it yields the same values it measured.
When a rule fails in a file, it leaves that file out of the measuring, names it in the header of the written file and ends with
exit code 2, because the configuration then rests only on the rest of the code. It never overwrites an existing configuration, so
when one exists, it asks nothing and refuses --set; if it cannot read the file, it stops and gives the reason before
measuring anything. Instead, it measures the project again against the standard that file names, over the files the configuration
checks (it leaves out the files to which an override gives decisions of their own, and says so). It lists every decision it would
write differently today, with the number of files a fix under the value in the file would rewrite, and every decision the file
sets differently from the standard although the code follows the standard. This is useful after you update DressCode or after you
have worked on the project for a while:
Compared the code with the standard and the decisions of `dresscode.neon`, over the files it checks Moved nothing: `dresscode.neon` says what init measures `dresscode.neon` exists, so nothing is written; `dresscode check --set path=value` shows what a value would change.