处理复杂路由的方式

开发中总会遇到各种需求, 通过本文学习如何在CP中通过事件回调函数来灵活处理路由.

一, 实现方法

在入口文件中注册回调函数来处理复杂路由. 关于回调函数的介绍请参考文档 事件处理

二, 具体步骤

打开入口文件, 以htdocs/web/index.php为例, 编辑该文件

require __DIR__ . '/../../crossboot.php';
$app = Cross\Core\Delegate::loadApp('web');

这里, 我们只获取当前APP的实例, 不处理路由. 在这一行下面添加代码, 注册router事件时触发的匿名函数

$app->on('router', function ($request, \Cross\Core\Router $router) {


});

假设我们的路由路由格式如下:

$customRouters = [
'/news/:id/' => ['main:index', ':id' => '\d+'],
'/article/:id/[:page].[:order].html' => ['main:index', ':id' => '\d{1,5}', ':page' => '\d{1,3}', ':order' => '\w+'],
];

以上两条路由规则, 期望要匹配以下的URL:

/news/1/

news后面跟数字, 并且以/结尾

/article/1.html 

/article/1/1.html

/article/1/1.hot.html

article/id.html, id的长度为1-5位数字, page的长度为1-3位数字, order为任意字母加数字, 后缀为.html, 实现代码如下, 有兴趣的同学请自行研究, 还有优化的空间等空了再说吧.

require __DIR__ . '/../../crossboot.php';
$app = Cross\Core\Delegate::loadApp('web');

//定义回调匿名函数
$app->on('router', function ($request, \Cross\Core\Router $router) {

//自定义路由规则
$customRouters = [
'/news/:id/' => ['main:index', ':id' => '\d+'],
'/article/:id/[:page].[:order].html' => ['main:index', ':id' => '\d{1,5}', ':page' => '\w+', ':order' => '\w+'],
];

//匹配结果
$matchResult = array();
//匹配到的规则配置
$routerConfig = array();
$paramsNameMap = array();

//当前url
$requestUri = $router->getUriRequest();
foreach ($customRouters as $p => $c) {

//相等时直接终止匹配
if (0 === strcasecmp($p, $requestUri)) {
$routerConfig = $c;
$matchResult = $c;
break;
}

//先获取url后缀
$suffix = '';
$hasSuffix = preg_match('#(\.(\w+)|(\/?))$#', $p, $urlSuffix);
if ($hasSuffix) {
$suffix = $urlSuffix[0];
}

//提取规则中的表达式
$matched = preg_match_all("#(.*?)(:\w+|\[(:\w+)\])#", $p, $matches);
if (!$matched) {
continue;
}

$matchParams = [];
$firstMatches = &$matches[2];
$selectableMatches = $matches[3];
//判断规则中是否有选传参数, 如果有的话每一段都需要匹配
if (!empty(array_filter($selectableMatches))) {
$uriPattern = '';
foreach ($firstMatches as $i => $m) {
$token = '';
$subject = '';
if ($m[0] == ':') {
$token = $m;
$subject = $matches[0][$i];
} elseif ($m[0] === '[') {
$token = &$selectableMatches[$i];
$subject = str_replace(['[', ']'], '', $matches[0][$i]);
}

if (isset($c[$token])) {
$params_pattern = &$c[$token];
} else {
$params_pattern = '\w+';
}

$name = substr($token, 1);
$pRegx = '#' . $token . '#';
$replacement = '(?<' . $name . '>' . $params_pattern . ')';

$matchParams[$token] = $name;
$p = preg_replace($pRegx, $replacement, $subject);
$uriPattern = $uriPattern . $p;

$matchUriPattern = '#^' . $uriPattern . $suffix . '$#';
$isMatchUri = preg_match($matchUriPattern, $requestUri, $matchResult);
if ($isMatchUri) {
$routerConfig = $c;
$paramsNameMap = $matchParams;
break 2;
}
}

} else {
//全量匹配
$a = [];
$patterns = [];
$tokens = $matches[2];
$matchedSubject = '';
if (!empty($tokens)) {
foreach ($tokens as $i => $token) {
if (isset($c[$token])) {
$params_pattern = &$c[$token];
} else {
$params_pattern = '\w+';
}

$a[] = '#' . $token . '#';
$name = substr($token, 1);

$matchParams[$token] = $name;
$patterns[] = '(?<' . $name . '>' . $params_pattern . ')';
$matchedSubject .= $matches[0][$i];
}

$uriPattern = preg_replace($a, $patterns, $matchedSubject);
$matchUriPattern = '#^' . $uriPattern . $suffix . '$#';
$isMatchUri = preg_match($matchUriPattern, $requestUri, $matchResult);
if ($isMatchUri) {
$routerConfig = $c;
$paramsNameMap = $matchParams;
break;
}
}
}
}

if (!empty($matchResult)) {
//处理控制器
$controller = array_shift($routerConfig);
if (false !== strpos($controller, ':')) {
list($controller, $action) = explode(':', $controller);
} else {
$action = \Cross\Core\Router::DEFAULT_ACTION;
}

$router->setController($controller);
$router->setAction($action);

//处理参数
$params = [];
foreach ($routerConfig as $name => $rule) {
if ($name[0] === ':' && isset($paramsNameMap[$name])) {
$key = $paramsNameMap[$name];
$params[] = $key;
$params[] = $matchResult[$key];
} elseif ($name[0] !== ':') {
$params[] = $name;
$params[] = $rule;
}
}

$router->setParams($params);
}

});

$app->run();
image
请作者喝杯咖啡
user avatar
快来点个赞吧!
1 条评论

不错

4个月前 举报