Calling closure assigned to object property directly

前端 未结 12 2082
孤街浪徒
孤街浪徒 2020-11-22 14:02

I would like to be able to call a closure that I assign to an object\'s property directly without reassigning the closure to a variable and then calling it. Is this possible

相关标签:
12条回答
  • 2020-11-22 15:02

    I know this is old, but I think Traits nicely handle this problem if you are using PHP 5.4+

    First, create a trait that makes properties callable:

    trait CallableProperty {
        public function __call($method, $args) {
            if (property_exists($this, $method) && is_callable($this->$method)) {
                return call_user_func_array($this->$method, $args);
            }
        }
    }
    

    Then, you can use that trait in your classes:

    class CallableStdClass extends stdClass {
        use CallableProperty;
    }
    

    Now, you can define properties via anonymous functions and call them directly:

    $foo = new CallableStdClass();
    $foo->add = function ($a, $b) { return $a + $b; };
    $foo->add(2, 2); // 4
    
    0 讨论(0)
  • 2020-11-22 15:02

    If you're using PHP 5.4 or above you could bind a callable to the scope of your object to invoke custom behavior. So for example if you were to have the following set up..

    function run_method($object, Closure $method)
    {
        $prop = uniqid();
        $object->$prop = \Closure::bind($method, $object, $object);
        $object->$prop->__invoke();
        unset($object->$prop);
    }
    

    And you were operating on a class like so..

    class Foo
    {
        private $value;
        public function getValue()
        {
            return $this->value;
        }
    }
    

    You could run your own logic as if you were operating from within the scope of your object

    $foo = new Foo();
    run_method($foo, function(){
        $this->value = 'something else';
    });
    
    echo $foo->getValue(); // prints "something else"
    
    0 讨论(0)
  • 2020-11-22 15:03

    Well, if you really insist. Another workaround would be:

    $obj = new ArrayObject(array(),2);
    
    $obj->callback = function() {
        print "HelloWorld!";
    };
    
    $obj['callback']();
    

    But that's not the nicest syntax.

    However, the PHP parser always treats T_OBJECT_OPERATOR, IDENTIFIER, ( as method call. There seems to be no workaround for making -> bypass the method table and access the attributes instead.

    0 讨论(0)
  • 2020-11-22 15:03

    Here's another alternative based on the accepted answer but extending stdClass directly:

    class stdClassExt extends stdClass {
        public function __call($method, $args)
        {
            if (isset($this->$method)) {
                $func = $this->$method;
                return call_user_func_array($func, $args);
            }
        }
    }
    

    Usage example:

    $foo = new stdClassExt;
    $foo->blub = 42;
    $foo->whooho = function () { return 1; };
    echo $foo->whooho();
    

    You are probably better off using call_user_func or __invoke though.

    0 讨论(0)
  • 2020-11-22 15:05

    As of PHP 7 you can do the following:

    ($obj->callback)();
    
    0 讨论(0)
  • 2020-11-22 15:08

    As of PHP7, you can do

    $obj = new StdClass;
    $obj->fn = function($arg) { return "Hello $arg"; };
    echo ($obj->fn)('World');
    

    or use Closure::call(), though that doesn't work on a StdClass.


    Before PHP7, you'd have to implement the magic __call method to intercept the call and invoke the callback (which is not possible for StdClass of course, because you cannot add the __call method)

    class Foo
    {
        public function __call($method, $args)
        {
            if(is_callable(array($this, $method))) {
                return call_user_func_array($this->$method, $args);
            }
            // else throw exception
        }
    }
    
    $foo = new Foo;
    $foo->cb = function($who) { return "Hello $who"; };
    echo $foo->cb('World');
    

    Note that you cannot do

    return call_user_func_array(array($this, $method), $args);
    

    in the __call body, because this would trigger __call in an infinite loop.

    0 讨论(0)
提交回复
热议问题