__get(): An Alternative to __autoload()

  • Posted by Mike Naberezny in PHP

    __autoload() is a magic function introduced in PHP 5 that provides a mechanism for on-demand loading of classes. After its inclusion in PHP, many argued that using such a feature is too magical or not a good design practice. Putting the religious debates over the appropriateness of __autoload() aside, its implementation does have one significant drawback: it is a function declared in the global scope. Once a function is declared, it cannot be redeclared. This means __autoload() can’t be used effectively in shared libraries, since any other code could have already declared it.

    Similar lazy-load functionality can be achieved on the class level by using __get() as shown in this example:

    class Test {
      public function __get($offset) {
          switch ($offset) {
              case 'obj':
                  // include() and instantiate $obj here or elsewhere.
                  // Set $this->obj so we never hit __get() again.
                  return $this->obj = $obj;
              default:
                  throw new Exception("Unknown property $offset");
          }
      }
      public function testLazyLoad() {
          $this->obj->test();
      }
    }
    

    In the code above, $instance->obj is initially unset. When testLazyLoad() first tries to access it, __get() is called and the object is then loaded and instantiated. Before returning, $instance->obj is set, so subsequent accesses won’t have the overhead of the __get() call. This behavior is somewhat analogous to that of __autoload().

    A downside is that $instance->obj will have public visibility, which may or may not be a problem depending on your application of the technique. Perhaps a small advantage is that while exceptions cannot be thrown from __autoload(), they can be thrown from __get().

    Update: A few people wrote in about spl_autoload(). I did not mention it here because it is only available since PHP 5.1 and thus is not widely available yet. However, if this is an option for you, it’s certainly worth considering.