PHP 8.5 bringt array_first() und array_last(): Endlich direkter Zugriff auf Array-Werte!

Von Never Code Alone
0 Kommentar
Roland Golla von 'never {CODE} alone' lächelt und zeigt mit dem rechten Zeigefinger auf einen grünen Balken mit der weißen Aufschrift 'PHP NEWS'. Darunter, links im Bild, steht in großen weißen Buchstaben '2 BRANDNEUE PHP FUNKTIONEN VERÖFFENTLICHT'. Oben links ist das 'never {CODE} alone' Logo in Grün und das offizielle PHP-Logo in einem blau-lila Oval zu sehen. Der Hintergrund des Bildes ist dunkelgrau/schwarz mit stilisierten weißen, geschwungenen Linien und einem großen grünen Kreiselement, vor dem Roland teilweise steht. Er trägt ein schwarzes T-Shirt mit einem grünen 'Bug Hunter'-Motiv.

PHP 8.5: array_first() und array_last() – Die lang ersehnten Helfer für direkten Array-Wert-Zugriff

Hallo liebe PHP-Community! Wer von Euch hat sich nicht schon einmal gewünscht, auf einfache und saubere Weise den ersten oder letzten Wert eines Arrays zu erhalten, ohne dabei den internen Array-Zeiger zu verändern oder umständliche Konstrukte zu verwenden? Gute Nachrichten: Mit PHP 8.5 wird dieser Wunsch Realität! Der kürzlich implementierte RFC von Niels Dossche führt die Funktionen array_first() und array_last() ein. Lasst uns gemeinsam einen detaillierten Blick darauf werfen, was diese Funktionen leisten, warum sie gebraucht werden und welche Überlegungen in ihr Design eingeflossen sind.

Das Problem: Warum der direkte Wertzugriff bisher umständlich war

Seit PHP 7.3 kennen und schätzen wir array_key_first() und array_key_last(), um den ersten bzw. letzten Schlüssel eines Arrays zu ermitteln. Doch der direkte Zugriff auf den dazugehörigen Wert war oft nicht so elegant, wie man es sich wünschen würde. Die Herausforderungen lagen dabei vor allem in folgenden Punkten:

  1. Nicht-numerische und nicht-sequenzielle Schlüssel: Array-Schlüssel in PHP sind nicht zwangsläufig aufsteigende Integer ab 0. Man kann also nicht einfach $array[0] für das erste Element annehmen.
  2. Nebenwirkungen bestehender „Tricks“: Methoden wie reset() (um zum ersten Element zu gelangen und dessen Wert zu erhalten) oder end() (für das letzte Element) haben den semantischen Nachteil, dass sie den internen Iterator-Zeiger des Arrays verändern. Das kann zu unerwarteten Nebeneffekten führen, wenn das Array später noch durchlaufen werden soll. Zudem funktionieren sie nicht zuverlässig mit allen Arten von Ausdrücken (z.B. ein direkt von einer Funktion zurückgegebenes Array), da sie eine Referenz erwarten und eine Notice werfen können.
  3. Umständliche Syntax: Die gängige Alternative $array[array_key_first($array)] ist zwar korrekt, aber doch recht lang und nicht besonders leserlich, besonders wenn man es häufiger benötigt.

Diese Punkte machten klar: Es bedarf einer dedizierten, sauberen Lösung.

Die Lösung: array_first() und array_last() stellen sich vor

Der angenommene RFC schlägt zwei neue, intuitive Funktionen vor:

function array_first(array $array): mixed {}
function array_last(array $array): mixed {}

Diese Funktionen tun genau das, was ihre Namen versprechen:

  • array_first() gibt den Wert des ersten Elements im übergebenen Array zurück.
  • array_last() gibt den Wert des letzten Elements im übergebenen Array zurück.

Im Wesentlichen sind sie also äquivalent zu $array[array_key_first($array)] bzw. $array[array_key_last($array)], nur eben deutlich eleganter und ohne die genannten Nachteile der Iterator-Manipulation. Eine alternative Denkweise für array_first() wäre eine foreach-Schleife, die direkt in der ersten Iteration den Wert zurückgibt.

Ein wichtiges Detail ist auch, wie mit Referenzen umgegangen wird: Sollte der zurückzugebende Array-Wert eine Referenz sein, wird diese automatisch dereferenziert. Du erhältst also immer den tatsächlichen Wert.

Design-Entscheidungen im Detail: Warum sie so sind, wie sie sind

Hinter jeder neuen Funktion in PHP stecken sorgfältige Überlegungen. Schauen wir uns einige der wichtigsten Diskussionspunkte aus dem RFC an:

1. Verhalten bei leeren Arrays: NULL als pragmatische Wahl

Eine der zentralen Fragen war, wie sich die Funktionen verhalten sollen, wenn ein leeres Array übergeben wird. Soll eine Exception geworfen werden oder NULL zurückgegeben werden? Die Entscheidung fiel zugunsten von NULL. Die Argumente dafür sind überzeugend:

  • Konsistenz mit $array[array_key_first($array)]: Der Zugriff auf einen nicht existierenden Schlüssel (was bei einem leeren Array der Fall wäre) liefert in PHP ebenfalls NULL.
  • Konsistenz mit ähnlichen Funktionen: array_find() gibt NULL zurück, wenn kein Element gefunden wird. Auch array_shift() und array_pop() (die ja ebenfalls erste/letzte Elemente holen, wenn auch modifizierend) geben bei leeren Arrays NULL zurück.
  • Konsistenz mit Framework-Helfern: Viele gängige Frameworks (z.B. Laravel mit Arr::first()) handhaben dies ähnlich.
  • Seltenheit von NULL als legitimer Wert: Obwohl NULL theoretisch ein gültiger Wert in einem Array sein kann (und man dann nicht zwischen einem leeren Array und einem Array mit NULL als erstem/letztem Wert unterscheiden könnte), ist dies in der Praxis eher selten.
  • Gute Zusammenarbeit mit dem Null-Coalescing-Operator (??): array_first($array) ?? $default wird sehr elegant.
  • Alternative bei NULL als legitimer Wert: Wenn NULL ein erwarteter Wert ist und man unterscheiden muss, kann man, genau wie bei der Alternative mit Exception-Wurf, einfach vorher prüfen, ob das Array leer ist (empty($array) oder count($array) > 0).

Die Idee, einen optionalen $default-Parameter einzuführen, wurde verworfen. Da PHP-Arrays nicht typisiert sind, ist es oft schwer, einen sinnvollen „Sentinel“-Defaultwert zu definieren. Zudem gibt es hierfür keine Präzedenz bei anderen Array-Funktionen, und die Syntax array_first($somevar, $someothervar) könnte ohne Kontextwissen verwirrend wirken.

2. Namensgebung: Kurz, bündig und konsistent

Warum array_first/array_last und nicht array_value_first/array_value_last?
Erstens folgt dies der etablierten Namenskonvention für PHP-Array-Funktionen: Jene, die mit Schlüsseln arbeiten, haben „key“ im Namen (z.B. array_key_exists, array_map_key), während jene, die mit Werten arbeiten, in der Regel nicht explizit „value“ im Namen tragen (z.B. in_array, array_search, array_map).
Zweitens sind die kürzeren Namen weniger sperrig und intuitiv verständlich.

3. Zusammenspiel mit Fibers: Kein Grund zur Sorge

In früheren Diskussionen gab es Bedenken bezüglich des Zusammenspiels mit Fibers – konkret die Sorge, dass ein Aufruf von array_key_first() gefolgt von array_first() inkonsistente Ergebnisse liefern könnte, wenn eine Fiber die Ausführung unterbricht. Diese Sorge basiert jedoch auf der falschen Annahme, dass Fibers wie Threads funktionieren, was sie nicht tun. Diese Situation kann also nicht eintreten.

4. Warum im PHP-Kern und nicht als Userland-Funktion?

Dafür gibt es drei Hauptgründe:

  • Konsistenz: Es ist nur logisch, diese Funktionen neben array_key_first() und array_key_last() direkt im PHP-Kern anzusiedeln.
  • Geringer Aufwand: Die Implementierung in C ist extrem einfach (wenige Zeilen Code pro Funktion) und der Wartungsaufwand daher praktisch null.
  • Performance: Eine C-Implementierung ist in der Regel schneller als eine äquivalente PHP-Implementierung. Erste Tests bestätigen dies. Es gäbe sogar Potenzial für weitere Optimierungen (z.B. eine „frameless variant“), falls dies je nötig würde.

Anwendungsbeispiele: array_first() und array_last() in Aktion

Schauen wir uns an, wie einfach die neuen Funktionen zu verwenden sind:

// Einfaches Array
array_first(["Einziges Element"]); // Gibt "Einziges Element" zurück
array_last(["Einziges Element"]);  // Gibt "Einziges Element" zurück

// Leeres Array
array_first([]); // Gibt NULL zurück
array_last([]);  // Gibt NULL zurück

// Assoziatives Array mit nicht-sequenziellen Schlüsseln
$assocArray = [1 => 'a', 0 => 'b', 3 => 'c', 2 => 'd'];
array_first($assocArray); // Gibt 'a' zurück (das Element mit dem Schlüssel 1, da es zuerst definiert wurde)
array_last($assocArray);  // Gibt 'd' zurück (das Element mit dem Schlüssel 2, da es zuletzt definiert wurde)

// Array mit Referenz (Wert wird dereferenziert)
$stringVar = "Hallo";
array_first([&$stringVar, false]); // Gibt "Hallo" zurück
array_last([false, &$stringVar]);  // Gibt "Hallo" zurück

Wie Du siehst, ist die Anwendung denkbar einfach und das Ergebnis stets der erwartete Wert, ohne Nebeneffekte auf den Array-Pointer.

Auswirkungen und Verfügbarkeit: Was Du wissen musst

Die neuen Funktionen array_first() und array_last() sind für die nächste PHP 8.x Version vorgesehen, also PHP 8.5 (Stand der RFC-Erstellung).

Rückwärtskompatibilität:
Die einzige potenzielle Inkompatibilität besteht, wenn Du bereits Funktionen namens array_first() oder array_last() im globalen Namespace Deiner Projekte definiert hast. In diesem Fall müsstest Du Deine eigenen Definitionen nun mit function_exists() schützen oder umbenennen. Es sei jedoch angemerkt, dass der globale Namespace üblicherweise für PHP-interne Funktionen reserviert ist.

Die Funktionen werden Teil der ext-standard Extension sein, wo auch ihre Pendants array_key_first() und array_key_last() beheimatet sind. Bestehende PHP-Funktionalitäten bleiben davon unberührt.

Der RFC wurde mit einer überwältigenden Mehrheit von 35 Ja-Stimmen zu 0 Nein-Stimmen angenommen, was die breite Zustimmung in der PHP-Community unterstreicht.

Fazit: Eine willkommene Bereicherung für die PHP-Array-Handhabung

Die Einführung von array_first() und array_last() in PHP 8.5 ist ein kleiner, aber feiner Schritt, der die tägliche Arbeit mit Arrays spürbar vereinfachen und Code lesbarer machen wird. Die umständlichen Workarounds oder potenziell fehleranfälligen Iterator-Manipulationen gehören damit für diese Anwendungsfälle der Vergangenheit an. Die durchdachten Design-Entscheidungen, insbesondere bezüglich des Verhaltens bei leeren Arrays und der Namensgebung, sorgen für Konsistenz und intuitive Nutzbarkeit.

Wir freuen uns auf PHP 8.5 und die Möglichkeit, diese praktischen Helfer bald in unseren Projekten einsetzen zu können!

Was denkst Du über die neuen Funktionen array_first() und array_last()? Siehst Du direkte Anwendungsfälle in Deinen Projekten? Teile Deine Gedanken in den Kommentaren!

0 Kommentar

Tutorials und Top Posts

Gib uns Feedback

Diese Seite benutzt Cookies. Ein Akzeptieren hilft uns die Seite zu verbessern. Ok Mehr dazu