我正在编写一个API类,我的总体目标是通过API可以轻松地访问任何类的方法,而无需对类本身进行任何重大更改。从本质上讲,我应该能够在我想要使用的任何类上实例化一个API类实例(在我的小框架内),并让它正常工作。
例如,在我的API类中,我有一个方法call
,我想使用$_GET
从我想要访问的类中调用正确的函数(让我们称之为{ {1}})。所以我在我的API中指定了一个Beep
参数,因此action
是调用action
的方法,其中Beep
中的其余参数可能是参数对于该方法。在$_GET
中,我可以API->call
,但我无法确定要从$ _GET发送哪些参数,以及发送它们的顺序。
$BeepInstance->$_GET['action']()
将仅返回调用它的函数的给定参数列表,并且我不一定知道使用func_get_args
传递它们的正确顺序。
有没有人试图做类似的事情?
答案 0 :(得分:4)
这是一个解决方案+示例,它使用反射将输入参数映射到方法参数。我还添加了一种方法来控制暴露哪些方法以使其更安全。
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 :(得分:1)
我们在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']);
}
答案 2 :(得分:1)
我建议传递一个关联数组相应的方法。自从联想。 array为值映射提供了一个名称。
此外,从不做类似的事情:
$BeepInstance->$_GET['action']()
这是非常不安全的。
可能定义另一个关联数组,它将作为GET'action'参数传递的动作映射到实际的方法名称。