20 Constructor Property Promotion

Constructor Property Promotion ist eine der elegantesten Spracherweiterungen in PHP 8.0, die dazu beiträgt, den Boilerplate-Code in Klassen erheblich zu reduzieren. Diese Funktion vereinfacht die Deklaration von Klasseneigenschaften, die über den Konstruktor initialisiert werden – ein Muster, das in objektorientiertem PHP-Code sehr häufig vorkommt.

20.1 Das Problem: Wiederholung im traditionellen Ansatz

Vor PHP 8.0 erforderte die Deklaration einer Klasse mit Eigenschaften, die im Konstruktor initialisiert werden, viel redundanten Code:

class Benutzer {
    private string $name;
    private string $email;
    private ?int $alter;
    
    public function __construct(string $name, string $email, ?int $alter = null) {
        $this->name = $name;
        $this->email = $email;
        $this->alter = $alter;
    }
    
    // Getter und Setter...
}

Diese Implementierung enthält dreifache Wiederholung: die Eigenschaftsdeklaration, die Parameterdeklaration im Konstruktor und die Zuweisungen innerhalb des Konstruktors.

20.2 Die Lösung: Constructor Property Promotion

Mit Constructor Property Promotion können wir die Eigenschaftsdeklaration und die Zuweisung direkt in die Parameterliste des Konstruktors integrieren:

class Benutzer {
    public function __construct(
        private string $name,
        private string $email,
        private ?int $alter = null
    ) {
        // Der Konstruktor kann leer sein oder zusätzliche Initialisierungslogik enthalten
    }
    
    // Getter und Setter...
}

Dieses kompaktere Syntax führt zu deutlich weniger Code, bleibt dabei aber ebenso ausdrucksstark und typsicher.

20.3 Wie es funktioniert

Wenn Sie ein Sichtbarkeitsmodifikator (public, protected, private oder seit PHP 8.2 auch readonly) vor einem Konstruktorparameter angeben, geschieht Folgendes:

  1. PHP erstellt automatisch eine Eigenschaft mit dem gleichen Namen und der angegebenen Sichtbarkeit
  2. Der Parameterwert wird beim Aufruf des Konstruktors automatisch dieser Eigenschaft zugewiesen
  3. Der Typ und alle Docblocks des Parameters werden zur Eigenschaftsdefinition hinzugefügt

20.4 Kombination mit anderen Sprachfeatures

Constructor Property Promotion lässt sich gut mit anderen PHP-Features kombinieren:

20.4.1 Mit Typed Properties und Nullable Types

class Produkt {
    public function __construct(
        private string $name,
        private float $preis,
        private ?string $beschreibung = null
    ) {}
}

20.4.2 Mit Readonly Properties (ab PHP 8.1)

class ImmutableConfig {
    public function __construct(
        private readonly string $apiKey,
        private readonly string $apiUrl,
        private readonly int $timeout = 30
    ) {}
}

20.4.3 Mit Union Types (ab PHP 8.0)

class Zahlungsmethode {
    public function __construct(
        private string $name,
        private string|int $identifier,
        private bool $isActive = true
    ) {}
}

20.5 Einschränkungen und Feinheiten

Bei der Verwendung von Constructor Property Promotion gibt es einige Besonderheiten zu beachten:

  1. Selektive Anwendung: Nicht alle Konstruktorparameter müssen zu Eigenschaften befördert werden:
class Newsletter {
    private array $abonnenten = [];
    
    public function __construct(
        private string $name,
        private string $absender,
        array $initialAbonnenten = []
    ) {
        // $initialAbonnenten wird nicht als Eigenschaft erstellt
        $this->abonnenten = $initialAbonnenten;
    }
}
  1. Kombinierbarkeit mit regulären Eigenschaften: Sie können weiterhin Eigenschaften außerhalb des Konstruktors deklarieren:
class Artikel {
    private array $kommentare = [];
    private DateTime $erstelltAm;
    
    public function __construct(
        private string $titel,
        private string $inhalt,
        private string $autor
    ) {
        $this->erstelltAm = new DateTime();
    }
}
  1. Keine Variadic-Parameter: Sie können keine variablen Argumentlisten (...) als Promoted Properties verwenden:
// Dies funktioniert NICHT:
public function __construct(private string ...$tags) {}
  1. Komplexe Standardwerte: Komplexe oder berechnete Standardwerte müssen im Konstruktorrumpf zugewiesen werden:
class Bestellung {
    public function __construct(
        private string $id,
        private array $positionen = [],
        private ?DateTime $datum = null
    ) {
        $this->datum = $datum ?? new DateTime();
    }
}

20.6 Anwendungsfälle und Best Practices

Constructor Property Promotion ist besonders nützlich in folgenden Szenarien:

  1. Datenübertragende Objekte (DTOs) und Value Objects:
class BenutzerDTO {
    public function __construct(
        public readonly int $id,
        public readonly string $name,
        public readonly string $email
    ) {}
}
  1. Serviceklassen und Dependency Injection:
class BenutzerService {
    public function __construct(
        private BenutzerRepository $repository,
        private LoggerInterface $logger,
        private EventDispatcherInterface $eventDispatcher
    ) {}
}
  1. Konfigurationsobjekte:
class AppConfig {
    public function __construct(
        private readonly string $appName,
        private readonly string $version,
        private readonly array $supportedLocales,
        private readonly bool $debugMode = false
    ) {}
}

20.7 Integration mit Docblocks

Um die Codequalität weiter zu verbessern, können Sie Docblocks mit Constructor Property Promotion kombinieren:

class Zahlungsvorgang {
    public function __construct(
        /** Die eindeutige Transaktions-ID */
        private string $transaktionsId,
        
        /** Der zu zahlende Betrag in Cent */
        private int $betrag,
        
        /** Das verwendete Zahlungsmittel */
        private string $zahlungsmethode,
        
        /** Zeitpunkt der Zahlung */
        private ?DateTime $zeitpunkt = null
    ) {
        $this->zeitpunkt ??= new DateTime();
    }
}