Determining callable type

Hi,

For reasons I won’t bother you with, I need to distinguish between callable types. By that I mean, I need to know if a callable is either a global function, a closure, an object containing the magic method __invoke and so on.

Currently I came up with the following code:

protected function _determineCallbackType( $callback )
{
    if ( is_array( $callback ) ) {
        if ( is_object( $callback[0] ) ) {
            $this->_callbackType = self::CALLBACK_OBJECT;
        } else {
            $this->_callbackType = self::CALLBACK_CLASS;
        }
    } elseif ( is_object( $callback ) ) {
        $r = new ReflectionClass( get_class( $callback ) );
        if ( $r->hasMethod( '__invoke' ) ) {
            $this->_callbackType = self::CALLBACK_INVOKABLE;
        } else {
            $this->_callbackType = self::CALLBACK_CLOSURE;
        }
    } else {
        $this->_callbackType = self::CALLBACK_FUNC;
    }
}

I am concerned about determining the closure callable type. I don’t want to rely on testing for the Closure type because the php manual says:

Anonymous functions are currently implemented using the [B]Closure[/B] class. This is an implementation detail and should not be relied upon.
.

Does the above code seem ok to you? Or am I missing something?

Note: I chose to use reflection instead of method_exists() to test for the existence of the __invoke method because prior to PHP 5.3.3:

var_dump( method_exists( '__invoke', get_class( $closure ) ) );

where $closure contains an anonymous function would output

boolean(true);

for some reason (at least on my ubuntu machine).

Thanks in advance.

Hi,

Thanks for your answer.

I don’t want to test for the “Closure” class name. PHP manual says the fact that anonymous functions are implemented using the Closure class it’s an implementation detail and should not be relied upon. My best bet to determine if a callable is a closure without testing for the “Closure” class name is through elimination. The code in my first post works, I was just wondering if there are other (perhaps more elegant) alternatives (thanks for your Reflection idea though, I may be able to use it).

Also, can anybody explain why:

$lambda = function() {};

var_dump( method_exists( $lambda, '__invoke' ) );
var_dump( method_exists( get_class( $lambda ), '__invoke' ) );

outputs:

bool(true);
bool(false);

For just getting an idea (at a more general level) of what’s going on, no. For hardcore debugging, definitely.

Hmm, so you’re logging an execution trace? Wouldn’t you be better off making use of debug_backtrace() ?

isn’t the whole point of closures and magic methods to work like a function? Imho, if you need to work this out the problem is not “how do I work it out”.

Hi,

Regarding

if anyone is interested

http://bugs.php.net/bug.php?id=53009

No, that leaves 3 other options: a closure, an instance method and a class method, unless function_exists works on instance and class methods besides global functions. Is callable returns true on all of the following:

class A {
    public function test() {}
    public static function staticTest() {}
    public function __invoke() {}
}

function func() {}

$lambda = function() {};

$a = new A();

is_callable( array( $a, 'test' ) ); // instance method
is_callable( array( 'A', 'staticTest' ) ); // static method
is_callable( $a ); // because of __invoke
is_callable( 'func' ); // global function
is_callable( $lambda ); // anonymous function

Seems like reinventing the wheel to me, but just looking back at your function if is_callable returns true, and it’s not an object or a declared function (use function_exists) through process of elimination it must be a closure mustn’t it?

Thanks for your answer.

My reason is simple (and perhaps silly).

If callable is of type “array( $instance, ‘method’ )” output in the log should be TheInstanceClassName->method().

If callable is of type “array( ‘class’, ‘method’ )” output in the log should be class::method().

If callable is of type “‘func’” output in the log should be func().

If callable is an anonymous function output in the log should be something cute like ?() :slight_smile: and so on.

This helps a bit to keep track of what is going on in my system.

Use Reflection. ReflectionClass/Object first, this will catch __invoke and Closures. (Closures are classes with a classname of “{Closure}” if I remember correclty.} The can use ReflectionFunction.