3

I'm trying to configure an object at runtime passing a callback function like so:

class myObject{
  protected $property;
  protected $anotherProperty;

  public function configure($callback){
    if(is_callable($callback)){
      $callback();
    }
  }
}

$myObject = new myObject(); //
$myObject->configure(function(){
  $this->property = 'value';
  $this->anotherProperty = 'anotherValue';
});

Of course I get the following error:

Fatal error: Using $this when not in object context

My question is if there is a way to achieve this behavior using $this inside a callback function or maybe get a suggestion for a better pattern.

PS: I prefer to use a callback.

gen_Eric
  • 223,194
  • 41
  • 299
  • 337
marcio
  • 10,002
  • 11
  • 54
  • 83

2 Answers2

8

If you are using (or are willing to upgrade to) PHP 5.4, you can the new bindTo method of Closures. This allows you to "re-bind" a closure to a new scope.

Before calling $callback, you can set its $this to what you want.

if(is_callable($callback)){
    $callback = $callback->bindTo($this, $this);
    $callback();
}

DEMO: http://codepad.viper-7.com/lRWHTn

You can also use bindTo outside of the class.

$func = function(){
  $this->property = 'value';
  $this->anotherProperty = 'anotherValue';
};
$myObject->configure($func->bindTo($myObject, $myObject));

DEMO: http://codepad.viper-7.com/mNgMDz

gen_Eric
  • 223,194
  • 41
  • 299
  • 337
6

Starting with your idea, you could pass $this as a parameter to your callback

But note that your callback (which is not declared inside your class) will not be able to access protected nor private properties/methods -- which means you'll have to set up public methods to access those.


Your class would then look like this :

class myObject {
  protected $property;
  protected $anotherProperty;
  public function configure($callback){
    if(is_callable($callback)){
      // Pass $this as a parameter to the callback
      $callback($this);
    }
  }
  public function setProperty($a) {
    $this->property = $a;
  }
  public function setAnotherProperty($a) {
    $this->anotherProperty = $a;
  }
}

And you'd declared you callback, and use it, like this :

$myObject = new myObject(); //
$myObject->configure(function($obj) {
  // You cannot access protected/private properties or methods
  // => You have to use setters / getters
  $obj->setProperty('value');
  $obj->setAnotherProperty('anotherValue');
});


Calling the following line of code just after :

var_dump($myObject);

Would output this :

object(myObject)[1]
  protected 'property' => string 'value' (length=5)
  protected 'anotherProperty' => string 'anotherValue' (length=12)

Which shows that the callback has been executed, and the properties of your object have indeed been set, as expected.

Pascal MARTIN
  • 395,085
  • 80
  • 655
  • 663
  • 1
    +1 This is a good step forward. I'm going to wait for a possible better answer for a while, before accept. – marcio May 18 '12 at 19:16
  • can you please describe the work flow of this code? there are some things there that I can't understand...I want to fully understand this...does $this inside $callback($this) gonna be $callback($this, 'func')? thanks! – Botea Florin Jun 26 '18 at 05:36