define a closure as method from class
i'm trying to play with php5.3 and closure.
I see here (Listing 7. Closure inside an object : http:/开发者_Python百科/www.ibm.com/developerworks/opensource/library/os-php-5.3new2/index.html) that it's possible to use $this in the callback function, but it's not. So I try to give $this as use variable :
$self = $this;
$foo = function() use($self) { //do something with $self }
So to use the same example :
class Dog
{
private $_name;
protected $_color;
public function __construct($name, $color)
{
$this->_name = $name;
$this->_color = $color;
}
public function greet($greeting)
{
$self = $this;
return function() use ($greeting, $self) {
echo "$greeting, I am a {$self->_color} dog named {$self->_name}.";
};
}
}
$dog = new Dog("Rover","red");
$dog->greet("Hello");
Output:
Hello, I am a red dog named Rover.
First of all this example does not print the string but return the function, but that's not my problem.
Secondly I can't access to private or protected, because the callback function is a global function and not in the context from the Dog object. Tha't my problem. It's the same as :
function greet($greeting, $object) {
echo "$greeting, I am a {$self->_color} dog named {$self->_name}.";
}
And I want :
public function greet($greeting) {
echo "$greeting, I am a {$self->_color} dog named {$self->_name}.";
}
Which is from Dog and not global.
Well, the whole reason that you can't use $this, is because the closure is an object in the background (the Closure class).
There are two ways around this. First, is add the __invoke method (What's called if you call $obj())..
class Dog {
public function __invoke($method) {
$args = func_get_args();
array_shift($args); //get rid of the method name
if (is_callable(array($this, $method))) {
return call_user_func_array(array($this, $method), $args);
} else {
throw new BadMethodCallException('Unknown method: '.$method);
}
}
public function greet($greeting) {
$self = $this;
return function() use ($greeting, $self) {
$self('do_greet', $greeting);
};
}
protected function do_greet($greeting) {
echo "$greeting, I am a {$this->_color} dog named {$this->_name}.";
}
}
If you want the closure to not change if you modify the host object, you can just change the return function to something like:
public function greet($greeting) {
$self = (clone) $this;
return function() use ($greeting, $self) {
$self('do_greet', $greeting);
};
}
The other option, is to provide a generic getter:
class Dog {
public function __get($name) {
return isset($this->$name) ? $this->$name : null;
}
}
For more information, see: http://www.php.net/manual/en/language.oop5.magic.php
As of PHP 5.4.0 Alpha1, you can access $this
from within the context of an object instance:
<?php
class Dog
{
private $_name;
protected $_color;
public function __construct($name, $color)
{
$this->_name = $name;
$this->_color = $color;
}
public function greet($greeting)
{
$func = function() use ($greeting) {
echo "$greeting, I am a {$this->_color} dog named {$this->_name}.";
};
$func();
}
}
$dog = new Dog("Rover","red");
$dog->greet("Hello");
You can also do this:
$dog = new Dog("Rover", "red");
$getname = Closure::bind($dog, function() { return $this->_name; });
echo $getname(); // Rover
As you can see, it's possible to easily mess with private data... so be careful.
Well it makes sense that you cannot access private and protected fields of an object. And by explicitly passing $self
to your function, it is treated just as a normal object.
You should create getters in order to access these values , i.e. :
class Dog
{
private $_name;
protected $_color;
public function __construct($name, $color)
{
$this->_name = $name;
$this->_color = $color;
}
public function getName() {
return $this->_name;
}
public function getColor() {
return $this->_color;
}
public function greet($greeting)
{
$self = $this;
return function() use ($greeting, $self) {
echo "$greeting, I am a {$self->getColor()} dog named {$self->getName()}.";
};
}
}
You should create getter (and setters) anyway, for matter of encapsulation.
Another note: The article you link to was published before the final version of PHP 5.3 was released. Maybe this implicit object passing was removed.
I use this create_closure() in my work to seperate callbacks into Classes:
<?php
function create_closure($fun, $args, $uses)
{$params=explode(',', trim($args.','.$uses, ','));
$str_params='';
foreach ($params as $v)
{$v=trim($v, ' &$');
$str_params.='\''.$v.'\'=>&$'.$v.', ';
}
return "return function({$args}) use ({$uses}) {{$fun}(array({$str_params}));};";
}
?>
example:
<?php
$loop->addPeriodicTimer(1, eval(create_closure('pop_message', '$timer', '$cache_key, $n, &$response, &$redis_client')));
function pop_message($params)
{extract($params, EXTR_REFS);
$redis_client->ZRANGE($cache_key, 0, $n)
->then(//normal
function($data) use ($cache_key, $n, &$timer, &$response, &$redis_client)
{//...
},
//exception
function ($e) use (&$timer, &$response, &$redis_client)
{//...
}
);
}
?>
精彩评论