【问题标题】:How can I load classes from multiple directories with __autoload?如何使用 __autoload 从多个目录加载类?
【发布时间】:2011-12-04 12:17:55
【问题描述】:

跟进此question,似乎只需使用下面的__autoload 代码即可解决重复问题,

function __autoload($class_name) 
{
    include AP_SITE."classes_1/class_".$class_name.".php";
}

$connection = new database_pdo(DSN,DB_USER,DB_PASS);
var_dump($connection);

结果,

object(database_pdo)[1]
  protected 'connection' => 
    object(PDO)[2]

但是这只从一个目录加载类,其他目录呢?因为我将类分组在不同的目录中。所以如果我想从其他目录加载类,我会得到错误,

function __autoload($class_name) 
{
    include AP_SITE."classes_1/class_".$class_name.".php";
    include AP_SITE."classes_2/class_".$class_name.".php";
}

消息,

警告: 包括(C:/wamp/www/art_on_your_doorstep_2011_MVC/global/applications/CART/classes_2/class_database_pdo.php) [function.include]:无法打开流:没有这样的文件或目录 在...

指的是这一行 - include AP_SITE."classes_2/class_".$class_name.".php";

所以,我的问题是 - 如何使用 __autoload 从多个目录加载类?

一种可能的解决方案:

function autoload_class_multiple_directory($class_name) 
{

    # List all the class directories in the array.
    $array_paths = array(
        'classes_1/', 
        'classes_2/'
    );

    # Count the total item in the array.
    $total_paths = count($array_paths);

    # Set the class file name.
    $file_name = 'class_'.strtolower($class_name).'.php';

    # Loop the array.
    for ($i = 0; $i < $total_paths; $i++) 
    {
        if(file_exists(AP_SITE.$array_paths[$i].$file_name)) 
        {
            include_once AP_SITE.$array_paths[$i].$file_name;
        } 
    }
}

spl_autoload_register('autoload_class_multiple_directory');

【问题讨论】:

    标签: php oop pdo autoload


    【解决方案1】:

    您可以使用spl_autoload_register 而不是单个__autoload 函数来注册多个自动加载函数。这是推荐的方式。

    如果一个自动加载器能够加载文件,堆栈中的下一个将不会被调用。

    然而,每个自动加载器都应该只加载它所针对的类,因此您需要通过类名和/或is_file 进行检查。使用类名通常会更好,因为如果您的应用程序增长,在文件系统上疯狂尝试可能会给系统带来压力。

    为了不重新发明轮子,您甚至可以使用已经存在的自动加载器,它能够处理文件名调用的 PSR-0 标准。这些通常允许在基本目录上注册特定的命名空间。在您的情况下,这意味着您必须根据PSR-0 convention 重命名和组织您的文件。


    快速解决方案(与您的问题有关):

    function __autoload($class_name) 
    {
        $file = sprintf('%sclasses_1/class_%s.php', AP_SITE, $class_name);
        if (is_file($file))
        {
            include $file;
            return;
        }
        $file = sprintf('%sclasses_2/class_%s.php', AP_SITE, $class_name);
        if (is_file($file))
        {
            include $file;
            return;
        }
    }
    

    如您所见,已经有重复的代码(和您的一样)。因此,这应该只是一个临时解决方案,因为您最终会为要测试的每个目录获得越来越多的重复行。如果您考虑更改设计,请考虑 PSR-0 shema,它有助于简化代码库并便于重用 PHP 世界中的其他现有组件。


    function autoload_class_multiple_directory($class_name) 
    {
    
        # List all the class directories in the array.
        $array_paths = array(
            'classes_1/', 
            'classes_2/'
        );
    
        foreach($array_paths as $path)
        {
            $file = sprintf('%s%s/class_%s.php', AP_SITE, $path, $class_name);
            if(is_file($file)) 
            {
                include_once $file;
            } 
    
        }
    }
    
    spl_autoload_register('autoload_class_multiple_directory');
    

    【讨论】:

    • 感谢 hakre,是的,当我有很多目录时,我可以长期看到这个解决方案的问题。
    • 我想出了另一个想法 - 请参阅上面的编辑/可能的解决方案 :)
    • @lauthiamkok:是的,我之前也想过这个问题,但想留点空间。查看我之前想到的解决方案,我现在添加了它。请参阅foreach,如果您将文件名分配给变量 ($file),您可以更好地重复使用它。
    • 请问-%s%s%s 是什么意思?
    • 是 sprintf 中字符串值的占位符。还有更多,您可以在php.net/manual/en/function.sprintf.php 中阅读更多相关信息。我通常使用 sprintf 而不是在字符串中将变量合并在一起,因为它可以进行更细粒度的控制,并且您可以在格式化字符串之后看到所有变量作为它自己的参数。
    【解决方案2】:

    关注 PSR-0

    正确的做法是采用PSR-0 naming convention,然后使用兼容PSR-0的自动加载器,例如Symfony2的ClassLoader组件中的UniversalClassLoader

    例如:

    a/src/ProjectA/Database/Pdo.php

    <?php
    
    namespace ProjectA\Database;
    
    class Pdo
    {
        // your code
    }
    

    b/src/ProjectB/Mail/Smtp.php

    <?php
    
    namespace ProjectB\Mail;
    
    class Smtp
    {
        // your code
    }
    

    symfony ClassLoader 在vendor/symfony/src/Symfony/Component/ClassLoader

    autoload.php

    <?php
    
    require __DIR__.'/vendor/symfony/src/Symfony/Component/ClassLoader';
    
    use Symfony\Component\ClassLoader\UniversalClassLoader;
    
    $loader = new UniversalClassLoader();
    $loader->registerNamespace('ProjectA', __DIR__.'/a/src');
    $loader->registerNamespace('ProjectB', __DIR__.'/b/src');
    $loader->register();
    

    本质上,它的作用是使用spl_register_autoload 注册一个自动加载器,该自动加载器尝试将请求的类与所有注册的路径匹配,如果匹配,则它是必需的。否则自动加载器会继续搜索。

    所以你的引导代码所做的就是包含autoload.php,之后所有其他类都将被自动加载。

    【讨论】:

    • 谢谢。我提出了另一个想法 - 请参阅上面的编辑/可能的解决方案:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-02
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-01
    相关资源
    最近更新 更多