改进
紧接上一篇文章Just for fun——PHP框架之简单的路由器(1)。
代码下载
对于以下合并的正则
~^(?: /user/([^/]+)/(d+) | /user/(d+) | /user/([^/]+) )$~x
最终匹配的是分组中的某一个,我们需要的子匹配也是那个分组中的,然而从结果看
preg_match($regex, "/user/nikic", $matches); => [ "/user/nikic", # 完全匹配 "", "", # 第一个(空) "", # 第二个(空) "nikic", # 第三个(被使用) ]
这里是最后一个路由被匹配了,但是其他分组的子匹配也被填充了,这是多余的。
解决思路PCRE正则里?|也是非捕获分组,那么?|和?:有什么区别呢??
区别在于?|会组号重置,看以下几个例子就懂了
preg_match("~(?:(Sat)ur|(Sun))day~", "Saturday", $matches) => ["Saturday", "Sat", ""] # 最后一个""其实是不存在的,写在这里是为了阐释概念 preg_match("~(?:(Sat)ur|(Sun))day~", "Sunday", $matches) => ["Sunday", "", "Sun"] preg_match("~(?|(Sat)ur|(Sun))day~", "Saturday", $matches) => ["Saturday", "Sat"] preg_match("~(?|(Sat)ur|(Sun))day~", "Sunday", $matches) => ["Sunday", "Sun"]
所有我们可以用?|来代替?:来减少多余的子匹配填充,但是这样一来的话,如何判断哪个分组被匹配了呢??(因为之前的判断技巧就失效了)
我们可以这样,添加一些多余子匹配
~^(?| /user/([^/]+)/(d+) | /user/(d+)()() | /user/([^/]+)()()() )$~x实现 dispatcher.php
1) { preg_match_all("~{([a-zA-Z0-9_]+?)}~", $route, $matchesVariables); return [ preg_replace("~{[a-zA-Z0-9_]+?}~", "([a-zA-Z0-9_]+)", $route), $matchesVariables[1], ]; } else { return [ $route, [], ]; } } throw new LogicException("register route failed, pattern is illegal"); } /** * 注册路由 * @param $httpMethod string | string[] * @param $route * @param $handler */ public function addRoute($httpMethod, $route, $handler) { $routeData = $this->parse($route); foreach ((array) $httpMethod as $method) { if ($this->isStaticRoute($routeData)) { $this->addStaticRoute($method, $routeData, $handler); } else { $this->addVariableRoute($method, $routeData, $handler); } } } private function isStaticRoute($routeData) { return count($routeData[1]) === 0; } private function addStaticRoute($httpMethod, $routeData, $handler) { $routeStr = $routeData[0]; if (isset($this->staticRoutes[$httpMethod][$routeStr])) { throw new LogicException(sprintf( "Cannot register two routes matching "%s" for method "%s"", $routeStr, $httpMethod )); } if (isset($this->methodToRegexToRoutesMap[$httpMethod])) { foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $route) { if ($route->matches($routeStr)) { throw new LogicException(sprintf( "Static route "%s" is shadowed by previously defined variable route "%s" for method "%s"", $routeStr, $route->regex, $httpMethod )); } } } $this->staticRoutes[$httpMethod][$routeStr] = $handler; } private function addVariableRoute($httpMethod, $routeData, $handler) { list($regex, $variables) = $routeData; if (isset($this->methodToRegexToRoutesMap[$httpMethod][$regex])) { throw new LogicException(sprintf( "Cannot register two routes matching "%s" for method "%s"", $regex, $httpMethod )); } $this->methodToRegexToRoutesMap[$httpMethod][$regex] = new Route( $httpMethod, $handler, $regex, $variables ); } public function get($route, $handler) { $this->addRoute("GET", $route, $handler); } public function post($route, $handler) { $this->addRoute("POST", $route, $handler); } public function put($route, $handler) { $this->addRoute("PUT", $route, $handler); } public function delete($route, $handler) { $this->addRoute("DELETE", $route, $handler); } public function patch($route, $handler) { $this->addRoute("PATCH", $route, $handler); } public function head($route, $handler) { $this->addRoute("HEAD", $route, $handler); } /** * 分发 * @param $httpMethod * @param $uri */ public function dispatch($httpMethod, $uri) { $staticRoutes = array_keys($this->staticRoutes[$httpMethod]); foreach ($staticRoutes as $staticRoute) { if($staticRoute === $uri) { return [self::FOUND, $this->staticRoutes[$httpMethod][$staticRoute], []]; } } $routeLookup = []; $regexes = []; foreach ($this->methodToRegexToRoutesMap[$httpMethod] as $regex => $route) { $index = count($route->variables); if(array_key_exists($index, $routeLookup)) { $indexNear = $this->getArrNearEmptyEntry($routeLookup, $index); array_push($regexes, $regex . str_repeat("()", $indexNear - $index)); $routeLookup[$indexNear] = [ $this->methodToRegexToRoutesMap[$httpMethod][$regex]->handler, $this->methodToRegexToRoutesMap[$httpMethod][$regex]->variables, ]; } else { $routeLookup[$index] = [ $this->methodToRegexToRoutesMap[$httpMethod][$regex]->handler, $this->methodToRegexToRoutesMap[$httpMethod][$regex]->variables, ]; array_push($regexes, $regex); } } $regexCombined = "~^(?|" . implode("|", $regexes) . ")$~"; if(!preg_match($regexCombined, $uri, $matches)) { return [self::NOT_FOUND]; } list($handler, $varNames) = $routeLookup[count($matches) - 1]; $vars = []; $i = 0; foreach ($varNames as $varName) { $vars[$varName] = $matches[++$i]; } return [self::FOUND, $handler, $vars]; } private function getArrNearEmptyEntry(&$arr, $index) { while (array_key_exists(++$index, $arr)); return $index; } }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/26125.html
摘要:路由路由的功能就是分发请求到不同的控制器,基于的原理就是正则匹配。 路由 路由的功能就是分发请求到不同的控制器,基于的原理就是正则匹配。接下来呢,我们实现一个简单的路由器,实现的能力是 对于静态的路由(没占位符的),正确调用callback 对于有占位符的路由,正确调用callback时传入占位符参数,譬如对于路由:/user/{id},当请求为/user/23时,传入参数$args...
摘要:使开发人员可以编写高性能的异步并发,服务。使用作为网络通信框架,可以使企业研发团队的效率大大提升,更加专注于开发创新产品。总之,这个库让可以常驻内存,并提供了,等功能。 swoole 使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(...
摘要:使开发人员可以编写高性能的异步并发,服务。使用作为网络通信框架,可以使企业研发团队的效率大大提升,更加专注于开发创新产品。总之,这个库让可以常驻内存,并提供了,等功能。 swoole 使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP,WebSocket 服务。Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏、物联网(...
摘要:原理使用模板引擎的好处是数据和视图分离。对于循环语句怎么办呢这个的话,请看流程控制的替代语法 原理 使用模板引擎的好处是数据和视图分离。一个简单的PHP模板引擎原理是 extract数组($data),使key对应的变量可以在此作用域起效 打开输出控制缓冲(ob_start) include模板文件,include遇到html的内容会输出,但是因为打开了缓冲,内容输出到了缓冲中 ob...
摘要:的话,是一个遵循规范微型的框架,作者这样说大致意思的核心工作分发了请求,然后调用回调函数,返回一个对象。执行的方法时,我们从中取出的依赖,这时候,注册的回调函数被调用,返回实例。 Slim Slim的话,是一个遵循PSR (PSR-7)规范微型的框架,作者这样说: Slim is a PHP micro framework that helps you quickly write si...
阅读 1681·2021-11-12 10:36
阅读 1597·2021-11-12 10:36
阅读 3403·2021-11-02 14:46
阅读 3759·2019-08-30 15:56
阅读 3458·2019-08-30 15:55
阅读 1428·2019-08-30 15:44
阅读 1028·2019-08-30 14:00
阅读 2714·2019-08-29 18:41