Monday, August 1, 2011

php invoke method

First of all, I'm not suggesting using this magic method in serious projects. I simply want to investigate this interesting method and see what it can do.

PHP 5.3 introduced this '__invoke' magic method. From PHP manual, "The __invoke method is called when a script tries to call an object as a function". Let's check this code:

class FirstClassFunction
{
                public function __invoke()
                {
                                var_dump(func_get_args());
                                var_dump("i was called");
                }
}

$f = new FirstClassFunction();
$f(1,2,3);

$callback = $f;
$callback('one','two', 'three');

call_user_func($callback);

Amazing feature, isn't it? It enables PHP to accommodate pseudo-first-class functions, somehow. We can probably also use this feature for tasks like passing a function around (a little like anonymous function?).

In the world of PHP, it seems wierd that something can be an object and a function at the same time. But this couldn't be more familiar to a javascript developer. In javascript, function is first-class object. But let's go back to PHP's OO system. Usually, when we try to define or model a class, the first thing to do is define this class's role, its responsibilities and jobs in business domain. So usually a domain class represents something. But quite often, we also write some classes that don't represent anything. A Utility class is a typical example. A Utility class usually contains some static helper functions. It is totally fine that we don't wrap these functions into a class but just use them as normal procedure functions. Check this code:

//put an escape function into Utility class
$text = Utility::escape($text);

//or we can do it the other way
//we put all helper functions into helpers.php
require 'helpers.php';
$text = escape($text);

For this example, Utility is simply a namespace for helper functions. Also, if we want to utilise autoloading instead of 'require', we need to put helper functions into a class. I personally would prefer wrapping everything into classes. But it is fine if some developers just want to use these kind of functions in procedure way. If I'm not wrong, CodeIgniter impelment its helper functions as procedure functions, although i'm not familiar with CodeIgniter.

Ok, Utility class does not represent anything but simply provides a group of helper functions. We also have some classes that are suppose to do only single job. Sometimes, these classes are named with verbs like a Login class, or sometimes not verbs but obviouslly some kind of action executer like a Router class. A Router class should only do route and a Login class should only do login, nothing else. Usually, what we do is we create some default methods for these classes. Say for example, run(), exec(), whatever and the code is like:

$login = new Login();
$login->run();
$login->exec();
$login->login();

Now, with the __invoke method, we can do:
$login = new Login();
$login();

From the readability perspective, is it worse than using default methods? I think it depends on different developers. Some might think __invoke is a nice feature but others might think it is a huge headache.

But, as i stated in the beginning, i'm not suggesting using it in serious projects. Every magic method does bring some costs and confusions and __invoke is not excluded. One issue with __invoke is if your object is a result of a function call, you have to save it into a variable first. This won't work: ObjectMaker::factory()(). You have to do $obj = ObjectMaker::factory(); $obj(); Another issue is, in my opinion, it does somehow make the code less clean and confusing to PHP developers. People will think $obj() is a function and don't know that it is actually an object.

No comments: