How is the performance of magic methods
(__call, __get, __set, …)?

Compared to direct method calls: bad. Here a short benchmark and the code I wrote for this benchmark (needs PHP 5.3):

Method Call: 3.01 seconds
Direct Access: 2.47 seconds
Magic Method (__call) 5.32 seconds
Magic Method (__get) 5.37 seconds
define('ITERATIONS', 2000000);
    function doBenchmark($name, Closure $closure){
        $start = microtime(true);
        for ($i=0; $i < ITERATIONS; ++$i) {
            $closure();
        }
        $stop = microtime(true);
        echo "Test $name: " . ($stop - $start) . " seconds";
    }

    class Test{
        public $data = array("view" => "view", "test2" => "test2", "test3" =>"test3");

        public function getView(){
            return $this->data['view'];
        }

        public function __call($name, $arg){
           return $name=="_getView" ? $this->data['view'] : false;
        }

        public function __get($key){
            return array_key_exists($key, $this->data) ? $this->data[$key] : null;
        }
    }

    $t = new Test();
    doBenchmark("Methode Call", function(){
        global $t;
        $v = $t->getView();
    });

    doBenchmark("Direct Access", function(){
        global $t;
        $v = $t->data['view'];
    });

    doBenchmark("Magic Methode (__call)", function(){
        global $t;
        $v = $t->_getView();
    });

    doBenchmark("Magic Methode (__get)", function(){
        global $t;
        $v = $t->view;
    });

So, you get the picure ;)

Don’t use magic methods as a replacement for a regular method if a direct access to a field or variable is possible. Magic methods are the sugar for dynamic created attributes (eg. for database objects) or if you don’t know the method/attribute while writing the code.

Use magic methods only rarely.

The only special magic method you should use often: __autoload ;-) It helps to load your needed classes on demand only when they are needed. If you use namespaces, here a short trick to load the name-mangled classes (the $path must of course be changed to your needs):

 

function __autoload($class) {
    $parts = explode('\\', $class);
    $class = end($parts);
    $path = __DIR__ . "/includes/"); //path for the includes

     require ($path . $class . ".php");
}

Happy coding ;-)

Share →

Leave a Reply