method_exists() vs. is_callable()

One thing I often see when re-factoring PHP applications, is the improper use of the method_exists() function, and I think this needs a little bit of clarification.

Here is a typical example of what I’m talking about:


if (method_exists($object, 'SomeMethod'))
{
  $object->SomeMethod($this, TRUE);
}

The purpose of this code snippet is quite easy to understand (even if I don’t encourage to do this kind of not-very-OOP-stuff): having an object named “$object”, we try to know if it has a method named “SomeMethod”, if so, we call it, and provide some arguments to it.

Yes, but…

This code will probably run very well during all its lifetime, but what if the object’s method is not visible from the current scope (like… a private or protected method)? PHP’s method_exists() function does what it says: it checks if the provided class or object has a method named like the provided one, and returns TRUE if so, or FALSE if not, visibility is not questionned. So, if you provide a private or protected existing method name (being out of current scope) to method_exists(), you’ll get TRUE as the return value, and a nice “Fatal error: Call to private method…”, immediately terminating the current script execution.

The right tool for the right job

The real intent of the previous code snippet was in fact to know if the application could call a method on the object, from the current scope.

This is why (among other reasons) is_callable() is part of the PHP built-in functions.

How does it work?

is_callable() receives a callback as its first argument, which, in our case, consists of an array of two values: the first being an object (or a string holding a class name), and the second being a string holding a method name. is_callable() returns TRUE when the provided callback can be called from the current scope, or FALSE if not.


if (is_callable(array($object, 'SomeMethod')))
{
  $object->SomeMethod($this, TRUE);
}

Here is another snippet of code to illustrate the differences between method_exists() and is_callable() in action:

class Foo {
  public function PublicMethod() {}
  private function PrivateMethod() {}
  public static function PublicStaticMethod() {}
  private static function PrivateStaticMethod() {}
}

$foo = new Foo();

$callbacks = array(
  array($foo, 'PublicMethod'),
  array($foo, 'PrivateMethod'),
  array($foo, 'PublicStaticMethod'),
  array($foo, 'PrivateStaticMethod'),
  array('Foo', 'PublicMethod'),
  array('Foo', 'PrivateMethod'),
  array('Foo', 'PublicStaticMethod'),
  array('Foo', 'PrivateStaticMethod'),
);

foreach ($callbacks as $callback) {
  var_dump($callback);
  var_dump(method_exists($callback[0], $callback[1])); // 0: object / class name, 1: method name
  var_dump(is_callable($callback));
  echo str_repeat('-', 40), "n";
}

Run it, and you’ll see that every test returns TRUE with method_exists(), even private methods, while is_callable() returns FALSE for these (and will also trigger strict errors with non-static methods being queried as static ones, be aware of this).

More details

is_callable() has other uses, like checking the syntax of the provided callback, without checking if there really is a class or a method with the provided names.
Like method_exists(), is_callable() can trigger a class autoloading process if the provided class is not already loaded.
If an object has the magic __call() method implemented, then is_callable() will return TRUE for any non-existent method, while method_exists() will return FALSE. I guess the same behavior can be observed with the recent (PHP 5.3.0) __callStatic() magic method, but I did not test it (yet).
Everything else you need to know is in the PHP manual.

14 comments

  1. Pingback: abcphp.com
    1. i guess @mdomba statement is right based on the following extract from your post

      If an object has the magic __call() method implemented, then is_callable() will return TRUE for any non-existent method, while method_exists() will return FALSE.

  2. @mdomba, it depends on what you’re attempting.

    1. If you just want to make sure you can call it, use is_callable. If you need to make sure that the method exists by that name (meaning it’s not callable just through a magic method) then use both.

    2. If you just want to know if the method exists, not caring whether you can call it at that point, then method_exists alone is enough.

    3. If you want to call the method and you don’t care why or how it’s callable, but just that it is, then use is_callable alone.

    Just notice the purpose of each: is_callable tells you whether calling it will abort the script with an error, while method_exists tells you if that method was actually hard-coded in a particular class, not caring if it’s gonna explode in your face when you try to call it.

  3. Good article. It makes perfect sense using is_callable to check for static methods, yet all private method names should be prepended with an underscore anyhow…

  4. It would be better design to implement an interface. I shouldn’t have to guess whether a given object has a specific method, it should be defined by the class.

  5. @Baz, that is true, but there can easily occur circumstances where you have an object which you don’t know anything about. This could for example happen in a generic “merge two objects by map” function that attempts to copy property values from one object to another as specified by a map, and tries to make use of accessor/mutator methods on the objects.

  6. Good post. As someone who’s taught himself some PHP by picking apart existing scripts, I always wondered why people were using method_exist() in situations where it didn’t apply imo. I first assumed they knew something I didn’t. Guess they were wrong 🙂 (And I was wrong for assuming they were right)

Leave a Reply

Your email address will not be published. Required fields are marked *