Когда использовать call_user_func_array

Я прочитал другие ответы на стек в отношении использования 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?

2

Решение

Пример, где 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, которые знают, что статические классы обычно не подходят, но они хотят иметь возможность вызывать методы, как если бы они были статическими. Я надеюсь это имеет смысл.

2

Другие решения

Дэвид Склар обратился к вопросу о том, когда использовать 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);

Увидеть живой код

2