Как связать $this с закрытием, которое передается как параметр метода в PHP 5.4?

Есть ли способ привязать $this к закрытию, которое передается как параметр? Я читал и перечитывал все, что мог найти в руководстве или через Интернет, но никто не упоминает об этом, кроме этого сообщения в блоге: http://www.christophh.net/2011/10/26/closure-object-binding-in-php-54/ который упоминает об этом, но не показывает, как это сделать.

Итак, вот пример. При вызове метода get(function() {}) я хочу, чтобы функция обратного вызова, которая была передана ему, была привязана к объекту, то есть привязана к $this, но, к сожалению, это не работает. Есть ли способ сделать это?

class APP
{
    public $var = 25;

    public function __construct() {

    }
    public function get($callback) {
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('Paran must be callable.');
        }
        // $callback->bindTo($this);
        $callback->bindTo($this, $this);
        $callback();
    }
}

$app = new APP();
$app->get(function() use ($app) {
    echo '<pre>';
    var_dump($app);
    echo '<br />';
    var_dump($this);
});

$app работает. $this имеет значение NULL.

Ответ 1

Я действительно не понял, почему использование метода bindTo в этом случае не помогло, но я мог заставить его работать, используя Closure::bind

public function get($callback) {
    if (!is_callable($callback)) {
        throw new InvalidArgumentException('Param must be callable.');
    }

    $bound = Closure::bind($callback, $this);
    $bound();
}

Edit

По-видимому, метод bindTo имеет такое же поведение, поэтому вы должны переназначить его возвращаемое значение на $callback. Например:

public function get($callback) {
    if (!is_callable($callback)) {
        throw new InvalidArgumentException('Param must be callable.');
    }

    $callback = $callback->bindTo($this);
    $callback();
}

Ответ 2

Сделайте это так:

class APP
{
  public $var = 25;

  public function __construct() {}

  public function get($callback) {
    if (!is_callable($callback)) {
      throw new InvalidArgumentException('Param must be callable.');
    }
    // $callback->bindTo($this);
    // you must save result in another var and call it
    $callback1 = $callback->bindTo($this, $this);
    $callback1();
  }
}

$app = new APP();
$app->get(function() use ($app) {
  echo '<pre>';
  var_dump($app);
  echo '<br />';
  var_dump($this);
});

Ответ 3

Просто передайте его как аргумент:

    public function get($callback) {
        if (!is_callable($callback)) {
            throw new InvalidArgumentException('Paran must be callable.');
        }
        // $callback->bindTo($this);
        return $callback($this);
    }

...

$app = new APP();
$app->get(function($that) use ($app) {
    echo '<pre>';
    var_dump($app);
    echo '<br />';
    var_dump($that);
});

В качестве альтернативы, если вам действительно нужно связать его, вам нужно будет использовать функцию, которая возвращает функцию, например:

    public function getCallback($callback) {
        return function($app){
            return $callback($this, $app);
        }
    }

...

$app = new APP();
$f = $app->get(function($that, $app) {
    echo '<pre>';
    var_dump($app);
    echo '<br />';
    var_dump($that);
});
$f($app);

Ответ 4

Небольшая поправка: не используйте is_callable, чтобы проверить, прошел ли Closure по параметру.

Потому что is_callable тоже принимает строку с именем функции.

public function get(\Closure $callback) {

    $bound = \Closure::bind($callback, $this);
    $bound();
}

С is_callable мы имеем такую ​​возможность:

$app = new App;


$app->get('my_function');

Если функция существует, эта ошибка возникает:

Closure:: bind() ожидает, что параметр 1 должен быть закрыт, строка задана

Потому что "My_function" передается в тесте is_callable, но Closure::bind ожидает экземпляр Closure.