從零開始編寫一個(gè)PHP框架 系列的《路由模塊》
項(xiàng)目地址:terse
前言
路由類左腔,是用戶請求的門神铃剔,它過濾了不符合當(dāng)前規(guī)則的鏈接。同時(shí)析恢,也美化了我們的 URL
。一個(gè)好的路由類秧饮,會(huì)讓我們的程序更加靈活映挂。
需求分析
- 可以指定請求類型泽篮,如果是
any
說明不限制類型 - 可以使用正則
- 可以指定控制器、方法及其前后綴
- 可以指定命名空間
文件結(jié)構(gòu)
└── Mvc
├── Router
│ └── Route.php [路由定義解析類]
└── Router.php [路由配置類]
路由配置類
指定請求類型的路由
我們想要實(shí)現(xiàn)一個(gè)類似于下面情況的路由:
<?php
$router = new Router();
$router->get('/', ['controller' => 'Home', 'action' => 'index']);
$router->post('/analysis.do', ['controller' => 'Home', 'action' => 'analysis']);
$router->get('/edit.do', ['controller' => 'Article', 'action' => 'edit']);
$router->post('/edit.do', ['controller' => 'Article', 'action' => 'doEdit']);
所以從上面可以看出柑船,我們可以給某一個(gè)路由添加不同的請求類型帽撑。
既然說到請求類型,那么就得用到上一篇?jiǎng)倢懞玫恼埱竽K了鞍时。
當(dāng)然亏拉,如果我們需要某一個(gè)路由無論什么請求方式都可以的話,可以考慮用 any
逆巍。
<?php
use Request;
class Router
{
const METHOD_ANY = 'any';
/**
* 路由信息
*
* @var array
*/
protected $_routes [];
/**
* 設(shè)置請求方式為GET的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function get($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_GET);
return $this;
}
/**
* 設(shè)置請求方式為POST的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function post($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_POST);
return $this;
}
/**
* 設(shè)置請求方式為PUT的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function put($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_PUT);
return $this;
}
/**
* 設(shè)置請求方式為DELETE的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function delete($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_DELETE);
return $this;
}
/**
* 設(shè)置請求方式為HEAD的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function head($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_HEAD);
return $this;
}
/**
* 設(shè)置請求方式為OPTIONS的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function options($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_OPTIONS);
return $this;
}
/**
* 設(shè)置請求方式為PATCH的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function patch($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_PATCH);
return $this;
}
/**
* 設(shè)置任意請求方式的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function any($uri, array $rules)
{
$this->add($uri, $rules, self::METHOD_ANY);
return false;
}
}
在上述代碼中及塘,我們看到,每一個(gè)方法都調(diào)用了 add
方法锐极,這個(gè)方法主要是用來統(tǒng)一定義路由的笙僚,如果有什么需要修改或者添加的,可以在這里擴(kuò)展灵再。
關(guān)于
add
方法里用到的Route
類肋层,會(huì)在后續(xù)說明。
<?php
use Request;
class Router
{
...
/**
* 添加一種規(guī)則
*
* @param string $uri
* @param array $rules
* @param string $method
*/
protected function add($uri, array $rules, $method)
{
$this->_routes[$method][] = new Route($uri, $rules);
}
}
懶人配置
路由添加好了翎迁,還可以考慮一下使用情況如下:
<?php
$router = new Router();
$router->get('/', ['controller' => '\APP\Controller\HomeController', 'action' => 'indexAction']);
$router->get('/version', ['controller' => '\APP\Controller\HomeController', 'action' => 'versionAction']);
$router->get('/about', ['controller' => '\APP\Controller\HomeController', 'action' => 'aboutAction']);
我們可以看到每個(gè)控制器都有一個(gè)后綴 Controller
和命名空間栋猖,每個(gè)方法都有一個(gè)后綴 Action
。(這個(gè)與實(shí)際項(xiàng)目規(guī)劃有關(guān))
可以想象汪榔,如果有一堆路由蒲拉,我們就得寫一堆的 Controller
、命名空間 和 Action
揍异。
懶人有懶招全陨,我們只需要給它兩個(gè)接口,去定義后綴是什么衷掷、命名空間是什么辱姨,就不需要每次都手打 Controller
、命名空間 和 Action
了戚嗅。
<?php
use Request;
class Router
{
/**
* 命名空間
*
* @var string
*/
protected $_namespace;
/**
* 控制器后綴
*
* @var string
*/
protected $_controllerSuffix;
/**
* 方法后綴
*
* @var string
*/
protected $_actionSuffix;
...
/**
* 設(shè)置命名空間
*
* @param string $namespace
*/
public function setNamespace($namespace)
{
$namespace = (string)$namespace;
$namespace = trim($namespace, '\\');
$this->_namespace = sprintf("\\%s", $namespace);
}
/**
* 設(shè)置控制器后綴
*
* @param string $suffix
*/
public function setControllerSuffix($suffix)
{
$this->_controllerSuffix = (string)$suffix;
}
/**
* 設(shè)置方法后綴
*
* @param string $suffix
*/
public function setActionSuffix($suffix)
{
$this->_actionSuffix = (string)$suffix;
}
}
默認(rèn)路由
再思考一下雨涛,如果用戶發(fā)了一個(gè)請求,而這個(gè)請求的路由并沒有定義懦胞,這個(gè)時(shí)候替久,我們不報(bào)錯(cuò),希望它能夠到一個(gè)指定的頁面躏尉,改怎么做蚯根?
默認(rèn)路由!U兔印颅拦!
<?php
use Request;
class Router
{
/**
* 默認(rèn)路由
*
* @var array
*/
protected $_default = [];
...
/**
* 設(shè)置默認(rèn)控制器
*
* @param string $controller
*/
public function setDefaultController($controller)
{
$this->_default['controller'] = (string)$controller;
}
/**
* 設(shè)置默認(rèn)方法
*
* @param string $action
*/
public function setDefaultAction($action)
{
$this->_default['action'] = (string)$action;
}
/**
* 設(shè)置默認(rèn)參數(shù)
*
* @param string $params
*/
public function setDefaultParams(array $params)
{
$this->_default['params'] = $params;
}
}
解析
所有配置都完成之后蒂誉,下面就涉及到解析路由了。
我們在存儲(chǔ)路由的時(shí)候距帅,用了一個(gè)數(shù)組右锨。不同請求類型的路由存放在不同的列表當(dāng)中,如果我們需要篩選出我們想要的路由碌秸,就需要對這個(gè)列表進(jìn)行遍歷绍移。當(dāng)然,如果想要減少遍歷次數(shù)讥电,也不是很難蹂窖,可以參考自動(dòng)加載模塊別名的設(shè)置與匹配。
過程分析:
1允趟、獲取當(dāng)前請求的路由
2恼策、獲取當(dāng)前請求方式
3、獲取此請求方式的列表和 any
列表
4潮剪、匹配路由
5涣楷、獲取匹配結(jié)果并存儲(chǔ)
<?php
use Request;
class Router
{
/**
* 匹配路由
*
* @var array
*/
protected $_matchRoute = [];
...
/**
* 路由解析
*
* @return void
*/
public function render()
{
// 獲取當(dāng)前請求的路由
$uri = $request->getServer('REQUEST_URI');
$baseUri = $this->parse2BaseUri($uri);
// 獲取當(dāng)前請求方式
$request = new Request();
$method = $request->getMethod();
// 獲取此請求方式的列表并匹配
$rules = [];
if (isset($this->_routes[$method])) {
$route = $this->getRules($method, $baseUri);
}
// 若沒有匹配到,則匹配 any 列表
if (!$route && isset($this->_routes[self::METHOD_ANY])) {
$route = $this->getRules(self::METHOD_ANY, $baseUri);
}
// 如果還是沒有匹配到抗碰,取默認(rèn)路由
if (!$route) {
$this->_matchRoute = $this->getDefaultMatchRoute();
return true;
}
// 獲取匹配到的信息
$controller = $route->getControllerName();
$controller = ltrim($controller, '\\');
$action = $route->getActionName();
$params = $route->getParams();
// 拼湊結(jié)果
$this->_matchRoute = $this->formatMatched($controller, $action, $params);
}
/**
* 匹配路由
*
* @param string $method
* @param string $uri
* @return mixed
*/
protected function getRules($method, $uri)
{
foreach ($this->_routes[$method] as $route) {
if ($route->match($uri)) {
return $route;
}
}
return false;
}
/**
* 獲取 base uri
*
* @param string $uri
* @return string
*/
protected function parse2BaseUri($uri)
{
if (!$uri) {
return '/';
}
$uriParts = explode('?', $uri);
$uri = reset($uriParts);
return $uri ? $uri : '/';
}
/**
* 獲取默認(rèn)匹配
*
* @throws \Exception
* @return array
*/
protected function getDefaultMatchRoute()
{
$default = $this->_default;
if (!$default
|| !isset($default['controller']) || !$default['controller']
|| !isset($default['action']) || !$default['action']) {
throw new \Exception('您訪問的網(wǎng)頁不存在', -__LINE__);
}
$params = isset($default['params']) ? $default['params'] : [];
return $this->formatMatched($default['controller'], $default['action'], $params);
}
/**
* 匹配結(jié)果格式化
*
* @param string $controller
* @param string $action
* @param array $params
* @return array
*/
protected function formatMatched($controller, $action, $params = [])
{
return [
'controller' => sprintf("%s\\%s%s", $this->_namespace, $controller, $this->_controllerSuffix),
'action' => sprintf("%s%s", $action, $this->_actionSuffix),
'params' => $params
];
}
}
獲取匹配結(jié)果
<?php
use Request;
class Router
{
/**
* 獲取匹配的路由
*
* @return array
*/
public function getMatchRoute()
{
return $this->_matchRoute;
}
}
路由定義解析類
在上面使用路由定義解析類的時(shí)候狮斗,我沒有多做解釋,主要是解釋之后弧蝇,就會(huì)有點(diǎn)混亂碳褒,還不如直接放到后面來介紹。
在使用過程中看疗,我們涉及到了幾個(gè)方法 __construct
沙峻、match
、getControllerName
两芳、getActionName
摔寨、getParams
。
現(xiàn)在開始介紹最后三個(gè)怖辆。
構(gòu)造函數(shù)
<?php
$router->get('/', ['controller' => 'Home', 'action' => 'index']);
$router->get('/articles/{gallery:[a-zA-Z]+}', ['controller' => 'Article', 'action' => 'list']);
從路由定義的角度來看是复,我們的構(gòu)造函數(shù)需要兩個(gè)參數(shù),分別是路由和路由信息竖螃。
過程分析:
1淑廊、設(shè)置路由和相關(guān)參數(shù)
2、如果路由里不包含正則特咆,則賦值類屬性
3季惩、如果路由里包含正則,則解析并返回路由正則表達(dá)式(這里用了PHP子組模式,有興趣的小伙伴可以看看)
<?php
class Route
{
/**
* 原始路由
*
* @var string
*/
protected $_pattern = '';
/**
* 解析后的正則表達(dá)式
*
* @var string
*/
protected $_compilePattern = '';
/**
* 路由正則參數(shù)
*
* @var array
*/
protected $_params = [];
/**
* 路由信息
*
* @var array
*/
protected $_rules = [
'controller' => '',
'action' => '',
'params' => []
];
/**
* 構(gòu)造函數(shù)
*
* @param string $pattern
* @param array $rules
*/
function __construct($pattern, array $rules)
{
$pattern = (string)$pattern;
// 解析路由規(guī)則
$this->reConfigure($pattern);
$this->_rules['controller'] = isset($rules['controller']) ? $rules['controller'] : '';
$this->_rules['action'] = isset($rules['action']) ? $rules['action'] : '';
$this->_rules['params'] = isset($rules['params']) ? $rules['params'] : [];
}
/**
* 解析路由規(guī)則
*
* @param string $pattern
*/
public function reConfigure($pattern)
{
if (!$pattern) {
throw new \Exception('路由規(guī)則不能為空', -__LINE__);
}
// 如果存在正則蜀备,則需要解析正則
if (strpos($pattern, '{') !== false) {
$prePattern = $this->extractNamePattern($pattern);
$this->_compilePattern = $prePattern;
} else {
$this->_compilePattern = $pattern;
}
$this->_pattern = $pattern;
}
/**
* 解析參數(shù)正則
*
* @param string $pattern
* @return string
*/
public function extractNamePattern($pattern)
{
$callback = function ($match) {
if (!isset($match[1])) {
return '';
}
$params = explode(':', $match[1]);
if (!isset($params[1])) {
return '';
}
list($ident, $pattern) = $params;
$this->_params[] = [
'ident' => $ident,
'pattern' => $pattern
];
return sprintf("(?'%s'%s)", $ident, $pattern);
};
return preg_replace_callback('/\B{([^}]+)}/x', $callback, $pattern);
}
}
獲取匹配參數(shù)
匹配好一個(gè)路由关摇,我們需要知道調(diào)用哪個(gè)控制器,哪個(gè)方法碾阁。所以這里需要解析類提供解析后的結(jié)果。
這里還有一個(gè)沒有做的些楣,那就是模塊脂凶。目前我自己寫的項(xiàng)目中,不同的模塊是使用域名來區(qū)分的愁茁,如果有需要可以后續(xù)加上蚕钦,也并不麻煩。
<?php
class Route
{
/**
* 路由信息
*
* @var array
*/
protected $_rules = [
'controller' => '',
'action' => '',
'params' => []
];
/**
* 獲取控制器名稱
*
* @return string
*/
public function getControllerName()
{
return $this->_rules['controller'];
}
/**
* 獲取方法名稱
*
* @return string
*/
public function getActionName()
{
return $this->_rules['action'];
}
/**
* 獲取參數(shù)
*
* @return array
*/
public function getParams()
{
return $this->_rules['params'];
}
}
匹配
過程分析:
1鹅很、如果請求路由和定義路由一致嘶居,直接返回真
2、不一致則獲取路由匹配正則促煮,查看是否匹配
3邮屁、如果匹配,則將匹配到的內(nèi)容拼湊成參數(shù)
<?php
class Route
{
/**
* 匹配
*
* @param string $uri
* @return bool
*/
public function match($uri)
{
// 如果路由和定義的一致菠齿,則直接返回真
if ($this->_pattern == $uri) {
return true;
}
// 拼湊正則
$preg = sprintf('#^%s$#u', $this->_compilePattern);
$matches = [];
// 匹配正則
if (preg_match($preg, $uri, $matches) == false) {
return false;
}
// 拼湊參數(shù)
foreach ($this->_params as $param) {
$ident = $param['ident'];
$this->_rules['params'][] = isset($matches[$ident]) ? $matches[$ident] : '';
}
return true;
}
}
完整代碼
路由設(shè)置類
<?php
namespace Terse\Mvc;
use Terse\Http\Request;
use Terse\Mvc\Router\Route;
/**
* Terse\Mvc\Router
*
* 路由設(shè)置
*
* @link https://gitee.com/imjcw/terse
* @author imjcw <imjcw@imjcw.com>
*/
class Router
{
const METHOD_ANY = 'any';
/**
* 路由信息
*
* @var array
*/
protected $_routes [];
/**
* 命名空間
*
* @var string
*/
protected $_namespace = '';
/**
* 控制器后綴
*
* @var string
*/
protected $_controllerSuffix = '';
/**
* 方法后綴
*
* @var string
*/
protected $_actionSuffix = '';
/**
* 匹配路由
*
* @var array
*/
protected $_matchRoute = [];
/**
* 默認(rèn)路由
*
* @var array
*/
protected $_default = [];
/**
* 構(gòu)造函數(shù)
*/
function __construct()
{
$this->_matchRoute = [];
$this->_namespace = '';
$this->_controllerSuffix = '';
$this->_actionSuffix = '';
}
/**
* 設(shè)置命名空間
*
* @param string $namespace
*/
public function setNamespace($namespace)
{
$namespace = (string)$namespace;
$namespace = trim($namespace, '\\');
$this->_namespace = sprintf("\\%s", $namespace);
}
/**
* 設(shè)置控制器后綴
*
* @param string $suffix
*/
public function setControllerSuffix($suffix)
{
$this->_controllerSuffix = (string)$suffix;
}
/**
* 設(shè)置方法后綴
*
* @param string $suffix
*/
public function setActionSuffix($suffix)
{
$this->_actionSuffix = (string)$suffix;
}
/**
* 設(shè)置默認(rèn)控制器
*
* @param string $controller
*/
public function setDefaultController($controller)
{
$this->_default['controller'] = (string)$controller;
}
/**
* 設(shè)置默認(rèn)方法
*
* @param string $action
*/
public function setDefaultAction($action)
{
$this->_default['action'] = (string)$action;
}
/**
* 設(shè)置默認(rèn)參數(shù)
*
* @param string $params
*/
public function setDefaultParams(array $params)
{
$this->_default['params'] = $params;
}
/**
* 設(shè)置請求方式為GET的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function get($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_GET);
return $this;
}
/**
* 設(shè)置請求方式為POST的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function post($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_POST);
return $this;
}
/**
* 設(shè)置請求方式為PUT的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function put($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_PUT);
return $this;
}
/**
* 設(shè)置請求方式為DELETE的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function delete($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_DELETE);
return $this;
}
/**
* 設(shè)置請求方式為HEAD的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function head($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_HEAD);
return $this;
}
/**
* 設(shè)置請求方式為OPTIONS的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function options($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_OPTIONS);
return $this;
}
/**
* 設(shè)置請求方式為PATCH的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function patch($uri, array $rules)
{
$this->add($uri, $rules, Request::METHOD_PATCH);
return $this;
}
/**
* 設(shè)置任意請求方式的規(guī)則
*
* @param string $uri
* @param array $rules
* @return \Terse\Mvc\Router
*/
public function any($uri, array $rules)
{
$this->add($uri, $rules, self::METHOD_ANY);
return false;
}
/**
* 路由解析
*
* @return void
*/
public function render()
{
// 獲取當(dāng)前請求的路由
$uri = $request->getServer('REQUEST_URI');
$baseUri = $this->parse2BaseUri($uri);
// 獲取當(dāng)前請求方式
$request = new Request();
$method = $request->getMethod();
// 獲取此請求方式的列表并匹配
$rules = [];
if (isset($this->_routes[$method])) {
$route = $this->getRules($method, $baseUri);
}
// 若沒有匹配到佑吝,則匹配 any 列表
if (!$route && isset($this->_routes[self::METHOD_ANY])) {
$route = $this->getRules(self::METHOD_ANY, $baseUri);
}
// 如果還是沒有匹配到,取默認(rèn)路由
if (!$route) {
$this->_matchRoute = $this->getDefaultMatchRoute();
return true;
}
// 獲取匹配到的信息
$controller = $route->getControllerName();
$controller = ltrim($controller, '\\');
$action = $route->getActionName();
$params = $route->getParams();
// 拼湊結(jié)果
$this->_matchRoute = $this->formatMatched($controller, $action, $params);
}
/**
* 獲取匹配的路由
*
* @return array
*/
public function getMatchRoute()
{
return $this->_matchRoute;
}
/**
* 添加一種規(guī)則
*
* @param string $uri
* @param array $rules
* @param string $method
*/
protected function add($uri, array $rules, $method)
{
$this->_routes[$method][] = new Route($uri, $rules);
}
/**
* 匹配路由
*
* @param string $method
* @param string $uri
* @return mixed
*/
protected function getRules($method, $uri)
{
foreach ($this->_routes[$method] as $route) {
if ($route->match($uri)) {
return $route;
}
}
return false;
}
/**
* 獲取 base uri
*
* @param string $uri
* @return string
*/
protected function parse2BaseUri($uri)
{
if (!$uri) {
return '/';
}
$uriParts = explode('?', $uri);
$uri = reset($uriParts);
return $uri ? $uri : '/';
}
/**
* 獲取默認(rèn)匹配
*
* @throws \Exception
* @return array
*/
protected function getDefaultMatchRoute()
{
$default = $this->_default;
if (!$default
|| !isset($default['controller']) || !$default['controller']
|| !isset($default['action']) || !$default['action']) {
throw new \Exception('您訪問的網(wǎng)頁不存在', -__LINE__);
}
$params = isset($default['params']) ? $default['params'] : [];
return $this->formatMatched($default['controller'], $default['action'], $params);
}
/**
* 匹配結(jié)果格式化
*
* @param string $controller
* @param string $action
* @param array $params
* @return array
*/
protected function formatMatched($controller, $action, $params = [])
{
return [
'controller' => sprintf("%s\\%s%s", $this->_namespace, $controller, $this->_controllerSuffix),
'action' => sprintf("%s%s", $action, $this->_actionSuffix),
'params' => $params
];
}
}
路由定義類
<?php
namespace Terse\Mvc\Router;
/**
* Terse\Mvc\Router\Route
*
* 路由規(guī)則解析和匹配
*
* @link https://gitee.com/imjcw/terse
* @author imjcw <imjcw@imjcw.com>
*/
class Route
{
/**
* 原始路由
*
* @var string
*/
protected $_pattern = '';
/**
* 解析后的正則表達(dá)式
*
* @var string
*/
protected $_compilePattern = '';
/**
* 路由正則參數(shù)
*
* @var array
*/
protected $_params = [];
/**
* 路由信息
*
* @var array
*/
protected $_rules = [
'controller' => '',
'action' => '',
'params' => []
];
/**
* 構(gòu)造函數(shù)
*
* @param string $pattern
* @param array $rules
*/
function __construct($pattern, array $rules)
{
$pattern = (string)$pattern;
// 解析路由規(guī)則
$this->reConfigure($pattern);
$this->_rules['controller'] = isset($rules['controller']) ? $rules['controller'] : '';
$this->_rules['action'] = isset($rules['action']) ? $rules['action'] : '';
$this->_rules['params'] = isset($rules['params']) ? $rules['params'] : [];
}
/**
* parse pattern
*
* @param string $pattern
*/
public function reConfigure($pattern)
{
if (!$pattern) {
throw new \Exception('pattern 不能為空', -__LINE__);
}
// 如果存在正則绳匀,則需要解析正則
if (strpos($pattern, '{') !== false) {
$prePattern = $this->extractNamePattern($pattern);
$this->_compilePattern = $prePattern;
} else {
$this->_compilePattern = $pattern;
}
$this->_pattern = $pattern;
}
/**
* 解析參數(shù)正則
*
* @param string $pattern
* @return string
*/
public function extractNamePattern($pattern)
{
$callback = function ($match) {
if (!isset($match[1])) {
return '';
}
$params = explode(':', $match[1]);
if (!isset($params[1])) {
return '';
}
list($ident, $pattern) = $params;
$this->_params[] = [
'ident' => $ident,
'pattern' => $pattern
];
return sprintf("(?'%s'%s)", $ident, $pattern);
};
return preg_replace_callback('/\B{([^}]+)}/x', $callback, $pattern);
}
/**
* 匹配
*
* @param string $uri
* @return bool
*/
public function match($uri)
{
// 如果路由和定義的一致芋忿,則直接返回真
if ($this->_pattern == $uri) {
return true;
}
// 拼湊正則
$preg = sprintf('#^%s$#u', $this->_compilePattern);
$matches = [];
// 匹配正則
if (preg_match($preg, $uri, $matches) == false) {
return false;
}
// 拼湊參數(shù)
foreach ($this->_params as $param) {
$ident = $param['ident'];
$this->_rules['params'][] = isset($matches[$ident]) ? $matches[$ident] : '';
}
return true;
}
/**
* 獲取控制器名稱
*
* @return string
*/
public function getControllerName()
{
return isset($this->_rules['controller']) ? $this->_rules['controller'] : '';
}
/**
* 獲取方法名稱
*
* @return string
*/
public function getActionName()
{
return isset($this->_rules['action']) ? $this->_rules['action'] : '';
}
/**
* 獲取參數(shù)
*
* @return array
*/
public function getParams()
{
return isset($this->_rules['params']) ? $this->_rules['params'] : [];
}
}
最后
這一篇沒有介紹太多,主要是細(xì)節(jié)比較多疾棵,直接讀代碼要比解釋的效果要好多了戈钢。
下一篇《中間件模塊》