【问题标题】:Problems with Autoloader which autodetects classes自动检测类的 Autoloader 问题
【发布时间】:2012-10-03 07:20:12
【问题描述】:

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

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。现在的问题是每次向框架添加新类时,我都必须删除缓存文件(让自动加载器重新创建索引)。

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

有什么解决办法吗?还是我必须完全重写我的 Autoloader?

【问题讨论】:

  • 移动班级时会发生什么?如何使缓存失效?
  • 如果我将一个类移动到另一个目录,则需要重新创建索引并且缓存将无效。如果添加或删除类,缓存也将无效。
  • 您尝试做的事情已经存在!看看:github.com/theseer/Autoload。您可以构建一个 autoload.php 文件。使用 ANT 很容易将其集成到您的部署中......
  • @MarkusI。我一定会仔细看看。
  • @MarkusI。我看了一眼。这并不能解决我的问题。如果我添加一个类,我必须运行$ phpab。这基本上与我执行$ rm my-cache-file 相同

标签: php autoload


【解决方案1】:

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

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

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

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

第三步也可以通过引入负缓存来改进;如果该类存在于该地图中,则根据先前的尝试,您知道该类不存在。负缓存的寿命会更短,但足够长,不会破坏您的服务器。

您还可以考虑在部署系统中离线创建缓存。

【讨论】:

  • 我已经尝试了您在此处建议的 3 个步骤,但负面清单将是一个相对较好的解决方案。谢谢你:-)
  • 在考虑更多之后,负缓存仍然无法解决我的问题。如果创建了一个在负缓存中的类,它不会重新创建索引。这种情况有时会发生......
  • @mogria 负缓存的刷新率会更短,而且它的寿命很短,因为它是一个错误,对吧?所以你会很快解决它,对吧? :)
【解决方案2】:

也许我没有正确理解您要做什么...但是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,但概念是一样的。

【讨论】:

  • 我不认为你理解它。我在自动加载时遇到问题,因为它要么太慢,要么不方便。
  • 看来我当时完全没看懂……如果你不介意我问,你能解释一下吗?我上面写的似乎对我来说很好......我设置了包含路径以包含我的目录结构,并使用自动加载功能来加载类。我没有看到任何特别的缓慢,一旦我设置了包含路径,我不需要做任何其他事情。
  • 包含路径不是问题。我有一个包含 PHP 类的相当大的文件夹结构。我的自动加载器递归地扫描这个文件夹并将找到的所有类放入一个数组中,因此如果以后需要一个类,自动加载器知道确切的位置,并且自动加载器只需要包含该文件。扫描资源非常密集,所以我尝试缓存类的位置。但这并不好。
  • 嗯……你肯定把我弄糊涂了。我这样做:我更改包含路径以包含我的目录结构,并在我的脚本开头创建一个函数 __autoload($class)。每次需要一个类时,都会使用类名自动调用 __autoload() 函数,然后加载该类。只要您注意不要重复类名,它似乎就可以正常工作。只要目录结构在包含路径中,您就不会错过任何课程。我从未觉得有必要更改此设置,而且我的框架中有数百个类,分布在许多文件夹中
  • 你的 __autoload 函数从哪里知道请求的类在哪个文件夹中?
【解决方案3】:

我前段时间走这条路,得出了同样的结论:资源密集型。您实际上在这里所做的是构建某种链接器。

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

我的地图是这样的(我选择 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;
  }    

【讨论】:

  • 问题是我没有在我的项目中使用很多命名空间来保持调用简短和简单。但这是有可能的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-29
  • 2011-03-20
  • 2022-06-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多