Zum Inhalt springen

Coding Standard: Konventionen, die Wartung sparen

Namensgebung, Einrückung, Dateiaufbau und Kommentare als prüfbare Regeln statt als Geschmacksfrage – mit den Werkzeugen, die den Standard automatisch durchsetzen.

Zuletzt geprüft: Gedacht für: Teams und Übergaben

Das Wichtigste

  • Ein Standard wirkt nur, wenn ein Werkzeug ihn prüft. Eine Regel im Wiki, die niemand misst, wird nach drei Monaten ignoriert.
  • PSR-12 ist der De-facto-Standard für PHP. Die Diskussion, ob Klammern in derselben Zeile stehen, ist damit erledigt.
  • PHP-CS-Fixer korrigiert automatisch, PHPStan findet Fehler. Beides gehört in die Continuous Integration, nicht in die Code-Review.
  • Bei Bestandscode nie alles auf einmal formatieren: ein Commit mit 4.000 geänderten Dateien macht die Versionsgeschichte unbrauchbar.

Ein Coding Standard ist keine Stilfrage, sondern eine Massnahme zur Fehlerreduktion. Einheitlich formatierter Code lässt sich schneller lesen, Änderungen sind in der Versionsgeschichte klarer zu erkennen, und der Diff zeigt nur, was sich fachlich geändert hat.

Was ein Standard regeln sollte

Bereichtypische Festlegung
Einrückungvier Leerzeichen, keine Tabulatoren
ZeilenlängeRichtwert 120 Zeichen, kein hartes Limit
KlassennamenPascalCase
Methoden und VariablencamelCase
KonstantenSCREAMING_SNAKE_CASE
Dateiaufbaueine Klasse pro Datei, Dateiname gleich Klassenname
NamensräumePSR-4, Verzeichnis spiegelt Namensraum
ZeilenendeLF, nie CRLF
KodierungUTF-8 ohne BOM
Schlusstag?> in reinen PHP-Dateien weglassen

Der letzte Punkt ist mehr als Kosmetik: Ein Leerzeichen nach dem Schlusstag wird ausgegeben und erzeugt “headers already sent” – ein Fehler, dessen Ursache man ohne diesen Hinweis lange sucht.

Nimm PSR-12

PSR-12 ist der breit akzeptierte Standard im PHP-Umfeld. Die Vorteile eines fertigen Standards gegenüber einem eigenen:

  • Er ist bereits ausdiskutiert. Niemand streitet mehr über die Position der geschweiften Klammer.
  • Alle Werkzeuge kennen ihn ab Werk.
  • Neue Teammitglieder bringen ihn mit.
  • Fremde Bibliotheken sehen aus wie eigener Code.

Ein projektspezifischer Zusatz ist trotzdem sinnvoll – etwa: strikte Typen in jeder Datei, keine Klassen ohne Rückgabetyp, keine else nach einem return.

Durchsetzung mit Werkzeugen

PHP-CS-Fixer korrigiert die Form automatisch:

<?php
// .php-cs-fixer.dist.php
return (new PhpCsFixer\Config())
    ->setRiskyAllowed(true)
    ->setRules([
        '@PSR12'                    => true,
        'declare_strict_types'      => true,
        'ordered_imports'           => ['sort_algorithm' => 'alpha'],
        'no_unused_imports'         => true,
        'array_syntax'              => ['syntax' => 'short'],
        'trailing_comma_in_multiline' => true,
        'single_quote'              => true,
    ])
    ->setFinder(
        PhpCsFixer\Finder::create()->in(__DIR__ . '/src')
    );
# nur prüfen, nichts ändern - für die CI
vendor/bin/php-cs-fixer fix --dry-run --diff

# korrigieren - lokal vor dem Commit
vendor/bin/php-cs-fixer fix

PHPStan prüft die Bedeutung:

# phpstan.neon
parameters:
  level: 6
  paths:
    - src
  treatPhpDocTypesAsCertain: false

Level 0 bis 9, wobei 9 sehr streng ist. Für ein Bestandsprojekt beginnt man bei 0 oder 1, erzeugt eine Baseline für die vorhandenen Meldungen und erhöht das Level, sobald der aktuelle Stand sauber ist:

vendor/bin/phpstan analyse --generate-baseline

Die Baseline friert bestehende Meldungen ein, ohne sie zu ignorieren – neuer Code wird ab sofort streng geprüft, alter kann nach und nach nachgezogen werden.

Einführung in einem gewachsenen Projekt

Der Fehler, den man einmal macht: das gesamte Projekt in einem Commit umformatieren. Danach zeigt git blame für jede Zeile diesen Commit, und die Frage “warum steht das so da” lässt sich nicht mehr beantworten.

Der bessere Weg:

  1. Konfiguration anlegen und einchecken, noch nichts ändern.

  2. In der CI nur die im Branch geänderten Dateien prüfen:

    git diff --name-only --diff-filter=ACM origin/main... -- '*.php' \
      | xargs -r vendor/bin/php-cs-fixer fix --dry-run --diff --path-mode=intersection
  3. Dateien, die aus anderen Gründen angefasst werden, dabei mit formatieren – in einem eigenen Commit, getrennt von der fachlichen Änderung.

  4. Wenn irgendwann fast alles konform ist, den Rest in einem klar benannten Commit nachziehen und diesen in .git-blame-ignore-revs eintragen:

    git config blame.ignoreRevsFile .git-blame-ignore-revs

Damit überspringt git blame den reinen Formatierungscommit.

Kommentare: weniger und besser

Kommentare, die beschreiben, was der Code tut, veralten und lügen irgendwann. Wertvoll sind die, die erklären, warum etwas so ist:

// Schlecht - wiederholt nur den Code
// Zähler um eins erhöhen
$i++;

// Gut - erklärt eine Entscheidung, die man sonst rückgängig machen würde
// Die API des Anbieters begrenzt auf 60 Anfragen pro Minute; ohne diese
// Pause liefert sie ab der 61. Anfrage HTTP 429 und sperrt für 15 Minuten.
usleep(1_000_000);

Für Signaturen leisten Typdeklarationen mehr als jeder Kommentar. Ein @param string $name über einer Methode mit string $name ist Redundanz, die irgendwann auseinanderläuft. PHPDoc bleibt sinnvoll für Dinge, die das Typsystem nicht ausdrückt – etwa die Form eines Arrays:

/** @param list<array{name: string, preis: float}> $produkte */
public function berechneSumme(array $produkte): float

PHPStan wertet solche Angaben aus und prüft sie – damit ist der Kommentar nicht mehr nur Dokumentation, sondern eine geprüfte Zusage.

Häufige Fragen

Welchen Standard soll ich nehmen?

PSR-12, ergänzt um die Regeln, die euer Projekt tatsächlich braucht. Ein eigener Standard kostet Diskussionszeit und bringt gegenüber PSR-12 keinen messbaren Vorteil – ausser dass neue Teammitglieder ihn erst lernen müssen.

Wie führe ich einen Standard in einem alten Projekt ein?

Schrittweise. Zuerst die Konfiguration anlegen und in der CI nur neue oder geänderte Dateien prüfen. Wer das ganze Projekt in einem Commit umformatiert, verliert die Nachvollziehbarkeit über git blame für jede einzelne Zeile.

Was ist der Unterschied zwischen PHP-CS-Fixer und PHPStan?

PHP-CS-Fixer arbeitet an der Form: Einrückung, Reihenfolge, Leerzeichen – und korrigiert selbstständig. PHPStan analysiert die Bedeutung und findet Typfehler, unerreichbaren Code und Aufrufe, die zur Laufzeit scheitern würden. Beide ergänzen sich, keines ersetzt das andere.

Braucht ein Ein-Personen-Projekt einen Standard?

Spätestens beim Wiedereinstieg nach sechs Monaten ja. Der Aufwand ist eine Konfigurationsdatei und ein Kommando, der Gewinn ist Code, der sich nach der Pause noch lesen lässt.