php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #53009 Weird behavior of method_exists with anonymous functions
Submitted: 2010-10-07 10:27 UTC Modified: 2010-10-08 02:19 UTC
From: acid24 at gmail dot com Assigned:
Status: Wont fix Package: Class/Object related
PHP Version: 5.3.3 OS: Ubuntu 10.04
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If this is not your bug, you can add a comment by following this link.
If this is your bug, but you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: acid24 at gmail dot com
New email:
PHP Version: OS:

 

 [2010-10-07 10:27 UTC] acid24 at gmail dot com
Description:
------------
method_exists() has a weird behavior when used on anonymous functions. One would expect that testing the variable that holds the anonymous function and the class of the anonymous function (Closure) to either have or not the __invoke method. Instead method_exists() reports that the variable that holds the anonymous function HAS an __invoke method and the class of the anonymous function HAS NOT an __invoke method. Is this behavior correct? If yes, maybe somebody can explain why. Thank you in advance.

Test script:
---------------
<?php

$lambda = function() {};

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

Expected result:
----------------
boolean(false)
boolean(false)

or 

boolean(true)
boolean(true)

Actual result:
--------------
boolean(true)
boolean(false)

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-10-07 10:44 UTC] acid24 at gmail dot com
-: acid24ro@gmail.com +: acid24 at gmail dot com
 [2010-10-07 10:44 UTC] acid24 at gmail dot com
provided wrong email address
 [2010-10-07 14:46 UTC] cataphract@php.net
In PHP, not all methods are created equal.

Normal classes have a structure wherein the declared methods are stored -- the class entry function table. method_exists returns true if a method exists in such table.

But there's another way to obtain methods -- through the get_method handler. See http://wiki.php.net/internals/engine/objects#get_method
method_exists(), if given an instance, calls this handler.

The handler table is associated to instances only; from the class entry, you cannot know which handler table would be used in instances of that class. 

Therefore, it's possible to have method_exists() return true if given an instance and false if given the class of that instance as a string.

In this case, there's a twist. method_exists() also returns false if the  function given by the get_method handler has the flag ZEND_ACC_CALL_VIA_HANDLER, which means the method was generated on-the-fly. This is the case with Closure's __invoke, but the implementation of method_exists has a special exception for the Closure class, and returns true there.

This could be "fixed" by removing the exception and returning false all the time, but this wouldn't fix the bigger issue -- that you can have method_exists returns true with an object and false with the class of the object. So I'd mark this Wont Fix, but let's see if someone thinks otherwise.
 [2010-10-08 02:19 UTC] cataphract@php.net
-Status: Open +Status: Wont fix
 [2010-10-08 09:04 UTC] acid24 at gmail dot com
Thank you for the clear explanation. 

For anyone interested I found that Reflection gives me the "correct" behavior.

<?php
$lambda = function() {};

$r = new ReflectionObject( $lambda );
var_dump( $r->hasMethod( '__invoke' ) );
$r = new ReflectionClass( get_class( $lambda ) );
var_dump( $r->hasMethod( '__invoke' ) );
?>

outputs

bool(false)
bool(false)
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Fri Mar 05 05:01:23 2021 UTC