Seit dem 25. November 2021 ist PHP 8.1 endlich da, vollgepackt mit vielen spannenden Funktionen.

In diesem Artikel werden wir detailliert auf die Neuerungen in PHP 8.1 eingehen. Von neuen Features und Leistungsverbesserungen bis hin zu signifikanten Änderungen und Verwerfungen werden wir sie alle im Detail durchgehen.

Bleib dran!

Neue Funktionen in PHP 8.1

Beginnen wir damit, alle neuen Features in PHP 8.1 zu beschreiben. Es ist eine ziemlich lange Liste.

.

Reine Schnittmengen-Typen

PHP 8.1 fügt Support für Kreuzungstypen hinzu. Es ist ähnlich wie die Union-Typen, die in PHP 8.0 eingeführt wurden, aber ihr Verwendungszweck ist das genaue Gegenteil.

Um es besser zu verstehen, lass uns noch einmal auffrischen, wie Typdeklarationen in PHP funktionieren.

Im Wesentlichen kannst du Typdeklarationen zu Funktionsargumenten, Rückgabewerten und Klasseneigenschaften hinzufügen. Diese Zuweisung wird Type Hinting genannt und stellt sicher, dass der Wert zur Aufrufzeit vom richtigen Typ ist. Ansonsten wirft es sofort einen TypeError aus. Das wiederum hilft dir, Code besser zu debuggen.

Allerdings hat die Deklaration eines einzelnen Typs seine Grenzen. Union-Typen helfen dir, das zu überwinden, indem sie dir erlauben, einen Wert mit mehreren Typen zu deklarieren, wobei die Eingabe mindestens einem der deklarierten Typen genügen muss.

Auf der anderen Seite beschreibt der RFC Kreuzungstypen als:

Ein „Kreuzungstyp“ erfordert, dass ein Wert mehrere Type Constraints erfüllt, anstatt nur einen.

…reine Kreuzungstypen werden mit der Syntax T1&T2&… spezifiziert und können an allen Stellen verwendet werden, an denen derzeit Typen akzeptiert werden…

Beachte die Verwendung des & (AND) Operators, um Schnittmengen zu deklarieren. Im Gegensatz dazu verwenden wir den | (OR) Operator, um Vereinigungstypen zu deklarieren.

Die Verwendung der meisten Standardtypen in einem Kreuzungstyp wird zu einem Typ führen, der niemals erfüllt werden kann (z.B. Integer und String). Daher können Kreuzungstypen nur Klassentypen enthalten (d.h. Interfaces und Klassennamen).

Hier ist ein Beispielcode, wie du Kreuzungstypen verwenden kannst:

class A {
    private Traversable&Countable $countableIterator;
 
    public function setIterator(Traversable&Countable $countableIterator): void {
        $this->countableIterator = $countableIterator;
    }
 
    public function getIterator(): Traversable&Countable {
        return $this->countableIterator;
    }
}

Im obigen Code haben wir eine Variable countableIterator als eine Kreuzung zweier Typen definiert: Traversable und Countable. In diesem Fall sind die beiden deklarierten Typen Interfaces.

Schnittpunkttypen entsprechen auch den Standard PHP Varianzregeln, die bereits für Typüberprüfung und Vererbung verwendet werden. Aber es gibt zwei zusätzliche Regeln, wie Kreuzungstypen mit der Subtypisierung interagieren. Du kannst mehr über die Varianzregeln von Intersektionstypen in ihrem RFC lesen.

In einigen Programmiersprachen kannst du Union Types und Intersection Types in der gleichen Deklaration kombinieren. Aber PHP 8.1 verbietet es. Daher wird seine Implementierung „reine“ Kreuzungstypen genannt. Der RFC erwähnt jedoch, dass es „ein zukünftiger Bereich“ ist.

Enums

PHP 8.1 fügt endlich Support für Enums (auch Aufzählungen oder enumerated types genannt) hinzu. Sie sind ein benutzerdefinierter Datentyp, der aus einer Menge von möglichen Werten besteht.

Das häufigste Beispiel für Enums in Programmiersprachen ist der boolesche Typ, mit true und false als zwei mögliche Werte. Es ist so weit verbreitet, dass es in vielen modernen Programmiersprachen eingebaut ist.

Gemäß dem RFC werden Enums in PHP zunächst auf „unit enumerations“ beschränkt sein:

Der Anwendungsbereich dieses RFCs ist auf „unit enumerations“ beschränkt, d.h. Aufzählungen, die selbst ein Wert sind und nicht nur eine ausgefallene Syntax für eine primitive Konstante, und die keine zusätzlichen zugehörigen Informationen enthalten. Diese Fähigkeit bietet einen stark erweiterten Support für die Datenmodellierung, benutzerdefinierte Typdefinitionen und monadenartiges Verhalten. Enums ermöglichen die Modellierungstechnik des „ungültige Zustände nicht repräsentierbar machen“, was zu robusterem Code mit weniger Bedarf an ausführlichen Tests führt.

Um zu diesem Stadium zu gelangen, hat das PHP-Team viele Sprachen untersucht, die bereits Enumerationen unterstützen. Ihre Umfrage ergab, dass man Aufzählungen in drei allgemeine Gruppen kategorisieren kann: Fancy Constants, Fancy Objects und vollständige algebraische Datentypen (ADTs). Es ist eine interessante Lektüre!

PHP implementiert „Fancy Objects“ Enums, mit Plänen, es in der Zukunft auf volle ADTs zu erweitern. Es ist konzeptionell und semantisch an die Aufzählungstypen in Swift, Rust und Kotlin angelehnt, auch wenn es nicht direkt an einen dieser Typen angelehnt ist.

Der RFC verwendet die berühmte Analogie der Farben in einem Kartenspiel, um zu erklären, wie es funktionieren wird:

enum Suit {
  case Hearts;
  case Diamonds;
  case Clubs;
  case Spades;
}

Hier definiert das Suit enum vier mögliche Werte: Herz, Karo, Kreuz und Pik. Du kannst direkt auf diese Werte zugreifen, indem du die Syntax verwendest: Suit::Hearts, Suit::Diamonds, Suit::Clubs, und Suit::Spades.

Diese Verwendung mag vertraut erscheinen, da Enums auf Klassen und Objekten aufgebaut sind. Sie verhalten sich ähnlich und haben fast die gleichen Anforderungen. Enums teilen sich die gleichen Namespaces wie Klassen, Interfaces und Traits.

Die oben erwähnten Enums werden Pure Enums genannt.

Du kannst auch Backed Enums definieren, wenn du allen Fällen einen skalaren Ersatzwert geben willst. Allerdings können Backed Enums nur einen Typ haben, entweder int oder string (niemals beides).

enum Suit: string {
  case Hearts = 'H';
  case Diamonds = 'D';
  case Clubs = 'C';
  case Spades = 'S';
}

Außerdem müssen alle verschiedenen Fälle einer backed enum einen eindeutigen Wert haben. Und du kannst niemals pure und backed enums mischen.

Der RFC geht weiter auf Enum Methoden, statische Methoden, Konstanten, konstante Ausdrücke und vieles mehr ein. Sie alle zu behandeln, würde den Rahmen dieses Artikels sprengen. Du kannst die Dokumentation zu Rate ziehen, um dich mit all ihren Vorzügen vertraut zu machen.

Der never Return Type

PHP 8.1 fügt einen neuen Rückgabetyp-Hinweis namens never hinzu. Es ist super hilfreich für Funktionen, die immer einen Throw oder Exit auslösen.

Wie im RFC beschrieben, sind URL-Redirect-Funktionen, die sich immer exit (explizit oder implizit), ein gutes Beispiel für seine Verwendung:

function redirect(string $uri): never {
    header('Location: ' . $uri);
    exit();
}
 
function redirectToLoginPage(): never {
    redirect('/login');
}

Eine never-declared function sollte drei Bedingungen erfüllen:

  • Es sollte kein explizites Return-Statement definiert sein.
  • Es sollte keine implizite Return-Anweisung enthalten (z.B. if-else-Anweisungen).
  • Es muss seine Ausführung mit einer Exit-Anweisung beenden (explizit oder implizit).

Das obige Beispiel der URL-Umleitung zeigt sowohl die explizite als auch die implizite Verwendung des never return Typs.

Der never return Typ hat viele Ähnlichkeiten mit dem void return Typ. Sie stellen beide sicher, dass die Funktion oder Methode keinen Wert zurückgibt. Jedoch unterscheidet er sich durch die Einhaltung strengerer Regeln. Zum Beispiel kann eine void-declared Funktion immer noch ohne einen expliziten Wert zurückkehren, aber du return das gleiche nicht mit einer never-declared Funktion tun.

Als Faustregel gilt: Verwende void, wenn du erwartest, dass PHP nach dem Funktionsaufruf weiter ausgeführt wird. Verwende never, wenn du das Gegenteil willst.

Außerdem ist never als „bottom“-Typ definiert. Daher kann jede Klassenmethode, die als never deklariert ist, ihren Rückgabetyp „never“ in etwas anderes ändern. Allerdings kannst du eine void-deklarierte Methode mit einer never-deklarierten Methode erweitern.

Fibers

Historisch gesehen ist PHP-Code fast immer synchroner Code. Die Codeausführung hält an, bis das Ergebnis zurückgegeben wird, auch bei I/O-Operationen. Du kannst dir vorstellen, warum dieser Prozess die Codeausführung langsamer machen kann.

Es gibt mehrere Lösungen von Drittanbietern, um dieses Hindernis zu überwinden und es Entwicklern zu ermöglichen, PHP-Code asynchron zu schreiben, insbesondere für gleichzeitige I/O-Operationen. Einige beliebte Beispiele sind amphp, ReactPHP und Guzzle.

Allerdings gibt es keinen Standardweg, um solche Instanzen in PHP zu behandeln. Außerdem führt die Behandlung von synchronem und asynchronem Code im selben Aufrufstapel zu anderen Problemen.

Fibers sind PHPs Art, Parallelität über virtuelle Threads (oder grüne Threads) zu behandeln. Es versucht, den Unterschied zwischen synchronem und asynchronem Code zu eliminieren, indem es PHP-Funktionen erlaubt, zu unterbrechen, ohne den gesamten Aufrufstapel zu beeinflussen.

Hier ist, was der RFC verspricht:

  • Hinzufügen des Supports für Fibers in PHP.
  • Einführung einer neuen Fiber-Klasse und der entsprechenden Reflection-Klasse ReflectionFiber.
  • Hinzufügen der Ausnahmeklassen FiberError und FiberExit zur Darstellung von Fehlern.
  • Fibers ermöglichen transparente nicht-blockierende I/O-Implementierungen von existierenden Interfaces (PSR-7, Doctrine ORM, etc.). Das liegt daran, dass das Platzhalter (promise) Objekt eliminiert wird. Stattdessen können Funktionen den I/O-Ergebnistyp deklarieren anstelle eines Platzhalterobjekts, das keinen Auflösungstyp angeben kann, da PHP keine Generics unterstützt.

Du kannst Fibers nutzen, um Full-Stack, unterbrechbare PHP-Funktionen zu entwickeln, die du dann nutzen kannst, um kooperatives Multitasking in PHP zu implementieren. Da Fibers den gesamten Ausführungsstapel unterbricht, kannst du sicher sein, dass es den Rest deines Codes nicht beeinträchtigt.

Diagramm zur Veranschaulichung des Ablaufs von PHP-Code mit Fibers (Quelle: PHP.net).
Diagramm zur Veranschaulichung des Ablaufs von PHP-Code mit Fibers (Quelle: PHP.net).

Um die Verwendung von Fibers zu veranschaulichen, verwendet der RFC dieses einfache Beispiel:

$fiber = new Fiber(function (): void {
    $value = Fiber::suspend('fiber');
    echo "Value used to resume fiber: ", $value, "\n";
});
 
$value = $fiber->start();
 
echo "Value from fiber suspending: ", $value, "\n";
 
$fiber->resume('test');

Du erstellst im obigen Code eine „Faser“ und setzt sie sofort mit dem String fiber aus. Die echo-Anweisung dient als visueller Hinweis für die Wiederaufnahme der Faser.

Du kannst diesen String-Wert durch den Aufruf von $fiber->start() abrufen.

Dann nimmst du die Faser mit der Zeichenkette „test“ wieder auf, die vom Aufruf von Fiber::suspend() zurückgegeben wird. Die vollständige Codeausführung führt zu einer Ausgabe, die lautet:

Value from fiber suspending: fiber
Value used to resume fiber: test

Das ist das einfache Lehrbuchbeispiel für PHP Fibers bei der Arbeit. Hier ist ein weiteres Fibers-Beispiel für die Ausführung von sieben asynchronen GET-Anfragen.

Trotz allem werden die meisten PHP-Entwickler nie direkt mit Fibers zu tun haben. Und der RFC legt das auch nahe:

Fibers sind ein fortgeschrittenes Feature, das die meisten Benutzer nicht direkt nutzen werden. Dieses Feature richtet sich in erster Linie an Bibliotheks- und Framework-Autoren, um eine Ereignisschleife und eine asynchrone Programmier-API bereitzustellen. Fibers ermöglichen die nahtlose Integration von asynchroner Codeausführung in synchronen Code an jedem beliebigen Punkt, ohne dass der Aufrufstapel der Anwendung geändert oder Boilerplate-Code hinzugefügt werden muss. 

Es wird nicht erwartet, dass die Fiber API direkt im Code auf Anwendungsebene verwendet wird. Fibers bieten eine grundlegende, Low-Level-API zur Flusskontrolle, um Abstraktionen auf höherer Ebene zu erstellen, die dann im Anwendungscode verwendet werden.

In Anbetracht der Performance-Vorteile kann man davon ausgehen, dass PHP-Bibliotheken und -Frameworks die Vorteile dieses neuen Features nutzen werden. Es wird interessant sein zu sehen, wie sie Fibers in ihrem Ökosystem implementieren.

Neue readonly Eigenschaften

PHP 8.1 fügt den Support für readonly Properties hinzu. Sie können nur einmal aus dem Scope, in dem sie deklariert wurden, initialisiert werden. Einmal initialisiert, kannst du ihren Wert nicht mehr ändern. Wenn du das tust, wird eine Error-Exception ausgelöst.

Der RFC lautet:

Eine Readonly-Eigenschaft kann nur einmal initialisiert werden und nur von dem Bereich, in dem sie deklariert wurde. Jede andere Zuweisung oder Änderung der Eigenschaft führt zu einer Fehler-Ausnahme.

Hier ist ein Beispiel, wie du es verwenden kannst:

class Test {
    public readonly string $kinsta;
 
    public function __construct(string $kinsta) {
        // Legal initialization.
        $this->kinsta = $kinsta;
    }
}

Einmal initialisiert, gibt es kein Zurück mehr. Dieses Feature in PHP eingebaut zu haben, reduziert den Boilerplate-Code, der oft verwendet wird, um diese Funktionalität zu aktivieren, erheblich.

Die readonly Eigenschaft bietet eine starke Unveränderlichkeitsgarantie, sowohl innerhalb als auch außerhalb der Klasse. Es spielt keine Rolle, welcher Code dazwischen läuft. Der Aufruf einer readonly Eigenschaft wird immer den gleichen Wert zurückgeben.

Allerdings kann die Verwendung der readonly-Eigenschaft in bestimmten Anwendungsfällen nicht ideal sein. Zum Beispiel kannst du sie nur neben einer typisierten Eigenschaft verwenden, da Deklarationen ohne Typ implizit null sind und nicht readonly sein können.

Außerdem macht das Setzen einer readonly-Eigenschaft die Objekte nicht unveränderlich. Die readonly-Eigenschaft wird das gleiche Objekt halten, aber das Objekt selbst kann sich ändern.

Ein weiteres kleines Problem mit dieser Eigenschaft ist, dass man sie nicht klonen kann. Es gibt bereits einen Workaround für diesen speziellen Anwendungsfall. Schau es dir bei Bedarf an.

Definiere final Klassen-Konstanten

Seit PHP 8.0 kannst du Klassenkonstanten mit ihren Unterklassen überschreiben. Das liegt an der Art und Weise, wie Vererbung in PHP implementiert ist.

Hier ist ein Beispiel, wie du den Wert einer zuvor deklarierten Konstante überschreiben kannst:

class Moo
{
    public const M = "moo";
}
 
class Meow extends Moo
{
    public const M = "meow";
}  

Wenn die Bedingungen strenger werden sollen (zumindest bei Konstanten), dann können sie das mit dem neuen final-Modifikator von PHP 8.1 tun.

Sobald du eine Konstante als final deklariert hast, bedeutet es das.

class Moo
{
    final public const M = "moo";
}
 
class Meow extends Moo
{
    public const M = "meow";
}
 
// Fatal error: Meow::M cannot override final constant Moo::M

Du kannst mehr darüber in den finalen Klassenkonstanten PHP RFC lesen.

Neue fsync() und fdatasync() Funktionen

PHP 8.1 fügt zwei neue Dateisystemfunktionen namens fsync() und fdatasync() hinzu. Sie werden denjenigen bekannt vorkommen, die mit den gleichnamigen Linux-Funktionen vertraut sind. Das liegt daran, dass sie miteinander verwandt sind, nur eben für PHP implementiert.

Tatsächlich war diese Ergänzung schon lange überfällig. PHP ist eine der wenigen großen Programmiersprachen, die fsync() und fdatasync() noch nicht implementiert haben – zumindest bis PHP 8.1.

Die fsync()-Funktion ähnelt der bereits existierenden fflush()-Funktion von PHP, aber sie unterscheidet sich in einem Punkt deutlich. Während fflush() die internen Puffer der Anwendung an das Betriebssystem leert, geht fsync() einen Schritt weiter und stellt sicher, dass die internen Puffer an den physischen Speicher geleert werden. Das stellt sicher, dass die Daten vollständig und persistent geschrieben werden, so dass du sie auch nach einem Applikations- oder Systemabsturz wieder abrufen kannst.

Hier ist ein Beispiel, wie du es verwenden kannst.

$doc = 'kinsta.txt';

$kin = fopen($doc, 'ki');
fwrite($kin, 'doc info');
fwrite($kin, "\r\n");
fwrite($kin, 'more info');

fsync($kin);
fclose($kin);

Das Hinzufügen des fsync()-Aufrufs am Ende stellt sicher, dass alle Daten, die im PHP- oder OS-internen Puffer gehalten werden, in den Speicher geschrieben werden. Alle anderen Codeausführungen werden bis dahin blockiert.

Die verwandte Funktion ist fdatasync(). Benutze es, um Daten zu synchronisieren, aber nicht unbedingt Metadaten. Für Daten, deren Metadaten nicht essentiell sind, macht dieser Funktionsaufruf den Schreibprozess einen Tick schneller.

Du solltest jedoch beachten, dass PHP 8.1 fdatasync() unter Windows noch nicht vollständig unterstützt. Es fungiert lediglich als Alias von fsync(). Unter POSIX ist fdatasync() korrekt implementiert.

Neue array_is_list() Funktion

PHP Arrays können sowohl Integer- als auch String-Schlüssel enthalten. Das bedeutet, dass du sie für verschiedene Dinge verwenden kannst, einschließlich Listen, Hash-Tabellen, Wörterbücher, Sammlungen, Stacks, Warteschlangen und vieles mehr. Du kannst sogar Arrays innerhalb von Arrays haben und so multidimensionale Arrays erstellen.

Du kannst effizient überprüfen, ob ein bestimmter Eintrag ein Array ist, aber es ist nicht so einfach zu überprüfen, ob es irgendwelche fehlenden Array-Offsets, ungeordnete Keys, etc. hat. Kurz gesagt, du kannst nicht schnell überprüfen, ob ein Array eine Liste ist.

Die Funktion array_is_list() prüft, ob die Schlüssel eines Arrays in sequentieller Reihenfolge, beginnend bei 0, und ohne Lücken sind. Wenn alle Bedingungen erfüllt sind, gibt sie true zurück. Standardmäßig gibt sie auch true für leere Arrays zurück.

Hier sind ein paar Beispiele für die Verwendung der Funktion, wenn sowohl die true als auch die false Bedingungen erfüllt sind:

// true array_is_list() examples
array_is_list([]); // true
array_is_list([1, 2, 3]); // true
array_is_list(['cats', 2, 3]); // true
array_is_list(['cats', 'dogs']); // true
array_is_list([0 => 'cats', 'dogs']); // true
array_is_list([0 => 'cats', 1 => 'dogs']); // true 

// false array_is_list() examples 
array_is_list([1 => 'cats', 'dogs']); // as first key isn't 0
array_is_list([1 => 'cats', 0 => 'dogs']); // keys are out of order
array_is_list([0 => 'cats', 'bark' => 'dogs']); // non-integer keys
array_is_list([0 => 'cats', 2 => 'dogs']); // gap in between keys 

Eine PHP-Array-Liste mit ungeordneten Schlüsseln ist eine potentielle Quelle für Fehler. Die Verwendung dieser Funktion, um eine strikte Einhaltung der Listenanforderungen zu erzwingen, bevor man mit der Codeausführung fortfährt, ist eine großartige Ergänzung zu PHP.

Neue Sodium XChaCha20 Funktionen

Sodium ist eine moderne, einfach zu benutzende kryptographische Bibliothek für Verschlüsselung, Entschlüsselung, Passwort-Hashing, Signaturen und mehr. Das PECL libsodium Paket fügt einen Wrapper für Sodium hinzu, damit PHP-Entwickler es nutzen können.

Auch führende Tech-Unternehmen wie Facebook, Discord, Malwarebytes und Valve nutzen libsodium, um ihre Nutzer mit schnellen und sicheren Verbindungen zu schützen.

libsodium unterstützt den XChaCha20 Verschlüsselungsalgorithmus, um Daten zu ver- und entschlüsseln, insbesondere für die Stream-Verschlüsselung. Ebenso unterstützt die PECL libsodium Erweiterung bereits XChaCha20, allerdings nur mit Poly1305 message-authentication code.

Viele PHP-Anwendungen nutzen XChaCha20 direkt für die Stream-Verschlüsselung. Um die Dinge einfacher zu machen, gibt es ab PHP 8.1 drei neue Funktionen, um Daten mit XChaCha20 zu ver- oder entschlüsseln, ohne dass eine Authentifizierung involviert ist. Dieser Modus wird „detached mode“ genannt.

Die neu eingeführten XChaCha20-Funktionen sind:

  • sodium_crypto_stream_xchacha20_keygen: Gibt einen sicheren Zufallsschlüssel für die Verwendung mit sodium_crypto_stream_xchacha20 zurück.
  • sodium_crypto_stream_xchacha20: Expandiert den Schlüssel und die Nonce in einen Keystream aus pseudozufälligen Bytes.
  • sodium_crypto_stream_xchacha20_xor: Verschlüsselt eine Nachricht mit einer Nonce und einem geheimen Schlüssel (keine Authentifizierung).

Zusätzlich gibt es zwei neue PHP-Konstanten, die im globalen Namespace definiert sind:

  • SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES (zugewiesen 32)
  • SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES (zugewiesen 24)

Benutze es aber mit Vorsicht. Da es ohne Authentifizierung funktioniert, ist die Entschlüsselung anfällig für gängige Ciphertext-Angriffe.

Du kannst mehr über die Verwendung und die Anforderungen auf der GitHub Seite lesen.

Neue IntlDatePatternGenerator Klasse

Die zugrundeliegende ICU-Bibliothek von PHP unterstützt die Erstellung von lokalisierten Datums- und Zeitformaten, ist aber nicht vollständig anpassbar.

Wenn du z.B. bis PHP 8.0 lokal-spezifische Daten- und Zeitformate erstellen möchtest, kannst du die vordefinierte Konstante IntlDateFormatter verwenden, um dies auf 6 Arten zu tun:

  • IntlDateFormatter::LONG: Lang, wie 10. November 2017 oder 11:22:33 Uhr
  • IntlDateFormatter::MEDIUM: Etwas kürzer, wie z.B. Nov 10, 2017
  • IntlDateFormatter::SHORT: Nur numerisch, wie 10/11/17 oder 11:22pm

Jede dieser Varianten hat auch ihre eigene RELATIVE_-Variante, die die Datumsformatierung in einem begrenzten Bereich vor oder nach dem aktuellen Datum setzt. In PHP sind das die Werte gestern, heute und morgen.

Angenommen, du möchtest die lange Variante für das Jahr und die kurze Variante für den Monat verwenden, wie z.B. 10/11/2017. Ab PHP 8.0 kannst du das nicht.

In PHP 8.1+ kannst du mit der neuen Klasse IntlDatePatternGenerator festlegen, welche Formate für das Datum, den Monat und die Uhrzeit verwendet werden sollen. Die genaue Reihenfolge dieser Komponenten kannst du dem Formatierer überlassen.

Du solltest beachten, dass diese Klasse zwar nur das Wort Date in sich trägt, aber mit dem DateTimePatternGenerator von ICU übereinstimmt. Das bedeutet, dass du sie auch benutzen kannst, um flexible Zeitformate zu erstellen. Um die Namensgebung zu vereinfachen, hat sich das PHP-Team für den kürzeren Begriff IntlDatePatternGenerator entschieden.

Hier ist ein Beispiel direkt aus dem RFC:

$skeleton = "YYYYMMdd";
 
$today = \DateTimeImmutable::createFromFormat('Y-m-d', '2021-04-24');
 
$dtpg = new \IntlDatePatternGenerator("de_DE");
$pattern = $dtpg->getBestPattern($skeleton);
echo "de: ", \IntlDateFormatter::formatObject($today, $pattern, "de_DE"), "\n";
 
$dtpg = new \IntlDatePatternGenerator("en_US");
$pattern = $dtpg->getBestPattern($skeleton), "\n";
echo "en: ", \IntlDateFormatter::formatObject($today, $pattern, "en_US"), "\n";
 
/*
de: 24.04.2021
en: 04/24/2021
*/

Im obigen Code definiert die Skelettvariable die jeweiligen Datums- oder Zeitformate, die verwendet werden sollen. Der Formatierer kümmert sich jedoch um die Reihenfolge des Endergebnisses.

Support für AVIF Image Format

AVIF, oder AV1 Image File Format, ist ein relativ neues lizenzfreies Bildformat, das auf dem AV1 Videocodierungsformat basiert. Es bietet nicht nur eine höhere Komprimierung (und damit kleinere Dateigrößen), sondern unterstützt auch verschiedene Features wie Transparenz, HDR und mehr.

Das AVIF-Format wurde erst kürzlich (8. Juni 2021) standardisiert. Das hat den Weg für Browser, wie Chrome 85+ und Firefox 86+, geebnet, die Unterstützung für AVIF-Bilder hinzufügen.

PHP 8.1’s Bildverarbeitung und GD-Erweiterung fügt Unterstützung für AVIF-Bilder hinzu.

Um diese Funktionalität einzubinden, musst du jedoch die GD-Erweiterung mit AVIF-Unterstützung kompilieren. Du kannst dies tun, indem du die folgenden Befehle ausführst.

Für Debian/Ubuntu:

apt install libavif-dev

Für Fedora/RHEL:

dnf install libavif-devel

Das wird alle aktuellen Abhängigkeiten installieren. Als nächstes kannst du die AVIF Unterstützung kompilieren, indem du das --with-avif Flag mit dem ./configure Skript ausführst.

./buildconf --force
./configure --enable-gd --with-avif

Wenn du eine neue Umgebung von Grund auf neu aufbaust, kannst du hier auch andere PHP-Erweiterungen aktivieren.

Nach der Installation kannst du testen, ob die AVIF-Unterstützung aktiviert ist, indem du den folgenden Befehl in deinem PHP-Terminal ausführst:

php -i | grep AVIF

Wenn du AVIF korrekt installiert hast, wirst du folgendes Ergebnis sehen:

AVIF Support => enabled

Du kannst auch den gd_info()-Aufruf verwenden, um eine Liste der GD-Funktionen abzurufen, einschließlich der Frage, ob die AVIF-Support-Funktionalität aktiviert ist.

Diese aktualisierte PHP 8.1 GD Erweiterung fügt auch zwei neue Funktionen für die Arbeit mit AVIF Bildern hinzu: imagecreatefromavif und imageavif. Sie funktionieren ähnlich wie ihre JPEG und PNG Gegenstücke.

Die imagecreatefromavif-Funktion gibt eine GdImage-Instanz von einem gegebenen AVIF-Bild zurück. Du kannst diese Instanz dann verwenden, um das Bild zu bearbeiten oder zu konvertieren.

Die andere imageavif Funktion gibt die AVIF Bilddatei aus. Du kannst sie zum Beispiel benutzen, um ein JPEG nach AVIF zu konvertieren:

$image = imagecreatefromjpeg('image.jpeg');
imageavif($image, 'image.avif');

Du kannst mehr über diese neue Funktion auf der GitHub Seite lesen.

Neue $_FILES: full_path Schlüssel für Verzeichnis Uploads

PHP verwaltet eine große Anzahl von vordefinierten Variablen, um verschiedene Dinge zu verfolgen. Eine davon ist die Variable $_FILES, die ein assoziatives Array von Elementen enthält, die über die HTTP POST Methode hochgeladen werden.

Die meisten modernen Browser unterstützen das Hochladen eines ganzen Verzeichnisses mit HTML-Datei-Upload-Feldern. Sogar PHP <8.1 unterstützte diese Funktionalität, aber mit einem großen Vorbehalt. Du konntest einen Ordner nicht mit seiner genauen Verzeichnisstruktur oder relativen Pfaden hochladen, da PHP diese Informationen nicht an das $_FILES Array weitergab.

Das ändert sich in PHP 8.1 mit der Hinzufügung eines neuen Schlüssels namens full_path zum $_FILES Array. Mit diesen neuen Daten kannst du relative Pfade speichern oder die genaue Verzeichnisstruktur auf dem Server duplizieren.

Du kannst diese Informationen testen, indem du das $FILES Array mit dem var_dump($_FILES); Befehl ausgibst.

Gehe jedoch mit Vorsicht vor, wenn du diese Funktion verwendest. Stelle sicher, dass du dich gegen Standard-Attacken beim Hochladen von Dateien absicherst.

Array Unpacking Support für String-Keyed Arrays

PHP 7.4 hat Unterstützung für das Entpacken von Arrays mit dem Array-Spread-Operator (…) hinzugefügt. Dies ist eine schnellere Alternative zur Verwendung der array_merge() Funktion. Allerdings war diese Funktion auf Arrays mit numerischen Schlüsseln beschränkt, da das Entpacken von Arrays mit String-Schlüsseln Konflikte beim Zusammenführen von Arrays mit doppelten Schlüsseln verursachte.

PHP 8 fügte jedoch Unterstützung für benannte Argumente hinzu, wodurch diese Einschränkung aufgehoben wurde. Daher wird das Entpacken von Arrays nun auch Arrays mit String-Schlüsseln unterstützen, indem die gleiche Syntax verwendet wird:

$array = [...$array1, ...$array2];

This RFC example illustrates how merging arrays with duplicate string keys is handled in PHP 8.1:

$array1 = ["a" => 1];
$array2 = ["a" => 2];
$array = ["a" => 0, ...$array1, ...$array2];
var_dump($array); // ["a" => 2]

Hier taucht der String-Schlüssel „a“ dreimal auf, bevor er per Array-Entpacken zusammengeführt wird. Aber nur sein letzter Wert, der zu $array2 gehört, gewinnt.

Explizite oktale Zahlendarstellung

PHP unterstützt verschiedene Zahlensysteme, einschließlich dezimal (Basis-10), binär (Basis-2), oktal (Basis-8) und hex (Basis-16). Das dezimale Zahlensystem ist die Standardeinstellung.

Wenn du ein anderes Zahlensystem verwenden möchtest, musst du jeder Zahl ein Standardpräfix voranstellen:

  • Hex: 0x prefix. (e.g. 17 = 0x11)
  • Binary: 0b prefix. (e.g. 3 = 0b11)
  • Octal: 0 prefix. (e.g. 9 = 011)

Du kannst sehen, wie sich das Präfix des oktalen Zahlensystems vom Rest unterscheidet. Um dieses Problem zu standardisieren, unterstützen viele Programmiersprachen eine explizite oktale Zahlendarstellung: 0o oder 0O.

Beginnend mit PHP 8.1 kannst du das oben gezeigte Beispiel (d.h. die Zahl 9 in Basis-10) im oktalen Zahlensystem als 0o11 oder 0O11 schreiben.

0o16 === 14; // true
0o123 === 83; // true
 
0O16 === 14; // true
0O123 === 83; // true
 
016 === 0o16; // true
016 === 0O16; // true

Außerdem funktioniert dieses neue Feature auch mit dem in PHP 7.4 eingeführten numerischen Literalseparator mit Unterstrich.

Lies mehr über dieses neue PHP 8.1 Feature in seinem RFC.

MurmurHash3 und xxHash Hash Algorithmen Support

PHP 8.1 bietet Unterstützung für die Hash-Algorithmen MurmurHash3 und xxHash. Sie sind nicht für den kryptographischen Einsatz konzipiert, bieten aber dennoch eine beeindruckende Zufälligkeit, Streuung und Einzigartigkeit der Ausgabe.

Diese neuen Hash-Algorithmen sind schneller als die meisten der existierenden Hash-Algorithmen von PHP. Tatsächlich sind einige der Varianten dieser Hash-Algorithmen schneller als der RAM-Durchsatz.

Da PHP 8.1 auch die Unterstützung für die Deklaration von Algorithmus-spezifischen $options-Parametern hinzufügt, kannst du dies auch mit diesen neuen Algorithmen tun. Der Standardwert für dieses neue Argument ist []. Es wird also keine unserer bestehenden Hash-Funktionen beeinflussen.

Du kannst mehr über diese neuen PHP 8.1 Features auf deren GitHub Seiten lesen: MurmurHash3, xxHash, Algorithmus-spezifische $options.

DNS-over-HTTPS (DoH) Support

DNS-over-HTTPS (DoH) ist ein Protokoll zur DNS-Auflösung über das HTTPS-Protokoll. Durch die Verwendung von HTTPS zur Verschlüsselung der Daten zwischen Client und DNS-Resolver erhöht DoH die Privatsphäre und Sicherheit der Benutzer, indem es MitM-Angriffe verhindert.

Beginnend mit PHP 8.1 kannst du die Curl-Erweiterung verwenden, um einen DoH-Server anzugeben. Sie erfordert, dass PHP mit libcurl 7.62+ Versionen kompiliert wird. Das ist für die meisten gängigen Betriebssysteme, einschließlich Linux-Distros, kein Problem, da sie oft Curl 7.68+ enthalten.

Du kannst die DoH Server URL konfigurieren, indem du die Option CURLOPT_DOH_URL angibst.

$doh = curl_init('https://kinsta.com');
curl_setopt($doh, CURLOPT_DOH_URL, 'https://dns.google/dns-query');
curl_exec($doh);

Im obigen Beispiel haben wir den öffentlichen DNS-Server von Google verwendet. Beachte auch die Verwendung von https:// in allen verwendeten URLs. Achte darauf, dass du dies perfekt konfigurierst, da es in Curl keinen Standard-DNS-Server gibt, auf den du zurückgreifen kannst.

Du kannst auch aus einer Liste von öffentlichen DoH-Servern wählen, die in der Curl-Dokumentation enthalten ist.

Außerdem wird in der Curl-Dokumentation in der CURLOPT_DOH_URL-Referenz ausführlich erklärt, wie die verschiedenen Argumente zu verwenden sind.

Datei-Uploads von Strings mit CURLStringFile

Die PHP Curl Erweiterung unterstützt HTTP(S)-Requests mit Datei-Uploads. Sie verwendet dazu die CURLFile Klasse, die eine URI oder einen Dateipfad, einen Mime-Type und den endgültigen Dateinamen akzeptiert.

Allerdings kannst du mit der CURLFile Klasse nur den Dateipfad oder URI akzeptieren, aber nicht den Inhalt der Datei selbst. In Fällen, in denen du die hochzuladende Datei bereits im Speicher hattest (z.B. bearbeitete Bilder, XML Dokumente, PDFs), musstest du data:// URIs mit Base64 Kodierung verwenden.

Aber libcurl unterstützt bereits einen einfacheren Weg, den Inhalt der Datei zu übernehmen. Die neue Klasse CURLStringFile unterstützt genau das.

Du kannst die GitHub Seite lesen, um mehr darüber zu erfahren, wie sie in PHP 8.1 implementiert ist.

Neue MYSQLI_REFRESH_REPLICA Konstante

Die mysqli Erweiterung von PHP 8.1 fügt eine neue Konstante namens MYSQLI_REFRESH_REPLICA hinzu. Sie ist äquivalent zur existierenden MYSQLI_REFRESH_SLAVE Konstante.

Diese Änderung wurde in MySQL 8.0.23 willkommen geheißen, um die Rassenunsensibilität im technischen Vokabular zu adressieren (die häufigsten Beispiele sind „Slave“ und „Master“).

Du solltest beachten, dass die ältere Konstante nicht entfernt oder veraltet wird. Entwickler und Anwendungen können sie weiterhin verwenden. Die neue Konstante ist nur eine Option für Entwickler und Unternehmen, die diese Terminologie hinter sich lassen wollen.

Performanceverbesserungen mit Inheritance Cache

Inheritance Cache ist eine neue Ergänzung zu opcache, die den Overhead der PHP-Klassenvererbung eliminiert.

PHP-Klassen werden von opcache separat kompiliert und zwischengespeichert. Allerdings werden sie bereits zur Laufzeit bei jeder Anfrage gelinkt. Dieser Prozess kann mehrere Kompatibilitätsprüfungen und das Ausleihen von Methoden/Properties/Konstanten von Elternklassen und Traits beinhalten.

Das Ergebnis ist, dass die Ausführung sehr viel Zeit in Anspruch nimmt, auch wenn das Ergebnis für jede Anfrage das gleiche ist.

Der Inheritance Cache verknüpft alle eindeutigen abhängigen Klassen (Parent, Interfaces, Traits, Property Typen, Methoden) und speichert die Ergebnisse im opcache shared memory. Da dies nur noch einmal geschieht, benötigt die Vererbung weniger Anweisungen.

Darüber hinaus werden Einschränkungen für unveränderliche Klassen, wie unaufgelöste Konstanten, typisierte Properties und kovariante Typüberprüfungen entfernt. Somit sind alle im Opcache gespeicherten Klassen unveränderlich, was die Anzahl der benötigten Anweisungen weiter reduziert.

Alles in allem verspricht es signifikante Leistungsvorteile. Dimitry Stogov, der Autor dieses Patches, fand heraus, dass er eine Verbesserung von 8% im Vergleich zum Basis Symfony „Hello, World!“-Programm zeigt. Wir können es kaum erwarten, ihn in unseren folgenden PHP-Benchmarks auszuprobieren.

First-Class Callable-Syntax

PHP 8.1 fügt eine First-Class-Callable-Syntax hinzu, die die bestehenden Kodierungen mit Strings und Arrays ablöst. Neben der Schaffung eines saubereren Closure ist diese neue Syntax auch für statische Analysetools zugänglich und respektiert den deklarierten Scope.

Hier sind ein paar Beispiele aus dem RFC:

$fn = Closure::fromCallable('strlen');
$fn = strlen(...);
 
$fn = Closure::fromCallable([$this, 'method']);
$fn = $this->method(...)
 
$fn = Closure::fromCallable([Foo::class, 'method']);
$fn = Foo::method(...);

Hier sind alle Ausdruckspaare gleichwertig. Die dreifach gepunktete () Syntax ist ähnlich der Syntax zum Auspacken von Argumenten (...$args). Nur dass hier die Argumente noch nicht ausgefüllt sind.

Änderungen in PHP 8.1

PHP 8.1 beinhaltet auch Änderungen an der bestehenden Syntax und den Funktionen. Lass uns diese besprechen:

PHP Interactive Shell benötigt readline Erweiterung

Die Readline-Erweiterung von PHP ermöglicht interaktive Shell-Funktionen wie Navigation, Autovervollständigung, Bearbeitung und mehr. Obwohl sie mit PHP mitgeliefert wird, ist sie standardmäßig nicht aktiviert.

Du kannst die interaktive PHP-Shell mit der -a Kommandozeilenoption von PHP CLI aufrufen:

php -a

Interactive shell

php >
php > echo "Hello";
Hello
php > function test() {
php { echo "Hello";
php { }
php > test();
Hello

Vor PHP 8.1 konnte man die interaktive Shell mit PHP CLI auch ohne aktivierte Readline-Erweiterung öffnen. Wie erwartet, funktionierten die interaktiven Funktionen der Shell nicht, was die Option -a sinnlos machte.

In PHP 8.1 CLI beendet sich die interaktive Shell mit einer Fehlermeldung, wenn du die Readline-Erweiterung nicht aktiviert hast.

php -a
Interactive shell (-a) requires the readline extension.

MySQLi Standard-Fehlermodus auf Exceptions gesetzt

Vor PHP 8.1 hat MySQLi die Fehler standardmäßig verschwiegen. Dieses Verhalten führte oft zu Code, der sich nicht an die strenge Fehler-/Ausnahmebehandlung hielt. Die Entwickler mussten ihre eigenen expliziten Funktionen zur Fehlerbehandlung implementieren.

PHP 8.1 ändert dieses Verhalten, indem es den Standard-Fehlermeldemodus von MySQLi so einstellt, dass eine Ausnahme geworfen wird.

Fatal error: Uncaught mysqli_sql_exception: Connection refused in ...:...

Da dies eine einschneidende Änderung ist, solltest du für PHP <8.1 Versionen den Fehlerbehandlungsmodus explizit mit der mysqli_report Funktion setzen, bevor du die erste MySQLi Verbindung herstellst. Alternativ kannst du dies auch tun, indem du den Wert für die Fehlerberichterstattung durch die Instanzierung einer mysqli_driver-Instanz auswählst.

Der RFC folgt einer ähnlichen Änderung, die in PHP 8.0 eingeführt wurde.

Anpassbare Zeilenenden für CSV Schreibfunktionen

Vor PHP 8.1 waren die in PHP eingebauten CSV-Schreibfunktionen fputcsv und SplFileObject::fputcsv so programmiert, dass sie am Ende jeder Zeile \n (oder das Line-Feed-Zeichen) anfügten.

PHP 8.1 fügt diesen Funktionen Unterstützung für einen neuen Parameter namens eol hinzu. Du kannst ihn benutzen, um ein konfigurierbares Zeilenendezeichen zu übergeben.  Standardmäßig wird immer noch das \n-Zeichen verwendet. Du kannst es also weiterhin in deinem bestehenden Code verwenden.

Für die Verwendung von Zeilenende-Zeichen gelten die Standardzeichen-Escaping-Regeln. Wenn du \r, \n oder \r\n als EOL-Zeichen verwenden willst, musst du sie in doppelte Anführungszeichen einschließen.

Hier ist die GitHub Seite, die diese neue Änderung dokumentiert.

Neue version_compare Operator-Einschränkungen

Die Funktion version_compare() von PHP vergleicht zwei Versionsnummern-Strings. Diese Funktion akzeptiert ein optionales drittes Argument namens operator, um auf eine bestimmte Beziehung zu testen.

Obwohl nicht explizit in der Dokumentation beschrieben, konnte man vor PHP 8.1 diesen Parameter auf einen Teilwert (z.B. g, l, n) setzen, ohne einen Fehler zu erhalten.

PHP 8.1 fügt strengere Einschränkungen für das Operator-Argument der Funktion version_compare() hinzu, um diese Situation zu umgehen. Die einzigen Operatoren, die du jetzt verwenden kannst, sind:

  • ==, =, und eq
  • !=, <>, und ne
  • > und gt
  • >= und ge
  • < und lt
  • <= und le

Keine partiellen Operatorwerte mehr.

HTML Kodierungs- und Dekodierungsfunktionen verwenden jetzt ENT_QUOTES | ENT_SUBSTITUTE

HTML-Entities sind textuelle Repräsentationen von Zeichen, die sonst als HTML interpretiert werden würden. Denke an Zeichen wie < und >, die verwendet werden, um HTML-Tags zu definieren (z.B. <a>, <h3>, <script>).

Die HTML Entität für < ist < (kleiner als Symbol) und > ist > (größer als Symbol).

Hinweis: Entferne das Leerzeichen zwischen „&“ und „amp“.

Du kannst diese HTML-Entities sicher in einem HTML-Dokument verwenden, ohne die Rendering-Engine des Browsers auszulösen.

Zum Beispiel wird <script> im Browser als <script> angezeigt, anstatt als HTML-Tag interpretiert zu werden.

Vor PHP 8.1 wandelten die Funktionen htmlspecialchars() und htmlentities() Symbole wie ", <, > und & in die entsprechenden HTML-Entitäten um. Aber sie wandelten das einfache Anführungszeichen (') standardmäßig nicht in seine HTML-Entität um. Außerdem gaben sie einen leeren String zurück, wenn der Text ein fehlerhaftes UTF-8 enthielt.

In PHP 8.1. werden diese HTML-Kodierungs- und Dekodierungsfunktionen (und ihre verwandten Funktionen) standardmäßig auch einfache Anführungszeichen in ihre HTML-Entität umwandeln.

Und wenn der angegebene Text ungültige Zeichen enthält, werden die Funktionen diese durch ein Unicode-Ersatzzeichen (�) ersetzen, anstatt einen leeren String zurückzugeben. PHP 8.1 erreicht dies, indem es die Signaturen dieser Funktionen standardmäßig auf ENT_QUOTES | ENT_SUBSTITUTE statt ENT_COMPAT ändert.

Die meisten Frameworks verwenden bereits ENT_QUOTES als Standard-Flag-Wert. Du wirst also keinen großen Unterschied durch diese Änderung sehen. Allerdings wird das neue ENT_SUBSTITUTE Flag nicht so häufig verwendet. PHP 8.1 bewirkt, dass ungültige UTF-8 Zeichen durch das � Zeichen ersetzt werden, anstatt einen leeren String zurückzugeben.

Warnung bei unzulässigen kompakten Funktionsaufrufen

Die Funktion compact() von PHP ist super praktisch. Du kannst sie benutzen, um ein Array mit Variablen zu erstellen, indem du ihre Namen und Werte benutzt.

Betrachte zum Beispiel den folgenden Code:

$animal = 'Cat';
$sound = 'Meow';
$region = 'Istanbul';
compact('animal', 'sound', 'region');
// ['animal' => "Cat", 'sound' => "Meow", 'region' => "Istanbul"]

Die Dokumentation der compact-Funktion besagt, dass sie nur String-Parameter oder Array-Werte mit String-Werten akzeptiert. Vor PHP 7.3 wurden jedoch alle Strings, die nicht gesetzt waren, stillschweigend übersprungen.

PHP 7.3 hat die compact()-Funktion dahingehend geändert, dass sie eine Meldung ausgibt, wenn du undefinierte Variablen verwendest. PHP 8.1 geht noch einen Schritt weiter und gibt eine Warnung aus.

Du kannst die GitHub Seite lesen, um zu verstehen, wie es zu dieser Änderung kam.

Neue Migrationen von Ressourcen zu Klassenobjekten

Eines der langfristigen Ziele von PHP ist es, weg von Ressourcen hin zu Standardklassenobjekten zu kommen.

Aus historischen Gründen werden Ressourcen-Objekte in PHP-Applikationen ausgiebig genutzt. Daher muss die Migration von Ressourcen zu Klassenobjekten so wenig störend wie möglich sein. PHP 8.1 migriert fünf solcher Ressourcen:

Die file_info Ressource migriert zu finfo Objekten

PHPs finfo Klasse bietet eine objektorientierte Schnittstelle für die fileinfo Funktionen. Allerdings gibt die Verwendung der finfo-Funktionen Resource nobjekte mit dem Typ file_info zurück, anstatt eine Instanz der finfo-Klasse selbst.

PHP 8.1 behebt diese Anomalie.

IMAP-Ressourcen werden zu Objekten der IMAP\Connection migriert

In Übereinstimmung mit dem Ziel der Migration von Ressourcen zu Objekten, minimiert die neue IMAP\Connection Klasse potentielle Änderungen, wenn PHP die Implementierungsdetails der Klasse modifiziert.

Diese neue Klasse ist auch als final deklariert, du darfst sie also nicht extend.

Lies mehr über die Implementierung auf ihrer GitHub Seite.

FTP-Verbindungsressourcen sind jetzt Objekte der Klasse FTP\Connection

Wenn du in PHP <8.1 eine FTP-Verbindung mit den Funktionen ftp_connect() oder ftp_ssl_connect() erstellst, erhältst du ein Ressourcen objekt vom Typ ftp zurück.

PHP 8.1 fügt die neue FTP\Connection Klasse hinzu, um das zu korrigieren. Und wie die IMAP\Connection Klasse ist auch diese als final deklariert, um zu verhindern, dass sie erweitert wird.

Lies mehr über die Implementierung auf ihrer GitHub Seite.

Font Identifier migriert zu GdFont Klassenobjekten

PHPs GD-Erweiterung bietet die Funktion imageloadfont(), um eine benutzerdefinierte Bitmap zu laden und ihre Font-Identifier-Ressource-ID (eine Ganzzahl) zurückzugeben.

In PHP 8.1 wird diese Funktion stattdessen eine GdFont-Klasseninstanz zurückgeben. Um die Migration problemlos zu gestalten, werden alle Funktionen, die bisher eine Resource ID von imageloadfont() akzeptiert haben, nun die neuen GdFont-Klassenobjekte annehmen.

Lies mehr über diese Migration auf der GitHub Seite.

LDAP Ressourcen zu Objekten migriert

LDAP, oder Lightweight Directory Access Protocol, wird für den Zugriff auf „Directory Servers“ verwendet. Wie eine Verzeichnisstruktur auf der Festplatte ist es eine einzigartige Datenbank, die Daten in einer Baumstruktur hält.

PHP beinhaltet eine LDAP-Erweiterung, die vor PHP 8.1 Ressourcen objekte akzeptierte oder zurückgab. Allerdings sind sie jetzt alle nahtlos in neue Klasseninstanzen migriert. Die Ressourcen typen, die umgestellt wurden, sind:

  • ldap link Ressource zu \LDAP\Connection Klassenobjekt
  • ldap result Ressource zu \LDAP\Result Klassenobjekt
  • ldap result entry Ressource zu \LDAP\ResultEntry Klassenobjekt

Schau dir die GitHub Seite an, um diese Migration besser zu verstehen.

Pspell Ressourcen sind jetzt Klassenobjekte

Die PHP-Erweiterung Pspell ermöglicht es dir, Rechtschreibfehler und Wortvorschläge zu überprüfen.

PHP <8.1 verwendete die Ressource-Objekttypen pspell und pspell config mit einem Integer-Bezeichner. Diese beiden Ressourcenobjekte werden nun durch die Klassenobjekte PSpell\Dictionary und PSpell\Config ersetzt.

Wie bei früheren Migrationen werden alle Pspell-Funktionen, die vorher Resource-Objekt-Identifikatoren akzeptiert oder zurückgegeben haben, die neuen Klassenobjekt-Instanzen annehmen.

Weitere Informationen findest du auf der GitHub-Seite.

Verwerfungen in PHP 8.1

Mit PHP 8.1 werden viele der bisherigen Funktionen veraltet sein. Die folgende Liste bietet einen kurzen Überblick über die Funktionalitäten, die mit PHP 8.1 veraltet sind:

Kann nicht null an nicht-nullbare interne Funktionsparameter übergeben

Seit PHP 8.0 akzeptieren die internen Funktionen stillschweigend Nullwerte, auch für nicht-nullbare Argumente. Das Gleiche gilt nicht für benutzerdefinierte Funktionen – sie akzeptieren nur null für nullbare Argumente.

Betrachte zum Beispiel diese Anwendung:

var_dump(str_contains("foobar", null));
// bool(true)

Hier wird der Null-Wert stillschweigend in einen leeren String umgewandelt. Somit gibt das Ergebnis true zurück.

Dieser RFC zielt darauf ab, das Verhalten der internen Funktionen zu synchronisieren, indem eine Deprecation-Warnung in PHP 8.1 geworfen wird.

var_dump(str_contains("foobar", null));
// Deprecated: Passing null to argument of type string is deprecated

Die Verwerfung wird in der nächsten größeren PHP-Version (d.h. PHP >=9.0) zu einem TypeError, wodurch das Verhalten der internen Funktionen mit den benutzerdefinierten Funktionen konsistent wird.

Eingeschränkte $GLOBALS-Verwendung

PHP’s $GLOBALS Variable bietet einen direkten Verweis auf die interne Symboltabelle. Die Unterstützung dieser Funktionalität ist komplex und beeinflusst die Performance von Array-Operationen. Außerdem wurde sie selten benutzt.

Laut RFC ist es nicht mehr erlaubt, $GLOBALS indirekt zu modifizieren. Diese Änderung ist nicht rückwärtskompatibel.

Der Einfluss dieser Änderung ist relativ gering:

In den Top 2k Composer Paketen habe ich 23 Fälle gefunden, die $GLOBALS verwenden, ohne es direkt zu derefenzieren. Basierend auf einer flüchtigen Inspektion, gibt es nur zwei Fälle, in denen $GLOBALS nicht schreibgeschützt verwendet wird.

Allerdings funktioniert die Nur-Lese-Verwendung von $GLOBALS weiterhin wie gewohnt. Was nicht mehr unterstützt wird, ist das Schreiben in $GLOBALS als Ganzes. Als Ergebnis kannst du einen leichten Performance-Schub erwarten, besonders wenn du mit gewöhnlichen PHP-Arrays arbeitest.

Rückgabetyp-Deklarationen für interne Funktionen

PHP 8.0 erlaubte es Entwicklern, Parameter und Rückgabetypen für die meisten internen Funktionen und Methoden zu deklarieren. Dies war dank verschiedener RFCs wie Consistent type errors for internal functions, Union Types 2.0 und Mixed Type v2 möglich.

Allerdings gibt es viele Fälle, in denen Typinformationen fehlen können. Einige davon sind ein Typ mit Ressourcen, out pass-by-ref Parameter, Rückgabetyp von nicht-finalen Methoden und Funktionen oder Methoden, die Parameter nicht nach den allgemeinen Regeln parsen. Die genauen Details kannst du in seinem RFC nachlesen.

Dieser RFC behandelt nur das Problem mit dem Rückgabetyp von nicht-finalen Methoden. Anstatt es jedoch sofort ganz abzuschaffen, bietet das PHP-Team einen schrittweisen Migrationspfad an, um deine Codebases mit den relevanten Methodenrückgabetypen zu aktualisieren.

Nicht-finale interne Methoden-Rückgabetypen werden – wenn möglich – in PHP 8.1 vorläufig deklariert, und sie werden in PHP 9.0 erzwungen werden. Das bedeutet, dass in PHP 8.x-Versionen eine „deprecated“-Meldung während der Vererbungsprüfung ausgegeben wird, wenn eine interne Methode so überschrieben wird, dass die Rückgabetypen inkompatibel sind, und PHP 9.0 wird dies zu einem fatalen Fehler machen.

Wenn du diese Deprecation Notice nach dem Update auf PHP 8.1 siehst, stelle sicher, dass du die Rückgabetypen deiner Methoden aktualisierst.

Serialisierbare Schnittstelle Veraltet

PHP 7.4 hat den Mechanismus der benutzerdefinierten Objektserialisierung mit zwei neuen magischen Methoden eingeführt: __serialize() und __unserialize(). Diese neuen Methoden zielen darauf ab, das kaputte Serializable Interface irgendwann zu ersetzen.

Dieser RFC schlägt vor, diese Entscheidung zu finalisieren, indem er einen Plan für die eventuelle Entfernung von Serializable aufstellt.

Wenn du in PHP 8.1 das Serializable Interface implementierst, ohne die __serialize() und __unserialize() Methoden zu implementieren, wird PHP eine „Deprecated“ Warnung ausgeben.

Deprecated: The Serializable interface is deprecated. Implement __serialize() and __unserialize() instead (or in addition, if support for old PHP versions is necessary) in ... on line ...

Wenn du PHP <7.4 und PHP >=7.4 unterstützt, solltest du sowohl das Serializable Interface als auch die neuen magischen Methoden implementieren. Bei PHP >=7.4 Versionen haben die magischen Methoden Vorrang.

Nicht kompatible float to int Conversions Veraltet

PHP ist eine dynamisch typisierte Sprache. Als solche gibt es viele Fälle, in denen Typenzwang natürlich vorkommt. Die meisten dieser Zwänge sind harmlos und superpraktisch.

Wenn jedoch eine Float-Zahl in eine Integer-Zahl umgewandelt wird, ist dies oft mit Datenverlust verbunden. Wenn zum Beispiel die Float-Zahl 3,14 in eine Integer-Zahl 3 umgewandelt wird, verliert sie ihren Nachkommawert.

Das Gleiche passiert, wenn der Float außerhalb des Plattform-Integer-Bereichs liegt, oder wenn ein Float-String in einen Integer konvertiert wird.

PHP 8.1 korrigiert dieses Verhalten und bringt die dynamische Typenzwangssteuerung mehr in Einklang mit den meisten modernen Programmiersprachen. Das Ziel ist es, solche Zwänge vorhersehbar und intuitiv zu machen.

In PHP 8.1 wirst du eine Deprecation-Notiz sehen, wenn ein nicht kompatibler Float implizit in einen Int umgewandelt wird. Aber was ist ein integer-kompatibler Float? Der RFC beantwortet dies:

Ein Float wird als integer-kompatibel bezeichnet, wenn er die folgenden Eigenschaften besitzt:

  • Ist eine Zahl (d.h. nicht NaN oder Infinity)
  • Liegt im Bereich eines PHP Integers (plattformabhängig)
  • Hat keinen Nachkommaanteil

Dieser Hinweis wird in der nächsten PHP Version (d.h. PHP 9.0) in einen TypeError umgewandelt.

Die mysqli::get_client_info Methode und mysqli_get_client_info($param) veraltet.

Die MySQL Client API definiert zwei Konstanten: client_info (ein String) und client_version (ein int). Der MySQL Native Driver (MySQLnd) ist Teil des offiziellen PHP-Source und bindet diese Konstanten an die PHP-Version. In libmysql repräsentieren sie die Version der Client-Bibliothek zum Zeitpunkt der Kompilierung.

Vor PHP 8.1 wurden diese Konstanten von mysqli auf vier Arten offengelegt: mysqli_driver properties, mysqli properties, mysqli_get_client_info() Funktion und mysqli::get_client_info Methode. Allerdings gibt es keine Methode für client_version.

MySQLnd gibt diese Konstanten auf 2 Arten an PHP weiter: als Konstante und als Funktionsaufruf. Um die mysqli-Zugriffsmethoden mit diesen beiden Optionen zu vereinheitlichen, veraltet PHP 8.1 diese beiden anderen Optionen:

  • Die get_client_info Methode in der mysqli Stattdessen kannst du einfach die Funktion mysqli_get_client_info() verwenden.
  • mysqli_get_client_info() Funktion mit Parametern. Rufe die Funktion ohne Parameter auf, um den Verwerfungshinweis zu vermeiden.

Lies mehr über diese Verwerfung auf der GitHub Seite.

Alle mhash*()-Funktionen (Hash-Erweiterung) sind veraltet

PHP 5.3 integrierte mhash*() Funktionen in ext/hash als Kompatibilitätsschicht für ext/mhash. Später, mit PHP 7.0, wurde ext/mhash entfernt.

Anders als die hash_*() Funktionen sind die mhash*() Funktionen nicht immer verfügbar. Du musst sie separat bei der Konfiguration von PHP aktivieren.

In PHP 7.4 wurde die Hash-Erweiterung zusammen mit PHP gebündelt, was sie zu einer Standarderweiterung für PHP machte. Aus Kompatibilitätsgründen wurde jedoch weiterhin die Option --enable-mhash aktiviert.

Das PHP-Team hat beschlossen, die mhash*()-Funktionen in PHP 8.1 zu verwerfen und sie in PHP 9.0 ganz zu entfernen. Die veralteten Funktionen sind mhash(), mhash_keygen_s2k(), mhash_count(), mhash_get_block_size() und mhash_get_hash_name(). Du kannst stattdessen die Standard ext/hash Funktionalität verwenden.

Beide filter.default und filter.default_options INI-Einstellungen sind veraltet

PHPs filter.default INI-Einstellungen erlauben es dir, einen Filter auf alle PHP-Superglobals anzuwenden – d.h. GPCRS-Daten ($_GET, $_POST, $_COOKIE, $_REQUEST und $_SERVER).

Zum Beispiel kannst du filter.default=magic_quotes oder filter.default=add_slashes (je nach PHP Version) setzen, um die umstrittene und unsichere Funktion der magischen Anführungszeichen (die in PHP 5.4 entfernt wurde) wieder aufleben zu lassen.

Die filter.default INI-Einstellung bietet zusätzliche Funktionalität, indem sie viele weitere Filter zulässt, was es noch schlimmer macht. Zum Beispiel ermöglicht eine weitere Option – filter.default=special_chars – magische Anführungszeichen nur für HTML. Es gibt viel weniger Bewusstsein für diese Einstellungen.

PHP 8.1 wird eine Deprecation-Warnung ausgeben, wenn filter.default auf einen anderen Wert als unsafe_raw (der Standard) gesetzt ist. Für filter.default_options gibt es keine gesonderte Warnung, aber PHP 9.0 wird diese beiden INI-Einstellungen entfernen.

Als Alternative kannst du die Funktion filter_var() verwenden. Sie filtert Variablen mit dem angegebenen Filter.

Deprecate autovivification auf false

PHP erlaubt die Autovivification (automatische Erstellung von Arrays aus falschen Werten). Diese Funktion ist sehr hilfreich, wenn die Variable undefiniert ist.

Nichtsdestotrotz ist es nicht ideal, ein Array automatisch zu erstellen, wenn der Wert falsch oder null ist.

Dieser RFC verbietet die Autovivification von falschen Werten. Beachte jedoch, dass Autovivification von undefinierten Variablen und null immer noch erlaubt ist.

In PHP 8.1 wird das Anhängen an eine Variable vom Typ false eine Deprecation Notice ausgeben:

Deprecated: Automatic conversion of false to array is deprecated in

PHP 9.0 wirft einen fatalen Fehler für dasselbe aus, was identisch mit anderen skalaren Typen ist.

Die mysqli_driver->driver_version Property ist veraltet

Das Property mysqli_driver->driver_version der MySQLi Extension wurde seit 13 Jahren nicht mehr aktualisiert. Trotz vieler Änderungen am Treiber seither, gibt sie immer noch den alten Wert der Treiberversion zurück, was diese Property bedeutungslos macht.

In PHP 8.1 ist die mysqli_driver->driver_version Property veraltet.

Andere kleinere Verwerfungen

Es gibt noch viele weitere Verwerfungen in PHP 8.1. Sie alle hier aufzulisten, wäre eine anstrengende Aufgabe. Wir empfehlen dir, direkt im RFC nachzuschauen, um diese kleineren Verwerfungen zu finden.

Auf der GitHub Seite von PHP gibt es auch einen PHP 8.1 UPGRADE NOTES Guide. Er listet alle Änderungen auf, die du vor dem Upgrade auf PHP 8.1 beachten solltest.

Zusammenfassung

PHP 8.1 ist besser als sein Vorgänger, was keine kleine Leistung ist. Die aufregendsten Funktionen von PHP 8.1 sind unserer Meinung nach Enums, Fibers, Pure Intersection Types und die vielen Leistungsverbesserungen. Außerdem können wir es kaum erwarten, PHP 8.1 auf Herz und Nieren zu prüfen und verschiedene PHP-Frameworks und CMS zu vergleichen.

Setze ein Lesezeichen für diesen Blogpost für deine zukünftige Referenz.

Welches PHP 8.1 Feature ist dein Favorit? Teile deine Gedanken mit der Community in den Kommentaren unten.

Salman Ravoof

Salman Ravoof is a self-taught web developer, writer, creator, and a huge admirer of Free and Open Source Software (FOSS). Besides tech, he's excited by science, philosophy, photography, arts, cats, and food. Learn more about him on his website, and connect with Salman on Twitter.