diolemo
diolemo

Reputation: 2661

PHP functions that have optional arguments but allow null (and similar) values?

I'm aware that you can have PHP functions with optional arguments like so:

function do_something($argument = null) 
{
   // argument not provided
   if ($argument === null) 
      ........
}

Consider the case that null/false and other values are all valid arguments to my function. How can I determine whether an argument was provided or not?

do_something(null);            // argument provided
do_something(false);           // argument provided
do_something(0);               // argument provided
do_something("test");          // argument provided
do_something(new stdClass());  // argument provided
do_something();                // nothing provided

How can I detect the last case? I have thought about using func_num_args which would work in most cases but it doesn't work if I have several optional arguments.

Is there anything that solves this problem?

Upvotes: 0

Views: 1608

Answers (4)

Taufik Nurrohman
Taufik Nurrohman

Reputation: 3409

In PHP 7, you can do:

function action(...$args) {
    if (count($args) === 0) {
        return action_default();
    }
    $var1 = array_shift($args);
    $var2 = array_shift($args);
    $var3 = array_shift($args);
    // etc.
}

Upvotes: 0

Till Helge
Till Helge

Reputation: 9311

func_num_args() should work exactly as you want it to, because you might be assuming something that's actually not the case: You can't have optional arguments left out if they are in the middle of your arguments list.

So let's look at this function:

function test1 ($param1 = null, $param2 = null) {
   return func_num_args();
}

If I call that with different parameter combinations I get the following results:

test1()              => 0
test1(true)          => 1
test1(true, true)    => 2

There is just no way to call the function in a way where $param2 would be set while $param1 isn't. So you can map every possible output of func_num_args() to exactly one parameter configuration.

In the example above you can rely on the fact that

  • if the return value is 1, $param2 definitely hasn't been set, while $param1 has been.
  • For 0 it's 100% sure that neither one has been given.
  • And, of course, if it's 2 both are there.

What you actually would need are named parameters, as many other languages have them. PHP doesn't at the moment. NikiC actually wrote an RFC that suggests the addition of named parameters to PHP, but I think that's still way off in the future. You can check that out here: https://wiki.php.net/rfc/named_params

As these are not yet available, here are a few workarounds you can try:

Workaround 1

If you really need to be able to have all the parameters optional, try a parameter array:

function test1 (array $opts) {
    if (!isset($opts['opt1'])) { $opts['opt1'] = 'default1'; }
    if (!isset($opts['opt2'])) { $opts['opt2'] = 'default2'; }
}

Then you can call it like this:

test1(array('opt2' => true))

It would set the first parameter to "default1" while keeping the second. And there are definitely better and more elegant ways to do this (e.g. using an object instead), but the general idea is the same.

Workaround 2

You could also go with alias functions:

function test ($param1, $patam2) { ... ]
function testNoParam1 ($param2) {
    test("default1", $param2);
}

That at least makes it very easy to read, but of course you need to pick the right function depending on the parameters you have.

Workaround 3

By adding a lot of additional code you could get really fancy and use a FactoryObject:

class FunctionExecutor {
    private $param1 = "default1";
    private $param2 = "default2";

    public function param1($val) {
       $this->param1 = $val;
       return $this;
    }

    public function param2($val) {
       $this->param2 = $val;
       return $this;
    }

    public function execute() {
       return yourFunction($this->param1, $this->param2);
    }
}

This could be used like this:

$doSomething = new FunctionExecutor();
$returnValue = $doSomething->param2(42)->execute();

In this approach it would probably be a better idea to actually put your function into the object instead of defining it globally. Anyway...this is definitely a possibility, but not the most practical one. Just wanted to add it, because it has some benefits.

Upvotes: 2

Marius L
Marius L

Reputation: 326

Passing "null", "0", or "false" means that you allocate memory to store a variable, regardless it's scope, type, or size. Then it is used as a parameter to a function.

In PHP you cannot override functions by arguments, but you can access them by calling the "func_get_args()", and this is the only way to handle different numbers / types of arguments passed to a function:

function do_something() {
 $args = func_get_args();

 //do_something(stdClass, 1)
 if($args[0] instanceof stdClass && is_numeric($args[1])) {
     //handle
     //return 

 //do_something(1, "string")
 } else if(is_numeric($args[0]) && is_string($args[1])) {
     //handle
     //return
 }
 throw new Exception('invalid arguments');
}

do_something(new StdClass(), 100); //ok
do_something(100, "hell world") // ok
do_someting(); //throws Exception('invalid arguments');

Upvotes: 0

Maarten van Middelaar
Maarten van Middelaar

Reputation: 1721

perhaps this will help: http://www.php.net//manual/en/function.func-get-args.php

$args = func_get_args();
if(!isset($arg[0])) {
    echo 'no argument';
}

or

isset(func_get_arg(0));

Upvotes: 0

Related Questions