How to use a PHP associative array as function call arguments? [duplicate] - php

This question already has answers here:
Does PHP allow named parameters so that optional arguments can be omitted from function calls?
(17 answers)
Closed 6 months ago.
Assume that there is a function with some parameters and I have an associative array (or a simple object with public properties - which is almost the same, since I can always use a type cast (object)$array) whose keys correspond to the function parameter names and whose values correspond to function call arguments. How do I call it and pass them in there?
<?php
function f($b, $a) { echo "$a$b"; }
// notice that the order of args may differ.
$args = ['a' => 1, 'b' => 2];
call_user_func_array('f', $args); // expected output: 12 ; actual output: 21
f($args); // expected output: 12 ; actual output: ↓
// Fatal error: Uncaught ArgumentCountError:
// Too few arguments to function f(), 1 passed

It turns out, I just had to use variadic function named param unpacking feature introduced in PHP 8 ( https://wiki.php.net/rfc/named_params#variadic_functions_and_argument_unpacking ) :
f(...$args); // output: 12
Prior to PHP 8, this code produces the error: Cannot unpack array with string keys.
Secondly, it turns out that call_user_func_array also works as expected in PHP 8 (see https://wiki.php.net/rfc/named_params#call_user_func_and_friends for explanation):
call_user_func_array('f', $args); // output: 12
- while it still outputs incorrect '21' in older versions.

As a hack for older versions of PHP you could also use Reflection:
<?php
function test($b, $a) {
echo "$a$b";
}
$callback = 'test';
$parameters = ['a' => 1, 'b' => 2];
$reflection = new ReflectionFunction($callback);
$new_parameters = array();
foreach ($reflection->getParameters() as $parameter) {
$new_parameters[] = $parameters[$parameter->name];
}
$parameters = $new_parameters;
call_user_func_array($callback, $parameters);
Demo

Related

Php | How to pass unknown length array of arguments to function individually

I have an array of arguments that are in order for a function.
The number of items in the array differ. I want to be able to pass in each item in the array individually to the functions (eg...func(arg1, arg2, arg3, arg4);). Opposed to just passing in the whole array.
Keep in mind, I do not know the length of the array, it differes depending on which function I am calling.
since PHP 5.6 you have the Variable-length argument lists so you can call your function like this:
$arr = array(1, 2, 5, "ten");
and you can send all the elements in the $arr to a function by calling it like this:
myFunc(...$arr);
If you mean you have a function that its argument is an array, you can use foreach to fetch all the array elements:
function my_func($arg_array)
{
foreach($arg_array as $key=>$value)
{
......
}
....
}
But if you mean you have a function that has unknown number of arguments you can use func_get_args function that returns an array containing arguments as an array.
function my_func()
{
foreach(func_get_args() as $key=>$value)
{
......
}
....
}
Use : call_user_func_array("functionName", $arrayofArgs);
<?php
function unknown($arg1,$arg2,$arg3){
echo "arg1 = $arg1 <br>";
echo "arg1 = $arg2 <br>";
echo "arg1 = $arg3 <br>";
}
$array = [
"arg1" => "Hi",
"arg2" => "Marhaba",
"arg3" => "Hola",
"arg4" => "foo"
];
call_user_func_array("unknown", $array);
?>
and you can call the function from class by passing the first param as array of ["className","methodName"]
php Docs :
http://php.net/manual/en/function.call-user-func-array.php
In PHP you have:
func_num_args()
func_get_args()
func_get_arg()
for that purpose.
EDIT
If you are fine using PHP 5.6 then you have splat operator. Quoting sample from docs (link):
<?php
function add($a, $b, $c) {
return $a + $b + $c;
}
$operators = [2, 3];
echo add(1, ...$operators);

What is the meaning of three dots (...) in PHP?

While I am installing Magento 2 on my Server, I got an error. After investigating the code and found that there is are three dots (...), which is producing the error. I included the code I found below:
return new $type(...array_values($args));
What is this operator called, and what is its purpose?
This is literally called the ... operator in PHP, but is known as the splat operator from other languages. From a 2014 LornaJane blog post on the feature:
This feature allows you to capture a variable number of arguments to a function, combined with "normal" arguments passed in if you like. It's easiest to see with an example:
function concatenate($transform, ...$strings) {
$string = '';
foreach($strings as $piece) {
$string .= $piece;
}
return($transform($string));
}
echo concatenate("strtoupper", "I'd ", "like ", 4 + 2, " apples");
(This would print I'D LIKE 6 APPLES)
The parameters list in the function declaration has the ... operator in it, and it basically means " ... and everything else should go into $strings". You can pass 2 or more arguments into this function and the second and subsequent ones will be added to the $strings array, ready to be used.
There are TWO uses for the ellipsis (...) PHP token—think of them as packing an array and unpacking an array. Both purposes apply to function arguments.
Pack
When defining a function, if you need a dynamic number of variables provided to the function (i.e., you don't know how many arguments will be provided to that function when called in your code), prefix the ellipsis (...) token to a variable name—e.g., ...$numbers—to capture all (remaining) arguments provided to that function into an array assigned to the named variable—in this case $numbers—that is accessible inside the function block. The number of arguments captured by prefixing ellipsis (...) can be zero or more.
For example:
// function definition
function sum (...$numbers) { // use ellipsis token when defining function
$acc = 0;
foreach ($numbers as $nn) {
$acc += $nn;
}
return $acc;
}
// call the function
echo sum(1, 2, 3, 4); // provide any number of arguments
> 10
// and again...
echo sum(1, 2, 3, 4, 5);
> 15
// and again...
echo sum();
> 0
When packing is used in function instantiation, prefixing ellipsis (...) to a variable name captures all remaining arguments, i.e., you can still have any number—zero or more—of initial, fixed (positional) arguments:
function sum ($first, $second, ...$remaining_numbers) {
$acc = $first + $second;
foreach ($remaining_numbers as $nn) {
$acc += $nn;
}
return $acc;
}
// call the function
echo sum(1, 2); // provide at least two arguments
> 3
// and again...
echo sum(1, 2, 3, 4); // first two are assigned to fixed arguments, the rest get "packed"
> 10
...the prefixed ellipsis variable captures all the rest. For this reason it must be the final function argument.
Unpack
Alternatively, when calling a function, if the arguments you provide to that function are previously combined into an array use a ellipsis (...) token prefixed variable "inline" to convert that array variable into individual arguments provided to the function. When any number of function arguments are replaced with an ellipsis prefixed variable, each array element is assigned to the respective function argument variable named in the function definition.
For example:
function add ($aa, $bb, $cc) {
return $aa + $bb + $cc;
}
$arr = [1, 2, 3];
echo add(...$arr); // use ellipsis token when calling function
> 6
$first = 1;
$arr = [2, 3];
echo add($first, ...$arr); // used with positional arguments
> 6
$first = 1;
$arr = [2, 3, 4, 5]; // array can be "oversized"
echo add($first, ...$arr); // remaining elements are ignored
> 6
Unpacking is particularly useful when using array functions to manipulate arrays or variables.
For example, unpacking the result of array_slice:
function echoTwo ($one, $two) {
echo "$one\n$two";
}
$steaks = array('ribeye', 'kc strip', 't-bone', 'sirloin', 'chuck');
// array_slice returns an array, but ellipsis unpacks it into function arguments
echoTwo(...array_slice($steaks, -2)); // return last two elements in array
> sirloin
> chuck
Every answer refers to the same blog post, besides them, here is the official documentation about variable-length argument lists:
http://php.net/manual/en/functions.arguments.php#functions.variable-arg-list
In PHP 5.6 and later, argument lists may include the ... token to denote that the function accepts a variable number of arguments. The arguments will be passed into the given variable as an array
It seems "splat" operator is not an official name, still it's cute!
In PHP 7.4 the ellipsis is also the Spread operator:
$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];
Source: https://wiki.php.net/rfc/spread_operator_for_array
Meaning is that it decomposes an associative array to a list. So you do not need to type N parameters to call a method, just one. If method allows a decomposed parameter and if parameters are of the same type.
For me, the most important thing about splat operator is that it can help to typehint array parameters:
$items = [
new Item(),
new Item()
];
$collection = new ItemCollection();
$collection->add(...$items); // !
// what works as well:
// $collection->add(new Item());
// $collection->add(new Item(), new Item(), new Item()); // :(
class Item {};
class ItemCollection {
/**
* #var Item[]
*/
protected $items = [];
public function add(Item ...$items)
{
foreach ($items as &$item) {
$this->items[] = $item;
}
}
}
it saves some effort on type control, especially while working with huge collections or very object-oriented.
Important to notice is that ...$array do decompose an array despite the type of its items, so you can go the ugly way also:
function test(string $a, int $i) {
echo sprintf('%s way as well', $a);
if ($i === 1) {
echo('!');
}
}
$params = [
(string) 'Ugly',
(int) 1
];
test(...$params);
// Output:
// Ugly way as well!
But please don't.
PHP 8 update - named arguments
Since PHP 8 you can now decompose associative arrays, it is, arrays that have keys for them values. If you use it in function that has arguments named the same, PHP will transfer values to proper variables:
$arr = [
'pi' => 3.14,
'r' => 4,
];
function circ($r, $pi) {
return 2*$pi*$r;
}
// so that call
circ(...$arr);
// will be effectively a call of
circ(pi: 3.14, r: 4);
and you can slightly less bother about order of parameters.
PHP 8.1 update - new syntax for creating callable
Despite usage with arrays, ... earned a whole new, very useful functionality that help creating callable from any context:
$f = strtoupper(...); // creating a callable
echo $f('fo');
class test {
public function func($a) {
echo $a . PHP_EOL;
}
}
$f = (new test)
->func(...); // creating a callable
$f('x');
In PHP 8.1, this syntax "(...)" is used as a new way to create callables.
Before PHP 8.1:
$callable = [$this, 'myMethod'];
After PHP 8.1:
$callable = $this->myMethod(...);
Source: https://wiki.php.net/rfc/first_class_callable_syntax
To use this feature, just warn PHP that it needs to unpack the array into variables using the ... operator. See here for more details, a simple example could look like this:
$email[] = "Hi there";
$email[] = "Thanks for registering, hope you like it";
mail("someone#example.com", ...$email);
This is the so called "splat" operator. Basically that thing translates to "any number of arguments"; introduced with PHP 5.6
See here for further details.
It seems no one has mentioned it, so here to stay[It will also help Google (& Other SEs) guide devs who asking for Rest Parameters in PHP]:
As indicated here its called Rest Parameters on JS & I prefer this meaningful naming over that splat thing!
In PHP, The functionality provided by ...args is called Variadic functions which's introduced on PHP5.6. Same functionality was used to be implemented using func_get_args().
In order to use it properly, you should use rest parameters syntax, anywhere it helps reducing boilerplate code.

How to use an array of arrays with array_map(...) in PHP? [duplicate]

This question already has answers here:
Transposing multidimensional arrays in PHP
(12 answers)
Closed 10 months ago.
The PHP function array_map(...) expects a callback as first parameter (or null for creating an array of arrays) and a variable number of array arguments, e.g.:
$foo => array_map(null, $bar, $buz);
Now I have a case, where I need to pass to array_map(...) a variable number of arrays. I cannot hard-code this, since the arrays for the array_map(...)'s input are generated dynamically.
function performSomeLogicAndGetArgumentsForMyFunction() {
...
return ['bar' => [...], 'buz' => [...]];
}
$foo = array_map(null, performSomeLogicAndGetArgumentsForMyFunction());
It doesn't work this way, since array_map(...) expects a variable number of array and not an array of arrays.
Is there a solution for this? How can I keep the call flexible and pass a variable number of arguments to the array_map(...)? (It also applies to every other variadic function I cannot manipulate.)
You're returning an array of arrays, and you want to map over the innermost of those arrays. You can use argument unpacking for this:
function say($n, $m) {
return "The number $n is called $m in Spanish";
}
function arrays() {
return [
[ 1, 2, 3 ],
[ 'uno', 'dos', 'tres' ],
];
}
print_r(
array_map('say', ...arrays())
);
See it online at 3v4l.org.
Alternatively, you could use call_user_func_array as mentioned in the RFC at a measurable run-time cost:
print_r(
call_user_func_array(
'array_map',
array_merge(array ('say'), arrays())
)
);
See it online at 3v4l.org.
Either of these patterns can implement variadic forms of common methods. For example, to emulate vsprintf one can use:
sprintf('%s %s', ...['Hello', 'World']);
call_user_func_array('sprintf', array_merge(['%s, %s'], ['Hello', 'World']));
As a last resort, use eval
//build you array of array variable names with the dynamic content coming in.
$arrays = ['$foo', '$bar', '$baz'];
$str = implode(', ', $arrays);
eval("\$map = array_map(null, $str);");
print_r($map);
Beware never to send un-sanitized input to eval.
See it working

Best way to emulate Ruby "splat" operator in PHP function signatures [Method overloading]

In Ruby
def my_func(foo,bar,*zim)
[foo, bar, zim].collect(&:inspect)
end
puts my_func(1,2,3,4,5)
# 1
# 2
# [3, 4, 5]
In PHP (5.3)
function my_func($foo, $bar, ... ){
#...
}
What's the best way to to do this in PHP?
Copying my answer from another question related to this:
This is now possible with PHP 5.6.x, using the ... operator (also
known as splat operator in some languages):
Example:
function addDateIntervalsToDateTime( DateTime $dt, DateInterval ...$intervals )
{
foreach ( $intervals as $interval ) {
$dt->add( $interval );
}
return $dt;
}
Try
func_get_args — Returns an array comprising a function's argument list
PHP Version of your Ruby Snippet
function my_func($foo, $bar)
{
$arguments = func_get_args();
return array(
array_shift($arguments),
array_shift($arguments),
$arguments
);
}
print_r( my_func(1,2,3,4,5,6) );
or just
function my_func($foo, $bar)
{
return array($foo , $bar , array_slice(func_get_args(), 2));
}
gives
Array
(
[0] => 1
[1] => 2
[2] => Array
(
[0] => 3
[1] => 4
[2] => 5
[3] => 6
)
)
Note that func_get_args() will return all arguments passed to a function, not just those not in the signature. Also note that any arguments you define in the signature are considered required and PHP will raise a Warning if they are not present.
If you only want to get the remaining arguments and determine that at runtime, you could use the ReflectionFunction API to read the number of arguments in the signature and array_slice the full list of arguments to contain only the additional ones, e.g.
function my_func($foo, $bar)
{
$rf = new ReflectionFunction(__FUNCTION__);
$splat = array_slice(func_get_args(), $rf->getNumberOfParameters());
return array($foo, $bar, $splat);
}
Why anyone would want that over just using func_get_args() is beyond me, but it would work. More straightforward is accessing the arguments in any of these ways:
echo $foo;
echo func_get_arg(0); // same as $foo
$arguments = func_get_args();
echo $arguments[0]; // same as $foo too
If you need to document variable function arguments, PHPDoc suggest to use
/**
* #param Mixed $foo Required
* #param Mixed $bar Required
* #param Mixed, ... Optional Unlimited variable number of arguments
* #return Array
*/
Hope that helps.

How to pass variable number of arguments to a PHP function

I have a PHP function that takes a variable number of arguments (using func_num_args() and func_get_args()), but the number of arguments I want to pass the function depends on the length of an array. Is there a way to call a PHP function with a variable number of arguments?
If you have your arguments in an array, you might be interested by the call_user_func_array function.
If the number of arguments you want to pass depends on the length of an array, it probably means you can pack them into an array themselves -- and use that one for the second parameter of call_user_func_array.
Elements of that array you pass will then be received by your function as distinct parameters.
For instance, if you have this function :
function test() {
var_dump(func_num_args());
var_dump(func_get_args());
}
You can pack your parameters into an array, like this :
$params = array(
10,
'glop',
'test',
);
And, then, call the function :
call_user_func_array('test', $params);
This code will the output :
int 3
array
0 => int 10
1 => string 'glop' (length=4)
2 => string 'test' (length=4)
ie, 3 parameters ; exactly like iof the function was called this way :
test(10, 'glop', 'test');
This is now possible with PHP 5.6.x, using the ... operator (also known as splat operator in some languages):
Example:
function addDateIntervalsToDateTime( DateTime $dt, DateInterval ...$intervals )
{
foreach ( $intervals as $interval ) {
$dt->add( $interval );
}
return $dt;
}
addDateIntervaslToDateTime( new DateTime, new DateInterval( 'P1D' ),
new DateInterval( 'P4D' ), new DateInterval( 'P10D' ) );
In a new Php 5.6, you can use ... operator instead of using func_get_args().
So, using this, you can get all the parameters you pass:
function manyVars(...$params) {
var_dump($params);
}
Since PHP 5.6, a variable argument list can be specified with the ... operator.
function do_something($first, ...$all_the_others)
{
var_dump($first);
var_dump($all_the_others);
}
do_something('this goes in first', 2, 3, 4, 5);
#> string(18) "this goes in first"
#>
#> array(4) {
#> [0]=>
#> int(2)
#> [1]=>
#> int(3)
#> [2]=>
#> int(4)
#> [3]=>
#> int(5)
#> }
As you can see, the ... operator collects the variable list of arguments in an array.
If you need to pass the variable arguments to another function, the ... can still help you.
function do_something($first, ...$all_the_others)
{
do_something_else($first, ...$all_the_others);
// Which is translated to:
// do_something_else('this goes in first', 2, 3, 4, 5);
}
Since PHP 7, the variable list of arguments can be forced to be all of the same type too.
function do_something($first, int ...$all_the_others) { /**/ }
For those looking for a way to do this with $object->method:
call_user_func_array(array($object, 'method_name'), $array);
I was successful with this in a construct function that calls a variable method_name with variable parameters.
You can just call it.
function test(){
print_r(func_get_args());
}
test("blah");
test("blah","blah");
Output:
Array ( [0] => blah ) Array ( [0] => blah [1] => blah )
I'm surprised nobody here has mentioned simply passing and extracting an array. E.g:
function add($arr){
extract($arr, EXTR_REFS);
return $one+$two;
}
$one = 1;
$two = 2;
echo add(compact('one', 'two')); // 3
Of course, this does not provide argument validation.
For that, anyone can use my expect function: https://gist.github.com/iautomation/8063fc78e9508ed427d5
I wondered, I couldn't find documentation about the possiblity of using named arguments (since PHP 8) in combination with variable arguments. Because I tried this piece of code and I was surprised, that it actually worked:
function myFunc(...$args) {
foreach ($args as $key => $arg) {
echo "Key: $key Arg: $arg <br>";
}
}
echo myFunc(arg1:"foo", arg2:"bar");
Output:
Key: arg1 Arg: foo
Key: arg2 Arg: bar
In my opinion, this is pretty cool.
Here is a solution using the magic method __invoke
(Available since php 5.3)
class Foo {
public function __invoke($method=null, $args=[]){
if($method){
return call_user_func_array([$this, $method], $args);
}
return false;
}
public function methodName($arg1, $arg2, $arg3){
}
}
From inside same class:
$this('methodName', ['arg1', 'arg2', 'arg3']);
From an instance of an object:
$obj = new Foo;
$obj('methodName', ['arg1', 'arg2', 'arg3'])
An old question, I know, however, none of the answers here really do a good job of simply answer the question.
I just played around with php and the solution looks like this:
function myFunction($requiredArgument, $optionalArgument = "default"){
echo $requiredArgument . $optionalArgument;
}
This function can do two things:
If its called with only the required parameter: myFunction("Hi")
It will print "Hi default"
But if it is called with the optional parameter: myFunction("Hi","me")
It will print "Hi me";
I hope this helps anyone who is looking for this down the road.

Categories