grithin/control-path

根据路径加载一系列控制文件

0.2 2021-07-22 15:44 UTC

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) 的过程中,流会经过以下步骤

  1. .
    • 加载 /Controller.php
    • $Controller = new Controller()
    • $Controller->_always()
  2. .
    • 加载 /section1/Controller.php
    • $Controller = new section1\Controller()
    • $Controller->_always()
  3. .
    • 加载 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访问之前的控制器类。这允许访问控制器上的公共数据(对象属性),这可能很有用。然而,由于公共方法可以通过路径访问控制器,因此只有页面(和内置)应该是公共方法,而实用方法应该是受保护的。

停止控制流程

有几种停止控制流程的方法

  1. 从控制中返回false
  2. 调用$Flow->stop()
  3. 抛出异常

第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的潜在问题也很复杂

Wares