Passing parameter to a function by parameter name? [duplicate] - php

Is it possible in PHP to specify a named optional parameter when calling a function/method, skipping the ones you don't want to specify (like in python)?
Something like:
function foo($a, $b = '', $c = '') {
// whatever
}
foo("hello", $c="bar"); // we want $b as the default, but specify $c

No, it is not possible (before PHP 8.0): if you want to pass the third parameter, you have to pass the second one. And named parameters are not possible either.
A "solution" would be to use only one parameter, an array, and always pass it... But don't always define everything in it.
For instance :
function foo($params) {
var_dump($params);
}
And calling it this way : (Key / value array)
foo([
'a' => 'hello',
]);
foo([
'a' => 'hello',
'c' => 'glop',
]);
foo([
'a' => 'hello',
'test' => 'another one',
]);
Will get you this output :
array
'a' => string 'hello' (length=5)
array
'a' => string 'hello' (length=5)
'c' => string 'glop' (length=4)
array
'a' => string 'hello' (length=5)
'test' => string 'another one' (length=11)
But I don't really like this solution :
You will lose the phpdoc
Your IDE will not be able to provide any hint anymore... Which is bad
So I'd go with this only in very specific cases -- for functions with lots of optional parameters, for instance...

PHP 8 was released on November 26, 2020 with a new feature called named arguments.
In this major version release, "named parameters" (aka "named arguments") afford developers some really cool new techniques when calling native and custom functions.
The custom function in this question can now be called with the first parameter (because there is no default for it) and then only the third parameter passed by using named parameters like this: (Demo)
function foo($a, $b = '', $c = '') {
echo $a . '&' . $b . '&' . $c;
}
foo("hello", c: "bar");
// output: hello&&bar
Notice that the second parameter did not need to be declared in the function call because it has a default value defined -- the default value is automatically used within the function body.
Part of the beauty of this new feature is that you don't need to be careful about the order of your named parameters -- the order of their declaration is irrelevant. foo(c: "bar", a: "hello"); works just the same. Having the ability to "skip" declarations and write declarative parameters will improve the readability of your scripts. The only downside of this new feature is that there will be a little bit more bloat in the function calls, but I (and many others) think the benefits outweigh this "cost".
Here is an example of a native function omitting the limit parameter, writing the parameters out of their normal order, and declaring a reference variable. (Demo)
echo preg_replace(
subject: 'Hello 7',
pattern: '/[a-z ]/',
count: $counted,
replacement: ''
)
. " & " . $counted;
// output: H7 & 5
There is more to tell about this new feature. You can even use an associative array to pass the named parameters to the function where the spread/splat operator can be used to unpack the data!
(*notice the slight difference in declaring the reference variable.) (Demo)
$params = [
'subject' => 'Hello 7', // normally third parameter
'pattern' => '/[a-z ]/', // normally first parameter
// 'limit' // normally fourth parameter, omitted for this demonstration; the default -1 will be used
'count' => &$counted, // normally fifth parameter
// ^-- don't forget to make it modifiable!
'replacement' => '', // normally second parameter
];
echo preg_replace(...$params) . " & " . $counted;
// same output as the previous snippet
For more information, here are a few leads that explain further about this feature and some common related errors: (I have no affiliation with the following sites)
https://wiki.php.net/rfc/named_params
https://stitcher.io/blog/php-8-named-arguments
https://stitcher.io/blog/why-we-need-named-params-in-php

No, PHP cannot pass arguments by name.
If you have a function that takes a lot of arguments and all of them have default values you can consider making the function accept an array of arguments instead:
function test (array $args) {
$defaults = array('a' => '', 'b' => '', 'c' => '');
$args = array_merge($defaults, array_intersect_key($args, $defaults));
list($a, $b, $c) = array_values($args);
// an alternative to list(): extract($args);
// you can now use $a, $b, $c
}
See it in action.

No, it isn't.
The only way you can somewhat do that is by using arrays with named keys and what not.

As of PHP 5.4 you have shorthand array syntax (not nessecary to specify arrays with cumbersome "array" and instead use "[]").
You can mimic named parameters in many ways, one good and simple way might be:
bar('one', ['a1' => 'two', 'bar' => 'three', 'foo' => 'four']);
// output: twothreefour
function bar ($a1, $kwargs = ['bar' => null, 'foo' => null]) {
extract($kwargs);
echo $a1;
echo $bar;
echo $foo;
}

You can keep the phpdoc and the ability to set defaults by passing an object instead of an array, e.g.
class FooOptions {
$opt1 = 'x';
$opt2 = 'y';
/* etc */
};
That also lets you do strict type checking in your function call, if you want to:
function foo (FooOptions $opts) {
...
}
Of course, you might pay for that with extra verbosity setting up the FooOptions object. There's no totally-free ride, unfortunately.

It's not exactly pretty, but it does the trick, some might say.
class NamedArguments {
static function init($args) {
$assoc = reset($args);
if (is_array($assoc)) {
$diff = array_diff(array_keys($assoc), array_keys($args));
if (empty($diff)) return $assoc;
trigger_error('Invalid parameters: '.join(',',$diff), E_USER_ERROR);
}
return array();
}
}
class Test {
public static function foobar($required, $optional1 = '', $optional2 = '') {
extract(NamedArguments::init(get_defined_vars()));
printf("required: %s, optional1: %s, optional2: %s\n", $required, $optional1, $optional2);
}
}
Test::foobar("required", "optional1", "optional2");
Test::foobar(array(
'required' => 'required',
'optional1' => 'optional1',
'optional2' => 'optional2'
));

Normally you can't but I think there a lot of ways to pass named arguments to a PHP function. Personally I relay on the definition using arrays and just call what I need to pass:
class Test{
public $a = false;
private $b = false;
public $c = false;
public $d = false;
public $e = false;
public function _factory(){
$args = func_get_args();
$args = $args[0];
$this->a = array_key_exists("a",$args) ? $args["a"] : 0;
$this->b = array_key_exists("b",$args) ? $args["b"] : 0;
$this->c = array_key_exists("c",$args) ? $args["c"] : 0;
$this->d = array_key_exists("d",$args) ? $args["d"] : 0;
$this->e = array_key_exists("e",$args) ? $args["e"] : 0;
}
public function show(){
var_dump($this);
}
}
$test = new Test();
$args["c"]=999;
$test->_factory($args);
$test->show();
live example here:
http://sandbox.onlinephpfunctions.com/code/d7f27c6e504737482d396cbd6cdf1cc118e8c1ff
If I have to pass 10 arguments, and 3 of them are the data I really need, is NOT EVEN SMART to pass into the function something like
return myfunction(false,false,10,false,false,"date",false,false,false,"desc");
With the approach I'm giving, you can setup any of the 10 arguments into an array:
$arr['count']=10;
$arr['type']="date";
$arr['order']="desc";
return myfunction($arr);
I have a post in my blog explaining this process in more details.
http://www.tbogard.com/2013/03/07/passing-named-arguments-to-a-function-in-php

With PHP, the order of arguments is what matters. You can't specify a particular argument out of place, but instead, you can skip arguments by passing a NULL, as long as you don't mind the value in your function having a NULL value.
foo("hello", NULL, "bar");

If you really really want, try the reflection.
And skip with null.
function getDefaultValueByNull($fn, $inputs) {
$ref = new ReflectionFunction($fn);
$args = array_map(function($p) {
return [
$p->getName(),
$p->isDefaultValueAvailable() ? $p->getDefaultValue() : NULL,
];
}, $ref->getParameters());
foreach($inputs as $i=>$val) { if ($val!==NULL) $args[$i][1] = $val; }
return array_column($args, 1, 0);
}
function sum($a=9, $b) {
extract(getDefaultValueByNull(__FUNCTION__, func_get_args()));
return $a+$b;
}
echo sum(NULL, 1); // 10

Here's what I've been using. A function definition takes one optional array argument which specifies the optional named arguments:
function func($arg, $options = Array()) {
$defaults = Array('foo' => 1.0,
'bar' => FALSE);
$options = array_merge($default, $options);
// Normal function body here. Use $options['foo'] and
// $options['bar'] to fetch named parameter values.
...
}
You can normally call without any named arguments:
func("xyzzy")
To specify an optional named argument, pass it in the optional array:
func("xyzzy", Array('foo' => 5.7))

No not really. There are a few alternatives to it you could use.
test(null,null,"hello")
Or pass an array:
test(array('c' => "hello"));
Then, the function could be:
function test($array) {
$c = isset($array[c]) ? $array[c] : '';
}
Or add a function in between, but i would not suggest this:
function ctest($c) { test('','',$c); }

I dont think so...
If you need to call, for example, the substr function, that has 3 params, and want to set the $length without set the $start, you'll be forced to do so.
substr($str,0,10);
a nice way to override this is to always use arrays for parameters

In very short, sometimes yes, by using reflection and typed variables. However I think this is probably not what you are after.
A better solution to your problem is probably to pass in the 3 arguments as functions handle the missing one inside your function yourself
<?php
function test(array $params)
{
//Check for nulls etc etc
$a = $params['a'];
$b = $params['b'];
...etc etc
}

You can't do it the python way. Anway, you could pass an associative array and than use the array entries by their name:
function test ($args=array('a'=>'','b'=>'','c'=>''))
{
// do something
}
test(array('c'=>'Hello'));
This doesn't reduce the typing, but at least it's more descriptive, having the arguments' names visible and readable in the call.

Here is a work around:
function set_param_defaults($params) {
foreach($params['default_values'] as $arg_name => $arg_value) {
if (!isset($params[$arg_name])) {
$params[$arg_name] = $arg_value;
}
}
return $params;
}
function foo($z, $x = null, $y = null) {
$default_values = ['x' => 'default value for x', 'y' => 'default value for y'];
$params = set_param_defaults(get_defined_vars());
print "$z\n";
print $params['x'] . "\n";
print $params['y'] . "\n";
}
foo('set z value', null, 'set y value');
print "\n";
foo('set z value', 'set x value');
ALTERNATIVELY:
Personally I would go with this method.
function foo($z, $x_y) {
$x_y += ['x' => 'default value for x', 'y' => 'default value for y'];
print "$z\n";
print $x_y['x'] . "\n";
print $x_y['y'] . "\n";
}
foo('set z value', ['y' => 'set y value']);
print "\n";
foo('set z value', ['x' => 'set x value']);
Print outs for both examples.
1st call:
set z value
default value for x
set y value
2nd call:
set z value
set x value
default value for y

Just use the associative array pattern Drupal uses. For optional defaulted arguments, just accept an $options argument which is an associative array. Then use the array + operator to set any missing keys in the array.
function foo ($a_required_parameter, $options = array()) {
$options += array(
'b' => '',
'c' => '',
);
// whatever
}
foo('a', array('c' => 'c’s value')); // No need to pass b when specifying c.

Related

pass value for last default parameter of function

From very long time i am working on php.
But one question may I have no idea about
like I have one function as bellow:
function hello($param1, $param2="2", $param3="3", $param4="4")
Now whenever I will use this function and if I need 4th params thats the $param4 then still I need to call all as blank like this one:
hello(1, '', '', "param4");
So is there any another way to just pass 1st and 4th param in call rather then long list of blanks ?
Or is there any other standard way for this ?
There was an RFC for this named skipparams but it was declined.
PHP has no syntactic sugar such as hello(1, , , "param4"); nor hello(1, default, default, "param4"); (per the RFC) for skipping optional parameters when calling a function.
If this is your own function then you can choose the common jQuery style of passing options into plug-ins like this:
function hello( $param1, $more_params = [] )
{
static $default_params = [
'param2' => '2',
'param3' => '3',
'param4' => '4'
];
$more_params = array_merge( $default_params, $more_params );
}
Now you can:
hello( 1, [ 'param4'=>'not 4, muahaha!' ] );
If your function requires some advanced stuff such as type hinting then instead of array_merge() you will need to manually loop $more_params and enforce the types.
One potential way you can do this, while a little bit hacky, may work well in some situations.
Instead of passing multiple variables, pass a single array variable, and inside the function check if the specific keys exist.
function hello($param1, $variables = ["param2" => "2", "param3" => "3", "param4" => "4"]) {
if(!array_key_exists("param2", $variables)) $variables['param2'] = "2";
if(!array_key_exists("param3", $variables)) $variables['param3'] = "3";
if(!array_key_exists("param4", $variables)) $variables['param4'] = "4";
echo "<pre>".print_r($variables, true)."</pre>";
}
This will allow you to set "param4" in the above variable, while still remaining default on all of the others.
Calling the function this way:
hello("test", ["param4" => "filling in variable 4"]);
Will result in the output being:
Array
(
[param4] => filling in variable 4
[param2] => 2
[param3] => 3
)
I don't generally recommend this if it can be avoided, but if you absolutely need this functionality, this may work for you.
The key here is that you have a specifically named index inside the array being passed, that you can check against inside the function itself.
The answer, as I see it, is yes and no.
No, because there's no way to do this in a standard fashion.
Yes, because you can hack around it. This is hacky, but it works ;)
Example:
function some_call($parm1, $parm2='', $parm3='', $parm4='') { ... }
and the sauce:
function some_call_4($parm1, $parm4) {
return some_call($parm1, '', '', $parm4);
}
So if you make that call ALOT and are tired of typing it out, you can just hack around it.
Sorry, that's all I've got for you.
It is an overhead, but you can use ReflectionFunction to create a class, instance of which that can be invoked with named parameters:
final class FunctionWithNamedParams
{
private $func;
public function __construct($func)
{
$this->func = $func;
}
public function __invoke($params = [])
{
return ($this->func)(...$this->resolveParams($params));
}
private function resolveParams($params)
{
$rf = new ReflectionFunction($this->func);
return array_reduce(
$rf->getParameters(),
function ($carry, $param) use ($params) {
if (isset($params[$param->getName()])) {
$carry[] = $params[$param->getName()];
} else if ($param->isDefaultValueAvailable()) {
$carry[] = $param->getDefaultValue();
} else {
throw new BadFunctionCallException;
}
return $carry;
},
[]
);
}
}
Then you can use it like this:
function hello($param1, $param2 = "2", $param3 = "3", $param4 = "4")
{
var_dump($param1, $param2, $param3, $param4);
}
$func = new FunctionWithNamedParams('hello');
$func(['param1' => '1', 'param4' => 'foo']);
Here is the demo.

How to name slightly different methods in PHP so it behaves like ruby "!" (bang)

It's been a long time I've been thinking on how to deal with this kind of situation:
Take for instance the hash example:
hash = {'a' => 'b', 'c' => 'd'}
other = {'a' => 'd'}
hash.merge(other) # returns a new hash
hash.merge!(other) # modifies hash
How would you deal with that in php?
$hash = new Hash(array( 'a' => 'b', 'c' => 'd' ));
$other = new Hash(array('a' => 'd'));
Option params:
public function merge($other, array $options = array('mutate' => false))
{
}
// or
public function merge($other, $mutate = false)
{
}
Or perhaps two different method names:
public function merge($other)
{
}
public function mergeIntoSelf($other)
{
}
I kind of like the 'options param' approach, but what if the method actually receives another optional param, like in ruby, which is a modifier callback.
$hash->merge($other, function ($key, $originalValue, $otherValue) {
if ($key === 'foo') {
return $originalValue;
}
return $otherValue;
}, array('mutate' => true));
The callback option could be the third one, instead of the second one, but I don't like that. I also don't like the idea of checking the params and trying to find out what is what. They doc block gets hairy.
So I would like to hear your opinions on how you would approach that.
Thank you in advance.
You can use & sign at function name but its not a good solition. i want to just give an idea its possible:
$a = array( 'a' => 'b', 'c' => 'd' );
$b = array('a' => 'd');
function mergeArray(& $a,$b) {
return $a = array_merge($a,$b);
}
$c = mergeArray($a,$b);
var_dump($a);
var_dump($c);
You can see that $c and $a will be same.

Can I pass an associative array as an argument to ReflectionMethod::invokeArgs?

Is it possible to pass parameters as an associative array in ReflectionMethod::invokeArgs? This would allow me to pass the arguments in a different order than declared.
For example:
class A
{
public function someMethod($a, $b)
{
return sprintf("a - %s, b - %s", $a, $b);
}
}
$rm = new ReflectionMethod('A', 'someMethod');
echo $rm->invokeArgs(new A(), array('b' => 1, 'a' => 2));
You can do this by overwriting the invokeArgs method and implementing the functionality you need (in your case it looks like you want to use named arguments):
class ReflectionMethodA extends ReflectionMethod
{
public function invokeArgs($object, Array $args = array())
{
$parameters = $this->getParameters();
foreach($parameters as &$param)
{
$name = $param->getName();
$param = isset($args[$name]) ? $args[$name] : $param->getDefaultValue();
}
unset($param);
return parent::invokeArgs($object, $parameters);
}
}
$rm = new ReflectionMethodA('A', 'someMethod');
echo $rm->invokeArgs(new A(), array('b' => 1, 'a' => 2));
Output:
a - 2, b - 1
Edit: An improved (supporting both named and numbered arguments as well as passing by reference) and more flexible variant (to be used for any callback) is the following class, taking any valid callback as parameter in it's constructor.
Usage:
$rc = new ReflectedCallback(array(new A(), 'someMethod'));
echo $rc->invokeArgs(array('b' => 1, 'a' => 2));
Gist
No, there's nothing in the manual page that suggests you can use an associative array to re-order arguments by name.

Does PHP allow named parameters so that optional arguments can be omitted from function calls?

Is it possible in PHP to specify a named optional parameter when calling a function/method, skipping the ones you don't want to specify (like in python)?
Something like:
function foo($a, $b = '', $c = '') {
// whatever
}
foo("hello", $c="bar"); // we want $b as the default, but specify $c
No, it is not possible (before PHP 8.0): if you want to pass the third parameter, you have to pass the second one. And named parameters are not possible either.
A "solution" would be to use only one parameter, an array, and always pass it... But don't always define everything in it.
For instance :
function foo($params) {
var_dump($params);
}
And calling it this way : (Key / value array)
foo([
'a' => 'hello',
]);
foo([
'a' => 'hello',
'c' => 'glop',
]);
foo([
'a' => 'hello',
'test' => 'another one',
]);
Will get you this output :
array
'a' => string 'hello' (length=5)
array
'a' => string 'hello' (length=5)
'c' => string 'glop' (length=4)
array
'a' => string 'hello' (length=5)
'test' => string 'another one' (length=11)
But I don't really like this solution :
You will lose the phpdoc
Your IDE will not be able to provide any hint anymore... Which is bad
So I'd go with this only in very specific cases -- for functions with lots of optional parameters, for instance...
PHP 8 was released on November 26, 2020 with a new feature called named arguments.
In this major version release, "named parameters" (aka "named arguments") afford developers some really cool new techniques when calling native and custom functions.
The custom function in this question can now be called with the first parameter (because there is no default for it) and then only the third parameter passed by using named parameters like this: (Demo)
function foo($a, $b = '', $c = '') {
echo $a . '&' . $b . '&' . $c;
}
foo("hello", c: "bar");
// output: hello&&bar
Notice that the second parameter did not need to be declared in the function call because it has a default value defined -- the default value is automatically used within the function body.
Part of the beauty of this new feature is that you don't need to be careful about the order of your named parameters -- the order of their declaration is irrelevant. foo(c: "bar", a: "hello"); works just the same. Having the ability to "skip" declarations and write declarative parameters will improve the readability of your scripts. The only downside of this new feature is that there will be a little bit more bloat in the function calls, but I (and many others) think the benefits outweigh this "cost".
Here is an example of a native function omitting the limit parameter, writing the parameters out of their normal order, and declaring a reference variable. (Demo)
echo preg_replace(
subject: 'Hello 7',
pattern: '/[a-z ]/',
count: $counted,
replacement: ''
)
. " & " . $counted;
// output: H7 & 5
There is more to tell about this new feature. You can even use an associative array to pass the named parameters to the function where the spread/splat operator can be used to unpack the data!
(*notice the slight difference in declaring the reference variable.) (Demo)
$params = [
'subject' => 'Hello 7', // normally third parameter
'pattern' => '/[a-z ]/', // normally first parameter
// 'limit' // normally fourth parameter, omitted for this demonstration; the default -1 will be used
'count' => &$counted, // normally fifth parameter
// ^-- don't forget to make it modifiable!
'replacement' => '', // normally second parameter
];
echo preg_replace(...$params) . " & " . $counted;
// same output as the previous snippet
For more information, here are a few leads that explain further about this feature and some common related errors: (I have no affiliation with the following sites)
https://wiki.php.net/rfc/named_params
https://stitcher.io/blog/php-8-named-arguments
https://stitcher.io/blog/why-we-need-named-params-in-php
No, PHP cannot pass arguments by name.
If you have a function that takes a lot of arguments and all of them have default values you can consider making the function accept an array of arguments instead:
function test (array $args) {
$defaults = array('a' => '', 'b' => '', 'c' => '');
$args = array_merge($defaults, array_intersect_key($args, $defaults));
list($a, $b, $c) = array_values($args);
// an alternative to list(): extract($args);
// you can now use $a, $b, $c
}
See it in action.
No, it isn't.
The only way you can somewhat do that is by using arrays with named keys and what not.
As of PHP 5.4 you have shorthand array syntax (not nessecary to specify arrays with cumbersome "array" and instead use "[]").
You can mimic named parameters in many ways, one good and simple way might be:
bar('one', ['a1' => 'two', 'bar' => 'three', 'foo' => 'four']);
// output: twothreefour
function bar ($a1, $kwargs = ['bar' => null, 'foo' => null]) {
extract($kwargs);
echo $a1;
echo $bar;
echo $foo;
}
You can keep the phpdoc and the ability to set defaults by passing an object instead of an array, e.g.
class FooOptions {
$opt1 = 'x';
$opt2 = 'y';
/* etc */
};
That also lets you do strict type checking in your function call, if you want to:
function foo (FooOptions $opts) {
...
}
Of course, you might pay for that with extra verbosity setting up the FooOptions object. There's no totally-free ride, unfortunately.
It's not exactly pretty, but it does the trick, some might say.
class NamedArguments {
static function init($args) {
$assoc = reset($args);
if (is_array($assoc)) {
$diff = array_diff(array_keys($assoc), array_keys($args));
if (empty($diff)) return $assoc;
trigger_error('Invalid parameters: '.join(',',$diff), E_USER_ERROR);
}
return array();
}
}
class Test {
public static function foobar($required, $optional1 = '', $optional2 = '') {
extract(NamedArguments::init(get_defined_vars()));
printf("required: %s, optional1: %s, optional2: %s\n", $required, $optional1, $optional2);
}
}
Test::foobar("required", "optional1", "optional2");
Test::foobar(array(
'required' => 'required',
'optional1' => 'optional1',
'optional2' => 'optional2'
));
Normally you can't but I think there a lot of ways to pass named arguments to a PHP function. Personally I relay on the definition using arrays and just call what I need to pass:
class Test{
public $a = false;
private $b = false;
public $c = false;
public $d = false;
public $e = false;
public function _factory(){
$args = func_get_args();
$args = $args[0];
$this->a = array_key_exists("a",$args) ? $args["a"] : 0;
$this->b = array_key_exists("b",$args) ? $args["b"] : 0;
$this->c = array_key_exists("c",$args) ? $args["c"] : 0;
$this->d = array_key_exists("d",$args) ? $args["d"] : 0;
$this->e = array_key_exists("e",$args) ? $args["e"] : 0;
}
public function show(){
var_dump($this);
}
}
$test = new Test();
$args["c"]=999;
$test->_factory($args);
$test->show();
live example here:
http://sandbox.onlinephpfunctions.com/code/d7f27c6e504737482d396cbd6cdf1cc118e8c1ff
If I have to pass 10 arguments, and 3 of them are the data I really need, is NOT EVEN SMART to pass into the function something like
return myfunction(false,false,10,false,false,"date",false,false,false,"desc");
With the approach I'm giving, you can setup any of the 10 arguments into an array:
$arr['count']=10;
$arr['type']="date";
$arr['order']="desc";
return myfunction($arr);
I have a post in my blog explaining this process in more details.
http://www.tbogard.com/2013/03/07/passing-named-arguments-to-a-function-in-php
With PHP, the order of arguments is what matters. You can't specify a particular argument out of place, but instead, you can skip arguments by passing a NULL, as long as you don't mind the value in your function having a NULL value.
foo("hello", NULL, "bar");
If you really really want, try the reflection.
And skip with null.
function getDefaultValueByNull($fn, $inputs) {
$ref = new ReflectionFunction($fn);
$args = array_map(function($p) {
return [
$p->getName(),
$p->isDefaultValueAvailable() ? $p->getDefaultValue() : NULL,
];
}, $ref->getParameters());
foreach($inputs as $i=>$val) { if ($val!==NULL) $args[$i][1] = $val; }
return array_column($args, 1, 0);
}
function sum($a=9, $b) {
extract(getDefaultValueByNull(__FUNCTION__, func_get_args()));
return $a+$b;
}
echo sum(NULL, 1); // 10
Here's what I've been using. A function definition takes one optional array argument which specifies the optional named arguments:
function func($arg, $options = Array()) {
$defaults = Array('foo' => 1.0,
'bar' => FALSE);
$options = array_merge($default, $options);
// Normal function body here. Use $options['foo'] and
// $options['bar'] to fetch named parameter values.
...
}
You can normally call without any named arguments:
func("xyzzy")
To specify an optional named argument, pass it in the optional array:
func("xyzzy", Array('foo' => 5.7))
No not really. There are a few alternatives to it you could use.
test(null,null,"hello")
Or pass an array:
test(array('c' => "hello"));
Then, the function could be:
function test($array) {
$c = isset($array[c]) ? $array[c] : '';
}
Or add a function in between, but i would not suggest this:
function ctest($c) { test('','',$c); }
I dont think so...
If you need to call, for example, the substr function, that has 3 params, and want to set the $length without set the $start, you'll be forced to do so.
substr($str,0,10);
a nice way to override this is to always use arrays for parameters
In very short, sometimes yes, by using reflection and typed variables. However I think this is probably not what you are after.
A better solution to your problem is probably to pass in the 3 arguments as functions handle the missing one inside your function yourself
<?php
function test(array $params)
{
//Check for nulls etc etc
$a = $params['a'];
$b = $params['b'];
...etc etc
}
You can't do it the python way. Anway, you could pass an associative array and than use the array entries by their name:
function test ($args=array('a'=>'','b'=>'','c'=>''))
{
// do something
}
test(array('c'=>'Hello'));
This doesn't reduce the typing, but at least it's more descriptive, having the arguments' names visible and readable in the call.
Here is a work around:
function set_param_defaults($params) {
foreach($params['default_values'] as $arg_name => $arg_value) {
if (!isset($params[$arg_name])) {
$params[$arg_name] = $arg_value;
}
}
return $params;
}
function foo($z, $x = null, $y = null) {
$default_values = ['x' => 'default value for x', 'y' => 'default value for y'];
$params = set_param_defaults(get_defined_vars());
print "$z\n";
print $params['x'] . "\n";
print $params['y'] . "\n";
}
foo('set z value', null, 'set y value');
print "\n";
foo('set z value', 'set x value');
ALTERNATIVELY:
Personally I would go with this method.
function foo($z, $x_y) {
$x_y += ['x' => 'default value for x', 'y' => 'default value for y'];
print "$z\n";
print $x_y['x'] . "\n";
print $x_y['y'] . "\n";
}
foo('set z value', ['y' => 'set y value']);
print "\n";
foo('set z value', ['x' => 'set x value']);
Print outs for both examples.
1st call:
set z value
default value for x
set y value
2nd call:
set z value
set x value
default value for y
Just use the associative array pattern Drupal uses. For optional defaulted arguments, just accept an $options argument which is an associative array. Then use the array + operator to set any missing keys in the array.
function foo ($a_required_parameter, $options = array()) {
$options += array(
'b' => '',
'c' => '',
);
// whatever
}
foo('a', array('c' => 'c’s value')); // No need to pass b when specifying c.

Is it possible to skip parameters that have default values in a function call? [duplicate]

This question already has answers here:
Does PHP allow named parameters so that optional arguments can be omitted from function calls?
(17 answers)
Closed 1 year ago.
I have this:
function foo($a='apple', $b='brown', $c='Capulet') {
// do something
}
Is something like this possible:
foo('aardvark', <use the default, please>, 'Montague');
If it’s your function, you could use null as wildcard and set the default value later inside the function:
function foo($a=null, $b=null, $c=null) {
if (is_null($a)) {
$a = 'apple';
}
if (is_null($b)) {
$b = 'brown';
}
if (is_null($c)) {
$c = 'Capulet';
}
echo "$a, $b, $c";
}
Then you can skip them by using null:
foo('aardvark', null, 'Montague');
// output: "aarkvark, brown, Montague"
If it's your own function instead of one of PHP's core, you could do:
function foo($arguments = []) {
$defaults = [
'an_argument' => 'a value',
'another_argument' => 'another value',
'third_argument' => 'yet another value!',
];
$arguments = array_merge($defaults, $arguments);
// now, do stuff!
}
foo(['another_argument' => 'not the default value!']);
Found this, which is probably still correct:
http://www.webmasterworld.com/php/3758313.htm
Short answer: no.
Long answer: yes, in various kludgey ways that are outlined in the above.
You pretty much found the answer, but the academic/high-level approach is function currying which I honestly never found much of a use for, but is useful to know exists.
You can use some quirks, either passing all arguments as an array like ceejayoz suggests, or some overcomplicated code that parses func_get_args() and merges with a list of defaults. Not to copy-paste it, you'll have to use objects and traits. Finally, to be able to pass all kinds of values (not excluding null or false by making them a signal for default param substitution), you'll have to declare a dummy special type DefaultParam.
Another minus is that you have to duplicate the names and default values in the function declaration, if you want to get type hints or help in any IDE.
class DefaultParam {}
trait multi_arg_functions
{
private static function multi_arg($defaults, $list, $preserve_index = false)
{
$arg_keys = array_slice(array_keys($defaults), 0, count($list));
if ($preserve_index) {
$listed_arguments = array_slice($list, 0, count($arg_keys));
$extras = array_slice($list, count($arg_keys), null, true);
} else {
$listed_arguments = array_splice($list, 0, count($arg_keys));
$extras = &$list;
}
unset($list);
$arguments = array_combine($arg_keys, $listed_arguments);
$arguments = array_filter($arguments, function ($entry) {
return !($entry instanceof DefaultParam); //remove entries that mean default, a special class in this case
});
$arguments = array_merge($defaults, $arguments);
return [$arguments, $extras];
}
}
class b {
use multi_arg_functions;
static function func1($an_argument = 'a value', $another_argument = 'another value', $third_argument = 'yet another value') { //give defaults here to get hints in an IDE
list($args, $extras) = self::multi_arg( //note: duplicate names and defaults
[
'an_argument' => 'a value',
'another_argument' => 'another value',
'third_argument' => 'yet another value!',
], func_get_args());
echo json_encode(['args' => $args, 'extras' => $extras])."\n";
}
}
$default_param = new DefaultParam();
b::func1('value 1');
b::func1('value 2', $default_param, 'third argument');
b::func1('value 3', $default_param, 'third argument', 'fourth argument');
Note: by using preserve_index = true you get the extra arguments to start from their original index.
As of PHP 8, use named parameters:
function foo($a='apple', $b='brown', $c='Capulet') {
// do something
}
foo('apple', c:'Montague');
This let's you bypass as many parameters as you want, allowing them to take on their default value. This is helpful in long-winded functions like setcookie:
setcookie('macadamia', httponly:true); // skips over 5 parameters
Note that named parameters require all non-optional parameters to be passed. These may be passed positionally (as I've done here, no names on them) or with names in any order.

Categories