Devin Dixon
Devin Dixon

Reputation: 12403

Anonymous Function/Closure and using self:: or static::

I am working with anonymous functions where I am creating anonymous function outside of the object, and then adding it to an object later in which it will be used with __callStatic magic function. The closures that are being added to contain methods from the parent class. I am wondering if I would be able to call those methods from the closure?

Right now I get this error:

EmptyObject::addMethod('open', function(){
    if (static::_hasAdapter(get_class(), __FUNCTION__))
            return self::_callAdapter(get_class(), __FUNCTION__, $details);

    echo '<p>You have mail!</p>';
});

throws this error:

Fatal error: Cannot access static:: when no class scope is active in

And

//Add the functions
EmptyObject::addMethod('open', function(){
    if (EmptyObject::_hasAdapter('EmptyObject', __FUNCTION__))
            return EmptyObject::_callAdapter('EmptyObject', __FUNCTION__, $details);

    echo '<p>You have mail!</p>';
});

throw this error because the method is protected

Fatal error: Uncaught exception 'BadMethodCallException' with message 'Method '_hasAdapter' was not found in class EmptyObject'

Upvotes: 11

Views: 8834

Answers (3)

Ari Asulin
Ari Asulin

Reputation: 189

Simply store the class name and pass it to the closure via use. You can call any public static method or grab public static properties or constants this way. If the closure is passed off to a different context it will still work as long as the correct value for $class was passed to it when it was created. Works for php 5.3:

class test {
    public static function callMe() { echo 'call me '; }

    public static function runTest() {
        $class = __CLASS__;
        $call = function() use ($class) {
            $class::callMe();   
        };
        $call();
    }
}
test::runTest();

Upvotes: 14

Ben Bidner
Ben Bidner

Reputation: 151

You can achieve this by using Closure::bind() (PHP >= 5.4.0)

abstract class EmptyObject
{
   protected static $methods = array();

   final public static function __callStatic($name, $arguments)
   {
      return call_user_func(self::$methods[$name], $arguments);
   }

   final public static function addMethod($name, $fn)
   {
      self::$methods[$name] = Closure::bind($fn, NULL, __CLASS__);
   }

   final protected static function protectedMethod()
   {
      echo __METHOD__ . " was called" . PHP_EOL;
   }
}

Now any anonymous function passed to EmptyObject::addMethod() will be run in the scope of the EmptyObject class

EmptyObject::addMethod("test", function()
{
   self::protectedMethod();
});


// will output:
// EmptyObject::protectedMethod was called

EmptyObject::test();

Upvotes: 15

Mchl
Mchl

Reputation: 62395

Closures are called like that for a reason. They 'enclose' the scope they're defined in. They are not simply blocks of code that can pick up the scope from the place they're pasted into.

Upvotes: 4

Related Questions