最近在我正在开发的项目中,我需要将回调存储在静态成员数组中,如下所示:
class Example {
private static $_callbacks = array(
'foo'=>array('Example','do_foo'),
'bar'=>array('Example','do_bar')
);
private static function do_foo() { }
private static function do_bar() { }
}
要调用它们,我尝试了明显的(甚至是天真的)语法(在Example
类中):
public static function do_callbacks() {
self::$_callbacks['foo']();
self::$_callbacks['bar']();
}
令我惊讶的是,这不起作用,导致我正在访问一个未定义的变量的通知,以及一个致命错误,指出self::$_callbacks['foo']
需要可调用。
然后,我尝试了call_user_func
:
public static function do_callbacks() {
call_user_func(self::$_callbacks['foo']);
call_user_func(self::$_callbacks['bar']);
}
它有效!
我的问题是:
为什么我需要使用call_user_func
作为中介,而不是直接调用它们?
答案 0 :(得分:4)
您无法通过附加()
来呼叫回叫。这只适用于PHP 5.3中的lambda函数和实现__invoke
魔法的对象(另请参阅内部get_closure
对象处理程序)。
首先,尽管你说的是,但这不起作用:
<?php
class Example {
private static $_callbacks;
private static function do_foo() { echo "foo"; }
private static function do_bar() { echo "bar"; }
public static function do_callbacks() {
self::$_callbacks['foo'] = array('Example','do_foo');
self::$_callbacks['bar'] = array('Example','do_bar');
self::$_callbacks['foo']();
self::$_callbacks['bar']();
}
}
Example::do_callbacks();
但是如果self::$_callbacks['foo']
是一个lambda,它甚至不会起作用:
<?php
class Example {
private static $_callbacks;
public static function do_callbacks() {
self::$_callbacks['foo'] = function () { echo "foo"; };
self::$_callbacks['foo']();
}
}
Example::do_callbacks();
原因是解析器。以上编译为:
Class Example: Function do_callbacks: (...) number of ops: 16 compiled vars: !0 = $_callbacks line # * op fetch ext return operands --------------------------------------------------------------------------------- 6 0 > EXT_NOP 7 1 EXT_STMT 2 ZEND_FETCH_CLASS 3 ZEND_DECLARE_LAMBDA_FUNCTION '%00%7Bclosure%7D%2Ftmp%2Fcp9aicow0xb7fcd09b' 4 FETCH_W static member $2 '_callbacks' 5 ZEND_ASSIGN_DIM $2, 'foo' 6 ZEND_OP_DATA ~3, $4 9 7 EXT_STMT 8 FETCH_DIM_R $5 !0, 'foo' 9 ZEND_FETCH_CLASS 10 ZEND_INIT_STATIC_METHOD_CALL $6, $5 11 EXT_FCALL_BEGIN 12 DO_FCALL_BY_NAME 0 13 EXT_FCALL_END 10 14 EXT_STMT 15 > RETURN null
永远不会获取静态成员(除了lambda的赋值)。事实上,PHP编译变量$_callbacks
,结果证明它不存在于运行时;因此你的错误。我承认这可能不是一个bug,但至少是解析器的一个极端情况。它首先评估$_callbacks['foo']
部分,然后尝试调用名称来自该评估的静态函数。
总之 - 坚持call_user_func
或forward_static_call
。
答案 1 :(得分:2)
因为PHP函数不是真正的数据类型,因为它们是更多函数式语言(这就是为什么它们以相对笨拙的数组语法指定 - 甚至create_function()只返回生成的函数名称,而不是功能指针)。那么,call_user_func和它的兄弟是解决方法(hacks?)允许某种形式的函数/回调传递能力。
答案 2 :(得分:0)
如果您使用的是PHP 5.3,并且回调始终是类的静态函数,那么您可以使用以下代码:
public static function do_callbacks() {
// Those can be defined in another function, or in the constructor.
// self::$_callbacks['foo'] = array('Example','do_foo');
// self::$_callbacks['bar'] = array('Example','do_bar');
$class = self::$_callbacks['foo'][0];
$method = self::$_callbacks['foo'][1];
$class::$method();
$class = self::$_callbacks['bar'][0];
$method = self::$_callbacks['bar'][1];
}
这样,您就不需要使用call_user_func()
。
如果回调总是具有相同的类型,则使用此方法是有意义的(在这种情况下,它们是类的静态方法,其名称作为数组的第一项给出)。如果您需要考虑call_user_func()
能够处理的所有可能类型的回调,那么最好使用call_user_func()
; PHP代码不能比用于实现PHP扩展的C代码更快。