Передача именованных параметров в php-функцию через call_user_func_array
При попытке вызвать функцию в дочернем классе с произвольным набором параметров у меня возникает следующая проблема:
class Base{
function callDerived($method,$params){
call_user_func_array(array($this,$method),$params);
}
}
class Derived extends Base{
function test($foo,$bar){
print "foo=$foo, bar=$bar\n";
}
}
$d = new Derived();
$d->callDerived('test',array('bar'=>'2','foo'=>1));
Выходы:
foo=2, bar=1
Что... не совсем то, что я хотел - есть ли способ достичь этого, кроме повторного компоновки массива с индексным порядком func_get_args? И да, конечно, я мог бы просто передать весь массив и разобраться с ним в функции... но это не то, что я хочу сделать.
Спасибо
Ответы
Ответ 1
Нет. PHP не поддерживает именованные параметры. Учитывается только порядок параметров. Вы могли бы, вероятно, взять код самостоятельно, используя ReflectionClass, чтобы проверить имена параметров функции, но в конце вам нужно будет использовать это для изменения порядка массива.
Ответ 2
Фондовый PHP-класс ReflectionMethod - ваш друг.
Пример:
class MyClass {
function myFunc($param1, $param2, $param3='myDefault') {
print "test";
}
}
$refm = new ReflectionMethod('MyClass', 'myFunc');
foreach ($refm->getParameters() as $p)
print "$p\n";
И результат:
Parameter #0 [ <required> $param1 ]
Parameter #1 [ <required> $param2 ]
Parameter #2 [ <optional> $param3 = 'myDefault' ]
На этом этапе вы знаете имена параметров целевой функции. С помощью этой информации вы можете изменить свой метод "callDerived", и вы можете перенастроить массив на call_user_func_array в соответствии с именами параметров.
Ответ 3
Хорошие новости, у меня была такая же забота (я искал именованные аргументы в PHP, например, Python), и нашел этот полезный инструмент: https://github.com/PHP-DI/Invoker
Это использует API отражения для подачи вызываемого с помощью некоторых аргументов из массива, а также использует необязательные аргументы по умолчанию для других параметров, которые не определены в массиве.
$invoker = new Invoker\Invoker;
$result = $invoker->call(array($object, 'method'), array(
"strName" => "Lorem",
"strValue" => "ipsum",
"readOnly" => true,
"size" => 55,
));
Удачи
Ответ 4
Вы можете просто передать массив и извлечь:
function add($arr){
extract($arr, EXTR_REFS);
return $one+$two;
}
$one = 1;
$two = 2;
echo add(compact('one', 'two')); // 3
Это будет извлекаться в качестве ссылок, поэтому нет никаких накладных расходов.
Ответ 5
Я использую битовую маску вместо логических параметров:
// Ingredients
define ('TOMATO', 0b0000001);
define ('CHEESE', 0b0000010);
define ('OREGANO', 0b0000100);
define ('MUSHROOMS', 0b0001000);
define ('SALAMI', 0b0010000);
define ('PEPERONI', 0b0100000);
define ('ONIONS', 0b1000000);
function pizza ($ingredients) {
$serving = 'Pizza with';
$serving .= ($ingredients&TOMATO)?' Tomato':'';
$serving .= ($ingredients&CHEESE)?' Cheese':'';
$serving .= ($ingredients&OREGANO)?' Oregano':'';
$serving .= ($ingredients&MUSHROOMS)?' Mushrooms':'';
$serving .= ($ingredients&SALAMI)?' Salami':'';
$serving .= ($ingredients&ONIONS)?' Onions':'';
return trim($serving)."\n" ;
}
// Now order your pizzas!
echo pizza(TOMATO | CHEESE | SALAMI);
echo pizza(ONIONS | TOMATO | MUSHROOMS | CHEESE); // "Params" are not positional
Ответ 6
Есть способ сделать это и использует массивы (самый простой способ):
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();
полное объяснение можно найти в моем блоге:
http://www.tbogard.com/2013/03/07/passing-named-arguments-to-a-function-in-php/
Ответ 7
Для тех, кто все еще может споткнуться о вопросе (как и я), вот мой подход:
поскольку PHP 5.6 вы можете использовать ..., как указано здесь:
В этом случае вы можете использовать что-то вроде этого:
class Base{
function callDerived($method,...$params){
call_user_func_array(array($this,$method),$params);
}
}
class Derived extends Base{
function test(...$params){
foreach ($params as $arr) {
extract($arr);
}
print "foo=$foo, bar=$bar\n";
}
}
$d = new Derived();
$d->callDerived('test',array('bar'=>'2'),array('foo'=>1));
//print: foo=1, bar=2