Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
324b752
tests: split Application.phpt by topic
dg Nov 2, 2026
db217b3
opened 1.4-dev
dg Oct 19, 2026
c926899
baseline: dresscode-baseline.* of the root applies without the config…
dg Oct 20, 2026
ad5ae64
console: the label a line of the output opens with is Markup::formatL…
dg Oct 21, 2026
bbc781c
presets: symfony
dg Oct 21, 2026
c5abc20
presets: laravel, the style of the preset laravel of Laravel Pint
dg Oct 21, 2026
a779a06
added interop
dg Oct 21, 2026
b5566fc
cli: migrate config translates a configuration file of PHP CS Fixer, …
dg Oct 22, 2026
c96788f
config: a name of a rule of another tool stands for the decisions cov…
dg Oct 22, 2026
eb232f1
suppression: the comments of phpcs hold
dg Oct 22, 2026
e4fa1c1
migrate config: without a file translates the configuration the proje…
dg Oct 22, 2026
e620d21
engine: the files of a run are found by FileFinder, which a run holds…
dg Oct 22, 2026
00becb5
cli: migrate suppressions
dg Oct 23, 2026
bdd63ae
docs: the procedure an agent writes a rule or ports one from another …
dg Oct 23, 2026
0bedda8
migrate config: writes the scope, what git tracks or what the autoloa…
dg Oct 23, 2026
e4f49c5
migrate config: the scope is narrowed to the paths the formatter checks
dg Oct 23, 2026
c8eaeb2
migrate config: writes overrides where the formatter leaves files out…
dg Oct 23, 2026
b5c8add
migrate config: says how many phpcs suppression comments its scope holds
dg Oct 23, 2026
5209d1b
released version 1.4.0
dg Oct 23, 2026
9cbe834
opened 1.5-dev
dg Oct 23, 2026
425a8e9
cli: the hidden option --root names the root of the run
dg Oct 26, 2026
5775c1b
measuring: a probe checks the code under a configuration
dg Oct 26, 2026
ca966ab
cli: the init command
dg Oct 26, 2026
63bc679
init: psr12 yields to perCs, its successor
dg Oct 26, 2026
027361f
init: the status line shows a spinner and the probes done in a color …
dg Oct 27, 2026
c641ae6
init: asks where the measuring left the choice
dg Oct 27, 2026
a69d87f
docs: init and the standards symfony and laravel
dg Oct 27, 2026
28b424c
released version 1.5.0
dg Oct 27, 2026
778d243
opened 1.6-dev
dg Oct 27, 2026
17089ad
console: the run over the files of a project is RunExecutor, which ch…
dg Oct 27, 2026
dfdb16f
engine: a run keeps its violations only counted
dg Oct 27, 2026
2aeeb5b
cli: upgrade today, the code upgraded to the versions the project req…
dg Oct 27, 2026
07437fb
upgrade today: the upgrading data a library of the project needs inst…
dg Oct 28, 2026
ac9fa5b
upgrading: every section of upgrading data is laid as a layer of its own
dg Oct 28, 2026
35f0424
added rule unseenReferences
dg Oct 28, 2026
2b50b49
cli: upgrade estimate says what an upgrade to the target changes, cha…
dg Oct 28, 2026
78d90f9
upgrade estimate: says what refuses each target and what the raise wo…
dg Oct 29, 2026
cbab487
cli: upgrade plan orders the estimate into steps, which --save writes…
dg Oct 29, 2026
e8db9c6
cli: upgrade run makes the steps of the plan, upgrade abort takes the…
dg Oct 29, 2026
06ac48b
upgrade run: raises the constraints with Composer and goes on in a ne…
dg Oct 29, 2026
56f3060
upgrade run: waits while the fixes leave tasks for a human
dg Oct 29, 2026
c782741
upgrade run: --commit makes a commit per topic once its step passed, …
dg Oct 29, 2026
1b3e88f
upgrade today: --max-warnings, so that CI fails where more warnings a…
dg Oct 30, 2026
ad3c85c
cli: upgrade tasks lists what the fixes of the saved plan leave for a…
dg Oct 30, 2026
ad29916
cli: upgrade status says where the saved plan stands and what a run k…
dg Oct 30, 2026
018f014
cli: the hidden option --profile writes where the time of a run went,…
dg Oct 30, 2026
bf94e6f
hygiene.__invokeCall: required calls an invokable object through __in…
kaskader201 Nov 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ It is my fervent wish that this file guide every AI coding agent working with co

## Documentation

`docs/internals.md` is the source of truth for how DressCode works: the tree as a rule sees it, the mutation API, the engine of gaps and the rules. Read it before any non-trivial change. `docs/dresscode.annotated.neon` is a configuration setting every decision, each with what its value means and the other values it takes, the value that of PER Coding Style where its preset decides and the best one where it says nothing: a decision added, renamed or given other values is written there too, which `tests/DressCode/Config/annotated.phpt` checks.
`docs/internals.md` is the source of truth for how DressCode works: the tree as a rule sees it, the mutation API, the engine of gaps, the rules and the interop. Read it before any non-trivial change. `docs/skills/writing-rules.md` is the procedure of writing a rule or porting one from another tool, the one an agent outside this repository follows too. `docs/dresscode.annotated.neon` is a configuration setting every decision, each with what its value means and the other values it takes, the value that of PER Coding Style where its preset decides and the best one where it says nothing: a decision added, renamed or given other values is written there too, which `tests/DressCode/Config/annotated.phpt` checks.

## Project overview

DressCode is a PHP code style checker and fixer built on a **lossless concrete syntax tree**: every token of the source is in the tree, whitespace and comments are trivia attached to tokens, and printing the tree reproduces the input byte for byte.

The tree itself is the `phpsyntax/phpsyntax` library (namespace `PhpSyntax`), developed in a repository of its own. This one holds `DressCode` (`src/`): engine, rules API, the library of generic rules, configuration, CLI.
The tree itself is the `phpsyntax/phpsyntax` library (namespace `PhpSyntax`), developed in a repository of its own. This one holds `DressCode` (`src/`): engine, rules API, the library of generic rules, configuration, CLI. The names of other tools live only in `Interop/`, never in a rule.

Rules use only the public API of `PhpSyntax`; whatever a rule in DressCode needs from it is public API for plugins too. What the rules only share among themselves (`Rules\NodeHelpers`, `Rules\Compiler` and the helpers beside the rules that carry `@internal`) is `@internal`; the grammar of a map of members (`Upgrading\MemberMapGrammar`, the map itself `MemberMap` with `findAccess()`, its `MemberMapEntry` and `MemberMapMatch`, `MemberPattern`, `ArgumentPattern`, `FunctionPattern`) is not, a rule shipped by a package being fed the same way, and neither is `GlobalCalls`, which answers which global function a call calls and why that may be another one, nor `CodeWriter`, which writes code in the shape of the file (a class written the way the file reaches it, an import added the way the file writes its imports) for a rule of a package as for one of the core. Presets define style; DressCode has no style of its own. The three standards carry one: `dresscode/perCs` (PER Coding Style 3.1), `dresscode/psr12` and `dresscode/nette` (the Nette Coding Standard, PER with tabs, the layout of the Nette libraries and their departures, which the repository itself is checked with). What a project wants beyond the looks of its code comes in a set, a preset of one intent that decides nothing about the layout and is used beside the standard (`modernizations`, `deprecations`, `cleanup`, `correctness`, `types`, `compilerOptimizations`); a decision the core gains goes into its set, one that writes a newer construct and the replacement of what PHP deprecated alike into every set whose intent it meets (`upgrading.syntax.firstClassCallables` in `modernizations` and `deprecations`), and what no set holds the project decides itself.
Rules use only the public API of `PhpSyntax`; whatever a rule in DressCode needs from it is public API for plugins too. What the rules only share among themselves (`Rules\NodeHelpers`, `Rules\Compiler` and the helpers beside the rules that carry `@internal`) is `@internal`; the grammar of a map of members (`Upgrading\MemberMapGrammar`, the map itself `MemberMap` with `findAccess()`, its `MemberMapEntry` and `MemberMapMatch`, `MemberPattern`, `ArgumentPattern`, `FunctionPattern`) is not, a rule shipped by a package being fed the same way, and neither is `GlobalCalls`, which answers which global function a call calls and why that may be another one, nor `CodeWriter`, which writes code in the shape of the file (a class written the way the file reaches it, an import added the way the file writes its imports) for a rule of a package as for one of the core. Presets define style; DressCode has no style of its own. The five standards carry one: `dresscode/perCs` (PER Coding Style 3.1), `dresscode/psr12`, `dresscode/nette` (the Nette Coding Standard, PER with tabs, the layout of the Nette libraries and their departures, which the repository itself is checked with), `dresscode/symfony` and `dresscode/laravel` (the preset laravel of Laravel Pint). What a project wants beyond the looks of its code comes in a set, a preset of one intent that decides nothing about the layout and is used beside the standard (`modernizations`, `deprecations`, `cleanup`, `correctness`, `types`, `compilerOptimizations`); a decision the core gains goes into its set, one that writes a newer construct and the replacement of what PHP deprecated alike into every set whose intent it meets (`upgrading.syntax.firstClassCallables` in `modernizations` and `deprecations`), and what no set holds the project decides itself.

## Essential commands

Expand All @@ -22,6 +22,7 @@ Rules use only the public API of `PhpSyntax`; whatever a rule in DressCode needs
- `composer reference`: regenerates `docs/reference/decisions.md` (every decision with its values, the values of the standards and the rules declaring it) from the catalogue. Commit the output; CI diffs it.
- `composer api`: regenerates `docs/reference/api.md`, the public classes and members as reflection sees them. Commit the output and read its diff, which is what a change does to the public surface; CI diffs it.
- `php bin/dresscode check`: DressCode over its own sources with `dresscode.neon`; CI runs it too.
- `php bin/dresscode init` in the root of another project: writes the `dresscode.neon` measured from its code, never over a configuration that exists; how it measures is in `docs/internals.md`.

## Conventions

Expand Down Expand Up @@ -58,7 +59,7 @@ Rules use only the public API of `PhpSyntax`; whatever a rule in DressCode needs
- A qualification writes a name imported or qualified only where that has an effect, so `function.optimized` takes a function only in a namespace where PHP optimizes one of its calls, the other functions there being decided by `function.global`.
- Comments only where the code itself is not enough; never restate what the code shows; density follows the surrounding file. No phpDoc for what the types already say.
- Code in a doc comment is written in backticks: a call, a variable or a property, an operator, a keyword, a literal, the case of an enum (`report()`, `$this`, `?->`, `static`, `'*'`, `Severity::Warning`). Doc comments are read as Markdown in an IDE, and what stands bare there is typeset as text: `->` becomes an arrow, `...` an ellipsis. Backticks mark the code itself, so what only resembles code stays bare, whatever the IDE makes of it: a keyword used as a word (a static method, a readonly class, but a `match` expression), `null`, `true` and `false` in "null when …", and the `=>` describing a map (path => its size).
- A framework or library is named in the core only where it is the subject of the code (its preset, a dependency, a file or key another tool defines). An example of a general mechanism uses invented names under the vendor `Acme`, in a domain of their own that mirrors no real API (`Acme\Shop\Order::STATUS_PAID`, `Acme\Mail\SmtpTransport`).
- A framework or library is named in the core only where it is the subject of the code (its preset, its interop, a dependency, a file or key another tool defines). An example of a general mechanism uses invented names under the vendor `Acme`, in a domain of their own that mirrors no real API (`Acme\Shop\Order::STATUS_PAID`, `Acme\Mail\SmtpTransport`).
- Code, comments, identifiers and messages in English.
- A string a `.neon` file has to quote is written in single quotes (`call: 'foo($a, $b)'`), double quotes only where it needs an escape or holds a single quote.

Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dresscode/dresscode",
"description": "👔 DressCode: checks and fixes PHP coding style, and upgrades code to newer PHP and to new versions of libraries. Built on a lossless syntax tree, so it changes only what it touches. PER Coding Style, PSR-12, Nette.",
"description": "👔 DressCode: checks and fixes PHP coding style, and upgrades code to newer PHP and to new versions of libraries. Built on a lossless syntax tree, so it changes only what it touches. PER Coding Style, PSR-12, Nette, Symfony and Laravel.",
"keywords": ["code style", "coding standard", "formatter", "fixer", "linter", "refactoring", "modernization", "upgrade", "deprecations", "psr-12", "per-cs", "php-cs-fixer", "phpcs", "rector"],
"homepage": "https://dresscode.run",
"license": "MIT",
Expand All @@ -13,7 +13,7 @@
"require": {
"php": "8.4 - 8.6",
"nette/command-line": "^2.0",
"nette/neon": "^3.4 || ^4.0",
"nette/neon": "^4.0",
"nette/schema": "^1.4 || ^2.0",
"nette/utils": "^4.1",
"phpsyntax/phpsyntax": "^1.0",
Expand Down Expand Up @@ -50,7 +50,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.3-dev"
"dev-master": "1.6-dev"
}
}
}
7 changes: 5 additions & 2 deletions docs/dresscode.annotated.neon
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
#
# The comment of a key says what its value means, then after "Also:" the other values it takes.

use: [perCs] # the conventions of PER Coding Style 3.1, which this file writes out. Also: psr12; nette; a list with sets: cleanup, correctness, modernizations, deprecations, types, compilerOptimizations
use: [perCs] # the conventions of PER Coding Style 3.1, which this file writes out. Also: psr12; nette; symfony; laravel; a list with sets: cleanup, correctness, modernizations, deprecations, types, compilerOptimizations

targets:
php: '8.2' # the oldest version the code runs on; quoted, NEON reads `8.10` as 8.1
Expand Down Expand Up @@ -472,7 +472,7 @@ hygiene: # what is there for nothing: a call
settype: forbidden # `settype($x, 'int');` is `$x = (int) $x;`. Also: keep
call_user_func: forbidden # `call_user_func($f, $x)` is `$f($x)`, `call_user_func_array()` alike; risky. Also: keep
call_user_funcMethods: no # parameter: also a method the callable names, `[$this, 'run']`. Also: yes
__invokeCall: forbidden # `$f->__invoke($x)` is `$f($x)`. Also: keep
__invokeCall: forbidden # `$f->__invoke($x)` is `$f($x)`, or the other way round for an object. Also: required; keep
dirnameOfFile: forbidden # `dirname(__FILE__)` is `__DIR__`. Also: keep
nestedDirname: forbidden # `dirname(dirname($p))` is `dirname($p, 2)`. Also: keep
strlenEmptyTest: forbidden # `strlen($s) === 0` is `$s === ''`; risky, with the types. Also: keep
Expand Down Expand Up @@ -580,3 +580,6 @@ upgrading: # what a newer PHP writes or retired
forbiddenMembers: {} # a member that may not be used
attributeForAnnotation: {} # an attribute written for an annotation; a namespace mask with `\*`
attributeForMember: {} # an attribute written for a member the class declares by convention
unseen: # what an upgrade cannot see, reported where it stands
classNameString: reported # the string `'Acme\Old'` naming a class the maps rename or forbid. Also: keep
dynamicAccess: reported # `$object->$method()`, `new $class`, `call_user_func($callback)`. Also: keep
Loading
Loading