如何设计插件系统,使其不会';Don’不要浪费那么多资源


How can plugin systems be designed so they don't waste so many resources?

我正在尝试构建一个基本的插件系统,就像你在WordPress这样的CMS中经常看到的那种。您有一个插件文件夹,这些插件通过使用Observerevent设计模式的事件通知与主系统的操作联系在一起。

问题是,系统不可能知道插件想要对哪些事件采取行动,因此系统必须为每个页面请求加载每个插件,以确定在某个时候是否真的需要该插件。不用说,这是浪费了很多资源——在WordPress的情况下,每个请求加起来会有几个额外的MB内存

有其他方法吗

例如,有没有一种方法可以一次性加载所有这些,然后缓存结果,这样您的系统就知道如何延迟加载插件了?换句话说,系统加载一个配置文件,指定插件希望绑定到的所有事件,然后将其保存在APC或其他文件中以备将来请求?

如果这也表现不佳,那么也许有一个特殊的文件结构可以用来对何时不需要某些插件来满足请求进行有根据的猜测。

我确实有一个插件管理工具,但我只将它主要用于过程插件,并且通常一次加载所有include。但对于一个基于事件和加载缓慢的API,我可以想象使用浅包装器进行插件管理,并对实际扩展进行自动加载。

<?php
  /**
   * api: whatever
   * version: 0.1
   * title: plugin example
   * description: ...
   * config: <var name="cfg[pretty]" type="boolean" ...>
   * depends: otherplugin
   */
 $plugins["title_event"] = "TitleEventClass";
 $plugins["secondary"] = array("Class2", "callback");
?>

在这个例子中,我假设插件API是一个简单的列表。这个示例feature-plugin-123.php脚本在加载时除了添加到数组之外什么都不做。因此,即使您有十几个功能插件,每个插件也只会产生额外的include_once

但是主应用程序/或插件API可以只是实例化提到的类(原始类名称为new $eventcb;,回调为call_user_func_array)。反过来,它会将实际任务卸载到自动加载器。因此,您有一个双重系统,其中一部分管理列表,另一部分定位真正的代码。

因此,我仍然想象一个简单的config.php,它只列出了插件和设置,如下所示:

<?php
include_once("user/feature-plugin-123.php");
include_once("user/otherplugin2.php");
include_once("user/wrapper-for-htmlpurifier.php");
$cfg["pretty"] = 1;

再次记住,这些只是包装器/数据脚本,带有插件的可管理性描述。还可以使用一个实际的register_even() API,并在每个API中定义一个额外的包装器函数。但列出类名似乎是最简单的选择。

上述管理工具有点生疏和丑陋:http://milki.include-once.org/genericplugins/
但是,如果您只需要一个列表(sql表)而不需要设置管理,那么这是不必要的。该开销仅用于预打印插件元数据并保持可读的config.php

总之:

include_path上的spl_autoload(),以及一个简单的事件->类名注册表,每个注册表都有一个包装器脚本,只是一次包含所有内容。

Wordpress和其他CMS系统就是非常糟糕的例子。

我们必须理解的是,模块化,几乎总是意味着更重。

我曾经使用过的解决这种情况的最佳方案是一个基于类的插件,使用自动加载器有严格的命名约定。

因此,在使用插件之前,您需要创建一个实例,或者使用静态函数。

你甚至可以调用这样的插件:

<?php $thePlugin::method(); ?>

例如:

<?php
    spl_autoload_register('systemAutoload');
    function systemAutoload($class)
    {
        $parts = explode('_',$class);

        switch($parts[1])
        {
            case "Plugin":
                include("/plugins/{$parts[2]}/{$parts[2]}.php");
            break;
        }
        // ...    
    }
?>

关于事件:

您必须静态地注册此事件,以避免以动态方式进行注册。

数据库将是执行此操作的正确位置。您可以有一个事件表,以及插件类上的install()和uninstall()方法,以添加特定事件或将方法绑定到其他事件。这是一个数据库查询,如果您想从中获得更多信息,请将其添加到memcached或平面ini文件中。

对我来说效果很好。这样,我就可以让一个每次请求消耗8mb的沉重系统降到1mb,具有完全相同的功能列表,而不需要高级缓存。现在我们可以添加更多功能并维护系统"干净"

希望对有所帮助

例如,我会将插件类名及其订阅的事件保存在配置文件中,然后将解析后的配置文件保存在APC中。然后,当事件被触发时,系统可以根据需要延迟加载适当的插件类。