Přechod na DressCode krok za krokem
Komentáře potlačující chyby fungují v kódu dál i s původními názvy pravidel a celou konfiguraci převedete jediným příkazem, který vás přehledně upozorní na vše, co nebylo možné přenést automaticky. Zde najdete osvědčený postup migrace z PHP CS Fixeru, PHP_CodeSnifferu i Laravel Pintu – včetně otevřeného přehledu toho, co bude fungovat jinak.
Instalaci popisuje stránka Začínáme s DressCode a
kompletní výčet parametrů pro příkazy migrate, init a fix najdete v referenci příkazové řádky.
Proč to nebolí
Víme, jak to v praxi chodí: v projektu máte konfiguraci laděnou dlouhé roky, ve zdrojácích stovky komentářů
phpcs:ignore, možná pár vlastních sniffů a k tomu zaběhnuté CI, které to celé hlídá. Nikdo nemění
nástroj jen proto, že ten nový vypadá moderněji. Změna dává smysl teprve ve chvíli, kdy přechod nebolí – a přesně
na to byl DressCode od začátku navržen.
Zásadní výhoda spočívá v tom, že DressCode u každého svého rozhodnutí přesně ví, která pravidla PHP CS
Fixeru, PHP_CodeSnifferu a Slevomatu pokrývá. Když spustíte dresscode catalogue, uvidíte původní pravidla
vypsaná u každého rozhodnutí v závorce covers. Z toho plyne vše podstatné: stávající komentáře
v kódu fungují beze změny a starou konfiguraci zvládne nástroj přeložit strojově. Do nové konfigurace a na příkazovou
řádku už ale zapisujete čisté cesty rozhodnutí DressCode, například imports.unused.
Celý proces probíhá ve třech bezpečných krocích a každý z nich můžete kdykoliv vrátit zpět. Druhý a třetí krok
navíc plně automatizují dva podpříkazy z rodiny dresscode migrate.
1. Kód nechte, jak je
Komentáře // phpcs:ignore, phpcs:disable, phpcs:enable, phpcs:ignoreFile
i anotace @phpcsSuppress fungují i nadále. DressCode je při průchodu přečte, původní název pravidla si
sám přeloží na odpovídající rozhodnutí a potlačení normálně uplatní. Do zdrojového kódu tedy zatím nemusíte
vůbec sahat a první kontrolu můžete spustit okamžitě:
dresscode check src tests --use perCs
Je tu však jeden důležitý rozdíl v chování, o kterém musíte vědět: phpcs:ignore na samostatném
řádku potlačoval v PHP_CodeSnifferu pouze bezprostředně následující řádek. DressCode jej naproti tomu interpretuje
stejně jako vlastní direktivu dresscode:ignore – tedy tak, že na samostatném řádku platí pro celý
následující příkaz, i když je roztažený přes více řádků. Potlačení tak může v některých případech pokrýt
o něco větší kus kódu než dříve.
Konfigurační soubor v této fázi ještě nemáte, a protože DressCode bez zadání pravidel nic nekontroluje, určuje
standard parametr --use. Hodnota perCs znamená PER
Coding Style 3.1. Pokud jste dosud používali @PER-CS nebo PSR12, uvidíte zhruba to, na co jste
zvyklí. Pokud jste měli pravidla detailně přizpůsobená nad rámec standardu, narazíte na odchylky, které vyřeší hned
následující krok.
2. Přeložte konfiguraci
Příkaz migrate config automaticky vyhledá konfiguraci vašeho projektu (ať už patří PHP_CodeSnifferu, PHP
CS Fixeru, nebo Laravel Pintu) a její ekvivalent pro DressCode zapíše včetně kontrolovaných cest přímo do
dresscode.neon. Pokud přecházíte z prostředí bez konfigurace, nebo žádnou nemáte, nechte si ji raději
změřit přímo z existujícího kódu pomocí příkazu dresscode init:
dresscode migrate config
dresscode migrate config conf/phpcs.xml --output dresscode.php
Nástroj hledá konfigurační soubory na obvyklých místech a vybere ten, který projekt reálně používá. Postupuje
systematicky: nejprve prověří skripty v composer.json, zda některý z nich nespouští formátovač
s konkrétním souborem (např. phpcs --standard=conf/phpcs.xml, a to i vnořeně přes aliasy typu
@lint). Následně zkontroluje konfiguraci nástroje, jehož balíček projekt vyžaduje v závislostech, a nakonec
hledá soubory ležící přímo v kořeni projektu (lokální .php-cs-fixer.php před
.php-cs-fixer.dist.php). Pokud v kořeni objeví více kandidátů, v interaktivním terminálu se vás zeptá,
který z nich platí; mimo terminál zvolí první a ostatní vypíše. Cestu k jinému souboru můžete samozřejmě předat
přímo jako argument. Kam se má výsledek uložit, určí přepínač --output; formát výstupu se řídí
příponou .neon nebo .php a již existující soubor migrate config nikdy nepřepíše.
Před samotným zápisem si vygenerovaný soubor cvičně načte a ověří, že z něj vzejdou přeložená rozhodnutí;
s přepínačem --dry-run výsledek pouze vypíše do konzole. Soubor patří do kořene projektu, protože uvedené
cesty jsou relativní právě k tomuto kořeni.
Zatímco phpcs.xml je běžné XML a pint.json standardní JSON (oba formáty DressCode přečte bez
jakýchkoliv závislostí), se souborem .php-cs-fixer.dist.php je to jiné: jde o spustitelný PHP skript, který
vrací instanci cizí knihovny. Pokud na něj migrate config narazí sám, nejprve se v terminálu zeptá na
svolení ke spuštění; bez interaktivního terminálu jej z bezpečnostních důvodů nespustí vůbec a pouze vypíše
doporučený příkaz. Skript spouští v izolovaném procesu s autoloaderem projektu, takže vyžaduje, aby byl PHP CS Fixer
v projektu stále nainstalovaný (samotný DressCode přitom může běžet odkudkoliv, viz podrobnosti na stránce PHP CS Fixer). Z toho důvodu překládejte konfiguraci
dříve, než starý nástroj z projektu odinstalujete.
Z konfigurace phpcs.xml, která kontroluje složky src a tests, vychází z
PSR12, omezuje délku řádku na 100 znaků, hlídá nepoužité importy a vyžaduje dokumentaci funkcí, vznikne
tento čistý dresscode.neon:
use: psr12
paths: [src, tests]
file:
lineLength: 100
lineLengthExcept: [imports]
longLines: forbidden
imports:
unused: forbidden # `SlevomatCodingStandard.Namespaces.UnusedUses` without `searchAnnotations` removes an import only a doc comment uses; DressCode keeps it.
Příkaz vypíše, jakou konfiguraci našel a kam uložil výsledek, a pod tím zobrazí souhrn včetně věcí, které se nepodařilo přenést:
Found `phpcs.xml` of PHP_CodeSniffer. Written `dresscode.neon`. Read 4 rules; set 4 decisions and 1 preset, the rules translated; `dresscode init` measures the code instead. `SlevomatCodingStandard.Namespaces.UnusedUses` without `searchAnnotations` removes an import only a doc comment uses; DressCode keeps it. No DressCode rule covers `Squiz.Commenting.FunctionComment`. 5 phpcs suppression comments: `dresscode migrate suppressions` rewrites them.
Tyto řádky mají stejnou cenu jako vygenerovaná konfigurace: přesně pojmenovávají místa, kde se musíte rozhodnout
sami. Zde vás například informují, že DressCode zachová import využívaný pouze v phpDoc komentáři, zatímco původní
sniff bez volby searchAnnotations by jej smazal. Varování k pravidlu, které se podařilo přeložit jen
částečně, se navíc uloží přímo jako komentář k danému rozhodnutí v souboru, aby nezmizelo se zavřením okna
terminálu. Pokud příkaz vypíše jakékoliv varování, skončí s exit kódem 1, i když soubor v pořádku zapsal.
Pravidlo bez přímého protějšku buď vůbec nepotřebujete, nebo si na něj snadno napíšete vlastní kontrolu. Obecné sady jako @PSR12 nebo
PSR12 se překládají na standardy; u sad bez jednoznačného protějšku (např. @PhpCsFixer) na to
příkaz upozorní a doporučí začít od standardu perCs či psr12. Výpis zároveň připomene druhou
cestu: dresscode init pravidla nepřekládá, ale změří reálný styl vašeho kódu. S přepínačem
--format json získáte strukturovaný dokument obsahující verzi, kořenový adresář, přehled varování a
nepřeložená pravidla – v tomto režimu se nástroj na nic neptá, stejně jako mimo terminál.
Jedno původní pravidlo se v DressCode často rozpadne do několika přesných rozhodnutí: například
Generic.Files.LineLength s volbou absoluteLineLimit vygeneruje rozhodnutí file.longLines,
file.lineLength a file.lineLengthExcept. Ne každá specifická volba má v DressCode svůj
protějšek, což výpis opět transparentně přizná. Pravidlo, které bylo v původním nástroji výslovně vypnuté, získá
hodnotu keep. Pokud však stejné rozhodnutí pokrývá i jiné pravidlo daného nástroje, nechá jej příkaz
netknuté a vypíše varování, protože nemůže vědět, které z nich má mít přednost. Výsledný soubor si projděte –
bývá příjemně krátký.
3. Přepište komentáře
Komentáře v kódu sice fungují i se starými názvy, ale nativní cesty rozhodnutí jsou kratší, jednotné a mnohem
čitelnější. Kolik takových komentářů v projektu máte, vyčíslil už předchozí příkaz migrate config.
Samotný přepis obstará jediný příkaz:
dresscode migrate suppressions src tests
Zápis phpcs:ignore SlevomatCodingStandard.Namespaces.UnusedUses se promění na
dresscode:ignore imports.unused, z phpcs:disable a phpcs:enable vzniknou
dresscode:disable a dresscode:enable a ze souborového phpcs:ignoreFile se stane
dresscode:ignoreFile. Anotace @phpcsSuppress zůstane zachována a změní se v ní pouze název
pravidla. Pokud příkaz narazí na phpcs:ignore na samostatném řádku, připomene rozdíl v rozsahu platnosti na řádku Widened a vypíše konkrétní místa,
která stojí za kontrolu. Názvy, které žádné rozhodnutí nepokrývá, ponechá beze změny a vypíše je na řádku
Unknown:
Migrated 5 suppressions in 1 file. Unknown `Acme.Custom.Sniff`, kept as written, as no decision answers to it: src/Legacy.php:12 Widened src/Legacy.php:10: a `dresscode:ignore` on a line of its own covers the whole statement below it, not just the next line
Získáte tak jasný přehled neplatných komentářů, které reálně nic nepotlačují, protože DressCode pravidlo
s takovým jménem nezná. Jelikož tyto komentáře nebylo možné přenést, skončí příkaz s exit kódem 2. Pokud se
objeví pouze rozšířená potlačení z řádku Widened, vrátí příkaz exit kód 1, protože tato místa pouze
čekají na vaši zběžnou revizi.
Přepisují se výhradně skutečné direktivy – tedy takové, za kterými bezprostředně končí komentář nebo řádek.
Pouze takto totiž DressCode potlačení čte; věta v komentáři, která phpcs:ignore pouze zmiňuje v textu,
zůstane nedotčena. Soubor, který nelze naparsovat nebo který se během práce změnil, příkaz přeskočí, uvede jej na
řádku Skipped a skončí s kódem 2. S volbou --dry-run ukáže všechny plánované změny
v podobě diffu a nezapíše nic. S přepínačem --format json vydá kompletní data pro další strojové
zpracování.
První oprava
Jakmile máte konfiguraci vyladěnou, spusťte dresscode fix a výsledek uložte do samostatného commitu bez
jakýchkoliv jiných úprav. I u pravidel, která dělají „totéž“, se může formátování v detailech lišit o mezeru
oproti starému nástroji. Nemá smysl procházet tisíce řádků ručně – zásadní je mít jistotu, že daný commit
neobsahuje žádnou věcnou změnu logiky. U rozsáhlejších projektů, kde by byl jeden obří commit nepřehledný, postupujte
po jednotlivých sadách či sekcích: spusťte dresscode fix --only cleanup nebo
dresscode fix --only blankLines a každý krok commitněte zvlášť. Závěrečný plný fix pak
doladí zbytek (více v kapitole Uklidit starý
kód po krocích). Problémy, které fix neumí vyřešit sám, zůstanou ve výpisu. Pokud je jich více, než
kolik stíháte vyřešit hned, příkaz dresscode baseline je uloží do baseline a od té chvíle se budou hlásit už jen
nově vzniklé chyby.
Co bude jinak
Návod, který slibuje stoprocentně bezbolestný přechod bez jakýchkoliv odlišností, ztratí vaši důvěru při prvním zádrhelu. Pojďme si proto otevřeně říct, v čem se věci liší:
- Rozhodnutí namísto stovek pravidel. V konfiguraci nevyjmenováváte konkrétní pravidla ani jejich interní volby,
ale přímo popisujete požadovaný stav:
blankLines.aroundMethods: 1neboimports.unused: forbidden. Která konkrétní pravidla se o to postarají, necháváte na DressCode (viz Rozhodnutí místo pravidel). Kompletní přehled všech možností nabízí stránka Pravidla. - Priority neexistují. Pokud jste byli zvyklí spoléhat na ručně laděné pořadí fixerů, můžete u některých specifických souborů získat trochu jiný výsledný kód. Pravidla v DressCode běží opakovaně, dokud se kód zcela neustálí; důvody vysvětluje kapitola Bez priorit: pravidla běží do ustálení.
- Některá pravidla nemají protějšek. Příkazy
migrate configimigrate suppressionsvás na ně přímo upozorní. Jde především o detailní sniffy kontrolující obsah dokumentačních komentářů nebo pravidla, která v původních nástrojích řešila vzájemné překryvy. - Riziková pravidla zde nemusí být riziková. To, co PHP CS Fixer zapínal pouze na výslovné vyžádání (protože
nad pouhými tokeny nedokázal odlišit bezpečný případ od nebezpečného), řeší DressCode na úrovni syntaktického stromu
a bezpečné výskyty opraví automaticky. Zásah, který by mohl změnit chování, se nahlásí jako riziková oprava (risky fix) a provede se teprve po schválení
v klíči
fixRiskynebo s parametrem--fix-risky. A rozhodnutí, které mění chování z podstaty věci (expressions.comparison.loose: forbiddennahrazující==za===), zůstává plně ve vaší režii. - Návratové kódy mají jasnou stupnici:
0znamená čisto,1nálezy k řešení,2selhání na některém souboru a3chybu v konfiguraci či parametrech. Jednotnou škálu popisuje Příkazová řádka. PHP_CodeSniffer i PHP CS Fixer používají vlastní škály, takže případné vyhodnocovací skripty v CI budou vyžadovat drobnou úpravu. - Jeden nástroj místo kombinace dvou. Mnoho projektů dříve kombinovalo CodeSniffer na kontrolu a Fixer na opravy, což znamenalo dvě konfigurace a dva kroky v CI. DressCode obojí sjednocuje pod jednu střechu.
- Rozhodnutí si vyzkoušíte bez zásahu do konfigurace. Příkaz
dresscode check --set controlFlow.elseAfterReturn=forbidden --only controlFlow.elseAfterReturn --diffukáže dopad konkrétního pravidla v diffu, adresscode fix --set …jej dočasně přidá ke zbytku konfigurace, takže změny vyjdou rovnou zformátované podle vašeho standardu (viz Jedno rozhodnutí na jeden běh). - Popisujete záměr, ne výčet pravidel. Konfigurace stojí na dvou pilířích: standard určí vzhled a sady definují, co dalšího od kódu očekáváte. Sad je
přesně šest a tvoří uzavřený výčet, takže z položky
cleanupv klíčiusese nikdy nestane dvacet nepřehledných řádků, i když katalog časem přinese desítky nových kontrol. - Modernizaci kódu obstará stejný nástroj. DressCode umí kód nejen formátovat, ale i automaticky přepsat na novější PHP či nová API vašich knihoven – a to v tomtéž běhu a podle stejného standardu. Na upgrade kódu tak nepotřebujete další externí nástroj s vlastní konfigurací (viz Přechod na novější PHP a Upgrade kódu).
Podrobné návody pro jednotlivé nástroje: PHP CS Fixer, PHP_CodeSniffer a Slevomat, Nette Coding Standard, Laravel Pint.