Autoloader自动检测类的问题

时间:2012-10-12 13:02:14

标签: php autoload

我目前正在使用PHP编程我的框架(主要是出于教育原因,以及一种传递时间的方式)。我写了一个Autoloader,它自动扫描目录树并检测其中的所有类,并以下列格式创建一个数组:

Array(
    'MyAwesomeClass' => 'path/to/file/in/some/subdirectory/MyAwesomeClass.php',
    'MyOtherAwesomeClass' => 'path/to/file/MyOtherAwesomeClass.php'
     // ....
);

起初这很整洁。因为我不需要担心我把课程放在哪里。

但后来我发现这是非常资源密集的。所以我创建了一个缓存机制,索引被序列化并保存到文件中。这使PHP脚本的响应时间减少了至少50%。但它带来了一些问题。

在框架中的其他类中,我经常使用以下内容:

class_exists('MyAwesomeClassController');

问题是如果我添加了新类MyAwesomeClassController,则该类尚未在索引中,并且class_exists返回false。现在的问题是我每次向框架添加新类时都必须删除缓存文件(让自动加载器再次重新创建索引)。

我尝试的另一件事(有点hacky): 在Autoloader类中,我检查了是否使用class_exists()调用了自动加载方法。如果是这种情况,我重新创建索引以查看是否可以在某处找到所请求的类。但是这似乎也没有成功,因为有时使用一个主要不存在的类来调用class_exists,所以这与在每个请求上重新创建索引的基本相同。

任何解决方案?或者我是否必须完全重写我的Autoloader?

3 个答案:

答案 0 :(得分:1)

我前一段时间走这条路,得出了同样的结论:资源密集。你在这里真正做的是构建某种链接器。

我切换到命名空间和地图的组合。我将命名空间映射到一个文件夹,当调用自动加载器时,它会剥离命名空间,查找相应的文件夹,并包含该文件。

我的地图看起来像这样(我为格式选择xml):

<?xml version="1.0" encoding="UTF-8"?>
<autoloader>  
  <namespace id="Ganymedes\Core">/system/core</namespace>
  <namespace id="Ganymedes\Core\Debug">/system/core/debug</namespace>
</autoloader>  

类加载器:

  public function ClassLoader( $class )
  {    
    if( class_exists( $class, false ))
      return true;

    $classparts = explode( '\\', $class );
    $classfile = '/' . strtolower( array_pop( $classparts )) . '.php';
    $namespace = implode( '\\', $classparts );

    if( isset( $this->namespaces[$namespace] )) {
      foreach ( $this->namespaces[$namespace] as $path ) 
        if( is_readable( $path . $classfile ))
          include_once $path . $classfile;

      return true;
    }

    return false;
  }    

答案 1 :(得分:1)

也许我没有正确理解你要做的事情......但get_include_path / set_include_path__autoload($class)内置函数有什么问题?

编辑解释我的评论:

在“header.php”中(包含在每个脚本开头的文件):

set_include_path(get_include_path().PATH_SEPARATOR."my/first/directory"
                                   .PATH_SEPARATOR."my/second/directory"
                                   .PATH_SEPARATOR."this/is/another/directory"
                                   ...
                                   .PATH_SEPARATOR."and/here/is/the/last/one");

function __autoload($class) {
    require_once($class.".class.php");
}

自动加载功能会检查路径中的每个目录,直到找到所需的文件。我从来没有检查它的性能,但我猜它是相当微不足道的,除非你有100个目录。如果你有条理,你可以将你经常使用的(基础)类放在第一个“额外”路径中,以便立即找到它们。

我刚刚在PHP手册中注意到,不再建议使用__autoload,而应该使用spl_autoload_register,但概念是相同的。

答案 2 :(得分:1)

假设您尝试加载的类应始终存在,您的自动加载器可以执行以下操作:

  1. 在地图中查找类名(缓存)。如果存在,请加载文件。

  2. 如果它不存在,请执行另一次扫描并在新生成的地图中查找类名。如果存在,请将新地图写入光盘并加载文件。

  3. 如果它仍然不存在,请发出警报。

  4. 第三步也可以通过引入否定缓存来改进;如果该类存在于该映射中,则您知道该类基于先前的尝试不存在。负缓存会缩短,但只要足够长就不会丢弃您的服务器。

    您还可以考虑将脱机缓存作为部署系统的一部分进行脱机。