Using Default Arguments in a Function

185,307

Solution 1

I would propose changing the function declaration as follows so you can do what you want:

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

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

    code here!

}

This way, you can make a call like foo('blah', null, 'non-default y value'); and have it work as you want, where the second parameter $x still gets its default value.

With this method, passing a null value means you want the default value for one parameter when you want to override the default value for a parameter that comes after it.

As stated in other answers,

default parameters only work as the last arguments to the function. If you want to declare the default values in the function definition, there is no way to omit one parameter and override one following it.

If I have a method that can accept varying numbers of parameters, and parameters of varying types, I often declare the function similar to the answer shown by Ryan P.

Here is another example (this doesn't answer your question, but is hopefully informative:

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

        // 3 parameters passed
    } elseif (func_num_args() == 5) {
        $args = func_get_args();
        // 5 parameters passed
    } else {
        throw new \InvalidArgumentException("Could not figure out parameters!");
    }
}

Solution 2

Optional arguments only work at the end of a function call. There is no way to specify a value for $y in your function without also specifying $x. Some languages support this via named parameters (VB/C# for example), but not PHP.

You can emulate this if you use an associative array for parameters instead of arguments -- i.e.

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

    ...
}

Then call the function like so:

foo(array('y' => 'my value'));

Solution 3

It is actually possible:

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

Whether you would want to do so is another story :)


UPDATE:

The reasons to avoid this solution are:

  • it is (arguably) ugly
  • it has an obvious overhead.
  • as the other answers proof, there are alternatives

But it can actually be useful in situations where:

  • you don't want/can't change the original function.

  • you could change the function but:

    • using null (or equivalent) is not an option (see DiegoDD's comment)
    • you don't want to go either with an associative or with func_num_args()
    • your life depends on saving a couple of LOCs

About the performance, a very simple test shows that using the Reflection API to get the default parameters makes the function call 25 times slower, while it still takes less than one microsecond. You should know if you can to live with that.

Of course, if you mean to use it in a loop, you should get the default value beforehand.

Solution 4

function image(array $img)
{
    $defaults = array(
        'src'    => 'cow.png',
        'alt'    => 'milk factory',
        'height' => 100,
        'width'  => 50
    );

    $img = array_merge($defaults, $img);
    /* ... */
}

Solution 5

PHP 8 using Named Arguments

What if I want to use the default value for argument $x and set a value for $y? foo($blah, $x = "some value", $y = "some other value")

Usage-1:

foo(blah: "blah", y: "test");

Usage-2:

// use names to address last arguments
foo("blah", y: "test");

Native functions will also use this feature

htmlspecialchars($string, double_encode: false);
// Same as
htmlspecialchars($string, ENT_COMPAT | ENT_HTML401, 'UTF-8', false);

About the Feature and Documentation

Named arguments allow passing arguments to a function based on the parameter name, rather than the parameter position. This makes the meaning of the argument self-documenting, makes the arguments order-independent, and allows skipping default values arbitrarily.

IDE Support

Netbeans 12.3 fully supports PHP 8.0 syntax including this feature, with the exception of code completion for named arguments.

Share:
185,307

Related videos on Youtube

renosis
Author by

renosis

Hobby coder.

Updated on July 08, 2022

Comments

  • renosis
    renosis almost 2 years

    I am confused about default values for PHP functions. Say I have a function like this:

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

    What if I want to use the default argument for $x and set a different argument for $y?

    I have been experimenting with different ways and I am just getting more confused. For example, I tried these two:

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

    But both of those do not result in a proper default argument for $x. I have also tried to set it by variable name.

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

    I fully expected something like this to work. But it doesn't work as I expected at all. It seems like no matter what I do, I am going to have to end up typing in the default arguments anyway, every time I invoke the function. And I must be missing something obvious.

    • Matti Virkkunen
      Matti Virkkunen over 12 years
      You're probably not missing anything, the PHP developers probably missed this.
    • Kai Qing
      Kai Qing over 12 years
      That's an interesting question. I've never actually had the need to do this as you've stated since I only use default args to expand functions inherited by other programmers when I am not sure what depends on the original. I'm curious about an answer.
    • Madara's Ghost
      Madara's Ghost over 12 years
      One of the best questions I've seen in a while! Have you tried passing nothing? foo("blah", , "test");?
    • Mouse Food
      Mouse Food over 12 years
      The rule is that default arguments can only follow arguments. That is you could have foo("blah") and x,y are default, or foo("Blah","xyz") and y is the default.
    • ThinkingMonkey
      ThinkingMonkey over 12 years
      What is the behavior that you are getting? Your code seems right!
    • renosis
      renosis over 12 years
      @ThinkingMonkey, well, when I set null or "" for the second default value it replaces the default value with the null... as for the foo("blah", $x, $y = "test); I get an undefined variable for $x. If I do something like foo("blah", $y = "test"); it replaces the $x value with test instead of $y.
    • David
      David over 5 years
    • jmdavalos
      jmdavalos over 4 years
      Expected behavior. See "Default argument values" section in: php.net/manual/en/functions.arguments.php -- null value and empty string are accepted as actual arguments to the function
    • RationalRabbit
      RationalRabbit over 3 years
      You could just send an array and deal with it however you need to.
    • ClarkeyBoy
      ClarkeyBoy over 2 years
      I tend to use a mix - either an array and use custom function getArrayValue($array, $key, $default) - if $key doesn't exist in $array, $default is returned, or I use function doSomething($a, $b = null, $c = null) and use if(is_null($b)) $b = $bDefault) within the function body. This way I can call doSomething("bob", null, "fred") and the null will be overridden with the default value of $b.
  • Tim Cooper
    Tim Cooper over 12 years
    Your condition should really be isset($args['x']), as it would currently replace an empty string, empty array, or false with the default value.
  • kitti
    kitti over 12 years
    @TimCooper lol I went to change based on your first comment that it should be '=== null', went and changed my answer to use isset instead, then came back to see you changed your comment too. :D
  • renosis
    renosis over 12 years
    Ah, drat! I don't like that at all... Oh well, you can't have everything. I suppose I will have to put a little more thought into the order of my default arguments then. Thanks!
  • TRiG
    TRiG about 12 years
    That would work, but I find using null in this instance to be conceptually neater than false.
  • zloctb
    zloctb over 8 years
    WHy not $x = isset($x) ? $x :'default value'; ?
  • DeveloperWeeks
    DeveloperWeeks over 8 years
    @zloctb this is a clean and readable null check. Looks like it is developer preference. Either $x = isset($x) ? $x :'default value'; or $x = is_null($x) ?'default value':$x; would work as well as this solution. It is a preference choice about readability and maintenance.
  • DiegoDD
    DiegoDD over 7 years
    Just to clarify for future readers. The first solution obviously makes it impossible to assign a real null value to the parameter, since every time it would assign the default value. Even in case you had another special value for when you actually want a null (e.g. when $x == "use_null" make $x = null), then you wouldn't be able to assign such special value as the literal value of the parameter (in this case, the "use_null" string). So just be sure to use a unique, never desired "key" for when you want to use the default value (and you want null to be a valid option)
  • Bryan
    Bryan almost 7 years
    What is the reason for not wanting to do this? Seems pretty handy.
  • Sean the Bean
    Sean the Bean over 6 years
    Am I missing something? This doesn't seem to answer the question at all, but it has 7 upvotes. Maybe adding a bit of explanation would help?
  • MestreLion
    MestreLion almost 6 years
    @SeantheBean: true, an explanation would be better, but his point is: since PHP does not have named arguments, use an associative array as its single parameter.
  • Frank Forte
    Frank Forte over 5 years
    Unfortunately, you will not be able to assign null with this answer. This is a better option: $x = !array_key_exists('x',$args) ? 'default x value' : $args['x'];
  • Frank Forte
    Frank Forte over 5 years
    It is more helpful to use the example given in the question, e.g. $defaults = [ 'x' => 'some value', 'y' => 'some other value'];
  • ToolmakerSteve
    ToolmakerSteve over 5 years
    Adding to TriG's comment, false is a riskier choice than null, because php is a non-strict language. !$x will be true for several different inputs, that may surprise the programmer: 0, ''. Whereas with null, you can use !isset as a stricter test.
  • SubjectDelta
    SubjectDelta almost 5 years
    Internal usage of ReflectionFunction on itself would save the price!
  • Sadee
    Sadee over 4 years
    You can use $args['item1'] = $args['item1']??'default value'; in PHP 7+. if $args['item1'] is null then default value string will assign to $args['item1']
  • Eaten by a Grue
    Eaten by a Grue over 3 years
    Lovely but unfortunately doesn't work on internal functions: Uncaught ReflectionException: Cannot determine default value for internal functions which is a bit stupid since the docs read: "Gets the default value of the parameter for any user-defined or internal function or method"
  • thelr
    thelr about 3 years
    "Modern"? This answer won't age well. Please specify what PHP versions this is relevant for.
  • ClarkeyBoy
    ClarkeyBoy over 2 years
    Regarding IDEs - it is easy enough to get to the function definition - ctrl + click function or class name in PHPStorm. So long as you stick to some common approach of putting the defaults as the very first line(s), it's easy to adapt. That being said, there is no solution without a downside - they all have their disadvantages.
  • Obed Parlapiano
    Obed Parlapiano about 2 years
    Ah yes. The beauty of PHP