如何在PHP中处理包含所需的类

时间:2008-08-23 00:43:18

标签: php class include autoload

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

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

我已经研究过使用__autoload函数了,但是如果你计划在目录树中组织你的类文件,那么帽子似乎不会很好用。如果你这样做了,看起来你最终会走到目录树,直到你找到了你正在寻找的课程。 另外,我不确定这会如何影响不同命名空间中具有相同名称的类。

有更简单的方法来解决这个问题吗?

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

7 个答案:

答案 0 :(得分:6)

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

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

<强> autobuild.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;
}

<强> autoload.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中查找。您也可以在找不到类时从autoload函数运行autobuilder,但这可能会使您的程序进入无限循环。

序列化数组非常快。

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

答案 1 :(得分: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;
}

答案 2 :(得分:1)

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

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

但是当您使用任何类型的动态类加载时,都会担心加载时的性能,例如,请参阅this blog post将Zend_Loader与类文件的硬加载进行比较。

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

  
    

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

  

答案 3 :(得分:0)

如果你的类具有一致的命名约定,那么

__autoload可以很好地告诉函数在目录树中找到它们的位置。 MVC非常适合这种事情,因为您可以轻松地将类拆分为模型,视图和控制器。

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

答案 4 :(得分:0)

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

答案 5 :(得分: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.phpclasses/Models/User.phpclasses/Utility/User.php中无关紧要。一旦在User.php目录中找到classes,它就会知道在User类需要自动加载时要包含的文件。

答案 6 :(得分:0)

@Kevin:

  

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

你确定吗? documentation的说法不同:

  

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

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