任何方式来指定PHP中的可选参数值?
假设我有一个PHP函数foo:
function foo($firstName = 'john', $lastName = 'doe') { echo $firstName . " " . $lastName; } // foo(); --> john doe
有什么办法只指定第二个可选参数?
例:
foo($lastName='smith'); // output: john smith
PHP本身不支持命名参数。 但是,有一些方法可以解决这个问题:
- 使用数组作为函数的唯一参数。 然后你可以从数组中取值。 这允许在数组中使用命名参数。
- 如果要根据上下文允许可选数量的参数,则可以使用func_num_args和func_get_args,而不是在函数定义中指定有效参数。 然后根据参数的数量,string长度等,你可以决定做什么。
- 将空值传递给您不想指定的参数。 没有真正解决它,但它的工作原理。
- 如果你在一个对象上下文中工作,那么你可以使用魔术方法__call()来处理这些types的请求,这样你就可以根据传入的参数来路由到私有方法。
arrays技术的一种变体,可以更容易地设置默认值:
function foo($arguments) { $defaults = array( 'firstName' => 'john', 'lastName' => 'doe', ); $arguments = array_merge($defaults, $arguments); echo $arguments['firstName'] . ' ' . $arguments['lastName']; }
用法:
foo(array('lastName' => 'smith')); // output: john smith
你可以稍微重构你的代码:
function foo($firstName = NULL, $lastName = NULL) { if (is_null($firstName)) { $firstName = 'john'; } if (is_null($lastName )) { $lastName = 'doe'; } echo $firstName . " " . $lastName; } foo(); // john doe foo('bill'); // bill doe foo(NULL,'smith'); // john smith foo('bill','smith'); // bill smith
如果您有多个可选参数,则一种解决scheme是传递一个散列数组的单个参数:
function foo(array $params = array()) { echo $params['firstName'] . " " . $params['lastName']; } foo(array('lastName'=>'smith'));
当然,在这个解决scheme中,没有validation哈希数组的字段是否存在,或拼写是否正确。 这完全取决于你来validation。
不。通常的做法是用一些启发式来确定哪个参数是隐含的,比如string长度,键入等等。
一般来说,你可以编写函数,按照最需要的顺序,把参数作为最不需要的参数。
你想要的方式:不。
你可以使用一些特殊的标记,比如NULL来表示没有提供值:
function foo($firstName, $lastName = 'doe') { if (is_null($firstName)) $firstName = 'john'; echo $firstName . " " . $lastName; } foo(null, 'smith');
这里提到了一些“选项”风格的实现。 如果你打算把它们作为标准使用,那么到目前为止都是非常防弹的。 试试这个模式:
function some_func($required_parameter, &$optional_reference_parameter = null, $options = null) { $options_default = array( 'foo' => null, ); extract($options ? array_intersect_key($options, $options_default) + $options_default : $options_default); unset($options, $options_default); //do stuff like if ($foo !== null) { bar(); } }
这给你函数局部variables(在这个例子中只是$foo
),并防止创build任何没有默认的variables。 这是没有人可以意外覆盖函数内的其他参数或其他variables。
参数需要按位置顺序传递,不能跳过参数本身; 你必须提供默认参数值才能跳过它。 可以说,这打败了你想要达到的目的。
不用重写你的函数来接受不同的参数,下面是一个调用方法来解决这个问题:
$func = 'foo'; $args = ['lastName' => 'Smith']; $ref = new ReflectionFunction($func); $ref->invokeArgs(array_map(function (ReflectionParameter $param) use ($args) { if (array_key_exists($param->getName(), $args)) { return $args[$param->getName()]; } if ($param->isOptional()) { return $param->getDefaultValue(); } throw new InvalidArgumentException("{$param->getName()} is not optional"); }, $ref->getParameters()));
换句话说,您正在使用reflection检查函数的参数并将其映射到可用参数的名称,跳过可选参数与其默认值。 是的,这是丑陋和繁琐的。 你可以使用这个示例来创build一个函数,如:
call_func_with_args_by_name('foo', ['lastName' => 'Smith']);
我希望这个解决scheme已经在二十五年前开始使用PHP。 它在我创build的例子中很好用,我不明白为什么它不应该是完全可扩展的。 它比以前提出的那些提供了以下好处:
(i)函数内所有对参数的访问都是通过命名variables进行的,就像参数被完全声明一样,而不需要访问数组
(ii)适应现有function非常快速和容易
(iii)任何函数只需要一行附加代码(除了不可避免地需要定义默认参数,无论如何您都要在函数签名中进行定义,而是将其定义在数组中)。 信用额外的线路完全归功于比尔·卡尔文。 这条线对于每个function都是一样的。
方法
用它的强制参数和一个可选的数组定义你的函数
将可选参数声明为局部variables
症结:使用已经通过数组传递的参数replace任何可选参数的预先声明的默认值。
extract(array_merge($arrDefaults, array_intersect_key($arrOptionalParams, $arrDefaults)));
调用该函数,传递它的强制参数,并且只有那些你需要的可选参数
例如,
function test_params($a, $b, $arrOptionalParams = array()) { $arrDefaults = array('c' => 'sat', 'd' => 'mat'); extract(array_merge($arrDefaults, array_intersect_key($arrOptionalParams, $arrDefaults))); echo "$a $b $c on the $d"; }
然后像这样调用它
test_params('The', 'dog', array('c' => 'stood', 'd' => 'donkey')); test_params('The', 'cat', array('d' => 'donkey')); test_params('A', 'dog', array('c' => 'stood'));
结果:
狗站在驴上
猫坐在驴上
一只狗站在垫子上
如果经常使用,只需定义一个新的专用函数:
function person($firstName = 'john', $lastName = 'doe') { return $firstName . " " . $lastName; } function usualFirtNamedPerson($lastName = 'doe') { return person('john', $lastName); } print(usualFirtNamedPerson('smith')); --> john smith
请注意,如果你愿意,你也可以在这个过程中改变$ lastname的默认值。
当一个新的函数估计过度膨胀时,只需要调用你所有参数的函数。 如果你想使它更清楚,你可以预先存储你的文字在fin命名variables或使用注释。
$firstName = 'Zeno'; $lastName = 'of Elea'; print(person($firstName, $lastName)); print(person(/* $firstName = */ 'Bertrand', /* $lastName = */ 'Russel'));
好吧,这并不person($lastName='Lennon')
简洁优雅person($lastName='Lennon')
,但似乎你不能在PHP中使用它。 这不是编码的最性感的方式,用超级元编程技巧或任何其他方法,但是你在维护过程中会遇到什么样的解决scheme?
可悲的是你要做的没有“语法糖”的方式。 他们都是不同forms的跆拳道。
如果您需要一个采用未定义数量的任意参数的函数,
function foo () { $args = func_get_args(); # $args = arguments in order }
会做的伎俩。 尽量避免使用它太多,因为对于Php来说,这是有点神奇的一面。
然后,您可以select应用默认值,并根据参数计数来执行奇怪的操作。
function foo(){ $args = func_get_args(); if( count($args) < 1 ){ return "John Smith"; } if( count($args) < 2 ) { return "John " .$args[0]; } return $args[0] . " " . $args[1]; }
另外,你可以select模拟Perl风格的参数,
function params_collect( $arglist ){ $config = array(); for( $i = 0; $i < count($arglist); $i+=2 ){ $config[$i] = $config[$i+1]; } return $config; } function param_default( $config, $param, $default ){ if( !isset( $config[$param] ) ){ $config[$param] = $default; } return $config; } function foo(){ $args = func_get_args(); $config = params_collect( $args ); $config = param_default( $config, 'firstname' , 'John' ); $config = param_default( $config, 'lastname' , 'Smith' ); return $config['firstname'] . ' ' . $config['lastname']; } foo( 'firstname' , 'john', 'lastname' , 'bob' ); foo( 'lastname' , 'bob', 'firstname', 'bob' ); foo( 'firstname' , 'smith'); foo( 'lastname', 'john' );
当然,在这里使用数组参数会比较容易,但是您可以select(即使是坏的方法)。
注意,这在Perl中更好,因为你可以做foo(firstname =>'john');
- func_get_args在php.net
没有,但你可以使用一个数组:
function foo ($nameArray) { // Work out which values are missing? echo $nameArray['firstName'] . " " . $nameArray['lastName']; } foo(array('lastName'=>'smith'));