199

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.

Carrie Kendall
  • 10,761
  • 5
  • 57
  • 79
renosis
  • 2,482
  • 2
  • 15
  • 20
  • 79
    You're probably not missing anything, the PHP developers probably missed this. – Matti Virkkunen Feb 06 '12 at 20:40
  • 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. – Kai Qing Feb 06 '12 at 20:40
  • 3
    One of the best questions I've seen in a while! Have you tried passing nothing? `foo("blah", , "test");`? – Madara's Ghost Feb 06 '12 at 20:41
  • 2
    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. – Mouse Food Feb 06 '12 at 20:41
  • What is the behavior that you are getting? Your code seems right! – ThinkingMonkey Feb 06 '12 at 20:44
  • @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. – renosis Feb 06 '12 at 21:00
  • Possible duplicate of ["*How would I skip optional arguments in a function call?*"](https://stackoverflow.com/q/1066625/5247200). – David Oct 12 '18 at 16:03
  • 1
    Expected behavior. See "Default argument values" section in: https://www.php.net/manual/en/functions.arguments.php -- null value and empty string are accepted as actual arguments to the function – jmdavalos Dec 10 '19 at 14:04
  • You could just send an array and deal with it however you need to. – RationalRabbit Dec 26 '20 at 06:15

14 Answers14

187

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!");
    }
}
drew010
  • 64,915
  • 11
  • 121
  • 148
  • WHy not $x = isset($x) ? $x :'default value'; ? – zloctb Oct 19 '15 at 07:44
  • @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. – DeveloperWeeks Dec 31 '15 at 00:04
  • 9
    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) – DiegoDD Nov 03 '16 at 18:03
  • `else if` should be written as `elseif` in php (PSR-12) – mickmackusa Oct 26 '20 at 05:19
41

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'));
Ryan P
  • 14,118
  • 27
  • 48
  • 1
    Your condition should really be `isset($args['x'])`, as it would currently replace an empty string, empty array, or `false` with the default value. – Tim Cooper Feb 06 '12 at 20:44
  • @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 – Ryan P Feb 06 '12 at 20:47
  • 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! – renosis Feb 06 '12 at 20:48
  • 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 Nov 07 '18 at 15:21
24

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.

Community
  • 1
  • 1
David
  • 5,090
  • 2
  • 21
  • 42
  • 1
    What is the reason for not wanting to do this? Seems pretty handy. – Bryan Sep 15 '17 at 16:22
  • 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*" – billynoah Oct 15 '20 at 20:38
10
function image(array $img)
{
    $defaults = array(
        'src'    => 'cow.png',
        'alt'    => 'milk factory',
        'height' => 100,
        'width'  => 50
    );

    $img = array_merge($defaults, $img);
    /* ... */
}
smottt
  • 3,091
  • 11
  • 37
  • 42
zloctb
  • 8,712
  • 5
  • 60
  • 76
  • 2
    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? – Sean the Bean Mar 20 '18 at 18:29
  • 3
    @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. – MestreLion Sep 15 '18 at 11:57
  • 2
    It is more helpful to use the example given in the question, e.g. `$defaults = [ 'x' => 'some value', 'y' => 'some other value'];` – Frank Forte Nov 07 '18 at 15:24
4

The only way I know of doing it is by omitting the parameter. The only way to omit the parameter is to rearrange the parameter list so that the one you want to omit is after the parameters that you HAVE to set. For example:

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

Then you can call foo like:

foo("blah", "test");

This will result in:

$blah = "blah";
$y = "test";
$x = "some value";
Wes Crow
  • 2,830
  • 18
  • 23
3

I recently had this problem and found this question and answers. While the above questions work, the problem is that they don't show the default values to IDEs that support it (like PHPStorm).

enter image description here

if you use null you won't know what the value would be if you leave it blank.

The solution I prefer is to put the default value in the function definition also:

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;
    ...

The only difference is that I need to make sure they are the same - but I think that's a small price to pay for the additional clarity.

Yehosef
  • 16,151
  • 4
  • 31
  • 53
2

In PHP 8 we can use named arguments for this problem.

So we could solve the problem described by the original poster of this question:

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

With:

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

Reference: https://wiki.php.net/rfc/named_params (in particular the "Skipping defaults" section)

Bram
  • 41
  • 2
2

You can't do this directly, but a little code fiddling makes it possible to emulate.

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

  // code
}
kba
  • 18,696
  • 5
  • 56
  • 84
  • 6
    That would work, but I find using `null` in this instance to be conceptually neater than `false`. – TRiG Apr 16 '12 at 20:33
  • 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. – ToolmakerSteve Feb 24 '19 at 20:27
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
  • 9
  • 2
1

My 2 cents with null coalescing operator ?? (since PHP 7)

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

You can check more examples on my repl.it

SandroMarques
  • 4,186
  • 34
  • 36
0

You can also check if you have an empty string as argument so you can call like:

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

Below the function:

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!

}

It doesn't matter if you fill null or "", you will still get the same result.

Mustafa Ekici
  • 128
  • 1
  • 8
0

Pass an array to the function, instead of individual parameters and use null coalescing operator (PHP 7+).

Below, I'm passing an array with 2 items. Inside the function, I'm checking if value for item1 is set, if not assigned default vault.

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

    function function_name ($args) {
        isset($args['item1']) ? $args['item1'] : 'default value';
    }
Roger
  • 53
  • 6
  • 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'] – Sadee Nov 26 '19 at 11:26
0

Modern way of doing this:

function foo($blah, ?$x, ?$y) {
    $x = $x ?? "some value";
    $y = $y ?? "some other value";
}
lufc
  • 1,826
  • 2
  • 12
  • 18
0

This is case, when object are better - because you can set up your object to hold x and y , set up defaults etc.

Approach with array is near to create object ( In fact, object is bunch of parameters and functions which will work over object, and function taking array will work over some bunch ov parameters )

Cerainly you can always do some tricks to set null or something like this as default

SergeS
  • 10,194
  • 1
  • 24
  • 34