Rector — enable instanceOf, earlyReturn and if. Each was measured at zero changes
across the analysed tree, so they cost nothing now and only hold the line on new
code. The counts for every set that stays off are recorded next to them, with the
reason, so the decision does not have to be re-derived: typeDeclarations(319) is
the native-type footgun TYPE_AUDIT.md tracks, typeDeclarationDocblocks(86) feeds
that same footgun one step removed because `make cs-fix` derives native types from
docblocks, codingStyle(255) rewrites the `use` imports check-procedural-use depends
on, naming(132) fights the $r-prefix convention and render()-by-name views, and
privatization reads as 0 only because there are almost no final classes yet.
Makefile — `make rector` now passes --clear-cache. Rector's cache key does not
track the rule set, so after editing the config a cached run prints "Rector is
done!" and a newly enabled set looks like a no-op. The measurements above first
came back all-zero for exactly this reason.
phpcs — silence the four sniffs that were still reporting. Every one of them was
checked for a fixer first (none calls addFixable), so `make cs-fix` could never
have cleared them; what was left was 769 warnings no tool can act on:
NoSilencedErrors(473) each @ is either deliberate or masking a real error, so
they need judging one at a time, with a test each
CyclomaticComplexity (201 err + 190 warn) and NestingLevel (2 + 84) — already
ratcheted per-function by the CRAP gate, which blocks NEW
complexity; the sniffs only restated the old backlog
PSR1.Files.SideEffects(22) needs files split, which moves loading and autoload
LineLength(3480) went the same way earlier and is marked as not part of the
ratchet: it has no fixer at all, so "re-enable and fix" would mean splitting those
lines by hand.
Own rules are commented out so they read as toggles; the PSR12-inherited ones
(SideEffects, LineLength) can only be <exclude>d, which the ratchet summary now
says out loud. That summary is updated with every new toggle and its count.
`make cs` is now clean, with 85 sniffs still active — checked, because an empty
report looks the same whether nothing is wrong or everything got muted.
Replace the `<severity>0</severity>` mute block with single-line toggles so
disabled sniffs can be re-enabled one at a time, per the technical-debt
ratchet. Two mechanisms, because phpcs `<exclude>` cannot be overridden by a
later ref:
- sniffs with their own `<rule ref>` are commented out in place (uncomment
to enable): CamelCapsFunctionName, MultiLineAssignment,
UnnecessaryStringConcat, SAPIUsage, AbstractClassNamePrefix,
InterfaceNameSuffix, TraitNameSuffix, DeprecatedFunctions, ClassFileName,
GlobalKeyword;
- sniffs pulled in by the PSR12 parent are `<exclude>`d inside it (delete the
exclude to enable): CamelCapsMethodName, FileHeader.IncorrectOrder,
MethodDeclaration.Underscore, SwitchDeclaration.TerminatingComment,
PSR1.Classes.ClassDeclaration, PropertyDeclaration.Underscore,
ValidClassName;
- CyclomaticComplexity/NestingLevel MaxExceeded are message-level excludes on
their own refs (TooHigh warning still reports).
ControlStructureSpacing and PEAR FunctionCallSignature (tab-incompatible) and
ParameterTypeHint (null-crash fixer footgun) are marked DO NOT ENABLE. Added a
ratchet legend documenting every toggle, its violation count, and a suggested
enable order. `make cs` stays green (0 errors) and `make cs-fix` converges;
both toggle directions verified.
`make cs-fix` reported FAILED TO FIX on 20+ files and exited non-zero.
Root cause: PSR12.ControlStructures.ControlStructureSpacing is a
space-alignment sniff (LineIndent expects "12 spaces" where the code has
3 tabs; FirstExpressionLine/CloseParenthesisLine force the expanded
layout). phpcbf inserts the spaces, Generic.WhiteSpace.DisallowSpaceIndent
strips them back, and the fixer never converges. It also contradicts the
project's K&R same-line multi-line-condition layout.
Retire that sniff (it cannot work as a fixer under tab indentation) and
enforce the same concern with its tab-compatible equivalent,
PEAR.ControlStructures.MultiLineCondition, which converges. Reformat the
22 affected files to the canonical PEAR layout (whitespace only —
token-identical to before). `make cs-fix` now converges and `make cs`
is green.
`make cs` fired 2321 errors across legacy code, so it could not act as a
gate. Set the ~20 error-level sniffs to severity 0 in a clearly-marked
TEMPORARY block (and ParameterTypeHint in its own block), and add
ignore_warnings_on_exit so advisory warnings (line length, cyclomatic
"too high", silenced errors) are still reported but do not fail the run.
`make cs` now exits 0.
These mutes are technical debt to unwind ONE sniff at a time (drop the
severity line, run make cs, fix, commit); no new violations should be
added under a muted rule. Muting ParameterTypeHint also stops cs-fix from
re-adding the null-crash param types (see TYPE_AUDIT.md). Biggest buckets:
ParameterTypeHint 1323, GlobalKeyword 452, camelCaps naming 231,
CyclomaticComplexity.MaxExceeded 174.
Reviewed the Generic.PHP.ForbiddenFunctions list against real usage: the
whole 20-function ban produced only two kinds of violations across src —
is_null (84) and extract (2); the other 18 entries have zero call sites
and stay as free guardrails.
- is_null: dropped from the ban. It is equivalent to `=== null` and the
prohibition was purely cosmetic; keeping it avoids churning 41 files
for no functional gain.
- extract: kept banned (it injects variables from array keys — a real
footgun), but the two legitimate uses in the view-render layer
(BaseAdminController, BasePlayerController) expose the payload to legacy
PHP templates and cannot be removed without rewriting every view, so
they are annotated with `// phpcs:ignore` and a rationale.
phpcs ForbiddenFunctions now reports 0 findings. 721 tests green.
Rework build/phpcs.xml.dist from strict PSR-12 to the project's actual
house style so 'make cs-fix' is idempotent against the codebase:
- K&R (one-true-brace) instead of Allman; enforce via
Generic.Classes.OpeningBraceSameLine +
Generic.Functions.OpeningFunctionBraceKernighanRitchie, and exclude the
PSR12/PEAR/Squiz messages that push the opening brace to a new line.
- Tab indentation instead of 4 spaces: DisallowSpaceIndent + ScopeIndent
(tabIndent), and disable the tab-incompatible alignment sniffs
(MultiLineCondition, FunctionCallSignature, ControlStructureSpacing) plus
ConcatenationSpacing newlines so phpcbf converges (0 FAILED TO FIX).
- Restrict to PHP only (extensions=php) so .js/.css are never touched.
- Drop Generic.Formatting.SpaceAfterNot and Generic.PHP.RequireStrictTypes
(the codebase does not use declare(strict_types)).
- Add SlevomatCodingStandard.TypeHints.ParameterTypeHint to catch
untyped parameters.
Update CONTRIBUTING.md to run 'make cs-fix' first and describe the style.
PHP-CS-Fixer's `no_unused_imports` is conservative — it treats a class name that
merely appears in a PHPDoc *description* as "used", so genuinely-dead imports
(e.g. `use ...Request;` next to a "Request IP" doc description) were never
flagged. Slevomat's UnusedUses is precise: it parses annotation *types*
(@param/@return/@var), so it keeps docblock-typed imports but removes truly
unused ones — matching what Intelephense (P1003) reports.
- Swap require-dev: friendsofphp/php-cs-fixer -> squizlabs/php_codesniffer +
slevomat/coding-standard (+ phpcodesniffer-composer-installer, allow-listed).
- New narrow ruleset build/phpcs.xml.dist (import/namespace hygiene only, NOT
full PSR-12): UnusedUses (searchAnnotations=true), UseFromSameNamespace,
UseDoesNotStartWithBackslash, AlphabeticallySortedUses, UseSpacing,
NamespaceSpacing. View templates stay excluded.
- Makefile: `make cs` -> phpcs, `make cs-fix` -> phpcbf (same target names).
- CI code-style job, CLAUDE.md, CONTRIBUTING.md, docs, .gitignore updated;
build/.php-cs-fixer.dist.php removed. Committed vendor stays production-only.