如何设计插件系统,以免浪费这么多资源?

时间:2012-01-21 16:52:04

标签: php performance design-patterns plugins content-management-system

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

问题是系统无法 知道插件想要对哪些事件采取行动 - 因此系统必须为每个页面请求加载每个插件找出在某个时刻是否真的需要该插件。毋庸置疑,那里有很多浪费的资源 - 在WordPress的情况下,每个请求增加了几个额外的MB内存!

有没有其他方法可以做到这一点?

例如,有没有办法加载所有这一次然后缓存结果,以便您的系统知道如何延迟加载插件?换句话说,系统加载一个配置文件,该文件指定插件希望绑定的所有事件,然后将其保存在APC中,或者将其保存以供将来的请求使用?

如果这也表现不佳,那么可能会有一个特殊的文件结构,可以用来做出有关何时不需要某些插件来完成请求的有根据的猜测。

3 个答案:

答案 0 :(得分:3)

我确实有一个插件管理工具,但我只使用它主要是程序插件,并且所有包含通常一次加载。但对于基于事件和延迟加载的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(),以及一个简单的event-&gt; classname注册表,每个注册表脚本都包含在一起,只包含所有内容。

答案 1 :(得分:2)

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,具有完全相同的功能列表,没有高级缓存。现在我们可以添加更多功能并保持系统“干净”

希望有所帮助

答案 2 :(得分:0)

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