Question

I have a function that I'd like to make variable length, with the last item optionally being a bitmask of class constants. However I can't think of a way to check if the last item is a bitmask and not something else since it should be optional.

class D {
    const A = 1;
    const B = 2;
    const C = 4;
    const D = 8;

    public function test(){
      $args = func_get_args();
      $possibly_flags = $args[count($args)-1];
      if(???){ // do some test here to find out
          // do stuff with the flags check
      }
    }
}

$d->test($val1, $val2, [...,], D::A|D::B); 

is there some way to pull this off?

Was it helpful?

Solution

Ended up going with a different class to hold flags, and any other settings for it, and then test if it's an instance of that class. Since that class would only be used for this purpose, it works out pretty decently. The example I used now looks like:

class D {
    const A = 1;
    const B = 2;
    const C = 4;
    const D = 8;

    public function test(){
      $args = func_get_args();
      $possibly_flags = $args[count($args)-1];
      if($possibly_flags instanceof Settings){
          // do stuff with the flags
      }
    }
}

class Settings(){
    public $flags;
    public function __construct($flags){
        $this->flags = $flags;
    }
}

$d->test($val1, $val2, [...,], new Settings(D::A|D::B));

OTHER TIPS

You're passing two different kinds of data, so you need two different arguments in the function declaration:

public function test(array $args, $flags = 0)
{
}

...
$d->test([$var1, $var2, $var3], D::A);

This avoids the ambiguity of the arguments.

I would suggest a more transparent class design:

class D {
    const A = 1;
    const B = 2;

    public function setBitmask($bitmask) {
        $this->bitmask = $bitmask;
        return $this;
    }

    public function test() {
        $args = func_get_args();
        if ($this->bitmask /* whatever */) {
            /* whatever else */
        }
    }

    protected $bitmask = self::A | self::B;
}

which you could call like (new D())->setBitmask(D::A)->test($x, $y);

If you insist on the parameters as given -- variable length function with optional last argument -- then you will need to use a sentinel to distinguish the two. Example, if null is your sentinel:

(new D())->test($val1, $val2, null, D::A)

Then in your method:

public function test() {
    $args = func_get_args();
    if (null === $args[count($args)-2]) {
        $bitmask = $args[count($args)-1];
        /* here you would check that bitmask is possible */
    } else {
        $bitmask = D::A; /* default if you want it */
    }

    if ($bitmask /* whatever */) {
        /* whatever else */
    }
}

This is getting pretty nasty and brittle, so definitely consider the accessor method I describe above or @Jack's method of two arguments.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top