1

Some PHP function code receives a $functionargument and its argument as a variable. It is then called using $function(...$args);

I wonder how i should test the variable so as to be sure i can apply the spread operator onto it. As for now i use this code :

if (!$args) {
  return $function();
}
elseif (is_array($args) or ($args instanceof \Traversable)) {
  return $function(... $args);
}
else {
  return 'Error';
}

Is this ok ?

JLuc
  • 333
  • 5
  • 15
  • Any array can be spread, right? Ah and generators and traversables and named arguments. https://www.phptutorial.net/php-tutorial/php-spread-operator/ – mickmackusa Mar 23 '22 at 13:15
  • 1
    Why do you not have better control of your variables' data types? Or is this an academic exercise? Here's [a post on named arguments](https://stackoverflow.com/a/64997399/2943403) – mickmackusa Mar 23 '22 at 13:21
  • 1
    Even a falsey/empty array can be spread. https://3v4l.org/FbnWn I think I'd remove the first condition. – mickmackusa Mar 23 '22 at 13:26
  • 2
    I would think that `!$args` might be `true` for a lot of potential values that a function might want, like `''` or `0` or even `[]`, and your first test throws away those values – Chris Haas Mar 23 '22 at 13:27
  • @mickmackusa This is not an academic exercice. It's for a kind of a php generic cron management library that receive function names and their serialized argument from all sorts of other code parts. So the question is how to check the unserialized arguments fit spread syntax before storing it in the job queue. + generators are \Traversable so it's OK. + Yes OK `!$args` isnt required. I feared to spread empty arrays for 0-arguments functions, but it's OK. – JLuc Mar 24 '22 at 14:50

1 Answers1

0

The PHP Manual states that you only need to check if the data type is iterable.

Verify that the contents of a variable is accepted by the iterable pseudo-type, i.e. that it is either an array or an object implementing Traversable

is_iterable() is available from PHP7.1.

An empty array can still be spread, but this is equivalent to pass no arguments into your $function.

So, you can simplify the check to:

if (is_iterable($data) {
    return $function(...$data);
}
return "Data is not iterable";

BUT, it is not enough to check is_iterable(). From PHP8 (it was proposed for PHP8.1, but is available in 8.0 -- see my demo link below), arrays with non-numeric keys can be spread. In PHP8, named parameters were introduced and this means that the spread operator can be used to unpack associative arrays with keys that match a function's argument signature.
In other words, if the function receiving this data is not designed to accommodate the spread-able data, then the script will fire a Fatal Error.

Code: (Demo)

function zeroOneTwo($one, $two, $three) {
    return [$one, $two, $three];
}

function abc($b, $c, $a) {
    return [$a, $b, $c];
}

function spread(...$data) {
    return $data;
}

$numericKeyedArray = [4,5,6];
var_export(abc(...$numericKeyedArray));
echo "\n---\n";
var_export(spread(...$numericKeyedArray));
echo "\n===\n";
var_export(zeroOneTwo(...$numericKeyedArray));
echo "\n===\n";

$nonNumericKeyedArray = ['c' => 'sea', 'a' => 'A', 'b' => 'bee'];
echo "\n---\n";
var_export(abc(...$nonNumericKeyedArray));
echo "\n===\n";
var_export(spread(...$nonNumericKeyedArray));
echo "\n===\n";
var_export(zeroOneTwo(...$nonNumericKeyedArray));

Output (on PHP8):

array (
  0 => 6,
  1 => 4,
  2 => 5,
)
---
array (
  0 => 4,
  1 => 5,
  2 => 6,
)
===
array (
  0 => 4,
  1 => 5,
  2 => 6,
)
===

---
array (
  0 => 'A',
  1 => 'bee',
  2 => 'sea',
)
===
array (
  'c' => 'sea',
  'a' => 'A',
  'b' => 'bee',
)
===

Fatal error: Uncaught Error: Unknown named parameter $c

This proves that you must not only check the type of the data being passed in, you must also acknowledge the context of what the called function does (specifically, what it can receive). If you can 100% guarantee that arrays will suit the function signature and/or that named parameters will never be used in function declarations, then you may be able to confidently go forward. Otherwise, there will remain the possibility of exposing your script to bugs. I am not sure if the toil involved in pre-checking the relationship of the data to the function call will be worth your intended gain in flexibility.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136