【问题标题】:How to handle including needed classes in PHP如何处理在 PHP 中包含所需的类
【发布时间】:2010-09-06 15:09:58
【问题描述】:

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

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

我已经研究过使用__autoload 函数,但是如果你打算将你的类文件组织在一个目录树中,这似乎不太好用。如果你这样做了,看起来你最终会遍历目录树,直到找到你正在寻找的类。 另外,我不确定这会如何影响不同命名空间中同名的类。

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

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

【问题讨论】:

    标签: php class include autoload


    【解决方案1】:

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

    序列化数组非常快。

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

    【讨论】:

      【解决方案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;
      }
      

      【讨论】:

        【解决方案3】:

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

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

        但是当您使用任何类型的动态类加载时,在负载下的性能一直存在问题,例如,请参阅this blog post 比较 Zend_Loader 与类文件的硬加载。

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

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

        【讨论】:

          【解决方案4】:

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

          或者,为您的班级保留一个与文件位置相关的名称数组,并让__autoload 查询该数组。

          【讨论】:

            【解决方案5】:

            到目前为止的建议中,我偏爱 Kevin 的建议,但不一定是绝对的。我看到了几个与 __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 中并不重要。一旦它在classes 目录中的某处找到User.php,它就会知道在需要自动加载User 类时要包含哪个文件。

            【讨论】:

              【解决方案6】:

              @凯文:

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

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

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

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

              【讨论】:

                【解决方案7】:

                __autoload 可以工作,但只适用于 PHP 5。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-01-22
                  • 1970-01-01
                  • 2019-04-22
                  • 2012-05-24
                  • 1970-01-01
                  • 2017-08-08
                  • 2016-08-02
                  相关资源
                  最近更新 更多