Вызов методов PHP с различными аргументами - PullRequest
3 голосов
/ 21 сентября 2011

Я пишу класс API, и моя общая цель - сделать так, чтобы методы любого класса были доступны через API, без каких-либо серьезных изменений в самом классе.По сути, я должен быть в состоянии создать экземпляр класса API для любого класса, который я хочу использовать (в рамках моей небольшой структуры), и он просто работает.

Например, в моем классе API у меня естьметод call, который я хочу использовать $_GET для вызова правильной функции из класса, который я хочу сделать доступным (давайте назовем его Beep).Поэтому я указываю параметр action в своем API, так что action - это метод для вызова Beep, а остальные аргументы в $_GET являются, предположительно, аргументами для метода.В API->call я могу сделать $BeepInstance->$_GET['action'](), но у меня нет способа определить, какие аргументы из $ _GET отправлять и в каком порядке их отправлять.

func_get_args будет возвращать только список заданных аргументов для функции, в которой она вызывается, и я не обязательно знаю правильный порядок их передачи с помощью call_user_func_array.

Кто-нибудь пытался сделать что-то подобное?

Ответы [ 3 ]

4 голосов
/ 22 сентября 2011

Вот решение + пример, который использует отражение для сопоставления ваших входных аргументов с параметрами метода. Я также добавил способ контролировать, какие методы доступны, чтобы сделать его более безопасным.

class Dispatch {
    private $apis;

    public function registerAPI($api, $name, $exposedActions) {
        $this->apis[$name] = array(
            'api' => $api,
            'exposedActions' => $exposedActions
        );
    }

    public function handleRequest($apiName, $action, $arguments) {
        if (isset($this->apis[$apiName])) {
            $api = $this->apis[$apiName]['api'];
            // check that the action is exposed
            if (in_array($action, $this->apis[$apiName]['exposedActions'])) {
                // execute action

                // get method reflection & parameters
                $reflection = new ReflectionClass($api);
                $method = $reflection->getMethod($action);

                // map $arguments to $orderedArguments for the function
                $orderedArguments = array();

                foreach ($method->getParameters() as $parameter) {
                    if (array_key_exists($parameter->name, $arguments)) {
                        $orderedArguments[] = $arguments[$parameter->name];
                    } else if ($parameter->isOptional()) {
                        $orderedArguments[] = $parameter->getDefaultValue();
                    } else {
                        throw new InvalidArgumentException("Parameter {$parameter->name} is required");
                    }
                }

                // call method with ordered arguments
                return call_user_func_array(array($api, $action), $orderedArguments);
            } else {
                throw new InvalidArgumentException("Action {$action} is not exposed");
            }
        } else {
            throw new InvalidArgumentException("API {$apiName} is not registered");
        }
    }
}

class Beep {
    public function doBeep($tone = 15000)
    {
        echo 'beep at ' . $tone;
    }

    public function notExposedInAPI()
    {
        // do secret stuff
    }
}

Пример:

// dispatch.php?api=beep&action=doBeep&tone=20000

$beep = new Beep();
$dispatch = new Dispatch();
$dispatch->registerAPI($beep, 'beep', array('doBeep'));

$dispatch->handleRequest($_GET['api'], $_GET['action'], $_GET);
1 голос
/ 21 сентября 2011

Я бы предложил передать ассоциативный массив соответствующим методом. Поскольку доц. массив предоставляет имя для сопоставления значений.

Более того, никогда сделайте что-то вроде этого:

  $BeepInstance->$_GET['action']()

Это крайне небезопасно.

Возможно, определите другой ассоциированный массив, который отображает действия, переданные как параметры GET 'action', в реальные имена методов.

1 голос
/ 21 сентября 2011

Мы сделали что-то похожее в нашем API.Мы использовали прокси-метод _methodName ($ p) и передали массив $ _GET или $ _REQUEST.Прокси-метод знает порядок параметров, необходимых для реального метода, поэтому он правильно вызывает реальный метод.Использование call_user_func_array () с этим довольно хорошо работало.

Не уверен, что это лучший способ, но у нас это хорошо работает.

Контроллер выглядит примерно так:*

if (method_exists($server, "_$method"))
        $resp = call_user_func_array("{$server}::_$method", array($_REQUEST));

И тогда модель настраивается как:

public function test($arg1, $arg2) { ... }
public function _test($p) {
    return $this->test($p['arg1'], $p['arg2']);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...