grithin/phproute

PHP Web 路由器。

2.0.0 2017-01-08 15:18 UTC

This package is not auto-updated.

Last update: 2024-09-14 18:31:30 UTC


README

最初,Apache 将文件系统路径映射到传入请求的 URL 路径。这对于脚本来说并不理想,因为 Apache 可能会失败并返回纯文本脚本。将文件系统与 URL 路径匹配仍然是出乎意料的途径。

此 Route 类提供了文件路径到 URL 路径映射的功能,而不使脚本文件公开,并且增加了一些功能

  • 一个 _routing.php 文件可以像 .htaccess 文件一样使用 mod_rewrite
    • 循环规则检查。当路径由于路由规则而更改时,可能会希望重新检查现有规则以匹配新路径,并进行后续重写。这是通过 Route 实现的。
  • 一个 _control.php 文件将在其放置的路径层次结构内的任何内容上运行。

这种方式的好处,而不是使用直接映射到平面控制器类的单个路由文件,是

  • 可预期的控制逻辑位置
    • /section/page 默认在 /section/page.php
  • 特定路径的路由规则的可预期位置
    • 如果一个部分有特定的路由,它们将位于 /section/_routing.php/_routing.php
  • 特定部分控制逻辑的可预期位置
    • 例如,如果只有特定类型的用户可以访问部分,那么逻辑将在 section/_control.php
  • 复杂路由:请参阅路由循环

这种路由方法有一些缺点

  • 为了获取所有路由规则,必须收集各种 _routing.php 文件
  • 控制逻辑作为文件而不是函数运行。尽管 Route 将文件的上下文隔离开来,以避免变量冲突,但使用文件的方式改变了测试的编写方式
  • 为了获取所有可能的路由,必须考虑路由规则和将 URL 路径匹配到文件路径的默认行为

让我们考虑一些框架 X 中的 UserController,该框架使用平面路由。对于 UserController,它处理类似 '/user/x' 的传入路径的能力是共享功能和变量的能力。有时,一组控制函数可以访问相同的控制相关实用函数是有用的,有时,部分控制器设置一些初始化或部分特定变量数据是有用的。但是,所有这些都可以通过 Route 通过部分 _control.php 文件和 Route 全局变量(请参阅路由全局变量)来实现

简单示例

文件路径

public/index.php
control/page.php
# public/index.php

$_SERVER['REQUEST_URI'] = '/page';

$Route = new Route(['folder'=>realpath(__DIR__.'/../control')]);
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$Route->handle($path); # loads control/page.php

结构

有一个控制文件夹(/control),在该控制文件夹中有路由文件和控制文件。

路由文件根据 URL 路径逐步加载。如果 URL 为 http://bobery.com/part1/part2/part3,则路由器会尝试加载

/control/_routing.php
/control/part1/_routing.php
/control/part1/part2/_routing.php
/control/part1/part2/part3/_routing.php

并非所有这些路由文件都需要存在。而且,在任何时候,路由文件都可以更改路由令牌,从而导致加载不同的路由文件系列。

路由文件的结果要么是最终路径,要么是回调。

如果路由文件没有做任何事情,最终路径将保持为 URL 路径。

控制文件以与路由文件相同的方式加载,并根据最终路径加载。如果最终路径是 /part1/part2/part3,则路由器会尝试加载

/control/_control.php
/control/part1/_control.php
/control/part1/part2/_control.php
/control/part1/part2/part3/_control.php

并非所有这些控制文件都需要存在。在路径中的任何位置,您都可以使用令牌名称而不是 _control.php,它将具有优先级。因此,它可以是

/control/_control.php
/control/part1.php
/control/part1/part2.php
/control/part1/part2/part3.php

在这里,/control/part1.php 替换了 /control/part1/_control.php/control/_control.php 是一个可选的全局控制文件,它为所有请求加载。

路由全局变量

在链式控制逻辑的过程中,链中的项目有时需要共享功能或变量。为了启用此功能,Route 提供了一个全局数组,它将其注入/解包到控制文件(如 _control.phppage.php)中。默认情况下,始终有两个全局变量可用:Route,它指的是路由实例,以及 control,它是一个 ArrayObject,您可以将控制功能和您想共享的变量放置在其中。然而,您可以添加全局变量。

# file: _control.php
$control['sale'] = '10% off all blah';
$Route->globals['customize_for_user'] = function(){};


# file: section/item.php
view($customize_for_user($control['sale']))

路由循环

路由文件只运行一次,但如果路径改变,它的规则可能被多次应用。这与 mod_rewrite 的规则循环操作类似。它还像 mod_rewrite 一样,允许规则具有最终标志,并停止循环。

路径:/test1/test2/test3 路由加载

  • 加载 /_routing.php
  • 运行来自 /_routing.php 的规则
  • 没有路径改变,继续
  • 加载 /test1/_routing.php
  • 运行来自 /test1/_routing.php 的规则
  • 没有路径改变,继续
  • 加载 /test1/test2/_routing.php
  • 运行来自 /test1/test2/_routing.php 的规则
  • 路径变为 /moved_section1/bob
  • 运行来自 /_routing.php 的规则
  • 路径变为 /section1/bob
  • 运行来自 /_routing.php 的规则
  • 没有路径改变,继续
  • 加载 /section1/_routing.php
  • 运行来自 /section1/_routing.php 的规则
  • 没有路径改变,继续
  • 路径最终结果:/section1/bob

停止循环

规则可以有一个 last 标志,如果该规则匹配,循环将在其之后停止。

您还可以在路由文件或路由规则回调中调用 $route->routing_end()

调试

只需检查 $route 实例

try{
	$route->handle('http://test.com/not/a/real/path');
}catch(RouteException $e){
	\Grithin\Debug::quit($route);
}

路由文件

路由文件包含 $route,包含路由实例。

路由文件应返回一个路由规则数组。

return [
	['bob','bill'],
	['bill','sue']
];

路由规则

[$match_pattern, $change, $flags]

$match_pattern

默认情况下,将 match_pattern 解释为精确、区分大小写的匹配模式。对于 http://bobery.com/part1/part2/part3part1/part2/part3 将匹配,但 part1/part2part1/part2/part3/ 则不会。

标志可以更改 match_pattern 的解释。

  • regex 作为正则表达式
  • caseless 对小写主题进行匹配

正则表达式

默认数字组匹配
# reposition id
['/item/([0-9]+)/view', '/item/view/[1]', 'regex']
命名匹配
# match anything and name it "path"
['(?<path>.*)', 'prefix/[path]', 'regex']

# match numbers and name it "id"
['old/(?<id>[0-9]+)', 'new/[id]','regex']
  • 最后一个匹配项也存储在 $route->regex_last_match
  • 匹配项的汇总存储在 $route->regex_matches
  • 这两个都将有键值,既有数字索引,也有命名组(如果存在命名组)
    • 例如 $Route->regex_matches['id']
使用匹配项

除了匹配回调(请参阅 $change > callable)之外,控制文件还可以访问路由实例。并且,在规则 ['test/from/(?<id>[0-9]+)','/test/to/[id]', 'regex,last'] 中,我们有

route.tokens = [
	"test",
	"to",
	"123"]
route.regexMatch = {
	"0": "test\/from\/123",
	"id": "123",
	"1": "123"}

$change

可以是字符串或可调用对象

字符串

没有 regex 标志,将替换整个路径。

regex 标志,用作专门的匹配替换(类似于 preg_replace 替换参数)。为了方便起见,可以使用匹配组

'['matchName']'

示例

$rules[] = ['user/(?<id>[0-9]+)','usr/[id]','regex'];
可调用对象

一个符合 Route::is_callable 的可调用对象 function($route, $rule),它应返回一个新的路径。

如果存在 regex 标志,可调用对象作为 preg_replace_callback 回调,其中第三个参数开始是正常的 preg_replace_callback 回调参数(function($route, $rule, $matches)

$flags

逗号分隔的标志,或标志数组

  • 'once' 规则只应用一次,其余时间忽略

  • 在文件中匹配到最后一条规则 'file:last'。路由器将不再解析包含文件中的更多规则,但会解析后续文件中的规则。

  • 'last' 是最后匹配的规则。路由器将在此之后停止解析规则。

  • '301' 将发送 http 重定向代码 301(永久重定向)。

  • '307' 将发送 http 重定向代码 307(临时重定向)。

  • '303' 将发送 http 重定向代码 303(告诉客户端以 GET 请求重新发出)。

  • 'params' 保持 GET 参数:将在 http 重定向的末尾附加查询字符串。

  • 'caseless': 忽略大小写。

  • 'regex': 应用正则表达式模式匹配。

有用示例

将文件夹指向索引控制文件。

return [
	['^$','index','regex,last'], # the root path, special in that the initial `/` is removed and the path is empty
	['^(?<path>.*)/$','[path]/index','regex,last'], # paths ending in `/` to be pointed to their corresponding index control files
]

id 的重新分配

  • _routing.php
['test/from/(?<id>[0-9]+)','/test/to/[id]', 'regex,last'],
  • 这也提供了 $Route->named_matches['id']

控制

已加载的控制文件将 $route 实例注入到它们的上下文中,以及通过 $route->globals 数组键入的其他任何内容。

如果您想在标记完成之前结束路由,您必须退出或调用 $route->control_end()。如果没有对应控制文件的剩余标记,路由器将考虑这是一个未找到页面的事件。

注意

Route 期望至少有一个文件,不包括主 _control.php 文件。如果某些路由将以主 _control.php 结尾,您必须退出或捕获并处理 RouteException。