Php自动加载和自动实例化所有类

时间:2014-11-29 21:04:09

标签: php class

我使用php spl_autoload_extensions自动加载所有类。我的问题是,是否可以同时实例化类,所以我不必在之后手动完成。

这是我的引导类:

  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);
              }
          }
      }
  }

现在函数autoLoad()中的任何地方都可以使用类似的东西:

new()ClassName

每当我需要一个类的实例来调用它时:

Bootstrap::ClassName()->someFunction()

1 个答案:

答案 0 :(得分:2)

一次实例化一切会破坏自动加载器的目的,因为它应该只加载你需要的东西,而不是一切。

理想情况下,您希望将其设置为当您尝试检索实例时,如果它不存在,请将其加载并返回。如果确实存在,请检索并返回。您可以使用magic method __callStatic,以便将每个可能的类视为Bootstrap的函数。我们可以将所有检索到的实例存储为private static Bootstrap属性。您可以使用new关键字call a class from a string,但如果要传递任意数量的参数,则需要使用ReflectionClass

这是基本的想法:

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;
    }

}

以下是一些示例类,可以看到它的实际效果:

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";
    }

}

<强>样本

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

注意:除此之外,还有一些问题:它没有尝试实例化不存在的类的任何异常处理。您也无法在bootstrap中使用与类同名的另一个方法名称。类名不区分大小写,因此Bootstrap::a()将引用与Bootstrap::A()不同的实例。根据您的需要,您应该能够根据需要自行为所有这些案例添加适当的处理。