Nachdem dieses Jahr die Scriptsprache PHP bereits zum 25. Mal ihren Jahrestag feierte, erschien heute auch das neue Major Release: PHP 8.
Neben einer ganzen Reihe neuer Funktionen und Verbesserungen enthält die neue Version von PHP aber auch nicht abwärtskompatible Änderungen. In diesem Beitrag möchte ich euch einen ersten Einblick in eben diese Änderungen und Verbesserungen geben. Zusätzlich verlinke ich euch für tiefergehende Informationen noch den PHP.net Beitrag zu jedem Thema.
Code sparen mit Constructor Property Promotion
Wenn eine Klasse mit Promoted Parametern verwendet wird, zum Beispiel für die Dependency Injection, kann die neue Syntax dabei unterstützen, ein wenig Code zu sparen.
Im bisherigen Aufbau mussten im Konstruktor noch die Parameter zu Properties zugewiesen werden. Dies entfällt bei der neuen Syntax:
// Alt class User { protected string $name; protected int $age; public function __construct(string $name, int $age) { $this->name = $name; $this->age = $age; } } // Neu class User { public function __construct( protected string $name, protected int $age ) {} }
Beitrag auf PHP.net zur Constructor Property Promotion
Match Expression als Alternative zu Switch
Mit der „match“ Funktion wurde eine Alternative zu „switch“ implementiert, wobei es aber noch einige wenige Unterschiede zwischen diesen gibt. Auch hier vereinfacht sich die Darstellung an Code deutlich:
// Abfrage mit switch switch ($user->getRole()) { case Role::USER: $isVisible = false; break; case Role::ADMIN: $isVisible = true; break; default: $isVisible = false; break; } // Abfrage mit match $isVisible = match ($user->getRole()) { Role::USER => false, Role::ADMIN => true, default => false, };
Beitrag auf PHP.net zur Match Expression
Attributes – endlich native Annotations für PHP
Aus vielen anderen Sprachen bekannt als Annotations, bekommt dieses Feature mit dem Namen Attributes nun auch sein Debüt in PHP. Mithilfe von Attributes lassen sich Meta-Angaben zu Funktionen oder Klassen definieren:
#[ExampleAttribute] class User { #[ExampleAttribute] public string $name; #[ExampleAttribute] public int $age; }
Beitrag über Attributes auf PHP.net
Mit union types mehrere Typen in PHP deklarieren
Mithilfe von union types können zum Beispiel für den Rückgabewert einer Methode mehrere Typen deklariert werden. In der Praxis kann dies wie folgt aussehen:
public function setLastLogin(DateTime|bool $lastLogin): User|Employee;
Beitrag zu union types auf PHP.net
Neue Datentypen „static“ & „mixed“
Mit PHP 8 wurden die neuen Datentypen „static“ und „mixed“ eingeführt.
„static“ kann hierbei wie folgt verwendet werden:
class User { public function test(): static { return new static(); } }
Bei dem neuen Typ „mixed“ können allerdings ein wenig „gemischte“ Gefühle entstehen.
Dies kommt daher, dass dieser Datentyp für eine Vielzahl an Typen steht und so unter Umständen zu einer schlechteren Code-Qualität beiträgt.
public function getUser(): mixed;
Beiträge auf PHP.net:
https://wiki.php.net/rfc/static_return_type
https://wiki.php.net/rfc/mixed_type_v2
Named Arguments zur Übergabe benannter Werte an Funktionen
Hiermit wird die Möglichkeit gegeben, benannte Werte an eine Funktion zu übergeben. Die Reihenfolge der Argumente spielt somit keine Rolle mehr und optionale Parameter können übersprungen werden:
$this->authenticate(user: $user, password: $password);
Abschließendes Komma in Parameterlisten
Seit PHP 7.3 ist es bereits möglich, Funktionen und Methoden mit einem abschließenden Komma in der Parameterliste aufzurufen. In PHP 8 wird dies nun so weit ergänzt, dass dies auch für die Deklaration der Funktion oder Methode gültig ist:
public function authenticate( User $user, string $password, ) { }
Beitrag auf PHP.net: https://wiki.php.net/rfc/trailing_comma_in_parameter_list
Nullsafe operator
Jeder von uns hat mit Sicherheit schon einmal folgende Fehlermeldung gelesen:
Call to a member function on null
Idealerweise wird der eigene Code so strukturiert, dass es gar nicht zu so einem Szenario kommen kann. Aber dies ist leider nicht immer möglich, weshalb oft Abfragen wie die folgende zum Einsatz kommen:
$user = $userManager->getUser(); if (!is_null($user)) { $username = $user->getUsername(); }
Mit dem neuen Nullsafe operator kann dies nun wie folgt vereinfacht werden:
$username = $userManager->getUser()?->getUsername();
Wenn die Methode „getUser“ ein Objekt vom Typ User zurückgibt, enthält die Variable „username“ nun einen entsprechenden String, falls nicht: null.
Beitrag über den nullsafe operator auf PHP.net
Weak maps
WeakMaps führen die Funktionalität der in PHP 7.4 eingeführten WeakRefs weiter fort.
Durch die WeakMaps können Objekte referenziert werden, die der Garbage Collector wieder zerstören kann. Gerade bei Systemen, die mit vielen Entitäten arbeiten, kann dies zu größeren Performance-Vorteilen führen.
class UserManager { private WeakMap $cache; public function getUserFromCache(object $user) { return $this->cache[$user] ??= $this->getUser($user); } }
Beitrag auf PHP.net: Weak Maps
Klassenkonstante bei Objekten
Eine kleine aber nützliche Neuerung ist, dass die „class“ Konstante nun nicht nur bei Klassen, sondern auch bei Objekten angewendet werden kann:
$user = new User(); var_dump($user::class);
Klassenkonstante bei Objekten auf PHP.net: https://wiki.php.net/rfc/class_name_literal_on_object
Stringable Interface
Mit dem neuen Stringable Interface wird nun eine Möglichkeit gegeben, die Implementation der „__toString“ Methode vorauszusetzen.
class User implements Stringable { public function __toString(): string { return $this->getUsername(); } } function showUsername(Stringable $user) { echo $user; }
Beitrag zum Stringable Interface auf PHP.net
Abstrakte Methoden in Traits deklarieren
Bis PHP 8 wurden abstrakte Deklarationen in Traits nicht validiert. Folgender Code war also gültig:
trait People { abstract public function getName(): string } class User { use People; public function getName() { return $this->name; } }
Ab PHP 8 sieht dies nun anders aus. Der Code müsste also wie folgt angepasst werden:
class User { use People; public function getName(): string { return $this->name; } }
Beitrag auf PHP.net: https://wiki.php.net/rfc/abstract_trait_method_validation
Exception handling
Ab PHP 8 ist es nicht mehr notwendig, beim Abfangen von Exceptions die Exception in einer Variable zu speichern:
try { $authenticator->authenticate($user); } catch (UserNotFoundException) { Log::error('...'); }
Excepting Handling auf PHP.net: https://wiki.php.net/rfc/non-capturing_catches
JIT
Kommen wir nun zu der wohl größten Änderung der neuen PHP Version: der Just in Time – JIT Compiler.
Um zu verstehen, was sich hier genau ändert, gehe ich zuerst einmal darauf ein, wie der Compiler bis PHP 8 arbeitet.
Der gesamte Prozess besteht dabei aus vier Schritten:
- Tokenisierung – Zeichen werden zu Einheiten zusammengefasst, Erkennen von Variablen und Befehlen
- Parsen – Es wird eine Hierarchische Struktur (AST) des Quellcodes erzeugt
- Kompilierung – Die AST-Nodes werden in Zend Opcodes übersetzt
- Interpretation – Die Zend Opcodes werden interpretiert und auf der Zend Virtual Machine ausgeführt.
Mit PHP 5.5 wurde bereits eine wichtige Ergänzung eingeführt: der OP-Cache. Hierbei wird der Opcode nach dem Kompilieren in einem Cache zwischengespeichert. Somit können bei der nächsten Ausführung die ersten drei Schritte übersprungen werden.
Was ändert sich nun mit der Einführung des JIT-Compilers? Mit JIT werden Teile des Zwischencodes direkt in Maschinencode übersetzt und somit nicht in der Zend VM, sondern direkt auf der CPU ausgeführt. Dies wird aber hauptsächlich einen Vorteil bei CPU-intensiven Prozessen bringen.
Bei typischen Anwendungsfällen, wie zum Beispiel dem Betreiben einer WordPress Webseite, wird dies zu nur wenigen Performance-Vorteilen verhelfen. Was dies aber an Performance bei rechenintensiven Scripten bringen kann, zeigt ein Experiment des Informatikers Pedro Escudero. Hier wurde ein PHP-Script in verschiedenen Umgebungen 100 Mal ausgeführt, um entsprechende durchschnittliche Bearbeitungszeiten zu ermitteln:
PHP 5.3 | 0,64574 s |
PHP 7.4 | 0,10254 s |
PHP 8 (ohne JIT) | 0,09822 s |
PHP 8 (mit JIT) | 0,04459 s |
Beitrag auf PHP.net: https://wiki.php.net/rfc/jit
Sonstige neue Funktionen und Breaking-Changes
Neben den hier vorgestellten Änderungen gibt es noch viele weitere neue oder geänderte Funktionalitäten.
Einen Überblick über diese zeigt PHP.net hier: PHP 8 Migration
Auch eine Übersicht von nicht abwärtskompatiblen Änderungen stellt PHP.net zur Verfügung: PHP 8 Backward Incompatible Changes