高效的PHP自动加载和命名策略

zom*_*bat 38 php naming-conventions spl-autoload-register

像大多数Web开发人员一样,我非常享受固态MVC架构对Web应用程序和站点的好处.使用PHP进行MVC时,自动加载显然非常方便.

我已成为spl_autoload_register过度简单定义单个__autoload()函数的粉丝,因为如果要合并每个使用自己的自动加载的不同基本模块,这显然会更灵活.但是,我从来没有对我写的加载函数感到满意.它们涉及大量字符串检查和目录扫描,以便查找可能加载的类.

举例来说,假设我有一个具有被定义为基本路径的应用程序PATH_APP和一个简单的结构,命名的目录models,viewscontrollers.我经常使用命名结构来命名文件IndexView.phpIndexController.php在相应的目录中,默认情况下模型通常没有特定的方案.我可能有一个这样的结构的加载器函数,注册时spl_autoload_register:

public function MVCLoader($class)
{
    if (file_exists(PATH_APP.'/models/'.$class.'.php')) {
        require_once(PATH_APP.'/models/'.$class.'.php');
        return true;
    }
    else if (strpos($class,'View') !== false) {
        if (file_exists(PATH_APP.'/views/'.$class.'.php')) {
            require_once(PATH_APP.'/views/'.$class.'.php');
            return true;
        }
    }
    else if (strpos($class,'Controller') !== false) {
        if (file_exists(PATH_APP.'/controllers/'.$class.'.php')) {
            require_once(PATH_APP.'/controllers/'.$class.'.php');
            return true;
        }
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

如果之后没有找到它,我可能有另一个函数来扫描models目录中的子目录.但是,所有if/else-ing,字符串检查和目录扫描对我来说似乎效率低下,我想改进它.

我很好奇其他开发人员可能采用的文件命名和自动加载策略.我正在寻找专门用于高效自动加载的良好技术,而不是自动加载的替代方案.

Mik*_*ers 28

这就是我在所有项目中使用的内容(直接从最后一个项目中提取):

public static function loadClass($class)
{
    $files = array(
        $class . '.php',
        str_replace('_', '/', $class) . '.php',
    );
    foreach (explode(PATH_SEPARATOR, ini_get('include_path')) as $base_path)
    {
        foreach ($files as $file)
        {
            $path = "$base_path/$file";
            if (file_exists($path) && is_readable($path))
            {
                include_once $path;
                return;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我查找SomeClass_SeperatedWith_Underscores,它将查找SomeClass_SeperatedWith_Underscores.php,然后查找以当前包含路径中每个目录为根的SomeClass/SeperatedWith/Underscores.php.

编辑:我只是想把它用于开发效率,而不一定是处理时间.如果您的路径上有PEAR,那么您可以使用这些类,而不必在需要时包含它们.

我倾向于将我的类保存在目录层次结构中,下划线打破了命名空间...这段代码让我保持文件结构好看和整洁如果我想要,或者如果我想要注入一个没有嵌套目录的快速类文件(对于将一个或两个类添加到它被告的库中,但不是我目前正在处理的项目的一部分.)

  • 在`$ files`数组周围包装`array_unique()`.如果类名中没有下划线,则每次尝试每个文件两次. (3认同)

gro*_*gel 13

我找到了这个解决方案:

我创建了一个遍历我的类库文件夹(包含单独模块/系统的子文件夹)的脚本,并解析文件内容以查找类定义.如果它在php文件中找到一个类定义(非常简单的正则表达式模式),它会创建一个符号链接:

class_name.php -> actual/source/file.php
Run Code Online (Sandbox Code Playgroud)

这让我可以使用一个简单的自动加载函数,它只需要类名和主符号链接文件夹的路径,而不必进行任何路径/字符串操作.

最好的部分是我可以完全重新安排我的源代码或添加一个新的子系统,只需运行链接生成脚本就可以自动加载所有内容.

  • 仅供参考,您可以使用`mklink`在Windows中创建符号链接:http://www.howtogeek.com/howto/windows-vista/using-symlinks-in-windows-vista/ (5认同)
  • 自从我开始使用linux以来,我对Windows的一个主要抱怨就是缺少符号链接.据我所知,此解决方案仅适用于unix. (4认同)

jmu*_*llo 8

如果您想要效率,那么您根本不应该使用自动加载功能.自动加载功能是懒惰的.当您包含文件时,您应该提供包含文件的显式路径.如果您的自动加载功能可以找到这些文件,那么您可以编写代码以明确地找到它们.当您处理代码的视图部分并且即将加载新的视图类时,通过让自动加载功能处理它,它首先假定您的类是模型类?那效率很低.相反,您的代码应该是:

include_once $this->views_path . $class . '.php';
Run Code Online (Sandbox Code Playgroud)

如果您需要多个"视图"路径,请创建一个加载视图的函数:

public function load_view($class) {
    // perhaps there's a mapping here instead....
    foreach ($this->views_paths as $path) {
        $filename = $path . $class . '.php';
        if (file_exists($filename)) {
            include_once $filename;
        }
    }
    throw ....
}
Run Code Online (Sandbox Code Playgroud)

在任何情况下,在包含发生的位置,您都可以获得有关要加载的类的最大/最准确的信息.使用该信息完全加载类是唯一有效的类加载策略.是的,你可能会得到更多的类变量或(天堂禁止)一些全局变量.但这是一个更好的权衡,而不仅仅是懒惰并为您的班级扫描部分文件系统.

  • 虽然你认为直接加载是最有效的整体,但它使代码难以维护.如果更改类或文件的名称怎么办?或者说我有动态视图片段可以由控制器加载,随着项目的继续,越来越多的视图类被创建.每次我创建一个视图类时,我都不想返回并修改控制器以手动将其包含在可能使用的任何地方.我同意自动加载比直接加载效率低,但我正在寻找最有效的自动加载. (10认同)
  • 如果你必须在生成后更改类的名称,那么在编写代码之前,你没有花足够的时间进行设计.如果效率很重要,那么预先花费更多时间可以节省更多的时间来维护,而不是懒惰的,不要考虑像autoload那样的所有功能. (3认同)
  • 那么你的load_view函数与自动加载有什么不同呢?这正是autoload的用途. (2认同)
  • 与隐式操作相比,我更喜欢显式操作。另外,在文档页面上有一些需要自动加载的警告,例如“注意:__autoload函数中引发的异常无法在catch块中捕获,并导致致命错误。” 健壮的软件不会忽略(或在这种情况下是掩盖并死掉)异常。 (2认同)
  • @Supericy仅仅因为在开发过程中应该捕获某些东西并不意味着它会或不会在野外发生。您并不总是完全控制安装环境,因此您不应该依赖它来避免失败。 (2认同)