Kodokon kodokon.com

Modernes OOP: Klassen, Property Promotion, Sichtbarkeit

Schreibe idiomatische PHP-8-Klassen: Property Promotion, readonly, benannte Konstruktoren und unveränderliche Value Objects.

9 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Du 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
<?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();
Vorher / nachher: Promotion entfernt den gesamten Boilerplate-Zuweisungscode.

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
<?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;
Unveränderliches Value Object: add() liefert eine neue Instanz zurück.

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
<?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;
Privater Konstruktor + statische Factories: Nur gültige Zustände lassen sich konstruieren.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Was macht Property Promotion in einem PHP-8-Konstruktor?
    • Sie macht automatisch jede Eigenschaft public
    • Sie deklariert die Eigenschaft und weist sie aus dem Parameter zu, in einem einzigen Schritt
    • Sie erzeugt Zugriffsmethoden (Getter) für jede Eigenschaft
    • Sie wandelt Eigenschaften in Klassenkonstanten um
  2. Wann kann eine readonly-Eigenschaft geschrieben werden?
    • Nie: Sie muss bei der Deklaration einen Standardwert erhalten
    • Genau einmal, aus dem Gültigkeitsbereich der Klasse heraus, die sie deklariert
    • Jederzeit, solange der Schreibzugriff aus der Klasse selbst kommt
  3. Welche Sichtbarkeit wählst du für eine Eigenschaft, die nur die Klasse und ihre Kindklassen nutzen sollen?
    • public
    • protected
    • private