编码工作中框架给我们提供了很多便利,自己更多处理的是业务层的代码,而很少接触较为基础的东西。
趁最近有空,自己想着写了一个处理PATH_INFO模式URL的路由类。因为自己工作常用框架是TP5,所以有些风格习惯定义和TP5很像。
完整DEMO请参阅我的码云:https://gitee.com/VwenX/Router
下面是路由部分核心类,URL解析后用反射API处理参数然后调用对应方法。
// 定义常量
define('URL_PATH',isset($_SERVER['PATH_INFO']) ? preg_replace('/\/+/','/',strtolower($_SERVER['PATH_INFO'])) : '/');
define('APP_PATH', 'app');
define('Control_PATH', 'controller');
class Route
{
// 路由规则
private static $rules = [
'get' => [],
'post' => [],
'put' => [],
'delete' => [],
'*' => []
];
/**
* 绑定路由
* @param string $rule 路由规则
* @param string $route 路由地址
* @param string $type 请求类型
* By.Vwen https://gitee.com/VwenX/Router
*/
static function rule($rule, $route = '', $type = '*')
{
$rule = strtolower($rule);
$rule = '/'==substr($rule,0,1) ? $rule : '/'.$rule;
$route = strtolower($route);
$type = strtolower($type);
if ('*' == $type) {
foreach (self::$rules as $key => $value) {
self::$rules[$key][$rule] = $route;
}
}else{
self::$rules[$type][$rule] = $route;
self::$rules['*'][$rule] = $route;
}
}
/**
* 提供快捷绑定
* 如 Route::get() Route::post()
* 仅为概念简写,实际应用请按需修改及验证
*/
static function __callStatic($func, $arguments)
{
$arguments[] = $func;
self::rule(...$arguments);
}
/**
* 解析当前请求URL
* @return array 路由信息数组
*/
static function parseUrl()
{
$analysis = ['status'=>404];
$METHOD = strtolower($_SERVER['REQUEST_METHOD']);
// 直接匹配
if (isset(self::$rules[$METHOD][URL_PATH])) {
$analysis['status'] = 200;
$analysis['rule'] = URL_PATH;
$analysis['route'] = self::$rules[$METHOD][URL_PATH];
}else{
// 列表匹配
foreach (self::$rules[$METHOD] as $rule => $route) {
if (substr($rule,-1)=='$' && substr_count($rule,'/')!=substr_count(URL_PATH,'/')) {
continue ;
}
$reg = '/^'.str_replace('/','\/',preg_replace('/:[a-z]+(?=\/|\$|$)/','\S+',$rule)).'/';
if (!preg_match($reg,URL_PATH)) {
continue ;
}
$analysis['status'] = 200;
$analysis['rule'] = $rule;
$analysis['route'] = $route;
break;
}
}
// 处理未注册的路由 /M/C/A
if (!isset($analysis['route'])) {
$path_list = explode('/', trim( URL_PATH,'/'));
$module = isset($path_list[0]) ? $path_list[0] : 'index';
$Controller = isset($path_list[1]) ? $path_list[1] : 'index';
$action = isset($path_list[2]) ? $path_list[2] : 'index';
$analysis['rule'] = $analysis['route'] = '/'.$module.'/'.$Controller.'/'.$action;
}
// 获取路由参数
if (isset($analysis['route'])) {
$param = [];
$rule_list = explode('/', $analysis['rule']);
$path_list = explode('/', URL_PATH);
if(strpos($analysis['rule'], ':')!==false){
foreach ($rule_list as $key => $value) {
if (substr($value,0,1)==':') {
$param[trim($value,':$')] = $path_list[$key];
}
}
}
// 获取剩余参数
for ($i=count($rule_list); $i < count($path_list); $i+=2) {
if (isset($path_list[$i+1])) {
$param[$path_list[$i]] = $path_list[$i+1];
}
}
$analysis['param'] = self::Params($param);
// 在控制器方法传参中用 $req 获取请求参数
$analysis['param']['req'] = $analysis['param'];
}
return $analysis;
}
/**
* 处理请求参数
* @param array $param PATH_INFO参数
* @return array
*/
static function Params($param=[])
{
return array_merge($param,$_GET,$_POST);
}
/**
* 生成URL
* @param string $route 路由地址 模块/控制器/方法
* @param array $vars 参数数组
* @param string $method 按特定请求模式解析
* @return string 处理好的URL
*/
static function getUrl($route='',$vars=[],$method='*')
{
$route = strtolower(trim($route,'/'));
$method = strtolower($method);
$rule = array_search($route,self::$rules[$method]);
if (!$rule) {
return '';
}
$rule_list = explode('/', trim($rule,'/'));
// 处理预定义参数
if (strpos($rule, ':')!==false) {
foreach ($rule_list as $key => $value) {
if (strpos($value, ':')!==false) {
$rule_list[$key] = isset($vars[trim($value,':$')]) ? $vars[trim($value,':$')] : '';
if (isset($vars[trim($value,':$')])) unset($vars[trim($value,':$')]);
}
}
}
// 处理剩余参数
if (strpos($rule, '$')==false && $vars != '') {
foreach ($vars as $key => $value) {
$rule_list[] = $key;
$rule_list[] = $value;
}
}
$rule = rtrim('/'.implode('/',$rule_list),'$');
return $rule;
}
}