1

I'm using php spl_autoload_extensions to auto load all classes. My question is, is it possible to instantiate classes at the same time, so I will not have to do it manually afterwards.

Here is my bootstrap class:

  class Bootstrap
  {
      private static $__loader;


      private function __construct()
      {
          spl_autoload_register(array($this, 'autoLoad'));
      }

      public static function init()
      {
          if (self::$__loader == null) {
              self::$__loader = new self();
          }

          return self::$__loader;
      }


      public function autoLoad($class)
      {
          $exts = array('.php', '.class.php');

          spl_autoload_extensions("'" . implode(',', $exts) . "'");
          set_include_path(get_include_path() . PATH_SEPARATOR . BASE);

          foreach ($exts as $ext) {
              if (is_readable($path = BASE . strtolower($class . $ext))) {
                  require_once $path;
                  return true;
              }
          }
          self::recursiveAutoLoad($class, BASE);
      }

      private static function recursiveAutoLoad($class, $path)
      {
          if (is_dir($path)) {
              if (($handle = opendir($path)) !== false) {
                  while (($resource = readdir($handle)) !== false) {
                      if (($resource == '..') or ($resource == '.')) {
                          continue;
                      }

                      if (is_dir($dir = $path . DS . $resource)) {
                          continue;
                      } else
                          if (is_readable($file = $path . DS . $resource)) {
                              require_once $file;
                          }
                  }
                  closedir($handle);
              }
          }
      }
  }

Now anywhere within function autoLoad() can I use something like:

new () ClassName

Than whenever I need an instance of a class to call it like:

Bootstrap::ClassName()->someFunction()
user3176519
  • 393
  • 1
  • 9
  • 16

1 Answers1

2

Instantiating everything at once kind of defeats the purpose of the autoloader, in that it should only load what you need, not everything.

So ideally, you want to set it up so that when you attempt to retrieve an instance, if it does not exist, load it up and return it. If it does exist, retrieve it and return it. You can use the magic method __callStatic so that each possible class will be considered a function of Bootstrap. We can store all of the retrieved instances as a private static property of Bootstrap. You can use the new keyword to call a class from a string, but you need to use a ReflectionClass if you want to pass an arbitrary number of arguments.

Here is the basic idea:

class Bootstrap {

    private static $instances = array();

    public static function __callStatic($name, $args) {

        if (!in_array($name, array_keys(self::$instances))) {
            //then we must make a new instance

            //check if we have arguments
            //for the constructor    
            if (!empty($args)) {
                //then we need reflection to instantiate
                //with an arbitrary number of args
                $rc = new ReflectionClass($name);
                $instance = $rc->newInstanceArgs($args);
            } else {

                //then we do not need reflection,
                //since the new keyword will accept a string in a variable
                $instance = new $name();
            }
            //and finally add it to the list
            self::$instances[$name] = $instance;
        } else {
            //then we already have one
            $instance = self::$instances[$name];
        }
        return $instance;
    }

}

Here are some sample classes to see it in action:

class A {

    function helloWorld() {
        return "class " . __CLASS__;
    }

}

class B {

    function helloWorld() {
        return "class " . __CLASS__;
    }

}

class C {

    public function __construct($name) {
        $this->name = $name;
    }

    function helloWorld() {
        return "class " . __CLASS__;
    }

    public function name() {
        return "my name is $this->name";
    }

}

DEMO

echo Bootstrap::a()->helloWorld(); //class A
echo Bootstrap::b()->helloWorld(); //class B
echo Bootstrap::c('Charles')->helloWorld(); //class C
echo Bootstrap::c()->name(); //my name is Charles

NOTE: Among other things, some issues with it: It doesn't have any exception handling for trying to instantiate a non existent class. You also wouldn't be able to have another method name in bootstrap with the same name as a class. And class names are not case sensitive, so Bootstrap::a() would refer to a different instance than Bootstrap::A(). Depending on your needs, you should be able to add in proper handling for all of these cases on your own as needed.

Community
  • 1
  • 1
chiliNUT
  • 18,989
  • 14
  • 66
  • 106
  • Thanks for your detailed example. The only reason I want to load bunch of classes, is that I will always need those through out the framework. Such as session class, logged in user info, validation etc.. – user3176519 Nov 30 '14 at 00:01
  • this should still accomplish that; they will always be there when needed, and they will not get loaded in those situations, however rare they might be, where you don't need them – chiliNUT Nov 30 '14 at 00:11
  • @user3176519 I get that you want all of that stuff available at the top of the page, but in general you shouldnt instantiate something until you are going to use it – chiliNUT Dec 01 '14 at 19:23