MWCodebymw.de ↗
PHP

First-Class-Callable-Syntax in PHP: aus strlen(...) wird ein echter Wert

Funktionen als Zeichenketten weiterzureichen – 'strlen', [$objekt, 'methode'] – hat nie jemand gemocht: kein Tippfehlerschutz, keine Sprungmarke in der IDE. Seit PHP 8.1 schreibst du drei Punkte und bekommst ein Closure-Objekt.

PHP kennt seit jeher „Callables", also Funktionen, die man irgendwohin weiterreicht. Nur sah das lange so aus:

$sauber = array_map('trim', $namen);              // Funktion als Zeichenkette
$brutto = array_map([$preis, 'mitSteuer'], $netto); // Methode als Array

Beides funktioniert – und beides ist eine Zeichenkette, die niemand prüft. Vertippst du dich, merkst du es zur Laufzeit. Deine IDE springt nicht zur Definition, die Umbenennen-Funktion übersieht die Stelle, und statische Analyse zuckt mit den Schultern.

Seit PHP 8.1 gibt es dafür eine eigene Schreibweise: den Namen aufrufen, aber statt der Argumente drei Punkte setzen.

So sieht das aus

<?php
$namen = ['  Anna ', 'Bert', ' Cem '];

// Funktion:
$sauber = array_map(trim(...), $namen);
// ['Anna', 'Bert', 'Cem']

// Methode eines Objekts:
class Preis {
    public function __construct(private float $satz) {}
    public function mitSteuer(float $netto): float {
        return round($netto * (1 + $this->satz), 2);
    }
}
$p = new Preis(0.19);
$brutto = array_map($p->mitSteuer(...), [10.0, 99.99]);
// [11.9, 118.99]

// Statische Methode:
$ids = array_map(Nutzer::idVon(...), $nutzer);

// Und man kann es auch einfach hinlegen:
$laenge = strlen(...);
var_dump($laenge instanceof Closure);   // bool(true)
echo $laenge('Hallo');                  // 5

Die drei Punkte sind kein Platzhalter für Argumente – sie sind die komplette Schreibweise. trim(...) ruft trim nicht auf, sondern erzeugt ein Closure-Objekt, das den Aufruf später erledigt. Das ist derselbe Effekt wie Closure::fromCallable('trim'), nur kürzer und vom Compiler geprüft.

Warum das mehr ist als Kosmetik

Der Name wird beim Kompilieren aufgelöst. Eine Funktion, die es nicht gibt, fällt sofort auf – nicht erst, wenn der Code das erste Mal durchlaufen wird. Bei einer Zeichenkette merkt es niemand, bis ein Nutzer darauf tritt.

$this wird mitgenommen. $p->mitSteuer(...) bindet das Objekt fest ein. Bei der Array-Schreibweise musste man selbst darauf achten, dass das Objekt zum Zeitpunkt des Aufrufs noch existiert.

Private Methoden gehen auch – wenn du die Schreibweise innerhalb der Klasse benutzt. array_map($this->pruefe(...), $zeilen) funktioniert, [$this, 'pruefe'] von außen nicht.

Zwei Stolpersteine

Es sind wirklich nur drei Punkte, sonst nichts. strlen(..., 5) oder trim(..., ' ') sind Syntaxfehler. Willst du ein Argument vorbelegen, brauchst du weiterhin eine kurze Pfeilfunktion: fn($s) => trim($s, '/').

Sprachkonstrukte sind keine Funktionen. isset(...), empty(...), echo(...) gibt es nicht – die sehen nur aus wie Funktionen. array_map(strlen(...), $x) geht, array_map(empty(...), $x) nicht.

Wo es sich lohnt

Am meisten dort, wo eine Liste durch eine Kette von Verarbeitungsschritten läuft: array_map, array_filter, usort, array_walk. Statt Zeichenketten stehen dort echte Namen, und ein usort($namen, strcmp(...)) liest sich endlich wie das, was es tut.

Ich nutze das im eigenen CMS hinter bymw.de überall da, wo früher Callables als Text herumlagen – gefunden habe ich beim Umstellen prompt zwei Stellen mit einem Tippfehler, die nur deshalb nie aufgefallen waren, weil der Zweig selten lief. Genau dafür ist die Schreibweise da.

Voraussetzung ist PHP 8.1 oder neuer. Läuft dein Hosting noch auf 8.0, bleibt es bei der alten Schreibweise – oder du hebst die Version an, es lohnt sich ohnehin.

Quellen

#PHP 8.1#Callables#Closures#array_map#Lesbarkeit

Du brauchst mehr als ein Snippet?

Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.

Projekt anfragen →