自动加载和多个目录

发布于 2024-08-03 09:20:03 字数 150 浏览 2 评论 0原文

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

你能建议吗 - 谢谢

I've just been looking at php's autoload() function. Seems a nice idea, but I'm not sure how it handles multiple directories. My current development basically has a library directory structure grouping classes into subdirectories by operation. I'm wondering I have to declare a include() for each directory ... which I really hope I don't have to do.

Can you advise - thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

北城孤痞 2024-08-10 09:20:03

您可能需要查看类名的 PEAR 约定,这对于自动加载来说真的很棒。

基本上,它指出:

PEAR 类层次结构也是
反映在类名中,每个
层次结构的级别由
一个下划线。

这意味着查找要包含类名 HTML_Upload_Error 的文件只需将 '_' 替换为 '/' 即可;为您提供 HTML/Upload/Error.php

有关更多说明和一些示例,您可以查看以下文章:

顺便说一句:许多大型框架/库都使用此约定;-)

例如,Zend Framework 使用此约定 - 它真的很有帮助!

You might want to take a look at the PEAR Convention for class names, which is really great for autoloading.

Basically, it states that :

The PEAR class hierarchy is also
reflected in the class name, each
level of the hierarchy separated with
a single underscore.

Which means finding the file to include for a classe name HTML_Upload_Error is just a matter of replacing '_' by '/' ; giving you HTML/Upload/Error.php

For more explanations, and a couple of examples, you can take a look at the articles :

BTW : this convention is used by many big Frameworks / libraries ;-)

For instance, Zend Framework uses this convention -- and it's really helpful !

|煩躁 2024-08-10 09:20:03

这是我不久前出于类似目的编写的一个课程。那个时候我还在学习阶段,所以可能会有一些愚蠢的想法;尽管如此,它仍然有效。

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

/* 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;
    }

}

Here is a class I wrote a while ago for a similar purpose. That time I was still in the learning phase, so there might be stupid ideas involved; it worked nevertheless.

The basic idea is that it scans the source directory once, and creates an array mapping classes to their source files. The class is registered as an autoloader, and when invoked, it includes the required file. If not found, it tries to rebuild the array on the fly.

/* 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;
    }

}
ゞ花落谁相伴 2024-08-10 09:20:03

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

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

Unfortunately, you do have to explicitly add each directory. This can either be done programmatically in a script that recursively traverses your directories, or you can specify a list.

Probably the most efficient way is to specify a list of directories and subdirectories to search, and add these to your 'include_path' using ini_set().

暮倦 2024-08-10 09:20:03

你看起来很困惑:)或者也许我对你的问题感到困惑。

完全由您编写一个定位和加载类的函数,PHP 不关心它的深度在哪里/有多少层。

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

You seem confused :) Or maybe I am confused by your question.

It is completely up to you to write a function that locates and loads the class, PHP does not care where/how many levels deep it is.

And, look into SPL autoload too, it has the same base functionality, but you can write multiple autoload functions and then chain them. Can be useful, if you want to use some external libraries, that define their own autoloaders that could possibly conflict with yours.

戏舞 2024-08-10 09:20:03

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

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

创建多个目录的一个好处是能够按使用顺序注册它们,从最常用的目录到最后最不使用的目录。另外,如果更改或删除目录,则只需更改和/或删除负责的函数即可。

您可以编写一个函数来遍历整个文件夹结构(尽管为了便于管理和代码解耦,我不推荐它)。没有“技术上正确”的方法来做到这一点:)

I assume you are talking about PHP's SPL autoload ability - where you write you're own function and then register it with the SPL.

How you do it depends on how you create your include function(s). It is possible to declare multiple include functions and then register them with PHP: how many is up to you. The SPL autoload ability simply allows you to create your own function and then tell PHP to run that function each time a class needs including.

One benefit of creating multiple is the ability to register them in their order of use, the most used directory first to the least used last. Also, if a directory is changed or deleted, then you simple change and/or delete the responsible function.

You can write one function that will go through you're entire folder structure too (though I wouldn't recommend it for ease of administration and code decoupling). There is no "technically right" way to do it :)

爱殇璃 2024-08-10 09:20:03

正如已经提到的,SPL 自动加载在功能上是一个结构,您必须将实际实现移植到该结构上 - 目录遍历和命名约定是这些考虑因素的一部分。

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

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”) ') 到目录映射中,然后加载并验证该类。

As mentioned already, SPL autoloading is functionally a structure onto which you have to graft practical implementation - directory traversal and naming conventions are part of those considerations.

Take a practical example in the form of the Zend Loader: at its basis, this is a singleton which uses a convention of correlating namespaces to directories that are registered with PHP's include path. Practical Example:

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.

Obviously specific implementation concerns will vary from project to project, but it may be best, both as an exercise in understanding and for code reuse, to try your hand at programming an autoloader that can parse a specific class format (e.g. 'directory_classname') into a directory map, then load and validate the class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文