自动加载和多个目录

时间:2009-08-31 19:00:42

标签: php autoload

我一直在看php的autoload()函数。似乎是一个好主意,但我不确定它如何处理多个目录。我当前的开发基本上有一个库目录结构,通过操作将类分组到子目录中。我想知道我必须为每个目录声明一个include()...我真的希望我不必这样做。

你能建议 - 谢谢

6 个答案:

答案 0 :(得分:17)

您可能需要查看PEAR Convention的类名,这对于自动加载非常有用。

基本上,它声明:

  

PEAR类层次结构也是   每个都反映在班级名称中   用以下分隔的层次结构的级别   一个下划线。

这意味着找到要包含在classe名称HTML_Upload_Error中的文件只需要用'/'替换'_';给你HTML/Upload/Error.php

有关更多解释和几个示例,您可以查看文章:

BTW:这个惯例被许多大型框架/图书馆使用;-)
例如,Zend Framework使用这个约定 - 它真的很有帮助!

答案 1 :(得分:10)

这是我前一段时间为了类似目的而写的课程。那个时候我还处于学习阶段,所以可能会有愚蠢的想法;尽管如此。

基本思想是它扫描源目录一次,并创建一个数组映射类到其源文件。该类被注册为自动加载器,并在调用时包含所需的文件。如果没有找到,它会尝试动态重建阵列。

/* register ClassLoader as class loader */
spl_autoload_register(array(ClassLoader::getInstance(), 'loadClass'));


class ClassLoader {

    private static $SAVE_FILE = 'ClassLoader.save.php';

    /* singleton */
    private static $instance;

    /* stores a className -> filePath map */
    private $classList;
    /* tells whether working from saved file */
    private $refreshed;


    public static function getInstance() {
        if (!isset(self::$instance)) {
            self::$instance = new ClassLoader();
        }
        return self::$instance;
    }

    private function __construct() {
        $this->initClassList();
    }

    public function loadClass($className) {
        if ( !array_key_exists($className, $this->classList) && !$this->refreshed ) {
            $this->refreshClassList();
        }
        require_once($this->classList[$className]);
    }

    private function initClassList() {
        if (file_exists(INCLUDES_DIR . self::$SAVE_FILE)) {
            require_once(INCLUDES_DIR . self::$SAVE_FILE);
            $this->refreshed = FALSE;
        } else {
            $this->refreshClassList();
        } 
    }

    private function refreshClassList() {
        $this->classList = $this->scanDirectory(INCLUDES_DIR);
        $this->refreshed = TRUE;

        $this->saveClassList();
    }

    private function saveClassList() {
        $handle = fopen(INCLUDES_DIR . self::$SAVE_FILE, 'w');
        fwrite($handle, "<?php\r\n");

        foreach($this->classList as $class => $path) {
            $line = '$this->classList' . "['" . $class . "'] = '" . $path . "';\r\n";
            fwrite($handle, $line);
        }

        fwrite($handle, '?>');
        fclose($handle);
    }

    private function scanDirectory ($directory) {
        // strip closing '/'
        if (substr($directory, -1) == '/') {
            $directory = substr($directory, 0, -1);
        }

        if (!file_exists($directory) || !is_dir($directory) || !is_readable($directory)) {
            return array();
        }

        $dirH = opendir($directory);
        $scanRes = array();

        while(($file = readdir($dirH)) !== FALSE) {

            // skip pointers
            if ( strcmp($file , '.') == 0 || strcmp($file , '..') == 0) {
                continue;
            }

            $path = $directory . '/' . $file;

            if (!is_readable($path)) {
                continue;
            }

            // recursion
            if (is_dir($path)) {
                $scanRes = array_merge($scanRes, $this->scanDirectory($path));

            } elseif (is_file($path)) {
                $className = explode('.', $file);
                if ( strcmp($className[1], 'class') == 0 && strcmp($className[2], 'php') == 0 ) {
                    $scanRes[$className[0]] = $path; 
                }
            }
        }

        return $scanRes;
    }

}

答案 2 :(得分:1)

不幸的是,您必须明确添加每个目录。这可以通过以递归方式遍历目录的脚本以编程方式完成,也可以指定列表。

最有效的方法可能是指定要搜索的目录和子目录列表,并使用ini_set()将它们添加到“include_path”。

答案 3 :(得分:1)

你似乎很困惑:)或许我对你的问题感到困惑。

完全取决于你编写一个定位和加载类的函数,PHP不关心它的深度/深度。

并且,查看SPL autoload too,它具有相同的基本功能,但您可以编写多个自动加载功能,然后将它们链接起来。如果你想使用一些外部库来定义他们自己的自动加载器可能会与你的自动加载器冲突,这可能很有用。

答案 4 :(得分:1)

我假设您正在讨论PHP的SPL自动加载功能 - 您可以在其中编写自己的功能,然后将其注册到SPL。

如何操作取决于您如何创建包含功能。可以声明多个包含函数,然后使用PHP注册它们:有多少取决于您。 SPL自动加载功能只允许您创建自己的函数,然后告诉PHP在每次类需要时运行该函数。

创建多个的一个好处是能够按照使用顺序注册它们,最常用的目录是最后使用最少的目录。此外,如果更改或删除目录,则只需更改和/或删除负责的功能。

您可以编写一个函数来完成整个文件夹结构(尽管我不建议使用它来简化管理和代码解耦)。没有“技术上正确”的方法:)

答案 5 :(得分:1)

如前所述,SPL自动加载在功能上是一个必须在其上实现实际实现的结构 - 目录遍历和命名约定是这些考虑的一部分。

以Zend Loader的形式提供一个实际示例:在它的基础上,这是一个单例,它使用将命名空间关联到使用PHP的include路径注册的目录的约定。实际例子:

set_include_path(get_include_path(). PATH_SEPARATOR. 'App/'); //Concat the "App" directory onto the existing include paths
$loader = Zend_Loader::getInstance(); //because the autoloader is a singleton, we get a reference to it without assuming we need to first create it
$loader->registerNamespace('App_'); //Tell autoloader it can look in the app directory to find classes if it can't find them in the default Zend directory.

显然,具体的实现问题因项目而异,但最好是作为理解和代码重用的练习,尝试编写可以解析特定类格式的自动加载器(例如'directory_classname) ')进入目录映射,然后加载并验证该类。