Nette Coding Standard

Chcete, aby váš PHP kód dodržoval Nette coding standard, a přitom sami nehlídat tabulátory, závorky a prázdné řádky? DressCode za vás projde každý soubor a co najde, opraví jediným příkazem.

DressCode je nová generace původního balíčku nette/coding-standard. Nette coding standard v něm najdete vestavěný pod jménem nette. Mění jen to, co standard porušuje, a zbytek souboru nechá přesně tak, jak byl, takže v diffu po opravě najdete opravy a nic jiného.

Instalace

DressCode nainstalujte globálně pomocí Composeru:

composer global require dresscode/dresscode

a ujistěte se, že váš globální adresář vendor/bin je v proměnné prostředí $PATH.

Samotný DressCode běží na PHP 8.4 až 8.6. To je ale PHP nástroje, ne PHP, pro které píšete kód: váš projekt může cílit na kteroukoliv verzi od PHP 8.0. DressCode ji vyčte z položky php v sekci require souboru composer.json vašeho projektu a nikdy nenapíše syntaxi, kterou tahle verze nezná.

Konfigurace

DressCode nemá žádný vlastní styl, takže mu nejdřív řeknete, jaký standard váš projekt dodržuje. Do kořene projektu vložte soubor dresscode.neon:

use:
	- nette

paths:
	- app
	- tests

Klíč use vybírá standard, paths vyjmenovává adresáře ke kontrole. Soubor commitněte, ať kód kontroluje stejně celý tým i vaše CI.

Použití

Vyzkoušíme to na třídě napsané ve spěchu, app/Model/CartService.php, odsazené mezerami a se závorkami na špatných řádcích:

<?php

declare(strict_types=1);

namespace App\Model;

class CartService {
    public function getTotal(array $items): float {
        $total = 0;
        foreach ($items as $item) {
            $total = $total + $item['price'] * $item['quantity'];
        }
        return $total;
    }
}

Příkaz check vypíše, co standard porušuje, a nic nemění:

dresscode check
app/Model/CartService.php
  error      1  `declare(strict_types=1)` must be on the line of the opening tag.                          file.strictTypes.position
  error      2  Expected 1 blank line before the namespace, 2 found.                                       blankLines.beforeNamespace
  error      6  Expected 2 blank lines before the class, 1 found.                                          blankLines.beforeDeclaration
  error   7:19  Expected a line break before the opening brace.                                            braces.position.class
  error      8  Expected the member indented by 1 tab, 4 spaces found.                                     indentation.unit
                Then also, on lines 9, 10, 13: Expected the statement indented by 2 tabs, 8 spaces found.  indentation.unit
                Then also, on line 11: Expected the statement indented by 3 tabs, 12 spaces found.         indentation.unit
                Then also, on line 12: Expected the closing brace indented by 2 tabs, 8 spaces found.      indentation.unit
                Then also, on line 14: Expected the closing brace indented by 1 tab, 4 spaces found.       indentation.unit
  error   8:51  Expected a line break before the opening brace.                                            braces.position.function
  error  11:13  The assignment must be written `+=` instead of repeating its target.                       expressions.assignment.targetInValue

FOUND  13 violations, 6 of them following from others, a fix leaves none in 1 file

U každého nálezu je řádek (a sloupec), popis chyby a na konci jméno nastavení, které porušuje, třeba braces.position.class. To jméno budete potřebovat, až budete chtít standard upravit. Řádky začínající Then also patří k nálezu nad nimi, jsou to jeho důsledky. Příkaz fix pak opraví všechno, co jde:

dresscode fix

A třída teď vypadá takhle:

<?php declare(strict_types=1);

namespace App\Model;


class CartService
{
	public function getTotal(array $items): float
	{
		$total = 0;
		foreach ($items as $item) {
			$total += $item['price'] * $item['quantity'];
		}
		return $total;
	}
}

Než poprvé spustíte fix, zazálohujte si soubory, nebo ho pusťte nad čistým pracovním stromem, ať můžete změny potom projít přes git diff.

Úprava standardu

Každé nastavení standardu je klíčem konfigurace a poslední slovo u každého z nich má váš dresscode.neon. Řekněme, že váš projekt chce mezi metodami jeden prázdný řádek místo dvou a nechce, aby se == přepisovalo na ===:

blankLines:
	aroundMethods: 1

expressions:
	comparison:
		loose: keep

Hodnota keep znamená „nech to tak, jak to má kód“. K čemu klíč slouží a jakou hodnotu má ve vašem projektu, vypíše dresscode explain blankLines.aroundMethods. A když má jeden řádek dobrý důvod standard porušit, připište k němu komentář // dresscode:ignore.

Novější PHP a Nette

DressCode umí víc než formátovat. Přidejte vedle standardu sadu modernizations a přepíše kód na to, co nabízí novější PHP, třeba $user === null ? null : $user->getName() na $user?->getName(). Jde přitom jen tak daleko, jak dovolí váš composer.json:

use:
	- nette
	- modernizations

Když aktualizujete knihovny Nette, balíček dresscode/upgrade-nette přepíše váš kód na jejich nové API, viz Upgrade Nette.

Continuous Integration

V CI nainstalujte DressCode jako samostatný projekt, ať máte pod kontrolou, která verze poběží:

- run: composer create-project dresscode/dresscode temp/dresscode ^1
- run: php temp/dresscode/bin/dresscode check

Když check najde prohřešek, skončí s kódem 1 a build selže. V GitHub Actions se nálezy zobrazí jako anotace přímo v pull requestu.

Přechod z verze 3

Původní balíček nette/coding-standard kontroloval standard pomocí PHP CS Fixeru a PHP CodeSnifferu, přes příkaz ecs a soubory ncs.php a ncs.xml. Jeho vývoj skončil, nahradil ho DressCode, a verze 3 je poslední. Při přechodu vytvořte dresscode.neon podle návodu výše, úpravy, které jste měli v ncs.php a ncs.xml, zapište jako jeho klíče a oba soubory smažte. Všechny klíče i s hodnotami, které mají ve vašem projektu, vypíše dresscode config.

První dresscode fix nejspíš sáhne na víc souborů, než čekáte, protože DressCode opravuje i některá místa, která verze 3 nechávala být. Udělejte z něj samostatný commit bez dalších změn.

Všechno ostatní, od všech nastavení až po spouštění DressCode v editoru nebo po každé úpravě, kterou udělá AI agent, najdete v manuálu DressCode.