Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bind $this to a closure that is passed as a method parameter in PHP 5.4?

Tags:

php

php-5.4

Is there any way to bind $this to a closure that is passed as a parameter? I read and reread everything I could find in manual or over the internet, but no one mentions this behaviour, except this blog post: http://www.christophh.net/2011/10/26/closure-object-binding-in-php-54/ which mentions it but doesn't show how to do it.

So here's an example. When calling the get(function() {}) method I want the callback function that is passed to it was bound to the object i.e. bound to $this, but unfortunately it doesn't work. Is there any way I can do it?

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 works. $this is NULL.

like image 736
unknown.bird Avatar asked Dec 09 '13 10:12

unknown.bird


4 Answers

I actually didn't understand why using the bindTo method didn't work in this case, but I could get it to work using Closure::bind

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

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

Edit

Aparently the bindTo method has the same behavior, so you should reassign its return value to $callback. For example:

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

    $callback = $callback->bindTo($this);
    $callback();
}
like image 117
Guilherme Sehn Avatar answered Nov 08 '22 02:11

Guilherme Sehn


Do it like this:

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);
});
like image 20
MaratMS Avatar answered Nov 08 '22 02:11

MaratMS


Just pass it as an argument:

    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);
});

Alternatively, if you really did need to bind it, you would have to use a function that returned a function, like this:

    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);
like image 28
Benubird Avatar answered Nov 08 '22 02:11

Benubird


A small correction: don't use is_callable to check if Closure is passed by parameter.

Because is_callable too accept String with name of function.

public function get(\Closure $callback) {

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

With is_callable we have this possibility:

$app = new App;


$app->get('my_function');

If function exists, this error is thrown:

Closure::bind() expects parameter 1 to be Closure, string given

Because "My_function" is passed in test of is_callable, but Closure::bind expects instance of Closure.

like image 1
Wallace Maxters Avatar answered Nov 08 '22 04:11

Wallace Maxters