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

发布于 2024-07-17 14:26:16 字数 1481 浏览 11 评论 0原文

像当今大多数 Web 开发人员一样,我非常享受 Web 应用程序和网站的可靠 MVC 架构的好处。 当使用 PHP 进行 MVC 时,自动加载显然非常方便。

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

例如,假设我有一个应用程序,其基本路径定义为 PATH_APP,以及一个简单的结构,其中包含名为 modelsviews控制器。 我经常采用一种命名结构,其中文件在适当的目录中被命名为 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;
}

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

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

Like most web developers these days, I'm thoroughly enjoying the benefits of solid MVC architecture for web apps and sites. When doing MVC with PHP, autoloading obviously comes in extremely handy.

I've become a fan of spl_autoload_register over simply defining a single __autoload() function, as this is obviously more flexible if you are incorporating different base modules that each use their own autoloading. However, I've never felt great about the loading functions that I write. They involve a lot of string checking and directory scanning in order to look for possible classes to load.

For example, let's say I have an app that has a base path defined as PATH_APP, and a simple structure with directories named models, views and controllers. I often employ a naming structure whereby files are named IndexView.php and IndexController.php inside the appropriate directory, and models generally have no particular scheme by default. I might have a loader function for this structure like this that gets registered with 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;
}

If it's not found after that, I might have another function to scan sub-directories in the models directory. However, all the if/else-ing, string checking and directory scanning seems inefficient to me, and I'd like to improve it.

I'm very curious what file naming and autoloading strategies other developers might employ. I'm looking specifically for good techniques to employ for efficient autoloading, and not alternatives to autoloading.

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

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

发布评论

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

评论(4

寒江雪… 2024-07-24 14:26:16

这是我在所有项目中使用的内容(直接从上一个项目的源代码中提取):

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

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

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

我倾向于将我的类保留在目录层次结构中,并用下划线打破命名空间...如果我愿意,这段代码可以让我保持文件结构整洁,或者如果我愿意的话可以注入一个没有嵌套目录的快速类文件(例如将一个或两个类添加到它所被告的库中,但不是我当前正在进行的项目的一部分。)

This is what I have been using in all of my projects (lifted straight from the source of the last one):

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

If I look for SomeClass_SeperatedWith_Underscores it will look for SomeClass_SeperatedWith_Underscores.php followed by SomeClass/SeperatedWith/Underscores.php rooted at each directory in the current include path.

EDIT: I just wanted to put out there that I use this for efficiency in development, and not necessarily processing time. If you have PEAR on your path then with this you can just use the classes and don't have to include them when you need them.

I tend to keep my classes in a hierarchy of directories, with underscores breaking up namespaces... This code lets me keep the file structure nice and tidy if I want, or to inject a quick class file without nested directories if I want (for adding a single class or two to a library that it is defendant on, but not part of the project I am currently working on.)

吲‖鸣 2024-07-24 14:26:16

我找到了这个解决方案:

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

class_name.php -> actual/source/file.php

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

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

I landed on this solution:

I created a single script that traverses my class library folder (which contains subfolders for separate modules / systems), and parses the file contents looking for class definitions. If it finds a class definition in a php file (pretty simple regex pattern), it creates a symlink:

class_name.php -> actual/source/file.php

This lets me use a single, simple autoload function that needs only the class name and the path to the main symlink folder, and doesn't have to do any path/string manipulation.

The best part is that I can rearrange my source code completely or add a new subsystem and just run the link generating script to have everything autoloaded.

毁梦 2024-07-24 14:26:16

如果您想要效率,那么您根本不应该使用自动加载功能。 自动加载功能就是为了偷懒。 当您包含它们时,您应该提供包含文件的显式路径。 如果您的自动加载函数可以找到这些文件,那么您可以编写代码来显式查找它们。 当您正在处理代码的视图部分并准备加载新的视图类时,通过让自动加载函数处理它,它首先假设您的类是模型类? 那是低效的。 相反,您的代码应该是:

include_once $this->views_path . $class . '.php';

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

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 ....
}

在任何情况下,在发生包含时,您都拥有有关要加载的类的最大/最准确的信息。 使用该信息完全加载类是唯一有效的类加载策略。 是的,您最终可能会得到更多的类变量或(但愿不会)一些全局变量。 但这比只是偷懒并为您的类扫描部分文件系统是一个更好的权衡。

If you want efficiency then you shouldn't be using the autoload feature at all. The autoload feature is for being lazy. You should be providing an explicit path to your include files when you include them. If your autoload function can find these files then you could code to find them explicitly. When you are working on the view part of the code and about to load a new view class, by letting the autoload function handle it, it first assumes your class is a model class? That's inefficient. Instead your code should just be:

include_once $this->views_path . $class . '.php';

If you need multiple "view" paths, make a function that loads views:

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 ....
}

In any case, at the point where the include occurs, you have the greatest/most accurate information about the class you want to load. Using that information to load the class fully is the only efficient class loading strategy. Yes, you may end up with more class variables or (heaven forbid) some global variables. But that is a better tradeoff than just being lazy and scanning parts of the file system for your class.

北风几吹夏 2024-07-24 14:26:16

PSR-4 是由 PHP 标准推荐 (PSR) 定义的标准,它提供了一致且可互操作的方式来自动加载类。 在 PSR-4 自动加载中,您定义类的名称空间与其在文件系统上的位置之间的映射。 Composer 包管理器默认实现此标准,因为它生成 vendor/autoload.php 文件。 您可以包含此文件并开始使用这些库提供的类,而无需任何额外的工作:

require __DIR__ . '/vendor/autoload.php';

$log = new Monolog\Logger('name');
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));
$log->warning('Foo');

您甚至可以通过向 composer.json 添加自动加载字段来将自己的代码添加到自动加载器。

{
    "autoload": {
        "psr-4": {"Acme\\": "src/"}
    }
}

参考:https://getcomposer.org/doc/01-basic-usage。 md#自动加载

PSR-4 is a standard defined by the PHP Standards Recommendation (PSR) that provides a consistent and interoperable way to autoload classes. In PSR-4 autoloading, you define a mapping between the namespace of a class and its location on the filesystem. The Composer package manager implements this standard by default as it generates a vendor/autoload.php file. You can include this file and start using the classes that those libraries provide without any extra work:

require __DIR__ . '/vendor/autoload.php';

$log = new Monolog\Logger('name');
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));
$log->warning('Foo');

You can even add your own code to the autoloader by adding an autoload field to composer.json.

{
    "autoload": {
        "psr-4": {"Acme\\": "src/"}
    }
}

Reference: https://getcomposer.org/doc/01-basic-usage.md#autoloading

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