|
@@ -1,8 +1,10 @@
|
|
<?php
|
|
<?php
|
|
namespace Qii\Base;
|
|
namespace Qii\Base;
|
|
|
|
|
|
|
|
+use Qii\Autoloader\Psr4;
|
|
use Qii\Config\Consts;
|
|
use Qii\Config\Consts;
|
|
use Qii\Config\Register;
|
|
use Qii\Config\Register;
|
|
|
|
+use Qii\Exceptions\Variable;
|
|
use Qii\Request\Http;
|
|
use Qii\Request\Http;
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -31,6 +33,11 @@ class Route
|
|
* @var array $stored
|
|
* @var array $stored
|
|
*/
|
|
*/
|
|
private $stored = [];
|
|
private $stored = [];
|
|
|
|
+ /**
|
|
|
|
+ * 存储默认 prefix
|
|
|
|
+ * @var array $default
|
|
|
|
+ */
|
|
|
|
+ private $default = [];
|
|
/**
|
|
/**
|
|
* 是否启用正则匹配
|
|
* 是否启用正则匹配
|
|
*
|
|
*
|
|
@@ -173,6 +180,7 @@ class Route
|
|
$uri = $request->getRequestUri();
|
|
$uri = $request->getRequestUri();
|
|
$middleware = [];
|
|
$middleware = [];
|
|
$callable = '';
|
|
$callable = '';
|
|
|
|
+ $args = null;
|
|
foreach ($route as $item) {
|
|
foreach ($route as $item) {
|
|
$middleware = $item[0];
|
|
$middleware = $item[0];
|
|
$subRoute = $item[1];
|
|
$subRoute = $item[1];
|
|
@@ -183,8 +191,12 @@ class Route
|
|
$subRoute[$method] = isset($subRoute[$method]) ? (array) $subRoute[$method] : array();
|
|
$subRoute[$method] = isset($subRoute[$method]) ? (array) $subRoute[$method] : array();
|
|
$routes = array_merge($subRoute[$method], $subRoute['ANY']);
|
|
$routes = array_merge($subRoute[$method], $subRoute['ANY']);
|
|
$callable = '';
|
|
$callable = '';
|
|
- $args = null;
|
|
|
|
foreach ($routes as $routeUri) {
|
|
foreach ($routes as $routeUri) {
|
|
|
|
+ //如果有设置默认,把默认规则拿出来
|
|
|
|
+ if(preg_match("/(\:default)/", $routeUri[0])) {
|
|
|
|
+ $arr = explode(":", $routeUri[0]);
|
|
|
|
+ $this->default[$arr[0]][$method] = [$middleware, $routeUri];
|
|
|
|
+ }
|
|
//启用正则匹配
|
|
//启用正则匹配
|
|
if($this->enableExpress) {
|
|
if($this->enableExpress) {
|
|
list($route, $args) = $this->parse($uri, $routeUri);
|
|
list($route, $args) = $this->parse($uri, $routeUri);
|
|
@@ -192,7 +204,7 @@ class Route
|
|
if(is_array($args)) {
|
|
if(is_array($args)) {
|
|
//将$args的值设置到get参数里
|
|
//将$args的值设置到get参数里
|
|
foreach ($args as $key => $value) {
|
|
foreach ($args as $key => $value) {
|
|
- $request->set($key, $value);
|
|
|
|
|
|
+ $request->set($key, urldecode($value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$callable = $route[1];
|
|
$callable = $route[1];
|
|
@@ -209,19 +221,20 @@ class Route
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!$callable || count($middleware) == 0) {
|
|
if (!$callable || count($middleware) == 0) {
|
|
- return $next($request);
|
|
|
|
|
|
+ //如果未命中规则,看看是否有默认规则
|
|
|
|
+ if(count($this->default) > 0) {
|
|
|
|
+ foreach ($this->default as $path => $value) {
|
|
|
|
+ if(substr($uri, 0, strlen($path)) == $path && isset($value[$method])) {
|
|
|
|
+ $middleware = $value[$method][0];
|
|
|
|
+ $callable = $value[$method][1][1];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if(!$callable) {
|
|
|
|
+ return $next($request);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- //命中后,执行相应程序后就不往下执行
|
|
|
|
- $middleware = array_reverse(array_unique($middleware));
|
|
|
|
- $result = array_reduce($middleware, function ($carry, $item){
|
|
|
|
- return function () use ($carry, $item){
|
|
|
|
- return _loadClass($item)->handle(\Qii::getInstance()->request, $carry);
|
|
|
|
- };
|
|
|
|
- }, function(){
|
|
|
|
- return true;
|
|
|
|
- })();
|
|
|
|
- if (!$result) return false;
|
|
|
|
- if (!$this->make($callable, $request, $args)){
|
|
|
|
|
|
+ if (!$this->make($callable, $middleware, $request, $args)){
|
|
return $next($request);
|
|
return $next($request);
|
|
}
|
|
}
|
|
return false;
|
|
return false;
|
|
@@ -236,13 +249,25 @@ class Route
|
|
* @return null[]
|
|
* @return null[]
|
|
*/
|
|
*/
|
|
public function parse($uri, $route) {
|
|
public function parse($uri, $route) {
|
|
|
|
+ //去掉后缀名,不匹配后缀名
|
|
|
|
+ $ext = ".". pathinfo($uri, PATHINFO_EXTENSION);
|
|
|
|
+ if(substr($uri, -1 * strlen($ext)) == $ext) {
|
|
|
|
+ $uri = substr($uri, 0,-1 * strlen($ext));
|
|
|
|
+ }
|
|
|
|
+ $routeExt = ".". pathinfo($route[0], PATHINFO_EXTENSION);
|
|
|
|
+ if(substr($route[0], -1 * strlen($routeExt)) == $routeExt) {
|
|
|
|
+ $route[0] = substr($route[0], 0,-1 * strlen($routeExt));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //优先精确匹配
|
|
if(preg_match("/\{.*?\}/i", $route[0])) {
|
|
if(preg_match("/\{.*?\}/i", $route[0])) {
|
|
$uriArray = explode("/", $uri);
|
|
$uriArray = explode("/", $uri);
|
|
$routeArray = explode("/", $route[0]);
|
|
$routeArray = explode("/", $route[0]);
|
|
- //如果uri的长度比route的端,那就直接不匹配
|
|
|
|
- if(count($uriArray) < count($routeArray)) {
|
|
|
|
|
|
+ //如果uri的长度比route不相等,且前缀不匹配就直接退出规则匹配
|
|
|
|
+ if(count($uriArray) != count($routeArray) && !preg_match("/(". str_replace("/", "\/", $uri).").*?/", $route[0])) {
|
|
return [null, null];
|
|
return [null, null];
|
|
}
|
|
}
|
|
|
|
+
|
|
$args = array();
|
|
$args = array();
|
|
$firstMatch = -1;
|
|
$firstMatch = -1;
|
|
foreach ($routeArray as $index => $val) {
|
|
foreach ($routeArray as $index => $val) {
|
|
@@ -251,63 +276,160 @@ class Route
|
|
$firstMatch = $index;
|
|
$firstMatch = $index;
|
|
}
|
|
}
|
|
$key = str_replace(array("{", "}"), "", $val);
|
|
$key = str_replace(array("{", "}"), "", $val);
|
|
- $args[$key] = $uriArray[$index];
|
|
|
|
|
|
+ $value = $uriArray[$index];
|
|
|
|
+ //如果key未xx:yy则yy表示xx的值类型,会做强制转换,支持的类型为number、string、bool、float
|
|
|
|
+ if(strpos($key, ":") > 0) {
|
|
|
|
+ $keyArr = explode(":", $key);
|
|
|
|
+ $key = $keyArr[0];
|
|
|
|
+ $value = $this->convertValueTo($uriArray[$index], $keyArr[1]);
|
|
|
|
+ }
|
|
|
|
+ $args[$key] = $value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//首次变量的位置之前的内容不匹配,页直接返回不匹配
|
|
//首次变量的位置之前的内容不匹配,页直接返回不匹配
|
|
- $uriMatch = join("/", array_slice($uriArray, 0, 4));
|
|
|
|
- $routeMatch = join("/", array_slice($routeArray, 0, 4));
|
|
|
|
|
|
+ $uriMatch = join("/", array_slice($uriArray, 0, $firstMatch));
|
|
|
|
+ $routeMatch = join("/", array_slice($routeArray, 0, $firstMatch));
|
|
if($uriMatch != $routeMatch) {
|
|
if($uriMatch != $routeMatch) {
|
|
return [null, null];
|
|
return [null, null];
|
|
}
|
|
}
|
|
return [$route, $args];
|
|
return [$route, $args];
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if($route[0] == $uri . "*") {
|
|
|
|
+ return [$route, null];
|
|
|
|
+ }
|
|
|
|
+ //匹配number
|
|
|
|
+ if(preg_match("/(:number)$/i", $route[0])) {
|
|
|
|
+ $exp = str_replace("/", "\\/", preg_replace("/(\:number)$/i", "", $route[0]));
|
|
|
|
+ $exp = "/(".$exp.")\d+?$/";
|
|
|
|
+ preg_match($exp, $uri, $match);
|
|
|
|
+ if($match) {
|
|
|
|
+ return [$route, null];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ //匹配任何字符
|
|
|
|
+ if(preg_match("/(\:any|\*)$/i", $route[0])) {
|
|
|
|
+ $exp = "(". preg_replace("/(\:any|\*)$/i", "", $route[0]) .")";
|
|
|
|
+ if(preg_match($exp, $uri)) {
|
|
|
|
+ return [$route, null];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
return [null, null];
|
|
return [null, null];
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 执行middleware
|
|
|
|
+ *
|
|
|
|
+ * @param array $middleware
|
|
|
|
+ * @return mixed|true
|
|
|
|
+ */
|
|
|
|
+ protected function handleMiddleware($middleware) {
|
|
|
|
+ //命中后,执行相应程序后就不往下执行
|
|
|
|
+ $middleware = array_reverse(array_unique((array) $middleware));
|
|
|
|
+ //获取全局middleware
|
|
|
|
+ return array_reduce($middleware, function ($carry, $item){
|
|
|
|
+ return function () use ($carry, $item){
|
|
|
|
+ return _loadClass($item)->handle(\Qii::getInstance()->request, $carry);
|
|
|
|
+ };
|
|
|
|
+ }, function(){
|
|
|
|
+ return true;
|
|
|
|
+ })();
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* 执行 $callable
|
|
* 执行 $callable
|
|
|
|
+ *
|
|
* @param mix $callable 执行方法
|
|
* @param mix $callable 执行方法
|
|
|
|
+ * @param array $middleware 中间件
|
|
* @param HTTP $request request
|
|
* @param HTTP $request request
|
|
* @param array | null $args 正则匹配后拿到的参数及值
|
|
* @param array | null $args 正则匹配后拿到的参数及值
|
|
* @return bool
|
|
* @return bool
|
|
|
|
+ * @throws Variable
|
|
*/
|
|
*/
|
|
- public function make($callable, $request, $args = null) {
|
|
|
|
|
|
+ public function make($callable, $middleware, $request, $args = null) {
|
|
if($args === null) {
|
|
if($args === null) {
|
|
$args = array($request);
|
|
$args = array($request);
|
|
}
|
|
}
|
|
if(is_array($args)) {
|
|
if(is_array($args)) {
|
|
array_unshift($args, $request);
|
|
array_unshift($args, $request);
|
|
}
|
|
}
|
|
|
|
+ $res = null;
|
|
switch ($callable) {
|
|
switch ($callable) {
|
|
case ($callable instanceof \Closure):
|
|
case ($callable instanceof \Closure):
|
|
- $res = call_user_func_array($callable, $args);
|
|
|
|
- if($res instanceof \Qii\Base\Response) {
|
|
|
|
- return $res->response();
|
|
|
|
|
|
+ if(count($middleware) > 0 && !$this->handleMiddleware($middleware)) {
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
- echo $res;
|
|
|
|
|
|
+ $res = call_user_func_array($callable, $args);
|
|
break;
|
|
break;
|
|
case is_array($callable):
|
|
case is_array($callable):
|
|
$action = (isset($callable[1]) ? $callable[1] : "index"). Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX);
|
|
$action = (isset($callable[1]) ? $callable[1] : "index"). Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX);
|
|
- $res = call_user_func_array(array(_loadClass($callable[0]), $action), $args);
|
|
|
|
- if($res instanceof \Qii\Base\Response) {
|
|
|
|
- return $res->response();
|
|
|
|
|
|
+ $controllerCls = _loadClass($callable[0]);
|
|
|
|
+ if(method_exists($controllerCls, 'beforeRun') && !$controllerCls->beforeRun()) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(count($middleware) > 0 && !$this->handleMiddleware($middleware)) {
|
|
|
|
+ return true;
|
|
}
|
|
}
|
|
|
|
+ if(method_exists($controllerCls, 'initView')) {
|
|
|
|
+ $controllerCls->initView();
|
|
|
|
+ }
|
|
|
|
+ if(method_exists($controllerCls, 'initialization')) {
|
|
|
|
+ $controllerCls->initialization();
|
|
|
|
+ }
|
|
|
|
+ $res = call_user_func_array(array($controllerCls, $action), $args);
|
|
break;
|
|
break;
|
|
case gettype($callable) == 'string':
|
|
case gettype($callable) == 'string':
|
|
|
|
+ if(count($middleware) > 0 && !$this->handleMiddleware($middleware)) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
$res = call_user_func_array(
|
|
$res = call_user_func_array(
|
|
array(_loadClass($callable),
|
|
array(_loadClass($callable),
|
|
Register::get("APP_DEFAULT_ACTION"). Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX)), $args);
|
|
Register::get("APP_DEFAULT_ACTION"). Register::get(Consts::APP_DEFAULT_ACTION_SUFFIX)), $args);
|
|
- if($res instanceof \Qii\Base\Response) {
|
|
|
|
- return $res->response();
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+ if($res instanceof Response) {
|
|
|
|
+ echo $res->response();
|
|
|
|
+ }else if($res !== null){
|
|
|
|
+ echo $res;
|
|
|
|
+ }
|
|
if ($callable) {
|
|
if ($callable) {
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 将值转换为指定类型
|
|
|
|
+ *
|
|
|
|
+ * @param $value
|
|
|
|
+ * @param $to
|
|
|
|
+ * @return bool|mixed|string
|
|
|
|
+ */
|
|
|
|
+ protected function convertValueTo($value, $to) {
|
|
|
|
+ if(!in_array($to, ['number', 'string', 'bool'])) {
|
|
|
|
+ return $value;
|
|
|
|
+ }
|
|
|
|
+ switch ($to) {
|
|
|
|
+ case 'number':
|
|
|
|
+ //取字符串中连续数字
|
|
|
|
+ preg_match('/(\d+)/',$value,$r);
|
|
|
|
+ if($r) {
|
|
|
|
+ return $r[0];
|
|
|
|
+ }
|
|
|
|
+ return 0;
|
|
|
|
+ case 'string':
|
|
|
|
+ return (string) $value;
|
|
|
|
+ case 'bool':
|
|
|
|
+ if(in_array($value, ['true', 'false'])) {
|
|
|
|
+ return $value == 'true';
|
|
|
|
+ }
|
|
|
|
+ return (bool) $value;
|
|
|
|
+ }
|
|
|
|
+ return "";
|
|
|
|
+ }
|
|
/**
|
|
/**
|
|
* 获取所有设置的路由
|
|
* 获取所有设置的路由
|
|
*
|
|
*
|