Ein Blick auf PHP 8: Teil 8

PHP 8.0-Features im Fokus: Constructor Property Promotion
Keine Kommentare

PHP 8 wurde veröffentlicht und mit ihm eine Reihe von Verbesserungen, neuen Funktionen und einer generellen Überarbeitung, um die weit verbreitete serverseitige Websprache noch besser zu machen. In dieser Serie wollen wir darauf eingehen, was Sie über die neue Version wissen sollten.

Im letzten Teil haben wir uns angesehen, wie PHP durch die neue Version 8.0 schneller wird. Heute werfen wir einen Blick darauf, wie PHP 8.0 den Entwicklern selbst hilft, effizienter zu arbeiten.

Der vielleicht größte Beitrag dazu, die Quality-of-Life mit PHP 8.0 zu erhöhen, besteht in der Constructor Property Promotion. Die Syntax wurde zu einem großen Teil von Hack, Facebooks PHP-Fork, übernommen, obwohl sie in anderen Sprachen wie TypeScript und Kotlin mit verschiedenen Syntaxen existiert. Am leichtesten ist das anhand eines Beispiels zu erklären. Man stelle sich die typische Service-Klasse einer modernen Applikation vor:

<?php

class FormRenderer
{
    private ThemeSystem $theme;
    private UserRepository $users;
    private ModuleSystem $modules;

    public function __construct(ThemeSystem $theme, UserRepository $users, ModuleSystem $modules) {
        $this->theme = $theme;
        $this->users = $users;
        $this->modules = $modules;
    }

    public function renderForm(Form $form): string
    {
        // ...
    }
}

Diese Klasse hat drei Dependencies. Jede einzelne muss vier Mal benannt werden: Einmal, um die Eigenschaft zu beschreiben, einmal für die Constructor-Parameter, einmal beim Referenzieren des Paramters und einmal beim Referenzieren der Eigenschaft. Das ist ziemlich langatmig. Manche IDEs unterstützen hier mittels einer Codevervollständigung, aber auch dann ist es noch umständlich. Es gibt Entwickler, die nur wegen dieser Langatmigkeit die Verwendung von Klassen und Dependency Injection vermeiden. Ab PHP 8.0 lässt sich das auf einen Punkt zusammenfassen:

<?php

class FormRenderer
{
    public function __construct(
        private ThemeSystem $theme, 
        private UserRepository $users, 
        private ModuleSystem $modules,
    ) { }

    public function renderForm(Form $form): string
    {
        // ...
    }
}

Wird der Visibility Marker auf die Constructor-Beschreibung bewegt, übermittelt man PHP damit: „Dieser Parameter ist auch eine Eigenschaft und du solltest ihm den angegebenen Wert zuteilen.“ Der Effekt in der Runtime ist genau der gleiche, wie beim ersten Beispiel. Allerdings braucht es nur ein Viertel so viel Aufwand beim Tippen. Falls jemandem das nachgestellte Komma in der Parameterliste aufgefallen ist: Das haben wir genau für dieses Feature hinzugefügt. Es ist immer noch möglich, weitere Eigenschaften auf die herkömmliche Art zu beschreiben und der Constructor kann auch immer noch einen Body haben. In diesem Beispiel ist der Body leer, aber falls es noch mehr Arbeit im Constructor zu erledigen gibt als den Eigenschaften nur irgendetwas zuzuweisen, dann kann der Code immer noch eingefügt werden und wird nach der Anweisung (oder „Promotion“) ausgeführt.

<?php

class FormRenderer
{
    private User $currentUser;

    public function __construct(
        private ThemeSystem $theme, 
        private UserRepository $users, 
        private ModuleSystem $modules,
    ) { 
        $this->currentUser = $this->users->getActiveUser();
    }

    public function renderForm(Form $form): string
    {
        // ...
    }
}

In der Praxis bestanden die große Mehrheit der Klassen, die ich in den letzten Jahren geschrieben habe, aus solch „langweiligen“ Constructors. Das bedeutet, dass die Promotion von Eigenschaften die Arbeit zukünftig extrem reduzieren wird.

Daten und Values

Das Promoten von Constructor-Eigenschaften ist für Value-Objekte äußerst wertvoll. (Value- und Service-Objekte sollten für PHP-Entwickler zu den am häufigsten auftauchenden Klassen gehören, es gibt nur wenig andere gute Modelle in PHP.) Viele Value-Objekte (oder Datentransfer-Objekte oder eine Reihe anderer Synonyme für sie) sind in der Praxis wenig mehr als Structs: Eine Ansammlung von benannten Eigenschaften, die möglicherweise Getter und Setter haben. In diesem Fall kann die ganze Klasse auf etwas mehr als einen Constructor reduziert werden:

<?php

Class MailMessage
{
    public function __construct(
        public string $to,
        public string $subject,
        public string $from,
        public string $body,
        public array $cc,
        public array $bcc,
        public string $attachmentPath,
    ) {}
}

Einigen mag es Unbehagen bereiten, dass die Eigenschaften auf „public“ gesetzt wurden. Allgemein würde ich sagen, dass das für ein internes (also nicht Teil vom API) Objekt in Ordnung ist. Aber für ein Objekt, das Teil eines öffentlichen APIs ist, lohnt es sich, wenn man eine Reihe an Getter-Methoden erstellt und die Eigenschaften privat stellt. Es ist nicht nur weniger Arbeit als vorher nötig, auch geht diese Variante noch effizienter mit dem Speicher um als es bei der Nutzung von Arrays der Fall wäre. Es ist außerdem möglich, nur einige Constructor-Parameter zu promoten und andere nicht. Letztere werden sich genauso verhalten, wie sie es immer getan haben.

An dieser Stelle sei auf einige Stolpersteine hingewiesen:

  • Die Schlüsselwörter für Sichtbarkeit gelten nur für Constructors. Werden sie in einer anderen Funktions- oder Methodensignatur verwendet, wird ein Fehler erzeugt.
  • Eine Eigenschaft darf nicht sowohl in der Constructor-Signatur als auch allein definiert werden. Falls die Eigenschaft mehr braucht, als in der Signatur enthalten sein kann, dann sollte man den bisherigen Weg gehen. Das funktioniert auch weiterhin gut.
  • Eigenschaften können nicht als aufrufbar (aufgrund der Komplexität der Implementierung können wir hier nicht tiefer darauf eingehen) typisiert werden, genauso wenig wie Eigenschaften, die vom Constructor promotet werden. Sie können allerdings ohne Typisierung bleiben.
  • Variadic Arguments können nicht promotet werden, weil es dadurch schwieriger wird, mit den Typen zu arbeiten.

Wieder einmal müssen wir uns bei Nikita Popov für diesen RFC  bedanken, auch wenn der Anlass ein Blogpost war, der Anfang letzten Jahres von meiner Wenigkeit verfasst wurde. Also beanspruche ich 0,1% des Ruhmes.

Wir sind auf der Zielgeraden. Es lohnt sich nächste Woche wieder vorbeizuschauen, wenn wir die nächsten PHP-8.0-Features behandeln.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Abonnieren
Benachrichtige mich bei
guest
0 Comments
Inline Feedbacks
View all comments
X
- Gib Deinen Standort ein -
- or -