一个php插件架构

w00*_*w00 8 php architecture plugins

我正在尝试为自己的框架创建一个插件架构的方法.我已经阅读了很多主题,并在这里和其他网站发布.基本上我来到以下解决方案,似乎是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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

我还可以在"checkForLateHooks()"函数中添加变量.因此钩子函数也可以操作它们,比如'baseLayout'变量.

钩子函数看起来像这样:

public function hookConstruct ( &$baseLayout )
{
    $baseLayout = 'login.html';
}
Run Code Online (Sandbox Code Playgroud)

现在基本上我的问题是,这种方法有什么好处吗?我知道也有很多其他方法可以做到这一点.但我主要不想在以后遇到设计问题.现在看来这是一个好主意,但你永远不会知道事情会如何发挥作用......

如果我记得正确(从我读过的所有帖子中),这有点像WordPress(以及其他一些框架).

rom*_*nsh 6

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

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

我将分享一些我自己通过一系列自己的框架学到的经验.目前,Agile UIAgile Data都支持许多扩展,但我将专注于"组件"

挂钩

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

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

主持人申请:

... some code here ..
$this->hook('beforeInit');
$this->init();
$this->hook('afterInit');
... code goes on ..
Run Code Online (Sandbox Code Playgroud)

插入:

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

UI组件

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

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

这种方法称为"渲染树",应用程序执行经历了两个阶段 - "渲染树的初始化",然后"渲染".

主持人申请:

$layout->menu = new \atk4\ui\Menu();
$layout->add($layout->menu, 'TopMenu');
Run Code Online (Sandbox Code Playgroud)

上面的代码显示了如何初始化和插入新的Component(Menu)$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);
});
Run Code Online (Sandbox Code Playgroud)

这可以用来替换标准菜单,使用附加组件中更强大的实现.

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

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

UI /数据分离

虽然对于一个问题可能没有那么多答案,但另一种有效的扩展方式是关注点的分离.Agile UI中的所有UI组件都不知道如何对数据执行任何操作.

虽然许多UI生成器需要开发人员手动构建它们并与数据链接,但我正在注入"Model"对象,如下所示:

$form->setModel(new User($db)); // populates name, surname and gender fields
Run Code Online (Sandbox Code Playgroud)

演示:http://ui.agiletoolkit.org/demos/form2.php(第2版)

在此方法中,对象User包含足够的元数据,用于填充表单的字段,字幕执行验证以及保存/加载数据.

由于"User"类也可以在附加组件中声明,因此通过添加对新数据实体的支持,它是扩展现有功能的一种非常强大的方法.

其他方法

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

厂:

将指定为字符串的类解析为namespace/file的能力:

$api->add('MyClass');
Run Code Online (Sandbox Code Playgroud)

为附加组件提供重新路由标准类的能力,但对IDE中的类型提示不是很友好.

新类型/特征:

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

结论

我认为附加设计归结为:

  • 简单的安装和使用
  • 附加组件是否需要开箱即用?
  • 避免加载项之间的冲突
  • 定义不同的附加组件类型 - 身份验证,UI,行为
  • 可以附加扩展另一个附加组件
  • 将附加适合的应用程序设计,数据和架构?
  • 在不牺牲性能的情况下为附加组件提供大量功能