Я прочитал другие ответы на стек в отношении использования call_user_func_array
против простого вызова функции, но я все еще не могу понять, когда следует использовать первый. Я понимаю, что вы можете использовать call_user_func_array
когда вы не знаете, сколько аргументов передано, вы можете сделать: $args = func_get_args();
… но разве вам не всегда нужно знать аргументы, если они будут использоваться в функции?
Обе из следующих работ, я полагаю, первая имеет меньше накладных расходов.
$format = new Foo;
$method = 'somemethod';
$arg = 'somevalue';
if (method_exists($format, $method)) {
return $format->$method($arg);
}
против
return call_user_func_array(array($format, $method), array($arg));
Когда кто-то действительно выиграет от использования call_user_func_array
?
Допустим, у вас есть желание получить доступ к объекту, но с помощью статических методов, например:
Helper::load();
Ну, это не сработает само по себе, потому что если вы посмотрите на конкретный класс, у него нет статического метода:
class Helper {
public function load()
{
// Do some loading ...
}
public function aFunctionThatNeedsParameters( $param1, $param2 )
{
// Do something, and $param1 and $param2 are required ...
}
}
Таким образом, во втором классе мы могли бы сделать что-то вроде этого, потому что вышеприведенный класс Helper загружается в контейнер внедрения зависимостей (обратите внимание, что эти два класса Helper названы одинаково, но будут в разных пространствах имен):
class Helper extends DIContainer {
public static $helper_instance = NULL;
public static function get_instance()
{
if( is_null( self::$helper_instance ) )
{
self::$helper_instance = parent::$container['helper'];
}
return self::$helper_instance;
}
public static function __callStatic( $method, $params )
{
$obj = self::get_instance();
return call_user_func_array( [ $obj, $method ], $params );
}
}
Дело в том, что может быть другой метод, которому нужны параметры, даже если у нашего метода загрузки их нет.
Так что в этом случае мы можем использовать Helper::load()
, но также Helper::aFunctionThatNeedsParameters( $param1, $param2 )
Я думаю, что это часто используется в средах PHP, которые знают, что статические классы обычно не подходят, но они хотят иметь возможность вызывать методы, как если бы они были статическими. Я надеюсь это имеет смысл.
Дэвид Склар обратился к вопросу о том, когда использовать call_user_func_array в его PHP Cookbook. Итак, чтобы сосредоточиться на одном комментарии ОП:
«… вам не всегда нужно знать аргументы, если они будут использованы
в функции? «
Вот пример, где call_user_func () может пригодиться:
<?php
function Test(){
$args = func_get_args();
call_user_func_array("printf",$args);
}
Test("Candidates 2014: %s %s %s %s\n","Tom","Debbie","Harry", "Sally");
Test("Candidates 2015: %s %s\n","Bob","Sam","Sarah");
Увидеть живой код
С помощью пары call_user_func_array () и func_get_args () пользовательская функция Test () может принимать переменное число аргументов.
Также полезно для методов агрегирования (см. PHP Cookbook p208-210), как демонстрирует следующий упрощенный пример:
<?php
class Address {
protected $city;
public function getCity() {
return $this->city;
}
public function setCity($city) {
$this->city=$city;
}
}
class Person {
protected $name="Tester";
protected $address;
public function __construct() {
$this->address = new Address;
}
public function getName(){
return $this->name;
}
public function __call($method, $args){
if (method_exists($this->address,$method)) {
return call_user_func_array( array($this->address,$method),$args);
}
}
}
$sharbear = new Person;
echo $sharbear->setCity("Baltimore");
echo "Name: ",$sharbear->getName(),"\n";
echo "City: ",$sharbear->getCity();
Увидеть живой код
добавление
Начиная с PHP 5.6, PHP поддерживает вариационные функции а также распаковка аргумента, поэтому call_user_func () и func_get_args () могут быть заменены относительно переменных аргументов следующим образом:
<?php
function Test(...$args){
printf(...$args);
}
$namesT = ["Timmy","Teddy","Theo","Tad"];
$namesB = ["Bobby","Bill","Brad"];
Test("T Names: %s %s %s %s\n",...$namesT);
Test("B Names: %s %s %s\n",...$namesB);
Увидеть живой код