9

我正在想办法为我自己的框架创建一个插件架构。我已经阅读了许多主题并在这里和其他网站上发帖。基本上我已经找到了以下解决方案,这似乎是 PHP 中唯一的好选择(目前)。

这个想法是每个类都扩展了一种类似于类的观察者。所以一个 Template 类、BaseController 等总是扩展一个 Plugin 类。

class BaseController extends Plugin
{
    public function __construct()
    {
        // Plugin check, notify all loaded plugins
        $this->checkForEarlyHooks();

        // Init some standard stuff
        $this->view = new Template();
        $this->baseLayout = 'layout.html';

        $this->something = new Something();

        // Plugin check, notify all loaded plugins
        $this->checkForLateHooks();
    }
}

所以这里基本上发生的是,当 indexController 扩展 baseController 时,插件检查就完成了。在这种情况下为构造函数。当您想在实际调用 Action 方法之前使用插件进行某种登录检查时,这会很方便。

Plugin 类可以从调用的类中解析,并知道在加载的插件中查找哪些函数。

另请注意,它会检查加载的插件列表 2 次。在构造函数中(早期)加载任何内容之前,一个在加载所有变量时(晚期)。

我还可以将变量添加到“checkForLateHooks()”函数中。所以钩子函数也可以操纵它们,比如'baseLayout'变量。

挂钩函数如下所示:

public function hookConstruct ( &$baseLayout )
{
    $baseLayout = 'login.html';
}

现在基本上我的问题是,这种方法有什么好处吗?我知道可能还有很多其他方法可以做到这一点。但我主要不想以后遇到设计问题。现在看来是个好主意,但你永远不知道以后会怎样……

如果我没记错的话(从我读过的所有帖子中),这有点像 WordPress 所做的(以及其他一些框架)。

4

2 回答 2

7

更新:答案现在反映了最新的链接和更好的描述。

当然有很多不同的方法来设计一个插件系统,也许在https://softwareengineering.stackexchange.com/上提问会给你更多的想法,但我会尝试通过分享我的想法和经验来提供帮助。

我将分享一些我自己通过一系列自己的框架学到的经验。目前Agile UIAgile Data都支持很多wasy 的扩展,但我将重点关注“组件”

挂钩

当您希望将代码注入现有对象时,挂钩是一种标准方法。这是使用已建立的结构扩展应用程序或流程的最佳选择。

在重构我的框架时,我将钩子实现分离为一个单独的特征并在此处记录:http: //agile-core.readthedocs.io/en/develop/hook.html

主机应用:

... some code here ..
$this->hook('beforeInit');
$this->init();
$this->hook('afterInit');
... code goes on ..

插入:

$host_app->addHook('beforeInit', function($object) {
    echo "About to execute init of $object";
});

用户界面组件

组件呈现出不同的设计模式,适用于用户界面。您从页面/应用程序布局开始,然后将其分解为菜单、页眉、页脚、内容。

组件是可以与布局或其他组件相关联的对象。每个组件都能够呈现额外的 HTML/JS 并将其传递给其父级。大多数组件也将是交互式对象。

这种方法称为“渲染树”,应用程序执行经历了两个阶段——“渲染树的初始化”,然后是“渲染”。

主机应用:

$layout->menu = new \atk4\ui\Menu();
$layout->add($layout->menu, 'TopMenu');

上面的代码显示了如何初始化一个新的组件(菜单)并将其插入到$layou. 此外,$menu 的 HTML 输出被定向到 {$TopMenu} 标记中,该标记在 Layout 的 HTML 模板中定义。

插件可以通过以下方式与渲染树交互:

  • 在树的任意位置添加更多组件
  • 影响现有组件(例如添加新菜单项)
  • 销毁任何现有组件

当这些方法结合起来时,你可以使用这样的东西:

$app->addHook('afterInitLayout', function($app) {

    $app->layout->menu->destroy(); // remove default menu
    $app->layout->menu = new \plugin\SuperMenu();
    $app->layout->add($app->layout->menu);
});

这可用于使用附加组件中更强大的实现替换标准菜单。

我的组件实现记录在这里:

http://agile-ui.readthedocs.io/en/latest/view.html#initializing-render-tree

用户界面/数据分离

虽然可能不是一个问题的太多答案,但另一种强大的扩展方式是关注点分离。Agile UI中的所有 UI 组件都不知道如何处理数据。

虽然许多 UI 生成器需要开发人员手动构建它们并与数据链接,但我正在注入这样的“模型”对象:

$form->setModel(new User($db)); // populates name, surname and gender fields

演示:http ://ui.agiletoolkit.org/demos/form2.php (第二种形式)

在这种方法中,对象User包含足够的元数据以供表单填充其字段,标题执行验证并保存/加载数据。

由于“用户”类也可以在附加组件中声明,它通过添加对新数据实体的支持来扩展现有功能是一种非常强大的方式。

其他方法

使用附加组件进行扩展的其他一些方法包括:

工厂:

将指定为字符串的类解析为命名空间/文件的能力:

$api->add('MyClass');

使附加组件能够重新路由标准类,但对 IDE 中的类型提示不是很友好。

新类型/特性:

附加组件可以提供添加持久性、表格列、表单字段、操作等的新类。

结论

我认为附加设计归结为:

  • 安装和使用简单
  • 插件是否需要开箱即用?
  • 避免附加组件之间的冲突
  • 定义不同的附加类型 - 身份验证、UI、行为
  • 可以附加组件扩展另一个附加组件
  • 附加组件是否适合应用程序设计、数据和架构?
  • 在不牺牲性能的情况下为附加组件提供强大的功能
于 2012-04-08T11:04:43.387 回答
1

在https://github.com/gheevel/PHPPlugin上查看 PHPPlugin 。一个简单的 PHP 插件框架,灵感来自 eclipse 和 jira 插件架构。基本上,您的应用程序可以定义扩展点,插件实例可以在这些扩展点上注册以提供额外的功能。与作曲家和/或 symfony 结合使用效果很好。

于 2016-09-18T12:29:16.063 回答