3

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

目前,我只是使用include_once来包含我直接访问的类。每一个都是include_once他们访问的类。

我已经研究过使用该__autoload功能,但如果您打算将类文件组织在目录树中,帽子似乎效果不佳。如果你这样做了,看起来你最终会遍历目录树,直到找到你正在寻找的类。 此外,我不确定这如何影响不同名称空间中具有相同名称的类。

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

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

4

7 回答 7

6

在我的应用程序中,我通常拥有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。您也可以在找不到类时从自动加载函数运行自动构建器,但这可能会使您的程序陷入无限循环。

序列化数组非常快。

@JasonMichael:PHP 4 已经死了。克服它。

于 2008-08-23T09:37:32.237 回答
2

您可以使用 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;
}
于 2008-08-23T01:33:01.760 回答
1

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

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

但是当您使用任何类型的动态类加载时,在负载下的性能一直存在问题,例如,请参阅这篇博客文章,将 Zend_Loader 与类文件的硬加载进行比较。

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

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

于 2008-08-23T10:58:06.257 回答
0

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

或者,为您的类保留一个关联的名称数组到文件位置,然后__autoload查询这个数组。

于 2008-08-23T00:46:44.273 回答
0

__autoload可以,但只在 PHP 5 中。

于 2008-08-23T00:48:23.460 回答
0

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

  1. 将所有类文件放到一个目录中。以类命名文件,即classes/User.phpclasses/User.class.php.
  2. Kevin 将模型放入一个目录,将控制器放入另一个目录等的想法。如果您的所有类都很好地适合 MVC 框架,则效果很好,但有时,事情会变得混乱。
  3. 在类名中包含目录。例如,名为 Model_User 的类实际上位于classes/Model/User.php. 您的 __autoload 函数会知道将下划线转换为目录分隔符以查找文件。
  4. 只需解析整个目录结构一次。无论是在 __autoload 函数中,还是在定义它的同一个 PHP 文件中,循环遍历classes目录的内容并缓存文件所在的位置。因此,如果您尝试加载User该类,它是否在classes/User.phporclasses/Models/User.php或中都没有关系classes/Utility/User.php。一旦它User.php在目录中找到某个位置classes,它就会知道当User需要自动加载类时要包含哪个文件。
于 2008-08-23T02:34:35.497 回答
0

@凯文:

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

你确定吗?文档说的不同:

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

=> 您还必须显式注册任何库__autoload。但除此之外,你当然是对的,这个功能是更好的选择。

于 2008-08-23T08:14:10.610 回答