Utilisation d'arguments par défaut dans une fonction

178

Je ne comprends pas les valeurs par défaut des fonctions PHP. Disons que j'ai une fonction comme celle-ci:

function foo($blah, $x = "some value", $y = "some other value") {
    // code here!
}

Que faire si je veux utiliser l'argument par défaut pour $ x et définir un argument différent pour $ y?

J'ai expérimenté différentes manières et je suis de plus en plus confus. Par exemple, j'ai essayé ces deux:

foo("blah", null, "test");
foo("blah", "", "test");

Mais les deux n'aboutissent pas à un argument par défaut approprié pour $ x. J'ai également essayé de le définir par nom de variable.

foo("blah", $x, $y = "test");   

Je m'attendais vraiment à ce que quelque chose comme ça fonctionne. Mais cela ne fonctionne pas du tout comme je m'y attendais. Il semble que peu importe ce que je fais, je vais quand même devoir taper les arguments par défaut, chaque fois que j'invoque la fonction. Et je dois manquer quelque chose d'évident.

renose
la source
68
Vous ne manquez probablement rien, les développeurs PHP l'ont probablement manqué.
Matti Virkkunen
C'est une question intéressante. Je n'ai jamais eu besoin de faire cela comme vous l'avez dit puisque je n'utilise que des arguments par défaut pour développer les fonctions héritées par d'autres programmeurs lorsque je ne suis pas sûr de ce qui dépend de l'original. Je suis curieux de savoir une réponse.
Kai Qing
3
Une des meilleures questions que j'ai vues depuis un moment! Avez-vous essayé de ne rien passer? foo("blah", , "test");?
Madara's Ghost
2
La règle est que les arguments par défaut ne peuvent suivre que les arguments. C'est-à-dire que vous pourriez avoir foo ("blah") et x, y sont la valeur par défaut, ou foo ("Blah", "xyz") et y est la valeur par défaut.
Nourriture pour souris
1
Comportement prévisible. Voir la section "Valeurs des arguments par défaut" dans: php.net/manual/en/functions.arguments.php - les valeurs nulles et les chaînes vides sont acceptées comme arguments réels de la fonction
jmdavalos

Réponses:

164

Je proposerais de changer la déclaration de fonction comme suit afin que vous puissiez faire ce que vous voulez:

function foo($blah, $x = null, $y = null) {
    if (null === $x) {
        $x = "some value";
    }

    if (null === $y) {
        $y = "some other value";
    }

    code here!

}

De cette façon, vous pouvez passer un appel comme foo('blah', null, 'non-default y value');et le faire fonctionner comme vous le souhaitez, où le deuxième paramètre $xobtient toujours sa valeur par défaut.

Avec cette méthode, passer une valeur nulle signifie que vous voulez la valeur par défaut pour un paramètre lorsque vous souhaitez remplacer la valeur par défaut pour un paramètre qui vient après.

Comme indiqué dans d'autres réponses,

les paramètres par défaut ne fonctionnent que comme derniers arguments de la fonction. Si vous souhaitez déclarer les valeurs par défaut dans la définition de fonction, il n'y a aucun moyen d'omettre un paramètre et d'en remplacer un qui le suit.

Si j'ai une méthode qui peut accepter un nombre variable de paramètres et des paramètres de types différents, je déclare souvent la fonction similaire à la réponse donnée par Ryan P.

Voici un autre exemple (cela ne répond pas à votre question, mais il est espérons-le informatif:

public function __construct($params = null)
{
    if ($params instanceof SOMETHING) {
        // single parameter, of object type SOMETHING
    } else if (is_string($params)) {
        // single argument given as string
    } else if (is_array($params)) {
        // params could be an array of properties like array('x' => 'x1', 'y' => 'y1')
    } else if (func_num_args() == 3) {
        $args = func_get_args();

        // 3 parameters passed
    } else if (func_num_args() == 5) {
        $args = func_get_args();
        // 5 parameters passed
    } else {
        throw new InvalidArgumentException("Could not figure out parameters!");
    }
}
drew010
la source
Pourquoi pas $ x = isset ($ x)? $ x: 'valeur par défaut'; ?
zloctb
@zloctb c'est un contrôle nul propre et lisible. On dirait que c'est la préférence des développeurs. Soit $ x = isset ($ x)? $ x: 'valeur par défaut'; ou $ x = is_null ($ x)? 'valeur par défaut': $ x; fonctionnerait aussi bien que cette solution. C'est un choix de préférence sur la lisibilité et la maintenance.
DeveloperWeeks
9
Juste pour clarifier pour les futurs lecteurs. La première solution rend évidemment impossible d'attribuer une nullvaleur réelle au paramètre, car à chaque fois elle attribuerait la valeur par défaut. Même dans le cas où vous auriez une autre valeur spéciale pour le moment où vous voulez réellement un null (par exemple, lorsque $ x == "use_null" make $ x = null), vous ne pourrez pas attribuer une telle valeur spéciale comme valeur littérale du paramètre (dans ce cas, la chaîne "use_null"). Veillez donc simplement à utiliser une "clé" unique, que vous ne souhaitez jamais utiliser lorsque vous souhaitez utiliser la valeur par défaut (et que vous voulez nullêtre une option valide)
DiegoDD
37

Les arguments facultatifs ne fonctionnent qu'à la fin d'un appel de fonction. Il n'y a aucun moyen de spécifier une valeur pour $ y dans votre fonction sans spécifier également $ x. Certains langages le prennent en charge via des paramètres nommés (VB / C # par exemple), mais pas PHP.

Vous pouvez émuler cela si vous utilisez un tableau associatif pour les paramètres au lieu des arguments - c.-à-d.

function foo(array $args = array()) {
    $x = !isset($args['x']) ? 'default x value' : $args['x'];
    $y = !isset($args['y']) ? 'default y value' : $args['y'];

    ...
}

Appelez ensuite la fonction comme ceci:

foo(array('y' => 'my value'));
Ryan P
la source
1
Votre condition devrait vraiment être isset($args['x']), car elle remplacerait actuellement une chaîne vide, un tableau vide ou falsela valeur par défaut.
Tim Cooper
@TimCooper lol Je suis allé changer en fonction de votre premier commentaire qu'il devrait être '=== null', je suis allé changer ma réponse pour utiliser isset à la place, puis je suis revenu pour voir que vous avez également changé votre commentaire. : D
Ryan P
Ah, drat! Je n'aime pas du tout ça ... Eh bien, tu ne peux pas tout avoir. Je suppose que je devrai alors réfléchir un peu plus sur l'ordre de mes arguments par défaut. Merci!
renose le
Malheureusement, vous ne pourrez pas attribuer de null avec cette réponse. C'est une meilleure option: $x = !array_key_exists('x',$args) ? 'default x value' : $args['x'];
Frank Forte
20

C'est effectivement possible:

foo( 'blah', (new ReflectionFunction('foo'))->getParameters()[1]->getDefaultValue(), 'test');

Que vous vouliez le faire est une autre histoire :)


METTRE À JOUR:

Les raisons d'éviter cette solution sont:

  • c'est (sans doute) moche
  • il a une surcharge évidente.
  • comme la preuve des autres réponses, il existe des alternatives

Mais cela peut en fait être utile dans les situations où:

  • vous ne voulez pas / ne pouvez pas changer la fonction d'origine.

  • vous pouvez changer la fonction mais:

    • using null(ou équivalent) n'est pas une option (voir le commentaire de DiegoDD )
    • vous ne voulez aller ni avec un associatif ni avec func_num_args()
    • votre vie dépend de la sauvegarde de quelques LOC

À propos des performances, un test très simple montre que l'utilisation de l'API Reflection pour obtenir les paramètres par défaut rend l'appel de fonction 25 fois plus lent , alors que cela prend toujours moins d'une microseconde . Vous devriez savoir si vous pouvez vivre avec ça.

Bien sûr, si vous voulez l'utiliser dans une boucle, vous devriez obtenir la valeur par défaut au préalable.

David
la source
1
Quelle est la raison de ne pas vouloir faire cela? Cela semble assez pratique.
Bryan
10
function image(array $img)
{
    $defaults = array(
        'src'    => 'cow.png',
        'alt'    => 'milk factory',
        'height' => 100,
        'width'  => 50
    );

    $img = array_merge($defaults, $img);
    /* ... */
}
zloctb
la source
2
Est-ce que je manque quelque chose? Cela ne semble pas du tout répondre à la question, mais il a 7 votes positifs. Peut-être que l'ajout d'un peu d'explication serait utile?
Sean the Bean
3
@SeantheBean: vrai, une explication serait meilleure, mais son point est: puisque PHP n'a pas d'arguments nommés, utilisez un tableau associatif comme paramètre unique.
MestreLion
1
Il est plus utile d'utiliser l'exemple donné dans la question, par exemple$defaults = [ 'x' => 'some value', 'y' => 'some other value'];
Frank Forte
4

La seule façon que je connaisse de le faire est d'omettre le paramètre. La seule façon d'omettre le paramètre est de réorganiser la liste des paramètres de sorte que celui que vous souhaitez omettre soit après les paramètres que vous DEVEZ définir. Par exemple:

function foo($blah, $y = "some other value", $x = "some value")

Ensuite, vous pouvez appeler foo comme:

foo("blah", "test");

Cela se traduira par:

$blah = "blah";
$y = "test";
$x = "some value";
Wes Crow
la source
3

J'ai récemment eu ce problème et j'ai trouvé cette question et ces réponses. Bien que les questions ci-dessus fonctionnent, le problème est qu'elles ne montrent pas les valeurs par défaut aux IDE qui les prennent en charge (comme PHPStorm).

entrez la description de l'image ici

si vous utilisez, nullvous ne saurez pas quelle serait la valeur si vous le laissez vide.

La solution que je préfère est de mettre également la valeur par défaut dans la définition de la fonction:

protected function baseItemQuery(BoolQuery $boolQuery, $limit=1000, $sort = [], $offset = 0, $remove_dead=true)
{
    if ($limit===null) $limit =1000;
    if ($sort===null) $sort = [];
    if ($offset===null) $offset = 0;
    ...

La seule différence est que je dois m'assurer qu'ils sont identiques - mais je pense que c'est un petit prix à payer pour la clarté supplémentaire.

Yehosef
la source
L'utilisation interne de ReflectionFunction seule permettrait d'économiser le prix!
SujetDelta
2

Vous ne pouvez pas faire cela directement, mais un peu de bidouillage de code permet d'émuler.

function foo($blah, $x = false, $y = false) {
  if (!$x) $x = "some value";
  if (!$y) $y = "some other value";

  // code
}
kba
la source
6
Cela fonctionnerait, mais je trouve que l'utilisation nulldans ce cas est conceptuellement plus soignée que false.
TRiG
Ajouter au commentaire de TriG falseest un choix plus risqué que null, car php est un langage non strict. !$xsera vrai pour plusieurs entrées différentes, ce qui peut surprendre le programmeur: 0, ''. Alors qu'avec null, vous pouvez utiliser !issetcomme test plus strict.
ToolmakerSteve
1
<?php
function info($name="George",$age=18) {
echo "$name is $age years old.<br>";
}
info();     // prints default values(number of values = 2)
info("Nick");   // changes first default argument from George to Nick
info("Mark",17);    // changes both default arguments' values

?>
Odin
la source
1

Mes 2 cents avec opérateur de fusion nul ?? (depuis PHP 7)

function foo($blah, $x = null, $y = null) {
    $varX = $x ?? 'Default value X';
    $varY = $y ?? 'Default value Y';
    // ...
}

Vous pouvez consulter plus d'exemples sur mon repl.it

SandroMarques
la source
0

C'est le cas, lorsque l'objet est meilleur - parce que vous pouvez configurer votre objet pour qu'il contienne x et y, configurer les valeurs par défaut, etc.

L'approche avec tableau est proche de créer un objet (en fait, l'objet est un tas de paramètres et de fonctions qui fonctionneront sur l'objet, et le tableau de prise de fonction fonctionnera sur certains paramètres)

Cerainly vous pouvez toujours faire quelques astuces pour définir null ou quelque chose comme ça par défaut

SergeS
la source
0

Vous pouvez également vérifier si vous avez une chaîne vide comme argument afin de pouvoir appeler comme:

foo('blah', "", 'non-default y value', null);

En dessous de la fonction:

function foo($blah, $x = null, $y = null, $z = null) {
    if (null === $x || "" === $x) {
        $x = "some value";
    }

    if (null === $y || "" === $y) {
        $y = "some other value";
    }

    if (null === $z || "" === $z) {
        $z = "some other value";
    }

    code here!

}

Peu importe que vous remplissiez nullou "", vous obtiendrez toujours le même résultat.

Mustafa Ekici
la source
0

Passez un tableau à la fonction, au lieu de paramètres individuels et utilisez l'opérateur de fusion nul (PHP 7+).

Ci-dessous, je passe un tableau avec 2 éléments. À l'intérieur de la fonction, je vérifie si la valeur de l'élément1 est définie, si elle n'est pas attribuée au coffre par défaut.

$args = ['item2' => 'item2',
        'item3' => 'value3'];

    function function_name ($args) {
        isset($args['item1']) ? $args['item1'] : 'default value';
    }
Roger
la source
Vous pouvez utiliser $args['item1'] = $args['item1']??'default value'; en PHP 7+. si $ args ['item1'] est nul alors la default valuechaîne sera assignée à $ args ['item1']
Sadee