New magic methods are awesome

2009 June 1
by monzee

The past couple days, I worked again on the little Sinatra-like DSL thing I mentioned before. It makes use of the new PHP 5.3 features. The ones I really love are the new magic functions __invoke and __callStatic. Invoke allows you to call an object instance like a function, e.g.

$front = Front::getInstance();
$front('/')->get(function () { echo 'hello world!'; });
// this is how my little DSL project is invoked at the moment.

and __callStatic allows you to catch all undefined static method calls, which is very handy for singletons.

class Front {
    // this is a singleton
    static public function __callStatic($method, $args)
    {
        return call_user_func_array(array(static::getInstance(), $method), $args);
    }
}
Front::setParam('foo', 'bar'); // this is the same as Front::getInstance()->setParam('foo', 'bar');

(Update: meh, I spoke too soon. The example above doesn’t pass through __callStatic if there is an instance method setParam. It works because PHP allows instance methods to be called non-statically along with a strict notice. Note to self: test first before making claims.)

In addition to singletons, __callStatic could be used in ORM or table gateway finder methods.

$user = User::findByEmail('foo@example.com');
// would return a row object
// whereas before you need to instantiate a User first before finding.

But you know what would really be nice? A combination of the two.

class Hello {
    static public function __invokeStatic($name)
    {
        echo 'Hello, ' . $name . '!';
    }
}
Hello('mon'); // Hello, mon!

It might look retarded because you could just define a Hello($name) function and accomplish the same. The reason I think this feature is reasonable is the inability to import and alias namespace functions with the use keyword. For example,

namespace greet {

class Hello {
    static public function __invokeStatic($name) {
        echo 'Hello, ' . $name . '!';
    }
}

function hi($name) {
    echo 'Hi, ' . $name . '!';
}

}

namespace somethingElse {
    use greet\Hello as hey; // you cant use greet\hi

    hey('mon'); // Hello, mon!
    \greet\hi('mon'); // Hi, mon!
}

How I wish this were possible. I dislike the namespace separator (although a lot less now than before) and I’d like to limit the use of it in the use and namespace declaration only. Also, it is easier to maintain state with a static class function rather than a namespace function because namespace variables are actually globals. These two reasons really make namespace functions undesirable for me, and I imagine others would find the same once 5.3 becomes prevalent.

No comments yet

Leave a Reply

Note: You can use basic XHTML in your comments. Your email address will never be published.

Subscribe to this comment feed via RSS