Vlastní preset a rozšíření

Jak zabalit vlastní pravidla a styl do presetu, jak z něj udělat rozšíření (extension), rozdávat ho přes Composer a nechat uživatele zapnout ho jediným řádkem.

Preset

Preset je třída s atributem #[PresetInfo], která vrátí sadu pravidel s volbami a může vycházet z jiných presetů:

namespace Acme\CodeStyle;

use DressCode\Preset;
use DressCode\PresetContext;
use DressCode\PresetInfo;
use DressCode\Presets\Per;

#[PresetInfo('acme/house', 'The Acme house style', indent: "\t", eol: "\n")]
final class HousePreset implements Preset
{
	public function getRules(PresetContext $context): array
	{
		return [
			'dresscode/ordered-imports' => true,
			'dresscode/line-length' => ['limit' => 100],
			'dresscode/no-alternative-syntax' => false,
			ExceptionMessagePeriodRule::class => true,
			'dresscode/octal-notation' => version_compare($context->getPhpVersion(), '8.1', '>='),
		];
	}


	public function getParents(): array
	{
		return [Per::class];
	}
}
  • Rodičovské presety se použijí nejdřív. Potomek pak přepisuje celé položky, takže se volby nikdy neslučují. Pořadí pravidel odpovídá pořadí první zmínky.
  • Vestavěná pravidla lze uvést jménem, vlastní názvem třídy; jméno vlastního pravidla se zaregistruje při první zmínce.
  • Hodnotou je true, false, mapa voleb, nebo továrna fn(): Rule pro pravidlo se závislostmi.
  • indent a eol v atributu PresetInfo je styl, se kterým preset počítá; konfigurace projektu ho může přepsat.

Poslední řádek ukázky je vlastně zbytečný: pravidlo s minPhpVersion se pod svou verzí vynechá samo. PresetContext se hodí spíš tam, kde má preset pod různými verzemi PHP zapínat různá pravidla nebo jim dávat jiné volby. Verze je řetězec ve tvaru major.minor a porovnává se funkcí version_compare().

Preset se dá zapnout názvem třídy, dokud nemá rozšíření, které mu dá jméno:

presets:
	- Acme\CodeStyle\HousePreset

Rozšíření

Rozšíření (extension) je to, co balíček dodá místo konfiguračního souboru: třída s metodou __invoke(), která dostane prázdný objekt Config a nastaví do něj, co uzná za vhodné. Takhle vypadá rozšíření Nette Coding Standardu:

namespace Nette\CodingStandard;

use DressCode\Config;

final class Extension
{
	public function __invoke(Config $config): void
	{
		$config
			->registerPresets([Presets\CleanCode::class, Presets\OptimizeFn::class, Presets\Types::class])
			->excludePaths(['expected', 'tmp', 'fixtures*'])
			->skipWhen(PhpVersionFilter::create());
	}
}

Všimněte si, co tam není: žádné volání preset(). Rozšíření dělá jména dostupnými a nastavuje výchozí hodnoty, ale který styl se nakonec použije, je rozhodnutí projektu. Uživatel rozšíření zapne jedním řádkem a presety si vybere sám:

extensions:
	- Nette\CodingStandard\Extension

presets:
	- dresscode/nette
	- nette/clean-code

Rozšíření může nastavit cokoli, co umí Config: zaregistrovat pravidla (registerRules()) a presety (registerPresets()), zapnout preset, přidat vyloučené cesty, přípony souborů, filtr skipWhen podle obsahu souboru nebo analýzu s továrnou. Všechno, co nastaví, je vrstva pod konfigurací projektu, takže to projekt může přepsat. Výjimkou jsou vyloučené cesty, které se jen sčítají. Na pořadí rozšíření v konfiguraci nezáleží a rozšíření, které zapnulo jiné rozšíření, se použije jen jednou.

Anonymní funkce patří sem, ne do NEONu: filtr skipWhen, továrna pravidla se závislostmi, továrna analýzy. NEON deklaruje, rozšíření implementuje.

Balíček

Balíček s presetem nebo s pravidly je obyčejný Composer balíček, který vyžaduje dresscode/dresscode a sdílí s projektem autoloader:

{
	"name": "acme/code-style",
	"require": {
		"dresscode/dresscode": "^1.0"
	},
	"autoload": {
		"psr-4": {"Acme\\CodeStyle\\": "src/"}
	}
}

Uvnitř jsou třídy pravidel, presety, rozšíření a fixtury s testy. Jména pravidel nesou vendor balíčku (acme/…), aby se nesrazila s jinými. A kdo chce, aby uživatel po composer require nemusel psát ani řádek extensions, přidá do composer.json balíčku záznam, podle kterého si DressCode rozšíření najde sám:

{
	"extra": {
		"dresscode": {
			"extensions": ["Acme\\CodeStyle\\Extension"]
		}
	}
}

Automaticky nalezené rozšíření se chová stejně jako ručně zapsané. U každého pravidla pak dresscode rules řekne, odkud přišlo.

verze: 1.0