Обратные вызовы как часть статического членского массива

Недавно в проекте я продолжаю работать, я должен был сохранить обратные вызовы в статическом членском массиве, как так:

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 как посредник, и не непосредственно называют их?

5
задан Austin Hyde 21 June 2010 в 01:47
поделиться

3 ответа

Вы не можете вызывать обратные вызовы, добавляя (). Это работает только в PHP 5.3 с лямбда-функциями и объектами, реализующими магию __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'] был лямбдой:

<?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

Здесь никогда не происходит выборка статического члена (за исключением присвоения лямбды). На самом деле, PHP компилирует переменную $_callbacks, которая, как оказалось, не существует во время выполнения; отсюда и ваша ошибка. Я допускаю, что это, возможно, не ошибка, но, по крайней мере, угловой случай парсера. Он сначала оценивает часть $_callbacks['foo'], а затем пытается вызвать статическую функцию, имя которой вытекает из этой оценки.

В итоге - придерживайтесь call_user_func или forward_static_call.

4
ответ дан 14 December 2019 в 13:26
поделиться

Если вы используете 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 не может быть быстрее, чем код C, используемый для реализации расширений PHP.

0
ответ дан 14 December 2019 в 13:26
поделиться

Потому что в PHP функции не являются типами данных, как в более функциональных языках (именно поэтому они задаются в относительно неудобном синтаксисе массива - даже create_function() возвращает только сгенерированное имя функции, а не указатель на функцию). Итак, call_user_func и его собратья являются обходными путями (хаками?), чтобы позволить некоторую форму передачи функций/обратных вызовов.

2
ответ дан 14 December 2019 в 13:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: