PHP 8.6 vychází 19. listopadu. DressCode pro něj kód přepisuje už teď. 16 dní Přechod na novější PHP →

Potlačení a baseline

Občas má kód dobrý důvod rozhodnutí nedodržet a občas nález dnes opravit neumíte. Na první případ je potlačení: umlčí rozhodnutí na řádku, v bloku, v souboru nebo pro celou cestu a zastaví i jeho opravu. Na druhý baseline: rozhodnutí zapnete hned, i když ho váš kód zatím nesplňuje.

Čtyři úrovně

Výjimky z pravidel osobně nemám rád a do kódu je nepíšu; když už, tak v konfiguraci pro celou cestu. Jsou ale místa, kde rozhodnutí prostě nemá pravdu, a tam se hodí přesný nástroj, ne kladivo. Potlačení (anglicky suppression) má čtyři úrovně, od nejužší po nejširší:

úroveň jak kde
jeden řádek nebo příkaz // dresscode:ignore v kódu
blok dresscode:disable a dresscode:enable v kódu
celý soubor dresscode:ignoreFile v kódu
cesta keep v přepisu, excludePaths v konfiguraci

Vedle nich stojí baseline, která není výjimka z rozhodnutí, ale z času: zapíše porušení, která dnes opravit neumíte, a hlásit se pak budou jen nová.

Ať zvolíte kteroukoli úroveň, platí jedna věc: potlačené porušení se neopraví. Pravidlo smí kód změnit jen poté, co porušení ohlásilo a hlášení nebylo potlačené, a hlídá to jádro nástroje, ne autor pravidla. Potlačení tedy není jen ticho ve výpisu, ale skutečné vypnutí. Baseline je jiný případ a opravě nebrání nikdy, jak popisuje její oddíl.

Potlačení na řádku

Komentář dresscode:ignore na konci řádku potlačí porušení na tomto řádku. Bez jména potlačí všechno, s cestou rozhodnutí jen to jedno; víc cest se odděluje čárkou:

$isEmpty = $value == null; // dresscode:ignore expressions.comparison.loose

Místo celé cesty stačí i sekce, která platí pro všechna rozhodnutí pod sebou: // dresscode:ignore spacing umlčí na řádku všechny mezery.

Komentář na vlastním řádku platí pro to, co začíná na řádku pod ním, i když se to táhne přes několik řádků. Může to být příkaz nebo člen třídy, ale i jediná položka pole nebo argument: komentář nad první položkou pole kryje jen ji, ne celé pole. Tady kryje celý příkaz:

// dresscode:ignore multiline.edges.array, multiline.trailingComma.array
$matrix = [[1, 0, 0],
	[0, 1, 0],
	[0, 0, 1]];

Proč výjimka platí, napište za dvě pomlčky. Text za nimi DressCode nečte, je pro toho, kdo kód otevře po vás, a stejný zápis používá ESLint i PHP_CodeSniffer:

// dresscode:ignore upgrading.functions.arraySearchFunctions -- a loop is faster than array_any() on this hot path
foreach ($kinds as $kind) {
	if ($this->kind === $kind) {
		return true;
	}
}

Funguje //, # i /* */. Cestu opíšete z výpisu, kde stojí u každého nálezu. Místo ní DressCode přijme i jméno pravidla PHP CS Fixeru nebo PHP_CodeSnifferu, které jeho rozhodnutí pokrývá, a rozumí komentářům phpcs:ignore, phpcs:disable, phpcs:enable, phpcs:ignoreFile a anotaci @phpcsSuppress, takže kdo přechází z jiného nástroje, nemusí do kódu vůbec sáhnout. Na cesty DressCode je pak přepíše dresscode migrate suppressions; stejně přepíše i komentář se jménem pravidla ze starší verze DressCode.

Jméno, které žádnému rozhodnutí neodpovídá, nic nepotlačí, a běh na to upozorní, aby v kódu nezůstal komentář, který jen vypadá, že něco dělá:

Comment `dresscode:ignore noIsNull` names `noIsNull`, which is no decision, section or rule, so it silences nothing.

Má-li projekt už vlastní komentář, kterým označuje záměr, třeba // intentionally == u porovnání, které se nemá zpřísnit, nemusíte ho přepisovat. Klíč suppressionComments přiřadí ke vzoru komentáře rozhodnutí, která na jeho řádku mlčí, jako by tam stálo dresscode:ignore:

suppressionComments:
	'~\bintentionally\s+[!=]=~': expressions.comparison.loose

Vzor je regulární výraz i s oddělovači a porovnává se s textem jednoho komentáře, takže ^ znamená jeho začátek. Hodnotou je cesta, sekce nebo jejich seznam. Komentář na konci řádku platí pro řádek, komentář na vlastním řádku pro příkaz pod ním. Standard nette takovou mapu nese: // intentionally == umlčí expressions.comparison.loose a // @ umlčí mistakes.errorSuppression.

Položka může nést i ukázku komentáře, který vzoru odpovídá, a pak se zapisuje jako mapa:

suppressionComments:
	'~^//\s*@\s+\S~':
		decisions: mistakes.errorSuppression
		example: '// @ <why>'

Ukázku DressCode ověří: místo <why> dosadí slovo a komentář musí vzoru odpovídat. Formát agent ji nabídne agentovi u porušení toho rozhodnutí jako způsob, jak doloženou výjimku zachovat, se skutečným důvodem místo <why>; dresscode config ji vypíše u vzoru. Vyšší vrstva, která napíše stejný vzor, nahradí celou položku i s ukázkou.

Potlačení v bloku

// dresscode:disable file.longLines
$expired = 'Your subscription has expired. Renew it in the account settings to keep access to every premium feature of the shop.';
$declined = 'Your card was declined by the bank. Check the card details or choose another payment method to complete the order.';
// dresscode:enable

disable bez jména vypne všechno až po enable; enable bez jména zapne všechno, co disable vypnul. Bez enable platí disable do konce souboru.

Potlačení v celém souboru

<?php // dresscode:ignoreFile

Tenhle komentář kdekoli v souboru vypne pro celý soubor všechno. Hodí se pro generovaný kód, který leží mezi ručně psaným. Když je takových souborů víc, je čistší vyloučit je cestou nebo podle obsahu, viz skipWhen v konfiguraci.

Vypnutí pro cestu

Výjimka, která platí pro celý adresář, patří do konfigurace, ne do stovky souborů. Rozhodnutí pro takovou cestu vypne přepis s hodnotou keep:

overrides:
	-
		paths: [legacy]
		expressions:
			comparison: keep
	-
		paths: [tests/fixtures]
		file:
			longLines: keep

Zbytek konfigurace takový soubor zkontroluje normálně. Celé cesty vynechá excludePaths; obojí popisuje stránka Konfigurace.

Často ale rozhodnutí pro tu cestu nemá zmizet, jen platit s jinou hodnotou: testy mají jiný odstup metod než zdrojový kód, ne žádný. I to umí přepis, který souborům odpovídajícím cestě dá vlastní hodnoty:

overrides:
	-
		paths: [tests/**.phpt]
		blankLines:
			aroundMethods: 2

Baseline pro to, co zatím opravit nejde

Nový standard nasadíte jedním commitem: dresscode fix opraví, co opravit jde, a výsledek commitnete samostatně, jak popisuje Formátování kódu krok za krokem. Po takovém commitu ale může zůstat něco, co za vás žádný nástroj neudělá. Rozhodnutí types.declaration.parameter: required chce u každého parametru typ a vy máte tři sta funkcí bez něj. U každé je potřeba rozmyslet, jaký typ tam patří, a to jsou týdny práce. Dokud rozhodnutí nezapnete, přibývají s každým commitem další.

Právě na tohle je baseline: soupis porušení, která dnes opravit neumíte. Rozhodnutí díky ní zapnete hned, stará porušení počkají, až na ně dojde, a každé nové se ohlásí. Totéž platí pro rizikovou opravu (risky fix), kterou projekt zatím nepřijal, nebo pro řádek, který je delší, než file.lineLength dovolí, a DressCode ho za vás zalomit neumí.

U statické analýzy je baseline samozřejmost, PHPStan ji má léta. PHP CS Fixer ani PHP_CodeSniffer ji ale nemají a žádost o ni se u obou zadrhla na téže otázce: co s ní má dělat automatická oprava? DressCode odpovídá jednoduše: baseline obsahuje jen to, co oprava nechá. Co opravit jde, se opraví, a baseline hlídá zbytek.

Proto ji příkaz baseline zapisuje až pro kód, na kterém už fix nemá co dělat. Oba příkazy vezmou cesty z klíče paths v konfiguraci; můžete je také vypsat za příkaz:

dresscode fix
dresscode baseline
Baseline with 312 violations written to `dresscode-baseline.neon`.

Na pořadí záleží. Kdyby fix ještě něco změnil, baseline nic nezapíše, vyjmenuje soubory, které by se změnily, a skončí s exit kódem 1; do baseline nepatří nic, co jde opravit:

Error: The baseline is generated over code a fix leaves alone: a fix would change 3 files. Run `fix` first.
  src/Cart.php
  src/Order.php
  tests/CartTest.php
See https://dresscode.run/suppressing#baseline

Rizikové opravy, které mění chování kódu a které projekt nepřijal v klíči fixRisky, do baseline patří, protože je nástroj bez vašeho svolení neudělá. Rizikové opravy, o kterých by rozhodly typy nebo nameResolution, do ní nepatří: jsou to jen varování a zmizí, jakmile DressCode dodáte, co mu chybí. Přepínač --only příkaz baseline nemá, protože baseline vzniká z celé konfigurace, ne z několika rozhodnutí.

Soubor vznikne vedle konfigurace a ve stejném formátu (.neon vedle dresscode.neon, .php vedle dresscode.php) a platí hned: pod tímhle jménem ho DressCode najde sám a hlavička výpisu check ho ukáže na řádku Baseline. Jiné jméno nebo umístění nastavíte klíčem baseline. Takový soubor pak musí existovat, jinak check skončí chybou konfigurace, aby překlep nebo smazaný soubor potichu nevypnuly baseline:

baseline: qa/dresscode-baseline.neon

Uvnitř je verze formátu a pro každý soubor seznam porušení s cestou rozhodnutí, zprávou a otiskem:

version: 2
files:
	src/Order.php:
		-
			decision: naming.method
			message: The method `get_total` must be written in camelCase.
			fingerprint: ea4c48c2e21d2b35

Otisk se počítá z rozhodnutí, obsahu řádku a pořadí porušení toho rozhodnutí na řádku, ne z čísla řádku, takže baseline přežije úpravy jinde v souboru. Zpráva v něm není: v položce stojí pro lidi, aby bylo vidět, o co jde, a když ji nová verze DressCode napíše jinak, baseline porušení pozná dál. Porušení, která baseline zná, se nehlásí, a shrnutí běhu je přizná, aby nikdo nechválil císařovy nové šaty:

OK  312 violations in the baseline in 214 files

Baseline nikdy nebrání opravě. Komentář dresscode:ignore říká „tady rozhodnutí nemá pravdu“, a proto zastaví i opravu. Baseline říká „tohle zatím neumíme“, a co už umíme, není důvod nechávat. Když se porušení z baseline stane opravitelným, třeba proto, že projekt přijal rizikové opravy daného rozhodnutí, fix ho opraví jako každé jiné.

Když nějaké porušení z baseline zmizí, protože ho opravil někdo ručně nebo fix, jakmile to šlo, běh upozorní, že položka už ničemu neodpovídá (1 entry of the baseline no longer matches a violation; regenerate it with `dresscode baseline`.). Pusťte fix a baseline vygenerujte znovu. Běh přitom upozorní jen na položky souborů, které zpracoval, a při --only jen na položky rozhodnutí, která běžela, takže kontrola části projektu nehlásí jako zastaralé to, na co se neptala. Baseline se má zmenšovat, a jakmile je prázdná, smažte řádek z konfigurace a soubor s ním.

Baseline z doby, kdy DressCode hlásil po pravidlech, nová verze nepřečte: otisky se počítají jinak, a proto ji odmítne s radou, ať ji vygenerujete znovu. Stačí pustit fix a dresscode baseline.

Zobrazit na GitHubu · Nahlásit problém se stránkou na GitHubu · 2. 11. 2026