SitePoint Sponsor

User Tag List

Results 1 to 10 of 10
  1. #1
    SitePoint Addict
    Join Date
    Sep 2004
    Posts
    211
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    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:

    PHP 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 ReflectionClassget_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 Closure 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:

    PHP Code:
    var_dumpmethod_exists'__invoke'get_class$closure ) ) ); 
    where $closure contains an anonymous function would output

    Code:
    boolean(true);
    for some reason (at least on my ubuntu machine).

    Thanks in advance.

  2. #2
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    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".

  3. #3
    SitePoint Addict
    Join Date
    Sep 2004
    Posts
    211
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    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".
    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 ?() and so on.

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

  4. #4
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Hmm, so you're logging an execution trace? Wouldn't you be better off making use of debug_backtrace() ?

  5. #5
    SitePoint Addict
    Join Date
    Sep 2004
    Posts
    211
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    Wouldn't you be better off making use of debug_backtrace() ?
    For just getting an idea (at a more general level) of what's going on, no. For hardcore debugging, definitely.

  6. #6
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    988
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    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?

  7. #7
    SitePoint Addict
    Join Date
    Sep 2004
    Posts
    211
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by TomB View Post
    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?
    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:

    PHP Code:
    class {
        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 

  8. #8
    . shoooo... silver trophy logic_earth's Avatar
    Join Date
    Oct 2005
    Location
    CA
    Posts
    9,013
    Mentioned
    8 Post(s)
    Tagged
    0 Thread(s)
    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.
    Logic without the fatal effects.
    All code snippets are licensed under WTFPL.


  9. #9
    SitePoint Addict
    Join Date
    Sep 2004
    Posts
    211
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    Quote Originally Posted by logic_earth View Post
    Closures are classes with a classname of "{Closure}" if I remember correclty.
    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:

    PHP Code:
    $lambda = function() {};

    var_dumpmethod_exists$lambda'__invoke' ) );
    var_dumpmethod_existsget_class$lambda ), '__invoke' ) ); 
    outputs:

    Code:
    bool(true);
    bool(false);

  10. #10
    SitePoint Addict
    Join Date
    Sep 2004
    Posts
    211
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    Regarding
    Quote Originally Posted by acid24ro View Post
    Also, can anybody explain why:

    PHP Code:
    $lambda = function() {};

    var_dumpmethod_exists$lambda'__invoke' ) );
    var_dumpmethod_existsget_class$lambda ), '__invoke' ) ); 
    outputs:

    Code:
    bool(true);
    bool(false);
    if anyone is interested

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


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •