Le placeholder ? de PHP 8.6 remplace-t-il une closure ?
1 min de lecture
Le placeholder ? de PHP 8.6 remplace-t-il une closure ?
L’application partielle consiste à appeler une fonction sans lui donner tous ses arguments. PHP ne l’exécute pas : il renvoie une closure qui attend ce qui manque. En PHP 8.6, le signe ? tient la place d’exactement un argument. Le signe ... placé en fin d’appel tient la place de tout ce qui n’a pas encore été fixé.
Le ? ne fait pas disparaître les closures. Il évite d’en écrire une lorsque le corps ne fait que transmettre les arguments manquants. Dès que cette closure doit tester une valeur, en calculer une autre ou appeler deux fonctions, la fonction fléchée reste plus claire.
PHP 8.6.0 RC3 est paru le 8 octobre 2026. Le calendrier du wiki place la RC4 au 22 octobre 2026 et la version générale au 19 novembre 2026. L’annonce sur php.net demande de ne pas utiliser cette RC en production. Le 17 octobre 2026, la branche stable la plus récente est PHP 8.5.11, publiée le 24 septembre 2026.
Ce que produit un point d’interrogation
Une application partielle est un appel dont au moins un argument est un ?, ou dont la fin est un .... Le moteur crée une closure. Chaque paramètre ouvert reprend le nom, le type et le mode de passage de l’argument d’origine. ReflectionFunction lit cette signature.
Le manuel de migration vers PHP 8.6 part d’une addition. Le troisième entier vaut 0 s’il est omis.
function add(int $a, int $b, int $c = 0): int
{
return $a + $b + $c;
}
$addOne = add(1, ?);
var_dump($addOne(5));
add(1, ?) fixe $a à 1 et laisse $b ouvert. $addOne(5) vaut 6. $c, ni fixé ni remplacé par un ?, garde 0. L’équivalent écrit à la main est static fn (int $b): int => add(1, $b). La même forme vaut pour une fonction interne : str_replace(' ', '', ?) retire les espaces de son sujet, et 'a b c' devient abc.
Les arguments déjà fournis sont évalués à la création de la closure, pas à son appel. Une fonction fléchée fait l’inverse.
function horodatage(): string
{
echo "horodatage\n";
return 'ok';
}
function dire(string $qui, string $message): void
{
echo $qui, ' : ', $message, "\n";
}
$partiel = dire(?, horodatage());
echo "apres\n";
$partiel('Ada');
L’affichage est horodatage, puis apres, puis Ada : ok. Avec fn ($qui) => dire($qui, horodatage()), horodatage ne s’afficherait qu’à l’appel. Une lecture d’horloge, ou un calcul coûteux, ne se place donc pas dans un argument déjà fixé si elle doit se rejouer à chaque utilisation.
Trois écritures qui ne disent pas la même chose
Depuis PHP 8.1, strlen(...) est un callable de première classe : rien n’est prérempli. Depuis PHP 8.5, publié le 20 novembre 2025, l’opérateur |> envoie la valeur de gauche à un callable à un seul paramètre. Le manuel des opérateurs fonctionnels en donne la règle. PHP 8.6 ajoute le cas du milieu.
strlen(...)reprend toute la signature. Cette forme existe en 8.1.str_replace(' ', '-', ...)fixe deux arguments et expose le reste, paramètres optionnels compris.str_replace(' ', '-', ?)ne laisse ouvert que le sujet. Un argument en trop n’atteint passtr_replace.
Le test optional_placeholder.phpt de php-src reprend le second RFC, accepté par 25 voix contre 0 et 1 abstention. Chaque ? devient un paramètre obligatoire, même si l’original avait une valeur par défaut. Les paramètres ramenés par ... restent optionnels.
function exemple(int $a = 1, string $b = 'defaut', float $c = 3.14): array
{
return [$a, $b, $c];
}
$deux = exemple(?, ?);
var_dump($deux(10, 'hello'));
$reste = exemple(10, ...);
var_dump($reste());
$deux(10, 'hello') renvoie [10, 'hello', 3.14] : les deux ? sont exigés, et $c reste à 3.14. $reste() s’appelle sans argument et renvoie [10, 'defaut', 3.14], parce que ... a gardé les défauts. Un ? nommé suit l’ordre d’écriture : exemple(b: ?, a: ?) a $b en premier paramètre. Un ? positionnel après un argument nommé est une erreur fatale, « Cannot use positional placeholder after named argument. » Les combinaisons sont dans le RFC Partial Function Application (v2).
Le cas qui justifie la syntaxe : le pipe
Le pipe refuse une fonction qui exige plusieurs arguments. Avant PHP 8.6, on écrit une fonction fléchée, obligatoirement entre parenthèses. L’application partielle fournit le callable à un argument sans ces parenthèses.
$slug = ' PHP 8.6 RC3 '
|> trim(...)
|> str_replace(' ', '-', ?)
|> strtolower(...);
Dès PHP 8.5, la ligne du milieu s’écrit encore avec une fonction fléchée. trim(...) et strtolower(...) ne changent pas : ils datent de PHP 8.1.
$slug = ' PHP 8.6 RC3 '
|> trim(...)
|> (fn (string $s): string => str_replace(' ', '-', $s))
|> strtolower(...);
Les deux chaînes donnent php-8.6-rc3. La seconde reste celle d’une bibliothèque qui promet PHP 8.5.
Lorsque le partiel est directement à droite de |> et que le compilateur y voit un seul argument, il supprime la closure intermédiaire. Le RFC cite foo(?), foo(1, ?), foo(1, a: ?) et foo(1, ...). str_replace(' ', '-', ?) est le deuxième cas. foo(a: 1, ...) n’est pas optimisé : la place de l’argument nommé n’est pas connue à la compilation. Hors du pipe, le coût est celui d’une closure écrite à la main, mise en cache par opcache.
Avec un seul tableau, array_map ne passe que la valeur au rappel. array_map(str_replace('hello', 'hi', ?), ['hello world', 'hello php']) renvoie ['hi world', 'hi php'].
Le piège du second argument
array_find, ajouté en PHP 8.4, appelle son rappel avec la valeur, puis la clé. intval lit un second paramètre comme une base : 0, ou un entier de 2 à 36. Une autre base lève un ValueError depuis PHP 8.
$sansCle = intval(?);
var_dump($sansCle('20', 1));
$avecCle = intval(...);
$avecCle('20', 1);
Le premier appel affiche int(20) : le 1 n’atteint pas intval. Le second lève un ValueError, car intval(...) transmet ce 1 comme base. Cette forme ... existe depuis PHP 8.1. La nouveauté de PHP 8.6 est le ? unique, qui coupe les arguments en trop.
Sur une liste, la clé vaut 0, puis 1, puis 2. La clé 0 est une base acceptée. La clé 1 ne l’est pas. Passé à array_find, intval(...) peut donc échouer au deuxième élément, alors que intval(?) ne lit que la valeur. Sans ..., les arguments en trop sont ignorés. Avec ..., une fonction interne non variadique lève une ArgumentCountError s’ils ne correspondent à aucun paramètre.
Ce que la syntaxe refuse
new Livre(1, ?) est une erreur de compilation : « Cannot create Closure for new expression. » Un constructeur n’est pas un callable ordinaire. Une méthode statique l’est : si Livre::depuisId(int $id) existe, Livre::depuisId(?) renvoie une closure. compact, extract, func_get_arg et get_defined_vars sont refusés, comme pour un callable de première classe. Elles lisent le contexte de l’appelant, que l’application partielle remplace.
Un partiel tient dans une expression constante si les arguments déjà fournis sont des constantes. str_replace(' ', '-', ?) peut ainsi devenir la valeur par défaut d’une propriété. Un argument calculé à l’exécution, non. Deux attributs seulement sont recopiés sur la closure : SensitiveParameter et NoDiscard. Un callable de première classe, lui, conserve tous les attributs.
Où l’essayer le 17 octobre 2026
La RC3 s’installe à côté d’un binaire stable, dans une suite de tests ou un conteneur jetable. Un site en production reste sur une branche publiée. PHP 8.5.11 est la plus récente ce 17 octobre 2026. Les branches 8.4, 8.3 et 8.2 ont aussi reçu un correctif de sécurité le 24 septembre : 8.4.26, 8.3.35 et 8.2.34. Aucune ne comprend le signe ?.
Une bibliothèque qui annonce PHP 8.5 garde la fonction fléchée. Le 19 novembre 2026, si le calendrier tient, le ? pourra remplacer les rappels qui ne font que fixer des arguments. La RC4 du 22 octobre sert à vérifier qu’un essai écrit pour la RC3 n’a pas bougé. Une autre échéance de version, côté JavaScript, est détaillée pour Node.js 26 avant son passage en LTS.
Le ? de PHP 8.6 crée une closure à la place d’un argument. Il ne remplace pas une closure qui contient de la logique, et il n’a pas sa place sur un PHP de production avant la 8.6.0 du 19 novembre 2026.
Questions fréquentes
Le signe ? fonctionne-t-il avec PHP 8.5.11 ?
Non. PHP 8.5.11 comprend l’opérateur |> et les callables foo(...), pas le placeholder ?. Sur cette version, add(1, ?) est une erreur de syntaxe. L’équivalent reste fn (int $b): int => add(1, $b).
foo(…) et foo(1, …) sont-ils identiques ?
Non. foo(...), depuis PHP 8.1, ne fixe aucun argument. foo(1, ...), en PHP 8.6, fixe le premier et expose le reste, défauts compris. foo(1, ?) ne laisse ouvert qu’un seul argument.
Faut-il PHP 8.6 pour utiliser le pipe ?
Non. Le pipe date de PHP 8.5. PHP 8.6 sert à obtenir un callable à un argument sans écrire de fonction fléchée. Sans 8.6, cette fonction, entre parenthèses, reste la forme correcte à droite de |>.
Pourquoi intval(?) et intval(…) ne réagissent-ils pas de la même façon ?
intval(?) n’a qu’un paramètre. La clé transmise par array_find est ignorée. intval(...) la transmet comme base. Une clé égale à 1 provoque un ValueError. Pour un rappel qui ne lit que la valeur, le ? est le bon choix.
Une closure écrite à la main est-elle plus rapide ?
Hors d’un pipe, non : la closure générée coûte autant qu’une closure écrite dans le fichier. Directement à droite de |>, avec un seul argument restant, PHP compile un appel direct. Ce raccourci ne s’applique pas à un partiel passé à array_map.
Faut-il passer un site sur la RC3 de PHP 8.6 ?
Non. La RC3 sert à tester le langage. Un site, WordPress compris, reste sur PHP 8.5.11 ou sur une autre branche stable encore corrigée. Le ? n’apporte rien à un thème tant que l’hébergement n’exécute pas PHP 8.6.0.
Le HTML, le CSS et Sass se travaillent pas à pas dans la section Apprentissage. Les notes ponctuelles, comme celle-ci, restent sur le blog. Pour un site WordPress, la page Me contacter est le bon point d’entrée.
Voir Egalement
My Blog
Muse Spark Open est-il un modèle open source ?
8 min de lecture Muse Spark Open se télécharge depuis le 15 octobre 2026. Les poids ouverts ne font pas de lui un modèle open source : la licence, le matériel et Muse Wonder, resté fermé, fixent la limite. Read more
Node.js 26 passe en LTS le 28 octobre : que tester avant ?
9 min de lecture Le 28 octobre 2026, Node.js 26 doit quitter le statut Current. La branche 24 reste corrigée jusqu’au 30 avril 2028. Cinq contrôles montrent ce qui casse, et ce qui peut attendre. Read more