grithin / control-path
根据路径加载一系列控制文件
Requires
- php: >=5.3.0
- grithin/file-include: ^0.1.0
- grithin/ioc-di: ^0.5.0
Requires (Dev)
- grithin/phpbase: ^5.0
- grithin/phpunit: ^0.1.0
This package is auto-updated.
Last update: 2024-09-22 22:20:38 UTC
README
在路径中加载控制文件,如果找到则使用部分控制器
为什么
我之前构建的路由器会并行加载请求URL路径的控制文件。它包含类似于前端软件的部分控制文件,并为控制文件提供了直观的位置。但是,它也有一些缺点
- 变量注入仅限于预定义数组
- 无法将多个页面捆绑到单个控制器类中,这可能会对页面和 __construct 有用
我还认为标准控制器类范式有一些 缺点。
因此,我决定取两者之长,制作了这个工具。
是什么
ControlPath 加载 部分 和 页面控制。控制器的返回值被收集,并形成由 load
函数返回的数组。
use \Grithin\ServiceLocator; $sl = new ServiceLocator(); $cp = new ControlPath(__DIR__.'/test_files/app1/', $sl->injector_get()); $returns = $cp->load('/section1/page');
如何解释这些返回值取决于应用程序。例如,控制器可以返回一个响应对象,或者一个字符串,然后将其转换为响应对象。
让我们看看一个例子。
在路径 /section1/page
下,在 while($Flow->next() !== false)
的过程中,流会经过以下步骤
- .
- 加载
/Controller.php
$Controller = new Controller()
$Controller->_always()
- 加载
- .
- 加载
/section1/Controller.php
$Controller = new section1\Controller()
$Controller->_always()
- 加载
- .
- 加载
page.php
- 加载
部分控制
section1\Controller.php
namespace App\Control\section1; class Controller{ # always called, but return is not used public function __construct(){ } # always called, return is used public function _always(){ } }
页面控制
页面控制可以有以下四种形式
部分类方法
在控制器中,在部分控制器.php 中,可以有一个方法对应页面名称 function page1(){}
页面类
与部分控制器类平行,但以页面命名。 /page1
-> class page1{}
闭包
可以返回一个闭包。闭包将具有注入的参数。
页面逻辑
页面本身可以执行一些操作。它还可以返回一些内容 - 例如模板的输出。
注入
方法、闭包和 __construct 具有正常的依赖注入。此外,一些参数基于参数名称注入。命名注入基于 inject
选项。始终注入两个变量
Flow
当前 Flow 实例control
和 ArrayObject 用于在控制器之间共享数据ControlPath
当前 ControlPath 实例
您可以通过名称注入这些变量
# within a method/constructor function __construct($ControlPath){} # within a closure return function($ControlPath){}
共享
部分控制器和页面控制器可能希望共享数据。为此,注入了一个名为 $share
的 ArrayObject 变量,这也可以用于捕获控制器类中的方法。
Controller.php
class Controller{ function __construct($share){ $share->do_x = function(){ $this->do_x(); }; } protected function do_x(){ echo 'bob'; } }
page.php
($share->do_x)();
而通常,部分控制器会有受保护的方法,它在类内的页面方法之间共享。如果部分控制器希望将这些方法提供给不是方法的页面控制器,或者更深层次的页面控制器,它必须像上面那样做。
如有必要,可以使用$Flow->controllers
访问之前的控制器类。这允许访问控制器上的公共数据(对象属性),这可能很有用。然而,由于公共方法可以通过路径访问控制器,因此只有页面(和内置)应该是公共方法,而实用方法应该是受保护的。
停止控制流程
有几种停止控制流程的方法
- 从控制中返回false
- 调用$Flow->stop()
- 抛出异常
第2点可能出现在多个位置。由于$Flow
被注入
- 在构造函数部分
- 在_always部分
- 在页面控制文件中
- 在返回处理程序中
Controller.php
class Controller{ public function __construct($Flow){ $Flow->stop(); } }
返回处理程序
$return_handler = function($return, $flow){ # there can be no "bill"'s, only bob's if($return == 'bill'){ $flow->stop(); } } $ControlPath->load('/section1/page', ['return_handler'=>$return_handler]);
符合路径令牌
由于路径字符可能与命名空间或类的允许字符不同,因此使用ControlPath::token_to_class将路径的标记部分进行转换。
使用'.'作为分隔符,删除不符合规范的字符,并将标记转换为驼峰式
029bob.bill-sue1
=> bobBillSue1
异常
Grithin\ControlPath\NotFound
: 当页面找不到时,或者当路径指向内置时Grithin\IoC\ContainerException
: 当注入无法解析类型参数的服务时Grithin\IoC\MissingParam
: 当注入无法解析参数时
其他设计说明
类文件与逻辑文件
当存在类文件和逻辑文件的混合时,多个load
会成为一个问题。您不能重新包含类文件,但逻辑文件期望被重新包含。因此,当ControlPath发现类文件时,它不会在后续运行中再次尝试加载它。
服务定位器单例ControlPath
有一个问题,即ControlPath是否应该克隆ServiceLocator。ControlPath将其自身添加到SL中。通过拥有克隆的SL,它确保如果控制文件调用SL->get(ControlPath),这将始终指向加载控制文件的实例。然而,这会产生期望不匹配的问题
- 控制文件可能添加中间件,并且可能为该中间件添加一个将被注入的服务,但由于控制文件使用的ServiceLocator是克隆的,因此该服务不会在加载中间件的服务定位器中注册。
最终,我决定不克隆ServiceLocater。ControlPath可以通过名称而不是类型进行注入,这意味着控制文件只需将参数命名为$ControlPath
即可确保它们正在获取当前实例。
为什么路由中间件不完整?
通常,特定的路径会有一些复杂性,这会使将其抽象为前端/中间件既不必要地断开连接,又会导致不必要复杂的通用中间件或特定于路由的前端/中间件。特定的前端ware,由路径指示,非常适合这些路径特定的复杂性,可以作为部分控制加载。例如,可能是一些复杂的权限检查,这些检查不适合在Auth中间件配置参数中。
控制器类的缺点
- 大方法或复杂方法会导致庞大、难以管理的控制器文件。
- 大型控制页面必须放在控制器类中,使类变得很大
- 埋藏页面
- 页面方法没有明确的顺序
- 页面语法错误会导致部分失败
为什么不将控制器作为API?
API和响应处理器适合处理标准。但是,控制器可以输出任何内容,有时需要从标准中有很大的不同。
计划
将此设置为框架需要一些努力。
拥有类似laravel的路由方法(Model $model)
意味着注入Model(Request $request)
,这意味着Request是服务或在该点可注入。
将ControlPath与其他ware结合,其中存在部分控制可能添加更多ware的潜在问题也很复杂