我正在尝试为自己的框架创建插件架构的方法。我已经阅读了很多主题,并在这里和其他网站发布。基本上我已经找到了以下解决方案,这似乎是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(以及其他一些框架)。
答案 0 :(得分:6)
更新:现在,answer会反映最新的链接和更好的说明。
设计插件系统肯定有很多不同的方法,也许在https://softwareengineering.stackexchange.com/上询问会给你更多的想法,但我会尝试分享我的想法和经验。
我将分享一些我自己通过一系列自己的框架学到的经验。目前Agile UI和Agile Data都支持扩展,但我会专注于“组件”
当你想要将代码注入现有对象时,钩子是一种标准的方法。这是使用已建立的结构扩展应用程序或流程的最佳选择。
在重构我的框架时,我将钩子实现分离为一个单独的特征并在此处记录: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}标签,该标签在布局的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生成器要求开发人员手动构建它们并与数据链接,但我正在注入“Model”对象,如下所示:
$form->setModel(new User($db)); // populates name, surname and gender fields
演示:http://ui.agiletoolkit.org/demos/form2.php(第二种形式)
在此方法中,对象User
包含足够的元数据,用于填充表单的字段,字幕执行验证以及保存/加载数据。
由于“User”类也可以在附加组件中声明,因此通过添加对新数据实体的支持,它是扩展现有功能的一种非常强大的方法。
使用附加组件扩展的其他一些方法包括:
厂:将指定为字符串的类解析为namespace / file的能力:
$api->add('MyClass');
为附加组件提供重新路由标准类的能力,但对IDE中的类型提示不是很友好。
新类型/特征:加载项可以提供添加持久性,表格列,表单字段,操作等的新类。
我认为附加设计归结为:
答案 1 :(得分:1)
在https://github.com/gheevel/PHPPlugin上查看PHPPlugin。一个简单的PHP插件框架,受eclipse和jira插件架构的启发。基本上,您的应用程序可以定义扩展点,插件实例可以在这些扩展点上注册以提供额外的功能。与作曲家和/或symfony结合使用效果很好。