Konfigurace naměřená z kódu
Svou první konfiguraci nemusíte psát ručně z hlavy: příkaz dresscode init spuštěný
v kořeni projektu detailně zanalyzuje váš existující kód a ušije vám dresscode.neon přesně na míru. Nic
netipuje naslepo. Hodnotu každého jednotlivého rozhodnutí měří přímo tím pravidlem, které jej později bude
kontrolovat, a přesně počítá místa, která by pravidlo nahlásilo. To, co nástroj naměří, se proto nikdy nemůže
rozejít s tím, co následně kontroluje.
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
Nejprve init vyhledá soubory projektu: projde vše, co sleduje Git, a v projektu bez Gitu vyjde z autoload cest
v composer.json společně se složkami src, tests, test, app,
lib, bin, cron a www*, které v kořeni reálně existují. Zcela stranou
ponechá testovací data (adresáře fixtures, Fixtures a expected), soubory označené
v hlavičce či v .gitattributes jako strojově generované (linguist-generated), testy ve formátu
PHPT, soubory ignorované Gitem a soubory se syntaktickou chybou, které nelze naparsovat – nic z toho totiž nikdo nepíše
ručně. Každou vynechanou cestu zapíše do konfiguračního klíče excludePaths i s přesným odůvodněním,
aby ji automaticky ignorovala i každá další kontrola (řádek Excluded je přehledně vypíše). Soubory, které
Git zatím nesleduje, ale ani neignoruje, init neměří; řádek not measured je však spočítá,
protože první automatický fix je zpracuje také.
Následně nástroj vybere nejvhodnější standard: cvičně zkontroluje kód podle každého z nich a zvolí ten, se kterým
by automatická oprava změnila nejmenší počet souborů (jak potvrzuje řádek Standard). Počítají se vždy
celé soubory, nikoliv dílčí chyby, protože odchylka napříč celým projektem má větší váhu než desítky chyb na
jediném místě. Pokud jsou dva standardy v těsném souboji (s rozdílem do deseti procent), rozhodne standard uvedený
v konfiguraci stávajícího formátovače projektu. Samotný framework, na kterém aplikace staví, nerozhoduje: aplikace
v Nette či Symfony jejich oficiální styl dodržovat nemusí. Pokud formátovač žádný standard nejmenuje, vybere nástroj
z blízkých kandidátů standard perCs (pokud je mezi nimi) a v interaktivním terminálu se vás zeptá, zda
nechcete raději jiný; pokud zvolíte alternativu, proměří kód podle ní. Mimo interaktivní terminál nahradí otázku
řádek Your call s hotovým příkazem pro měření podle druhého standardu. Starší standard psr12
ustoupí svému nástupci perCs, pokud jej formátovač výslovně nejmenuje a oprava podle perCs
nezmění o více než polovinu souborů navíc: PSR-12 totiž o modernějších prvcích PHP vůbec nemluví, a kód psaný
podle PER Coding Style 3.1 se proto od PSR-12 formálně liší
v menším počtu rozhodnutí. Projekt formátovaný nástrojem Laravel Pint automaticky získá standard svého presetu a
standard zadaný přepínačem --use se použije přesně tak, jak jej zadáte. Sady přidané dalšími volbami
--use zapíše nástroj přímo za standard, takže příkaz
dresscode init --use nette --use modernizations promítne do klíče use obě jména.
V dalším kroku init detailně proměří rozhodnutí, ve kterých se kód od vybraného standardu
odchyluje – každé samostatně a se všemi jeho povolenými hodnotami. Odsazení, maximální délku řádku a zakončení
řádků měří přednostně, protože na nich stojí všechna ostatní pravidla. Menší projekty do tisíce souborů a 8 MB
proměří kompletně celé, u větších projektů sáhne po reprezentativním vzorku rozděleném rovným dílem mezi
jednotlivé adresáře, aby velká složka nepřehlušila zbytek kódu (kolik souborů který adresář poskytl, vyčíslí
řádek Sample). Hodnotu, na které panuje shoda na alespoň 70 % míst, zapíše do konfigurace i s přesným
podkladem (your code, 92% of 13 files). Kde kód stejnou věc zapisuje několika různými způsoby a žádný
z nich jasně nepřevládá, zapíše hodnotu keep („nekontrolovat“) s komentářem not checked: a
s výčtem nalezených variant; kolik takových nerozhodnutých pravidel v projektu zůstalo, shrnuje řádek
Unchecked.
Pokud některý adresář vykazuje odlišné odsazení nebo specifická souborová pravidla ze sekce file.* oproti
zbytku projektu (alespoň v pěti souborech a v devíti desetinách z nich), upozorní na to init na řádku
Differs a navrhne přesný přepis konfigurace, díky kterému by si
složka svůj styl ponechala. Do souboru jej však automaticky nezapíše: init navrhuje jednotný styl pro celý
projekt a vytvoření lokální výjimky nechává plně na vašem rozhodnutí:
Differs tests: indentation.unit 4 spaces in all 6 files; an override for `tests` would keep it, none is written
Na závěr init prověří celou navrženou konfiguraci nad všemi soubory projektu. První automatická oprava po
spuštění init rozhodně nemá přepsat půlku repozitáře, a proto nástroj striktně hlídá rozpočet změn:
oprava smí ve výchozím stavu změnit nanejvýš 1 % souborů (minimálně 2 a maximálně 20; jiný limit určí přepínač
--budget). Kdyby měly navržené opravy tento rozpočet překročit, nechá init některá rozhodnutí
zatím neaktivní, dokud se zbytek do limitu nevejde, a v interaktivním průvodci se vás na ně přímo zeptá i s ukázkou
konkrétního místa v kódu. Odsazení, šířku řádku a konce řádků neodloží nikdy, protože na nich závisejí všechna
ostatní pravidla; soubory, které je mají rozbité, opraví fix bez ohledu na rozpočet a řádek
Checked je vyčísluje odděleně. Do limitu se nezapočítávají ani vaše přímé odpovědi v průvodci.
Nálezy, které žádné pravidlo neumí opravit automaticky, patří do baseline – jejich počet uvádí
tentýž řádek:
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]
Odpovídáte jednoduše zadáním čísla volby. Doporučená odpověď je vždy na prvním místě (s číslem 1) a stačí
ji potvrdit klávesou Enter; ponechává kód v původním stavu, takže ani při nejistotě nic nepokazíte. U variant, které
kód upravují, rovnou vidíte přesný počet souborů, které by oprava přepsala. Pokud vaše odpovědi konfiguraci pozmění,
init ji okamžitě znovu otestuje, aby řádek Checked odpovídal vašim volbám. Pokud by kvůli tomu
rozpočet musel odložit jiné pravidlo, zeptá se na něj v dalším kole a upozorní, že jde o důsledek vašich
předchozích odpovědí. Otázky jsou přehledně číslované (např. (2/3)), abyste měli přehled, kolik
voleb zbývá.
Na co přesně se init ptá? Především na rozhodnutí odložená rozpočtem a na volbu mezi těsnými
standardy. Dále na hodnoty, které přímo odporují existující konfiguraci původního formátovače, a na identifikátory se
společnou předponou či příponou, které neodpovídají standardu (např. třídy končící podtržítkem); ty lze totiž
hromadně povolit vzorem v naming.except.
Průvodce položí nanejvýš pět otázek, přičemž ty s největším dopadem řeší jako první.
Zda převzít standard v jeho čisté podobě bez úprav, na to se init neptá – na řádku
Measured vám pouze ukáže, kolik souborů by to stálo: v naší ukázce by čisté nette přepsalo
všech 13 souborů, a to především kvůli jedinému pravidlu, protože váš kód umisťuje
declare(strict_types=1) jinam než Nette. Takový rozdíl bývá stopou standardu, který se mezitím posunul.
Chcete-li jít s ním, stačí z dresscode.neon smazat řádek file.strictTypes.position a spustit
fix; jinak zůstane v platnosti hodnota vašeho kódu, kterou init zapsal.
Mimo interaktivní terminál, s přepínačem --yes nebo s volbou --format json se
init na nic nedotazuje a automaticky potvrdí doporučené odpovědi (výsledkem je tedy naprosto stejný soubor,
jako byste na vše odpověděli Enterem). Každou nezodpovězenou otázku pak vypíše na řádku Your call
s hotovým zápisem pro dresscode.neon nebo s příkazem, kterým můžete volbu změnit:
Your call `literals.quotes: single` in `dresscode.neon` checks it too; its fix changes 3 files
Konkrétní volbu můžete zadat i předem: přepínač dresscode init --set literals.quotes=single zapíše
danou hodnotu bez ohledu na to, co převažuje v kódu, a rozpočet ji nikdy neodloží. Nástroj platnost cesty i hodnoty
ověří v katalogu ještě před zahájením měření, takže se o případném překlepu dozvíte okamžitě.
S přepínačem --dry-run vypíše init navrženou konfiguraci do konzole a na disk nic nezapíše.
Volba --jobs omezí počet procesů pro paralelní měření (--jobs 1 měří v jediném procesu).
S přepínačem --format json vygeneruje namísto textu strukturovaný dokument pro externí nástroje či AI
agenty: verzi formátu version, kořen projektu root, rozsah scope, vzorek
sample, zvolený standard standard i s počty souborů pro ostatní standardy, naměřené hodnoty
values s komentáři a původem, odchylující se složky regions, ověření všech souborů
verification, instrukce z řádků Your call jako instructions, soubory s chybujícími
pravidly failed, navrženou konfiguraci configuration a cestu k zapsanému souboru
written; u existující konfigurace navíc v poli shifts popíše, co se od minula posunulo.
Výsledný soubor má u každé hodnoty přehledně zdokumentováno, o co se opírá:
# 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
Kromě formátovacích pravidel init zmapuje i deklarace v jmenných prostorech. Zde nerozhoduje většina, ale
absolutní úplnost. Projde proto všechny soubory, detekuje funkce a konstanty deklarované v namespaces, zapíše jejich
seznamy do klíče namespaces a doplní direktivu nameResolution: certain (význam těchto klíčů
podrobně rozebírá stránka Funkce a konstanty ve jmenných
prostorech). Pokud v projektu objeví balíček phpstan/phpstan, zapíše i direktivu typeAnalysis: phpstan s vysvětlujícím komentářem.
Klíč fixRisky nástroj nezapíše nikdy: souhlas s rizikovými opravami, které mohou změnit chování programu,
totiž z kódu naměřit nelze.
Pokud projekt obsahuje konfiguraci PHP_CodeSnifferu, PHP CS Fixeru nebo Laravel Pintu, init si ji načte, ale bez
vašeho svolení ji nikdy nespustí. Konfiguraci vybere stejným postupem jako migrate config (tedy tu, kterou projekt reálně
spouští), a pokud to nelze jednoznačně určit, v terminálu se zeptá. Převezme z ní sledované cesty, vynechané soubory
a pravidla vypnutá pouze v části projektu, pro která připraví přepis. Pravidla původního formátovače
porovná s reálným stavem kódu, a pokud si vzájemně odporují, zeptá se, kterému nastavení dát přednost. Konfigurace
PHP CS Fixeru je však spustitelný PHP kód: cesty pro Finder sice init z kódu staticky vyčte (a pod řádkem
Formatter uvede ty, které přečíst nešlo), ale konkrétní pravidla zjistí pouze spuštěním skriptu –
v terminálu se proto nejprve výslovně zeptá na svolení. Jinou konfiguraci můžete předat přepínačem
--formatter, čímž zároveň udělujete souhlas s jejím načtením. Na závěr nástroj shrne, kde všude může
dresscode check původní formátovač nahradit, a připomene, že namísto měření kódu umí stará pravidla
přímo přeložit příkaz dresscode migrate config. Uvede také přesný počet potlačovacích komentářů,
které dokáže převést dresscode migrate suppressions.
Po dokončení zbývají nanejvýš dva přímočaré kroky, které init sám vypíše: příkaz
dresscode fix srovná soubory odchylující se od zapsané konfigurace a dresscode baseline uloží
místa, která automaticky vyřešit nelze, aby kontrola v CI okamžitě prošla.
Před samotným uložením souboru si init konfiguraci znovu načte a ověří, že z ní vzejdou přesně
tytéž hodnoty, jaké naměřil. Pokud v některém souboru selže pravidlo, daný soubor z měření vynechá, jmenuje jej
v hlavičce a skončí s exit kódem 2, protože konfigurace pak stojí na neúplných datech. Již existující konfigurační
soubor init nikdy nepřepíše (při jeho existenci se na nic neptá a volbu --set odmítne);
nečitelnou konfiguraci ohlásí s chybou ještě před zahájením prací. Namísto přepisu proměří projekt znovu podle
standardu uvedeného v existujícím souboru na všech sledovaných cestách. Vypíše každé rozhodnutí, které by dnes
nastavil jinak (včetně počtu souborů, jež by oprava změnila), a upozorní na pravidla, která máte v souboru nastavena
odlišně od standardu, přestože se jich kód mezitím začal sám držet. To oceníte po aktualizaci DressCode nebo po delším
čase aktivního vývoje:
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.