Reputation: 143765
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
Upvotes: 41
Views: 19688
Reputation: 2011
Named Arguments are not supported in PHP 7.* or lower.
Named Arguments are implemented since PHP 8.0.
Approved RFC: https://wiki.php.net/rfc/named_params
Stitcher blog: https://stitcher.io/blog/php-8-named-arguments
Upvotes: 6
Reputation: 16281
PHP did not directly support named parameters.
In PHP, you can use a fairly reasonable substitute. PHP’s array handling is particularly good, and you can use an associative array to pass an array of parameters, as follows:
function foo($parms=[]) {
$username = $parms['username'] ?? '…';
$password = $parms['password'] ?? '…';
$timeout = $parms['timeout'] ?? '…';
}
foo(['timeout'=>10]);
Note the use of the ??
operator to allow a simple default if the input array element doesn’t exist. Before PHP 7, you would use ?:
which is subtly different, but gives the same result in this case.
Not as slick, but named parameters have another important role: they allow you to work with a large number of options without an excessively long list of ordered parameters.
An alternative way of writing the function is to use the extract
function:
function foo($parms=[]) {
$username = '…';
$password = '…';
$timeout = '…';
extract($parms,EXTR_IF_EXISTS);
}
Here the extract
function copies the values into the variables, but only if they have already been defined. This prevents importing variables you didn’t know about.
Upvotes: 9
Reputation: 743
PHP 8 supports named arguments.
Both native functions and your current code can use named parameters.
Let's say you have a function defined:
function foo($a = 10, $b = 20, $c = 30) {
return $a * $b - $c;
}
you will now be able to call this function like so:
foo(a: 30, c: 10, b: 5);
foo(5, c: 60, b: 10);
foo(c: 30, b: 20, a: 10);
Upvotes: 8
Reputation: 47864
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
The same behavior can be enjoyed via call_user_func_array()
. Demo using my first declared user function
call_user_func_array('foo', ['c' => 9, 'a' => 7]);
// output: 7&&9
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)
Upvotes: 21
Reputation: 400932
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.
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 :
So I'd go with this only in very specific cases -- for functions with lots of optional parameters, for instance...
Upvotes: 26
Reputation: 2386
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
Upvotes: -2
Reputation: 15364
PHP 8.0 added support for named arguments with the acceptance of an RFC.
Named arguments are passed by prefixing the value with the parameter name followed by a colon. Using reserved keywords as parameter names is allowed. The parameter name must be an identifier, specifying dynamically is not allowed.
E.g. to pass just the 3rd optional parameter in your example:
foo(timeout: 3);
Prior to PHP 8 named parameters were not possible in PHP. Technically when you call foo($timeout=3)
it is evaluating $timeout=3
first, with a result of 3
and passing that as the first parameter to foo()
. And PHP enforces parameter order, so the comparable call would need to be foo("", "", $timeout=3)
. You have two other options:
func_get_args()
or use the ...
variable length arguments feature in PHP 5.6+. Based on the number of parameters you can then decide how to treat each. A lot of JQuery functions do something like this. This is easy but can be confusing for those calling your functions because it's also not self-documenting. And your arguments are still not named.Upvotes: 56
Reputation: 8308
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.
Upvotes: -2
Reputation: 1625
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;
}
Upvotes: 1
Reputation: 158
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
Upvotes: 0
Reputation: 437336
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
}
Upvotes: 7
Reputation: 574
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.
Upvotes: 2
Reputation: 29248
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))
Upvotes: -2
Reputation: 1031
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'
));
Upvotes: 0
Reputation: 154513
No, it isn't.
The only way you can somewhat do that is by using arrays with named keys and what not.
Upvotes: 3