Schreibe idiomatische PHP-8-Klassen: Property Promotion, readonly, benannte Konstruktoren und unveränderliche Value Objects.
Diese Lektion in Kodokon öffnenDu schreibst schon lange Klassen, aber PHP 8 hat mehrere Gewohnheiten überflüssig gemacht. Die auffälligste: Property Promotion im Konstruktor. Wenn du ein Sichtbarkeits-Schlüsselwort (private, protected, public) vor einen Konstruktor-Parameter setzt, entsteht die Eigenschaft und wird in einem einzigen Schritt zugewiesen. Die drei sich wiederholenden Blöcke des alten Stils - Deklaration, Parameter, Zuweisung - schrumpfen auf einen zusammen.
<?php
declare(strict_types=1);
class LegacyProduct
{
private string $name;
private int $priceCents;
public function __construct(
string $name,
int $priceCents,
) {
$this->name = $name;
$this->priceCents = $priceCents;
}
}
final class Product
{
public function __construct(
private string $name,
private int $priceCents,
) {
}
public function label(): string
{
return sprintf(
'%s (%.2f EUR)',
$this->name,
$this->priceCents / 100,
);
}
}
$product = new Product('Keyboard', 4990);
echo $product->label();Die Sichtbarkeit bleibt dein erstes Werkzeug zur Kapselung: private als Standard, protected nur, wenn eine Kindklasse Zugriff braucht, public als letzte Wahl. Seit PHP 8.1 fügt readonly eine stärkere Garantie hinzu: Die Eigenschaft kann nur einmal geschrieben werden, und zwar aus dem Gültigkeitsbereich der Klasse heraus, die sie deklariert. Kombiniert mit Promotion entstehen daraus unveränderliche Value Objects in wenigen Zeilen: Jede "Änderung" liefert eine neue Instanz zurück, was geteilte Nebenwirkungen ausschließt.
<?php
declare(strict_types=1);
final class Money
{
public function __construct(
public readonly int $amount,
public readonly string $currency,
) {
}
public function add(Money $other): self
{
if ($other->currency !== $this->currency) {
throw new InvalidArgumentException(
'Currency mismatch'
);
}
return new self(
$this->amount + $other->amount,
$this->currency,
);
}
}
$total = (new Money(500, 'EUR'))
->add(new Money(250, 'EUR'));
echo $total->amount;Auf der Methodenseite dominieren bei erfahrenen Entwicklern zwei Muster. Benannte Konstruktoren: Ein private Konstruktor zusammen mit statischen Factories macht jede Absicht explizit und verbietet inkonsistente Instanziierungen. Fluent Interfaces: Indem du $this (veränderlich) oder new self(...) (unveränderlich) zurückgibst, kannst du Aufrufe verketten. Der Kompromiss ist bekannt: Die unveränderliche Variante ist sicherer, erzeugt aber ein Objekt pro Aufruf - fast überall vernachlässigbar, in einer heißen Schleife messbar.
<?php
declare(strict_types=1);
final class User
{
private function __construct(
public readonly string $email,
public readonly string $role,
) {
}
public static function admin(string $email): self
{
return new self($email, 'admin');
}
public static function member(string $email): self
{
return new self($email, 'member');
}
}
$user = User::admin('lea@example.com');
echo $user->role;readonly-Eigenschaft geschrieben werden?