Vlastní preset a plugin
Styl, který chcete mít na všech projektech, zabalíte do presetu, vlastní pravidla do pluginu se sekcí
rozhodnutí a obojí rozdáte přes Composer. Projekt si to pak zapne jediným řádkem v klíči use, pro celý
kód nebo jen pro jeho část.
Preset v NEON
Preset je soubor NEON ve tvaru konfigurace. Nese rozhodnutí, klíč use s tím, z čeho vychází, a
případně suppressionComments; nic víc. Takhle vypadá domácí styl firmy Acme:
# Acme house style
#
# PER Coding Style with tabs, lines of up to 100 characters, and the messages of exceptions as sentences.
use: perCs
file:
lineLength: 100
indentation:
unit: tab
expressions:
comparison:
# a comparison reads in the order it is spoken
yoda: forbidden
acme:
exceptionMessagePeriod: required
- Preset začíná komentářem: na prvním řádku název, pod ním, co preset je. Proč má rozhodnutí svou hodnotu, říká komentář nad jeho klíčem; tak jsou psané i standardy a sady DressCode.
usejmenuje presety, ze kterých ten váš vychází, ať standardy, sady, nebo jiné presety. Leží pod vaším presetem v pořadí zápisu a každý jen jednou, takže co preset napíše, přebije to, z čeho vychází, a projekt nad ním přebije preset. Hodnoty se slévají stejně jako v konfiguraci: mapa podle jmen, slovo a seznam nahrazují celou hodnotu,keepu sekce maže všechno pod ní.- Rozhodnutí presetu mohou patřit jádru i pluginu, jako
acme.exceptionMessagePeriodv ukázce; projekt pak musí mít i plugin, jinak klíč katalog nezná. - Co rozhoduje projekt, preset rozhodnout nemůže. Fakty o projektu (
targets,typeAnalysis,namespaces,nameResolution), rozsah běhu (paths,excludePathsa další),fixRisky, pluginy arulesnechává projektu. Preset, který by je nastavil, DressCode odmítne s chybouPreset `acme/house` sets `fixRisky`, which the project decides, not a preset.Preset ale smí říct, že se porušení rozhodnutí hlásí jen jako varování, a to klíčemseverity, jakofile.longLines: warningve standardupsr12. - Preset, který se má dát přidat ke kterémukoli standardu, je sada: o rozvržení kódu nerozhoduje, nevychází ze standardu a nesahá na mezery, odsazení, zalamování ani pořadí. Že je takový, řekněte v jeho úvodním komentáři.
Na verzi PHP ani na to, co má projekt nainstalované, preset myslet nemusí: rozhodnutí, jehož pravidlo si v
requires řekne o verzi PHP nebo o balíček, se tam, kde je projekt nemá, tiše neuplatní. Stejně tiše se
neuplatní rozhodnutí, jehož pravidlo potřebuje typy, v projektu bez typeAnalysis: phpstan.
Projekt preset použije cestou k souboru v use, třeba use: tools/house.neon, nebo jménem, když ho
přinese plugin. Protože je to vrstva jako každá jiná, dá se použít i jen
pro část projektu, v přepisu:
use:
- Acme\CodeStyle\Plugin
- perCs
overrides:
-
paths: [src]
use: [acme/house]
Plugin s pravidly a presety
Plugin je to, co balíček přinese projektu: třída s rozhraním DressCode\Plugin, jejíž metoda
getManifest() vrátí PluginManifest. Ten vyjmenuje pravidla, presety se jmény a soubory, sekci,
ve které rozhodují pravidla pluginu, rozhodnutí sdílená víc jeho pravidly, pluginy, na kterých stojí, analýzy,
vyloučené cesty, filtr souborů skipWhen a adresu stránek pravidel ruleUrl. Takhle vypadá plugin
balíčku firmy Acme:
namespace Acme\CodeStyle;
use DressCode\PluginManifest;
final class Plugin implements \DressCode\Plugin
{
public function getManifest(): PluginManifest
{
return new PluginManifest(
rules: [ExceptionMessagePeriodRule::class, NoDebugCallsRule::class],
presets: ['acme/house' => __DIR__ . '/house.neon'],
section: 'acme',
excludePaths: ['generated'],
ruleUrl: 'https://acme.dev/rules/{slug}',
);
}
}
Sekce je jméno pluginu v konfiguraci. Všechna rozhodnutí jeho pravidel leží pod ní,
acme.exceptionMessagePeriod a acme.debugCalls, takže se nesrazí s jádrem, s jiným pluginem ani
s pravidly projektu v sekci project. Plugin, který přináší pravidla, sekci mít musí, a nesmí to být
vyhrazený klíč ani sekce jádra. Uživatel pak píše rozhodnutí pluginu stejně jako kterékoli jiné, vidí je u nálezů,
potlačí je komentářem dresscode:ignore acme.debugCalls, přijme jejich rizikové opravy v fixRisky a
nechá si je vysvětlit příkazem dresscode explain acme.
Sdílené rozhodnutí. Rozhodnutí obvykle patří jednomu pravidlu, které ho vrací z getDecisions().
Když jednu věc vynucují dvě pravidla s nezávislou logikou, třeba ladicí výstup ve volání a v komentáři, deklaruje
rozhodnutí manifest v decisions a obě pravidla ho jmenují v #[RuleInfo(decisions: [...])]. Uživatel
píše jeden klíč, který zapne obě pravidla, a nálezy obou se hlásí pod ním:
return new PluginManifest(
rules: [DebugCallsRule::class, DebugCommentsRule::class],
section: 'acme',
decisions: [new Decision('acme.debugOutput', Domain::state('forbidden'), 'Debugging output left in the code')],
);
#[RuleInfo(Stage::Structure, decisions: ['acme.debugOutput'])]
final class DebugCommentsRule extends NodeRule
Pravidlo smí jmenovat i rozhodnutí jádra, třeba file.lineLength, a řídit se jím stejně jako pravidla
jádra. Pravidlo, které nějaké rozhodnutí jen čte a nevymáhá, ho jmenuje v reads místo v
decisions: rozhodnutí ho nezapne a nálezy se pod ním nehlásí, ale hodnotu pravidlo najde ve svých
Values, i když běh pravidla, která ho vymáhají, nepouští. Testy si sdílená rozhodnutí najdou samy:
RuleTester přečte plugin z klíče extra.dresscode.plugin v composer.json balíčku, ve
kterém pravidlo leží.
Presety z manifestu dostanou svá jména ve tvaru vendor/name, takže je projekt použije jménem v
use (acme/house) nebo na jeden běh přepínačem --use. V ruleUrl stojí
adresa stránek pravidel se zástupným {slug}, za který se dosadí jméno třídy pravidla bez přípony
Rule a s malým prvním písmenem; vypíše ji dresscode explain u každého rozhodnutí pravidla.
Vyloučené cesty se přidají k cestám projektu, filtr skipWhen přeskočí soubor, i když má projekt vlastní
filtr, a když projekt uvede analýzu stejné třídy jako plugin, platí ta projektová. Plugin, na kterém jiný stojí, leží
pod ním a každá třída se použije jen jednou.
Všimněte si, co v manifestu není: plugin nedá žádnému rozhodnutí hodnotu a nenastaví styl, protože takový
klíč PluginManifest vůbec nemá; decisions rozhodnutí jen deklaruje. Pravidla pluginu se zapnou, až
jejich rozhodnutí napíše projekt nebo preset, který projekt použije. Který styl se použije, je rozhodnutí projektu, a
proto ho vždycky přečtete v jeho vlastním souboru:
use:
- Acme\CodeStyle\Plugin
- acme/house
acme:
debugCalls: forbidden
mistakes:
debugOutputFunctions: [dump]
Pravidla pluginu smějí číst i klíče jádra. acme.debugCalls v ukázce bere seznam ladicích funkcí
z parametru jádra mistakes.debugOutputFunctions, takže ho projekt napíše jednou pro všechna pravidla, která se
na ladicí funkce ptají; jak to pravidlo udělá, popisuje Pravidlo do detailu.
Přesně takhle pracuje balíček dresscode/upgrade-nette: jeho plugin má sekci nette
s rozhodnutími jako nette.linkArguments nebo mapou nette.flagsAsNamedArguments a data pro upgrade
knihoven nese vedle v adresáři upgrading/.
Plugin se uvádí v klíči use konfigurace vedle presetů, a to třídou nebo entitou s argumenty. Přepis ani preset ho mít
nemohou, protože pravidla a presety zpřístupňuje celému projektu; preset, který by ho uvedl, DressCode odmítne s chybou
Preset `acme/house`: Plugin `Acme\CodeStyle\Plugin` is used by the configuration of the project, never by a preset or an override.
Filtr skipWhen a továrnu analýzy ale zapíše i samotný dresscode.neon, a to entitou, a továrnu pravidla se závislostmi soubor
dresscode.php. Plugin se hodí tam, kde nastavení nese balíček a projekt ho jen zapne.
Balíček pro Composer
Balíček s presetem nebo s pravidly je obyčejný balíček pro Composer, který vyžaduje dresscode/dresscode
a sdílí s projektem autoloader. Svůj plugin ohlásí v composer.json pod klíčem
extra.dresscode.plugin, takže projekt po instalaci nemusí znát jeho třídu a pustí ho dovnitř jménem balíčku
v use:
{
"name": "acme/code-style",
"require": {
"dresscode/dresscode": "^1.0"
},
"autoload": {
"psr-4": {"Acme\\CodeStyle\\": "src/"}
},
"extra": {
"dresscode": {
"plugin": "Acme\\CodeStyle\\Plugin"
}
}
}
Vedle plugin zná DressCode pod extra.dresscode ještě klíče upgrading-dir a
packages s daty pro upgrade knihoven;
jiný klíč ohlásí jako varování a přeskočí ho, protože balíček může být novější než DressCode, který ho čte.
Uvnitř balíčku jsou třídy pravidel, soubory presetů, plugin a fixtury s testy. Jména presetů nesou vendor balíčku
(acme/…) a rozhodnutí sekci pluginu, aby se nesrazila s jinými. Uživatel pak balíček nainstaluje, napíše
jeho jméno do use a všechno ostatní si vybere v konfiguraci:
use:
- perCs
- acme/code-style
- acme/house
Jméno balíčku žádný preset není, jen pouští dovnitř jeho plugin. Plugin totiž přináší kód, který běží při
každé kontrole, a tak se načte, jen když ho projekt jmenuje, stejně jako Composer spouští jen pluginy, které projekt
povolí. Nainstalovaný balíček, který use nejmenuje, běh ohlásí varováním
Package `acme/code-style` brings the plugin `Acme\CodeStyle\Plugin`, which loads only where `use` names the package or the plugin; skipped.
Data pro upgrade pod klíčem upgrading se načtou i bez jména, protože kód nenesou.