PHP 8.0: Benannte Parameter (named arguments)

Artikel geschrieben am 26.01.2022 um 01:22 Uhr.

Parameter nicht anhand ihrer Position zu definieren sondern mithilfe eines Namens ist keine grundsätzliche Revolution. In anderen Programmiersprachen, z.B. Objective-C, gehörte es zum Inventar und guten Ton.
Trotzdem ist es erfreulich, dass PHP wieder eine weitere Stufe der Flexibilität errungen hat und schmälert in keinem Fall die neuen Möglichkeiten! Steigen wir doch direkt ein und schauen uns zunächst ein durchaus alltägliches Beispiel an, das wohl in so ziemlich jedem Projekt vorkommt.

$Database = new \mysqli("127.0.0.1", "root", "secret1234", "test_db", 3306, "");

Im Beispiel sehen wir die vollständige Deklaration zum Aufbau einer MySQL / MariaDB - Verbindung. Die Parameter $hostname, $username, $password, $database, $port und $socket werden oft einfach mit den Standardwerten angegeben, die meistens bereits vorhanden sind.
Wenn wir unsere lokale XAMPP-Installation in einer Standardinstallation anschauen, dann sehen wir sofort, dass der einzige Parameter, der notwendig ist, die Angabe für die Datenbank selbst sein dürfte. Ziel des neuen Features ist es, den Code von Programmen übersichtlicher zu gestalten. Entsprechend würde es ausreichend mit folgender Zeile eine erfolgreiche Verbindung aufzubauen, ohne alle Parameter davor angeben zu müssen.

$Database = new \mysqli( username: "root", database: "test_db" );
Natürlich kommt es darauf an, welche Angaben in der php.ini-Datei im Abschnitt mysqli.default_* gesetzt sind!

Das erforderliche Label definiert sich aus dem Namen der Variablen, nur ohne das $-Zeichen. Es gab Überlegungen zur optionalen Angabe eines Alias-Namens, z.B. new \mysqli( withUser: "root", atDatabase: "test_db" );, aber dieser hat es am Ende nicht in die finale (erste) Version der named arguments geschafft. Sie werden im RFC unter dem Punkt Alternativen aufgeführt und vielleicht kommt diese Erweiterung noch mit einer späteren Version.
Oft wird ein alternativer Bezeichner genutzt, damit Code bzw. ein Funktionsaufruf sich wie ein Satz lesen lässt. Ob das unbedingt notwendig ist? Sicherlich nicht, aber bisher ist mir ein solcher Syntax nur in sehr guten Entwicklungsumgebungen untergekommen.
Natürlich ist es nicht verboten die alten Positionsparameter zu verwenden. Tatsächlich ist auch ein Mischbetrieb aus beiden Varianten möglich. Ein, zwei generelle Regeln, die beachtet werden müssen:

  1. Positionsparameter müssen immer zuerst kommen
  2. Ein Parameter darf nicht doppelt übergeben werden (durch die Nutzung von Positions- und Benannten Parametern)
    PHP wirft eine Error-Exception in diesem Falle.
  3. Optionale Parameter können mithilfe von Benannten Parametern übersprungen werden.
    Kurzum: man gibt nur die an, bei denen man einen anderen Wert übergeben möchte. (wie im ersten Beispiel von new \mysqli( /* [...] */ );)
  4. Den Bezeichner für eine Variable selbst als Variable zu hinterlegen funktioniert nicht und führt zu einem Syntax-Fehler!
    Beispiel: $paramBezeichner = "username"; foobar( $paramBezeichner: $Wert );

Schauen wir uns noch ein paar Beispiele an, wie hilfreich die Verwendung von benannten Parametern sein kann.
Die Funktionen \htmlentities und \htmlspecialchars sind oft genutzte Funktionen, bei denen meistens der mittlere Parameter für die $flags im Standard übernommen wird, nur um das $encoding anzupassen. Diese Angabe zu setzen ist im übrigen sogar nach Handbuch explizit gewünscht und empfohlen. Problem bei dieser Variante ist immer, falls sich der Standardwert für $flags irgendwann ändern sollte, denn dann nutzt man von heute auf morgen eine angepasste Variante. In Zukunft kann man das umgehen, wenn man die Funktion einfach mit \htmlentities($str, encoding: "iso-8859-1"); aufruft.
Der Parameter $flags wird nicht mehr verändert und daher zählt in Zukunft der Standard, den PHP vorgibt. Natürlich gibt es Stellen, an denen dieses Verhalten unerwünscht ist, aber dann wären alle drei Parameter relevant und unser Ziel ist die Beseitigung von unnötigen Parametern.

Falls erforderliche Parameter weggelassen werden oder benannte Parameter auftauchen, die diese Methode oder Funktion nicht besitzt, wird ebenfalls ein Fehler geworfen.

Ein besonderes Beispiel ist das Entpacken von Arrays für Parameter. Nachfolgendes Beispiel verdeutlicht unser bereits bekanntes Beispiel und kombiniert ebenfalls Positionsparameter mit Benannten Parametern.

Achtung: Das Entpacken eines assoziativen Arrays wird erst ab PHP 8.1 unterstützt!
$params = [
	"Zeichenkette",
	"encoding" => "UTF-8"
];
\htmlentities( ...$params );

Auch in dieser Variante ist darauf zu achten, das innerhalb des Arrays erst die Werte für die Positionsparametern und danach die restlichen angegeben sind! Ansonsten führt das zu einem Cannot use positional argument after named argument during unpacking in [...] on line [...]. Auf die gleiche Weise kann natürlich auch ein Aufruf mittels \call_user_func_array(); erfolgen.