Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
559 views
in Technique[技术] by (71.8m points)

php - 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://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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...