Storing a Closure Function in a Class Property in

2019-02-27 09:58发布

问题:

ok I do have the code below

<?php
    class foo{
       public $bar = NULL;

       public function boo(){
          $this->bar();
       }
    }

    $mee = new foo();

    //save a closure function on the property
    $mee->bar = function(){
        echo 'hahaha';
    };

    //invoke the closure function by using a class method
    $mee->boo();
?>

and you can see it running here http://codepad.org/s1jhi7cv

now what i want here is to store the closure function on the class method.

well closures are possible as i read the documentation about it here http://php.net/manual/en/functions.anonymous.php

is this possible? did i went to something wrong? please correct me

回答1:

Your example code at codepad.org does not work because codepad.org uses PHP 5.2.5, and closure support was only added in 5.3.

However, your code will also not work in a PHP version that supports closures, although you will get a different error: http://codepad.viper-7.com/Ob0bH5

This is a limitation of PHP at present. $obj->member() looks for a method named member and will not look at properties to see if they are callable. It is, frankly, annoying.

The only way I am aware of to make this work without call_user_func()/call_user_func_array() is:

public function boo() {
   $func = $this->bar;
   $func();
}


回答2:

You need to exploit some magic functionality of PHP (__call) to make use of that. Extend from Extendable for example:

class Extendable {
    static function import($context) {
        $self = new static();
        while (is_callable($context)) $context = $context($self);
        if (is_array($context) || is_object($context) || is_a($context, 'Traversable')) {
            foreach($context as $key => $value)
                $self->$key = &$value; # preserve keys if
        }
        return $self;
    }
    public function __call($name, $args) {
        if (isset($this->$name) && is_callable($this->$name)) {
            return call_user_func_array($this->$name, $args);
        }
        throw new BadFunctionCallException(sprintf('Undefined function %s.', $name));
    }
}

And you can do the job. It's not that nice. Background and examples are in one of my blog posts:

  • PHP: Extending stdClass with Closures (plus Visitor)

You can naturally implement that magic functionality your own, too.



回答3:

PHP is not a prototype based language hence you cannot redefine functions



回答4:

You will not be able to do that.

Take for example this code:

class T {
  function foo() {
    echo 'T::foo';
  }
}

$t = new T;
$t->foo = function() {
  echo 'Closure::foo';
};
$t->foo();

It works fine on PHP 5.4.6 and/or PHP 5.3.16, however it will result in T::foo getting printed.

This happens because methods, in PHP, are not modifiable class properties, as they are for example in javascript.

However,

$foo = $t->foo;
$foo();

will print Closure::foo as expected.



回答5:

Use __call to catch all non-defined methods and then look up the closure and invoke it. Take a look at my post on this SitePoint thread.



回答6:

Use call_user_func() function:

<?php
    class foo{
       public $bar = NULL;

       public function boo(){
          call_user_func($this->bar);
       }
    }

    $mee = new foo();

    //save a closure function on the property
    $mee->bar = function(){
        echo 'hahaha';
    };

    //invoke the closure function by using a class method
    $mee->boo();

This will display "ahahah"

Hope it helps.