菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
2333
1

Just for fun——PHP 框架之简单的路由器(2)

原创
05/13 14:22
阅读数 3123

上篇文章

Just for fun——PHP框架之简单的路由器(1)
我的Github

代码讲解

路由分类

对于注册的路由,需要分成两类(下文提到的$uri是指$_SERVER['REQUEST_URI']去掉查询字符串的值)

  • 静态路由(就是没有占位符的路由,例如/articles)
  • 带参数路由(有占位符的路由,例如/user/{uid})
    其实这是很明显的,因为静态的路由的话,我们只需要和$uri直接比较相等与否就行了,而对于带参数路由,譬如/user/{uid},我们需要在注册的时候,提取占位符名,把{}这一部分替换为([a-zA-Z0-9_]+)这样的正则字符串,使用()是因为要做分组捕获,把占位符对应的值要取出来。
    Dispatcher.php**中有两个数组
  • $staticRoutes
  • $methodToRegexToRoutesMap
    分别对应静态路由和带参数路由,另外要注意,这两个数组是二维数组,第一维存储http method,第二维用来存储正则字符串(静态路由自身就是这个值,而带参数路由是把占位符替换后的值),最后的value是一个Route对象

    Route类

    这个类很好理解,用来存储注册路由的一些信息

    • $httpMethod:HTTP方法,有GET,POST,PUT,PATCH,HEAD

    • $regex:路由的正则表达式,带参数路由是占位符替换后的值,静态路由自身就是这个值

    • $variables:路由占位符参数集合,静态路由就是空数组

    • $handler:路由要回调的对象
      当然,这个类还可以多存储一点信息,譬如带参数路由最原始的字符串(/user/{uid}),这里简单做了

分发流程

  1. 根据http method取数据,因为第一维都是http method
  2. 一个个匹配静态路由
  3. 对于带参数路由,把所有的正则表达式合起来,形成一个大的正则字符串,而不是一个个匹配(这样效率低)

第一步很简单,主要说明第二步
对于三个独立的正则字符串(定界符是~):

~^/user/([^/]+)/(\d+)$~
~^/user/(\d+)$~
~^/user/([^/]+)$~

我们可以合起来,得到

~^(?:
    /user/([^/]+)/(\d+)
    | /user/(\d+)
    | /user/([^/]+)
)$~x

?:是非捕获型分组
这个转化很简单,我们怎么知道那个正则被匹配了呢??
举个例子:

preg_match($regex, '/user/nikic', $matches);
=> [
    "/user/nikic",   # 完全匹配
    "", "",          # 第一个(空)
    "",              # 第二个(空)
    "nikic",         # 第三个(被使用)
]

可以看到,第一个非空的位置就可以推断出哪个路由被匹配了(第一个完全匹配要剔除),我们需要一个数组要映射它

[
    1 => ['handler0', ['name', 'id']],
    3 => ['handler1', ['id']],
    4 => ['handler2', ['name']],
]

1是因为排除第一个匹配
3是因为第一个路由有两个占位符
4是因为第二个路由有一个占位符

上面的数组,我们可以注册的methodToRegexToRoutesMap这个量形成的,我是这么写的

$regexes = array_keys($this->methodToRegexToRoutesMap[$httpMethod]);
foreach ($regexes as $regex) {
    $routeLookup[$index] = [
        $this->methodToRegexToRoutesMap[$httpMethod][$regex]->handler,
        $this->methodToRegexToRoutesMap[$httpMethod][$regex]->variables,
    ];
    $index += count($this->methodToRegexToRoutesMap[$httpMethod][$regex]->variables);
}

最后

调用回调函数,返回一个数组,第一个值用来判断最终有没有找到

未完待续

下一篇文章说一下怎么把这个合起来的正则优化一下效率

发表评论

0/200
2333 点赞
1 评论
收藏
为你推荐 换一批