kan*_*der 14 php reflection callback
注意:由于写了这个问题,我已经发现我对使用新语言功能过于热心.更清洁的解决方案是使用策略模式而不是......我仍然很好奇是否有适当的方法解决这个问题.
TL; DR:您是否可以在PHP中反映一般的Callable,而无需手动检测各种可调用类型?
在PHP 5.4中,我们有一个新的typehint:callable.这看起来很有趣.我以为我会通过以下方式利用这个:
<?php
public function setCredentialTreatment(callable $credentialTreatment) {
// Verify $credentialTreatment can be used (ie: accepts 2 params)
... magic here ...
}
?>
Run Code Online (Sandbox Code Playgroud)
到目前为止,我的思路是对可调用进行一系列类型检查,并从使用的Reflection*类推断:
<?php
if(is_array($callable)) {
$reflector = new ReflectionMethod($callable[0], $callable[1]);
} elseif(is_string($callable)) {
$reflector = new ReflectionFunction($callable);
} elseif(is_a($callable, 'Closure') || is_callable($callable, '__invoke')) {
$objReflector = new ReflectionObject($callable);
$reflector = $objReflector->getMethod('__invoke');
}
// Array of ReflectionParameters. Yay!
$parameters = $reflector->getParameters();
// Inspect parameters. Throw invalidArgumentException if not valid.
?>
Run Code Online (Sandbox Code Playgroud)
现在,对我而言,这感觉过于复杂.我是否缺少某种快捷方式来实现我在这里尝试做的事情?任何见解都会受到欢迎:)
我创建了一个更短的版本,它的工作原理与 call_user_func() 非常相似,并在 PHP 手册中针对回调/可调用对象的所有不同类型上对其进行了测试。这样你几乎可以在任何地方使用它。call_user_func() 不只接受对象,对我来说这个函数也应该没有意义,因为它只处理回调。
下面包含了有关如何使用它的测试和建议,我希望这会有所帮助:
function getNrOfParams($callable)
{
$CReflection = is_array($callable) ?
new ReflectionMethod($callable[0], $callable[1]) :
new ReflectionFunction($callable);
return $CReflection->getNumberOfParameters();
}
Run Code Online (Sandbox Code Playgroud)
整个测试及其结果:
<?php
class Smart
{
public function __invoke($name)
{
}
public function my_callable($one, $two, $three)
{
}
public static function myCallableMethod($one, $two)
{
}
public static function who()
{
echo "smart the parent class";
}
}
class Smarter extends Smart
{
public static function who()
{
echo "smarter";
}
}
function my_ca($one)
{
}
function getNrOfParams($callable)
{
$CReflection = is_array($callable) ? new ReflectionMethod($callable[0], $callable[1]) : new ReflectionFunction($callable);
return $CReflection->getNumberOfParameters();
}
// Test 1 Callable Function
echo "Test 1 - Callable function:" . getNrOfParams('my_ca');
// Test 2 Static method
echo " Test 2 - Static class method:" . getNrOfParams(array('Smart', 'myCallableMethod'));
// Test 3 Object method
$smart = new Smart();
echo " Test 3 - Object method:" . getNrOfParams(array($smart, 'my_callable'));
// Test 4 Static method call (As of PHP 5.2.3)
//echo " Test 4 - Static class method call:" . getNrOfParams('Smart::myCallableMethod');
// Calling a static method this way does not work in ReflectionFunction.
// However, Test 2 provides a solution.
// Test 5 Relative static method (As of PHP 5.3.0)
//echo " Test 5 - Relative static class method:" . getNrOfParams(array('Smarter', 'parent::who'));
// Calling a relative static method doesn't work either. ReflectionMethod lacks support.
// All other tests work.
// Tesy 6 __invoke
echo " Test 6 - __invoke:" . getNrOfParams(array($smart, '__invoke'));
// Test 7 Closure
$closure = function($one, $two, $three)
{
// Magic
};
echo " Test 7 - Closure:" . getNrOfParams($closure);
Run Code Online (Sandbox Code Playgroud)
TL;DR 我不这么认为。如果您想要通用解决方案,您需要检查所有可调用类型。
以下函数可用于获取ReflectionFunctionAbstractPHP 中任何通用可调用对象的实例:
function reflectionOf(callable $callable): ReflectionFunctionAbstract
{
if ($callable instanceof Closure) {
return new ReflectionFunction($callable);
}
if (is_string($callable)) {
$pcs = explode('::', $callable);
return count($pcs) > 1 ? new ReflectionMethod($pcs[0], $pcs[1]) : new ReflectionFunction($callable);
}
if (!is_array($callable)) {
$callable = [$callable, '__invoke'];
}
return new ReflectionMethod($callable[0], $callable[1]);
}
Run Code Online (Sandbox Code Playgroud)
那么可以得到参数个数如下:
reflectionOf($func)->getNumberOfParameters();
Run Code Online (Sandbox Code Playgroud)
希望这对某人有帮助。这个答案可能迟到了,但其他解决方案都没有提供通用可调用项的完整覆盖。