Recently in a project I am working on, I needed to store callbacks in a static member array, like so:
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() { }
}
To call them, I tried the obvious (maybe even naive) syntax (inside the Example class):
public static function do_callbacks() {
self::$_callbacks['foo']();
self::$_callbacks['bar']();
}
To my surprise, this did not work, resulting in a notice that I was accessing an undefined variable, and a fatal error stating that self::$_callbacks['foo'] needed to be callable.
Then, I tried call_user_func:
public static function do_callbacks() {
call_user_func(self::$_callbacks['foo']);
call_user_func(self::$_callbacks['bar']);
}
And it worked!
My question is:
Why do I need to use call_user_func as an intermediary, and not directly call them?
You can’t call callbacks by appending
(). That only works in PHP 5.3 with lambda functions and objects that implement the__invokemagic (see also the internalget_closureobject handler).First, despite what you say, this doesn’t work:
But it wouldn’t even work if
self::$_callbacks['foo']was a lambda:The reason is the parser. The above compiles to:
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 nullThere’s never a fetching of a static member (except for the assignment of the lambda). In fact, PHP compiles a variable
$_callbacks, which turns out not to exist at runtime; hence your error. I concede this is, maybe not a bug, but at least a corner case of the parser. It evaluates the$_callbacks['foo']part first and then attempts to call the static function whose name results from that evaluation.In sum – stick to
call_user_funcorforward_static_call.