我想知道处理必须在 PHP 脚本中“包含”如此多文件的问题的最佳实践是什么,以确保我的脚本可以访问我需要使用的所有类。

目前,我只是使用 包含一次 包括我直接访问的类。其中每一个都会 include_once 他们访问的课程。

我已经研究过使用 __autoload 函数,但是如果您打算将类文件组织在目录树中,那么帽子似乎不能很好地工作。如果您这样做,似乎您最终会遍历目录树,直到找到您正在寻找的类。 另外,我不确定这如何影响不同命名空间中具有相同名称的类。

有没有更简单的方法来处理这个问题?

或者 PHP 只是不适合“进取心” 类型的应用程序具有许多不同的对象,所有对象都位于单独的文件中,这些文件可以位于许多不同的目录中。

有帮助吗?

解决方案

我通常有我的应用程序 setup.php 包含所有核心类的文件(即框架和附带的库)。我的自定义类是使用目录布局图辅助的自动加载器加载的。

每次添加新类时,我都会运行命令行构建器脚本,该脚本扫描整个目录树以搜索模型类,然后构建关联数组,其中类名作为键,路径作为值。然后, __autoload 函数在该数组中查找类名并获取包含路径。这是代码:

自动构建.php

define('MAP', 'var/cache/autoload.map');
error_reporting(E_ALL);
require 'setup.php';
print(buildAutoloaderMap() . " classes mapped\n");

function buildAutoloaderMap() {
    $dirs = array('lib', 'view', 'model');
    $cache = array();
    $n = 0;
    foreach ($dirs as $dir) {
        foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $entry) {
            $fn = $entry->getFilename();
            if (!preg_match('/\.class\.php$/', $fn))
                continue;
            $c = str_replace('.class.php', '', $fn);
            if (!class_exists($c)) {
                $cache[$c] = ($pn = $entry->getPathname());
                ++$n;
            }
        }
    }
    ksort($cache);
    file_put_contents(MAP, serialize($cache));
    return $n;
}

自动加载.php

define('MAP', 'var/cache/autoload.map');

function __autoload($className) {
    static $map;
    $map or ($map = unserialize(file_get_contents(MAP)));
    $fn = array_key_exists($className, $map) ? $map[$className] : null;
    if ($fn and file_exists($fn)) {
        include $fn;
        unset($map[$className]);
    }
}

请注意,文件命名约定必须为 [class_name].class.php。更改将在其中查找类的目录 autobuild.php. 。当找不到类时,您还可以从自动加载函数运行自动构建器,但这可能会使您的程序陷入无限循环。

序列化数组速度非常快。

@杰森迈克尔:PHP 4 已经死了。克服它。

其他提示

您可以使用 spl_autoload_register 定义多个自动加载函数:

spl_autoload_register('load_controllers');
spl_autoload_register('load_models');

function load_models($class){
    if( !file_exists("models/$class.php") )
        return false;

    include "models/$class.php";
    return true;
}
function load_controllers($class){
    if( !file_exists("controllers/$class.php") )
        return false;

    include "controllers/$class.php";
    return true;
}

您还可以使用映射到物理目录的结构化命名约定以编程方式确定类文件的位置。这就是 Zend 的做法 Zend框架. 。所以当你打电话时 Zend_Loader::loadClass("Zend_Db_Table"); 它通过下划线分割将类名分解为一个目录数组,然后 Zend_Loader 类去加载所需的文件。

与所有 Zend 模块一样,我希望您可以仅将加载器与您自己的类一起使用,但我仅将其用作使用 Zend 的 MVC 的站点的一部分。

但是,当您使用任何类型的动态类加载时,都会担心负载下的性能,例如参见 这篇博文 将 Zend_Loader 与类文件的硬加载进行比较。

除了必须搜索 PHP 包含路径的性能损失之外,它还破坏了操作码缓存。来自对该帖子的评论:

当使用任何动态类加载器时,APC 无法完全缓存这些文件,因为它不确定将在任何单个请求上加载哪些文件。通过硬加载文件,APC 可以完整缓存它们。

__autoload 如果您的类有一致的命名约定,告诉函数它们在目录树中的位置,那么效果很好。MVC 特别适合这种事情,因为您可以轻松地将类拆分为模型、视图和控制器。

或者,将名称的关联数组保留到类的文件位置,并让 __autoload 查询这个数组。

到目前为止的建议中,我偏向凯文的,但不一定是绝对的。我看到有几个与 __autoload 一起使用的不同选项。

  1. 将所有类文件放入一个目录中。在类之后命名文件,即 classes/User.php 或者 classes/User.class.php.
  2. Kevin 的想法是将模型放入一个目录,将控制器放入另一个目录,等等。如果所有类都很好地适合 MVC 框架,那么效果很好,但有时,事情会变得混乱。
  3. 在类名中包含目录。例如,名为 Model_User 的类实际上位于 classes/Model/User.php. 。您的 __autoload 函数会知道将下划线转换为目录分隔符以查找文件。
  4. 只需解析一次整个目录结构即可。无论是在 __autoload 函数中,还是在定义它的同一个 PHP 文件中,循环遍历 classes 目录并缓存哪些文件在哪里。因此,如果您尝试加载 User 类,是否在中并不重要 classes/User.php 或者 classes/Models/User.php 或者 classes/Utility/User.php. 。一旦发现 User.php 某处在 classes 目录,它会知道要包含什么文件 User 类需要自动加载。

@凯文:

我只是想指出 spl_autoload_register 是 __autoload 的更好替代品,因为您可以定义多个加载器,并且它们不会相互冲突。如果您还必须包含定义 __autoload 函数的库,那么这会很方便。

你确定吗?这 文档 说法不同:

如果您的代码具有现有的 __autoload 函数,则必须在 __autoload 堆栈上显式注册该函数。这是因为 spl_autoload_register() 将通过 spl_autoload() 或 spl_autoload_call() 有效地替换 __autoload 函数的引擎缓存。

=> 你必须显式注册任何库 __autoload 以及。但除此之外,你当然是对的,这个函数是更好的选择。

__autoload 可以工作,但只能在 PHP 5 中使用。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top