【问题标题】:Callbacks as part of a static member array回调作为静态成员数组的一部分
【发布时间】:2010-06-21 01:22:45
【问题描述】:

最近在我正在做的一个项目中,我需要将回调存储在静态成员数组中,如下所示:

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作为中介,而不是直接调用它们?

【问题讨论】:

    标签: php static callback


    【解决方案1】:

    您不能通过附加 () 来调用回调。这仅适用于 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();
    

    原因是解析器。以上编译为:

    类示例: 函数do_callbacks: (...) 操作数:16 编译后的变量:!0 = $_callbacks line # * op fetch ext 返回操作数 -------------------------------------------------- ------------------------------------------- 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 静态成员 $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 > 返回空

    永远不会获取静态成员(除了 lambda 的赋值)。事实上,PHP 编译了一个变量$_callbacks,结果在运行时并不存在;因此你的错误。我承认这可能不是一个错误,但至少是解析器的一个极端情况。它首先评估$_callbacks['foo'] 部分,然后尝试调用其名称来自该评估的静态函数。

    总之——坚持call_user_func或forward_static_call。

    【讨论】:

    • 哦,哇,你是对的:这行不通!这真的很奇怪,因为当我最初试图弄清楚为什么我的原版不起作用时,我试过了,它确实起作用了。那么,我将把它从我的问题中提取出来。
    【解决方案2】:

    因为在 PHP 中函数不是真正的数据类型,因为它们在更多函数式语言中(这就是为什么它们以相对笨拙的数组语法指定的原因 - 甚至 create_function() 只返回函数的生成名称,而不是函数指针)。因此, call_user_func 和它的兄弟是允许某种形式的函数/回调传递能力的解决方法(黑客?)。

    【讨论】:

    • 这是一个原因。另一个是脚本没有按照他认为的方式解析。
    【解决方案3】:

    如果您使用的是 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 代码快。

    【讨论】:

    • 是的,我考虑过这一点,但我确实需要支持 is_callable() 的任何内容。
    猜你喜欢
    • 1970-01-01
    • 2014-04-14
    • 1970-01-01
    • 2011-01-07
    • 1970-01-01
    • 1970-01-01
    • 2014-04-06
    • 1970-01-01
    • 2012-07-28
    相关资源
    最近更新 更多