Contact salesFree trial
Blog

Arguments nommés en PHP 8.0

PHPcaractéristiquesAméliorations
19 novembre 2020
Larry Garfield
Directeur de l'expérience des développeurs
Partager

Dans tous les langages de programmation qui possèdent des fonctions (c'est-à-dire tous), les fonctions peuvent être appelées en leur passant une liste ordonnée d'arguments en tant que paramètres d'entrée. Cette méthode fonctionne assez bien, mais elle n'est pas idéale dans certains cas. En particulier, lorsqu'il y a plus d'un paramètre, il est facile d'oublier leur ordre ou la signification d'un paramètre donné. (Et même lorsqu'il n'y en a qu'un, sa signification peut ne pas être évidente sur le site d'appel).

Il existe plusieurs solutions de contournement, comme le passage d'un tableau associatif au lieu de paramètres discrets, mais elles posent toutes leurs propres problèmes. Le passage d'un tableau associatif, par exemple, contourne toute sécurité de type.

Quelques langages abordent ce problème en permettant (généralement de manière optionnelle) aux appelants de spécifier les paramètres par leur nom, plutôt que par leur position. PHP 8.0 est l'un de ces langages.

Paramètres nommés

En PHP, les paramètres nommés sont entièrement contrôlés par l'appelant. Toutes les fonctions et méthodes supportent automatiquement les paramètres nommés. Lorsque la fonction est appelée, l'appelant peut choisir d'utiliser un argument positionnel, un argument nommé, ou même une combinaison des deux.

Par exemple, la fonction array_fill() de PHP produit un nouveau tableau d'une taille spécifiée où tous les éléments ont la même valeur de départ. Voici comment procéder :

<?php $new = array_fill(0, 100, 50) ;

À la lecture de ce texte, que signifient ces chiffres ? Est-ce que $new sera un tableau de 100 éléments de 50 ou un tableau de 50 éléments de 100? Ce n'est pas évident à moins de savoir comment fonctionne array_fill().

Avec des paramètres nommés, vous pouvez l'appeler comme ceci :

<?php array_fill(start_index : 0, count : 100, value : 50) ;

Nous savons maintenant ce que nous allons obtenir : 100 éléments de tableau, dont les clés commencent à 0, et dont toutes les valeurs sont fixées à 50. Mais vous pouvez également ordonner les paramètres comme vous le souhaitez :

<?php array_fill( value : 50, count : 100, start_index : 0, ) ;

Les noms des arguments correspondent aux noms des paramètres dans la définition de la fonction, quel que soit l'ordre. Dans cet exemple, nous avons également présenté les paramètres sous forme verticale, ce qui est correct, et nous avons ajouté une virgule à la fin du dernier paramètre. (Ce qui, comme nous l'avons noté, est nouveau en PHP 8.0 et aide à supporter exactement ce cas).

Les noms de paramètres doivent être littéraux ; vous ne pouvez pas avoir une variable comme nom de paramètre.

Il est également possible de spécifier certains paramètres par leur nom et d'autres par leur position. Plus précisément, vous pouvez lister les paramètres par position jusqu'à ce que vous passiez aux arguments nommés, puis utiliser les arguments nommés par la suite. Cette méthode est donc tout à fait acceptable :

<?php array_fill(0, value : 50, count : 100, ) ;

Mais ceci ne l'est pas :

<?php array_fill( value : 50, 0, count : 100, ) ;

Variables nommées

Une question délicate concernant les paramètres nommés est celle des variables. Les "variables" sont le nom fantaisiste de la possibilité de passer un nombre variable de paramètres à une fonction. Cela fait longtemps qu'il est possible de le faire en PHP :

<?php include_these($product, 1, 2, 3, 4, 5) ; function include_these(Product $product, int ...$args) { // $args est maintenant un tableau d'ints. $vals = ['a', 'b'] ; do_other_thing(...$vals) ; }

Ici, l'opérateur ..., affectueusement appelé "splat", rassemble les arguments dans un tableau ou les répartit à partir d'un tableau, selon le contexte. Mais comment cela interagit-il avec les arguments nommés ?

La façon dont ils interagissent est, je dirais, ce à quoi vous vous attendez logiquement. Un tableau indexé qui est étalé le sera en tant qu'arguments positionnels. Un tableau associatif qui est étalé sera étalé en tant qu'arguments nommés. Reprenons l'exemple précédent :

<?php // Il s'agit d'un tableau nommé, donc les valeurs correspondront à des paramètres nommés. $params = ['count' => 100, 'start_index' => 0, 'value' => 50] ; array_fill(...$params) ;

Lors de la collecte d'arguments variables, ceux-ci seront collectés sous forme de valeurs de tableau indexées numériquement s'ils sont passés en position, ou sous forme de valeurs de tableau à clé de chaîne s'ils sont passés par nom. Sachez que, si vous avez un paramètre variadique, cela signifie que vous pouvez avoir un tableau qui est un mélange de clés numériques et de clés nommées.

Cela conduit également à des possibilités intéressantes pour construire dynamiquement un appel de fonction, en construisant d'abord dynamiquement un tableau associatif et en appelant ensuite la fonction à l'aide de splat.

<?php $args['value'] = $request->get('val') ? ? 'a' ; $args['start_index'] = 0 ; $args['count'] = $config->getSetting('array_size') ; $array = array_fill(...$args) ;

Limitations

Le principal argument contre les arguments nommés était, et a toujours été, que le fait de rendre "trop facile" l'utilisation de fonctions avec de nombreux arguments encouragerait une mauvaise conception de l'API. Par exemple, cet appel de méthode est incontestablement difficile à comprendre :

<?php read_value($object, true, false, false, false, true, true, true, 7, false, true) ;

Une telle API devrait vraiment être repensée. La crainte est que les paramètres nommés permettent aux concepteurs d'API de s'en tirer en disant "eh bien, il suffit d'utiliser des noms pour eux, et vous pouvez même ignorer toutes les valeurs par défaut dont vous ne vous souciez pas de cette façon". C'est vrai, mais cela ne tient pas compte du fait que le problème est que l'API est trop compliquée.

Il n'y a pas d'autre moyen de prévention que "n'utilisez pas cela comme une béquille pour créer de mauvaises API", ce qui pourrait être dit à propos de presque n'importe quelle caractéristique du langage.

Mais ... pourquoi ?

Les paramètres nommés ont été sérieusement proposés pour la première fois en 2013, mais n'ont pas eu de succès jusqu'à maintenant. Ce qui les a vraiment ramenés sur le devant de la scène, ce sont plusieurs discussions sur la manière de faciliter la construction d'objets. Plusieurs propositions ont été émises au début de l'année 2020 pour des syntaxes dédiées et uniques pour des objets "struct" plus faciles, c'est-à-dire des objets qui ne sont qu'une collection de propriétés probablement publiques.

Des langages comme Go et Rust facilitent la création de structures avec des paramètres nommés, parce qu'ils n'ont pas techniquement d'objets comme PHP. Ils ont une structure avec des propriétés, que vous pouvez même définir comme une structure littérale, et ensuite vous pouvez y accrocher des méthodes. Le résultat net est similaire mais pas tout à fait le même que le style d'objet de Java/C++/PHP. Pourtant, de nombreuses personnes ont été jalouses, à juste titre, de la facilité avec laquelle ces langages permettent de créer de nouvelles structures complexes.

Aucune des syntaxes uniques qui ont été proposées n'a vraiment abordé le problème d'une manière qui "colle" à PHP. Plusieurs personnes, dont moi-même, ont noté qu'une approche plus robuste serait de résoudre les problèmes sous-jacents d'une manière qui permettrait à une syntaxe de construction d'objets plus agréable de "tomber" naturellement.

J'ai exposé les arguments en faveur de cette approche en détail dans un billet de blog datant du mois de mars. En bref, la promotion des propriétés des constructeurs et les arguments nommés nous donneraient effectivement une syntaxe d'initialisation des structures. Le tout serait plus grand que la somme de ses parties. Heureusement, le toujours très occupé Nikita Popov a été d'accord avec moi et a pris la balle au bond.

Cela nous amène à l'un des rares endroits où les arguments nommés devraient vraiment être utilisés : la construction d'objets struct.

Pour le démontrer, considérons un objet valeur pour un objet URL PSR-7.

<?php class Url implements UrlInterface { public function __construct( private string $scheme = 'https', private ?string $authority = null, private ?string $userInfo = null, private string $host = '', private ?int $port = 443, private string $path = '', private ?string $query = null, private ?string $fragment = null, ) {} }

Nous avons déjà vu comment la promotion des constructeurs facilite grandement l'écriture. Les arguments nommés rendent également l'utilisation beaucoup plus facile, puisque la plupart de ces paramètres peuvent légitimement être absents dans une URL valide, y compris ceux qui se trouvent en début de liste comme $authority. En PHP 7.4, vous devriez l'utiliser comme suit :

<?php $url = new Url('https', null, null, 'platform.sh', 443, '/blog', null, 'latest') ;

Ce qui est ... grossier, mais vous devez le faire pour accéder aux arguments ultérieurs. Avec des paramètres nommés, cela se résume à cette alternative plus courte et plus auto-documentée :

<?php $url = new Url(host : 'platform.sh', path : '/blog', fragment : 'latest') ; // Ou si vous préférez le vertical : $url = new Url( path : '/blog', host : 'platform.sh', fragment : 'latest', ) ;

Vous pouvez ensuite placer les paramètres dans n'importe quel ordre pour démarrer. Ces deux fonctionnalités se complètent pour rendre le travail avec des objets légers beaucoup plus propre que dans les versions précédentes. Les constructeurs d'objets de valeur sont, à mon avis, l'un des trois endroits clés où l'on peut utiliser des arguments nommés.

Arguments dans les attributs

La deuxième cible clé se trouve dans les attributs :

<?php class SomeController { #[Route('/path', name : 'action')] public function someAction() { // ... } }

Les attributs sont syntaxiquement un appel de constructeur d'objet, qui est appelé paresseusement à travers l'API de réflexion selon les besoins. Comme il s'agit d'un appel de constructeur, il peut faire (presque) tout ce qu'un appel de constructeur peut faire, y compris utiliser des arguments nommés.

En fait, je prédis que la plupart des attributs seront utilisés avec des arguments nommés. Le but est de rendre les métadonnées flexibles disponibles dans la syntaxe du langage lui-même. De nombreuses annotations Doctrine reposent aujourd'hui très fortement sur des clés nommées, car elles sont plus auto-documentées et plus flexibles lorsqu'il y a beaucoup d'arguments optionnels. Il est logique de s'attendre (et d'encourager) les attributs à suivre le même modèle.

Arguments nommés sur les arguments

Le troisième domaine dans lequel je m'attends à ce que les arguments nommés soient largement utilisés est celui des fonctions où les paramètres sont légitimement nécessaires et légitimement déroutants, et où il n'y a pas de moyen évident de contourner le problème. L'exemple précédent de array_fill() en est un bon exemple.

Un autre bon exemple ? Le canard préféré de tous, $haystack, $needle vs. $needle, $haystack. Les fonctions de chaînes de caractères utilisent généralement un ordre, les fonctions de tableaux utilisent généralement l'autre, pour des raisons qui ont un sens lorsque vous modélisez votre API sur le C, mais pas autrement. Maintenant, vous n'avez plus besoin de vous rappeler quel est l'ordre.

<?php if (in_array(haystack : $arr, needle : 'A')) { // ... }

Est-ce l'ordre dans lequel ces paramètres se trouvent dans la fonction, en termes de position ? Qui s'en soucie ? L'appel de la fonction spécifie exactement ce qui est transmis, ce qui rend l'ordre sans importance. (Ils ne sont pas dans l'ordre, au cas où vous vous poseriez la question).

Enfin, nous pouvons arrêter de nous plaindre de l'ordre des paramètres et de citer cela comme une raison pour laquelle PHP est mauvais (je suis sûr que les gens continueront à le faire ; ils auront juste encore plus tort qu'ils ne l'ont déjà fait).

Implications pour l'API

Un avertissement, cependant. Comme nous l'avons vu, les arguments nommés fonctionnent automatiquement pour toutes les fonctions et méthodes. Cela signifie que les noms des paramètres dans les fonctions, les méthodes et les interfaces sont désormais significatifs pour l'API. Les modifier peut perturber les utilisateurs qui les appellent par leur nom. (C'est le bon moment pour revoir vos noms de paramètres, tout en vérifiant que vos bibliothèques sont prêtes pour PHP 8.0).

La signification des noms de variables n'est pas, pour l'instant, appliquée dans l'héritage. C'est une décision pragmatique pour éviter de casser trop de code existant, car la grande majorité des méthodes ne seront pas appelées avec des paramètres nommés 99.9% du temps, donc créer encore plus de casse pour le code existant qui pourrait renommer les arguments pour des raisons tout à fait logiques n'avait pas de sens. Python et Ruby ont adopté la même approche et n'ont pas rencontré de problèmes sérieux, ce que PHP a pris comme un bon signe qu'il était sûr de faire la même chose.

Il n'est pas surprenant que le RFC sur les arguments nommés nous soit offert par Nikita Popov.

Votre meilleur travail
est à l'horizon

Essai gratuit
Discord
© 2025 Platform.sh. All rights reserved.