2008-08-23 26 views
3

我想知道最好的做法是如何处理这个问题,必须在我的PHP脚本中包含这么多文件以确保我需要使用的所有类都是我的脚本可以访问。如何处理在PHP中包括所需的类

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

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

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

还是PHP只是不适应“enterprisey”类型的应用,有很多不同的对象全部位于单独的文件,可以在许多不同的目录中。

回答

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函数运行自动编译器,但这可能会导致程序陷入无限循环。

串行化阵列快速修补。

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

0

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

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

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; 
} 
0

的建议,到目前为止,我偏爱凯文的,但它不不需要绝对。我发现有几个不同的选项可以与__autoload一起使用。

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

@Kevin:

我只是想指出,spl_autoload_register是一个更好的选择,因为__autoload可以定义多个装载机,他们不会互相冲突。如果您必须包含定义__autoload函数的库,则很方便。

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

如果你的代码有一个现有的__autoload函数,那么这个函数必须显式注册在__autoload栈上。这是因为spl_autoload_register()将通过spl_autoload()或spl_autoload_call()有效地替代__autoload函数的引擎缓存。

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

1

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

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

但是,当您使用任何类型的动态类加载时,一直存在关于负载性能的担忧,例如,请参阅this blog post比较Zend_Loader与加载类文件的难度。

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

当使用任何动态类加载器APC不能完全缓存这些文件作为其不能确定哪些文件可以在任何单一的请求负载。通过硬加载文件,APC可以完全缓存它们。