request = $request; } /** * 完整控制器名 * @return mixed */ public function controllerFullName() { return $this->controllerFullName; } /** * 控制器名 * @return mixed */ public function controller() { return $this->controller; } /** * 模块名 * @return mixed */ public function module() { return $this->module; } /** * 方法名 * @return mixed */ public function method() { return $this->method; } /** * 请求参数 * @return mixed */ public function params() { return $this->params; } /** * 是否绑定模块 * @param bool $isBindModule */ public function isBindModule($isBindModule) { $this->isBindModule = $isBindModule; } /** * 查找最适合的路由匹配 * @param $rules * @param $uri * @return mixed */ private function findRule($rules, $uri) { $result = []; if ($uri == '/' && isset($rules['/'])) { $result[] = '/'; } else { // 如果不是首页则unset掉首页的规则,避免参与计算导致出错 unset($rules['/']); $keys = array_keys($rules); foreach ($keys as $key) { $pos = strpos($uri, $key); if ($pos !== false) { $endPos = $pos + strlen($key); $result[$endPos] = $key; } } } if (!empty($result)) { $max = max(array_keys($result)); $rest = str_replace($result[$max], '', $uri); if (($result[$max] == '/' && $uri != '/') || ($rest != '' && substr($rest, 0, 1) != '/')) { return false; } else { $this->loadRuleParameters = $rules[$result[$max]]; return [ 'rule' => $result[$max], 'parameters' => $this->loadRuleParameters, ]; } } return false; } /** * 解析路由规则 * @param $requestMethod * @param $uri * @return array|bool|mixed */ private function parseRouteRule($requestMethod, $uri) { // 获取所有路由配置(可能从缓存文件读取) $routeConfig = $this->getRouteConfig(); $rule = []; if (isset($routeConfig[$requestMethod])) { // 第一次去当前请求方法中查找 $rule = $this->findRule($routeConfig[$requestMethod], $uri); } if (empty($rule) && isset($routeConfig['any'])) { // 全部中查找 $rule = $this->findRule($routeConfig['any'], $uri); } return (!empty($rule)) ? $rule : false; } /** * 普通路由处理 * @param $uri * @return array */ private function parseRoute($uri) { // 普通处理 $uriArray = explode('/', trim($uri, '/')); // 如果没有参数,则使用默认的控制器和方法 $uriArray[0] = (isset($uriArray[0]) && $uriArray[0]) ? $uriArray[0] : config('default_controller'); $uriArray[1] = (isset($uriArray[1]) && $uriArray[1]) ? $uriArray[1] : config('default_method'); $controller = ucfirst($uriArray[0]); $rule['class'] = APP_NS . '\\' . $this->module . '\\controller\\' . $controller; $rule['method'] = $uriArray[1]; return [ 'rule' => $uriArray[0] . '/' . $uriArray[1], 'parameters' => $rule, ]; } /** * 解析请求参数 * @param $class * @param $method * @param $prefix * @param $uri * @return array */ private function parseParameters($class, $method, $prefix, $uri) { $paramsString = ltrim(substr_replace($uri, '', 0, strlen($prefix)), '/'); $paramsArray = explode('/', $paramsString); $params = []; if (config('complete_parameter')) { // 如果开启了完全参数名 for ($i = 0; $i < count($paramsArray); $i += 2) { if (isset($paramsArray[$i + 1])) { $_GET[$paramsArray[$i]] = $params[$paramsArray[$i]] = $paramsArray[$i + 1]; } } } else { // 未开启完全参数名,则利用反射得到参数名做映射 if (!empty($paramsArray)) { $reflectionMethod = Application::getReflectionMethod($class, $method); $index = 0; foreach ($reflectionMethod->getParameters() as $parameter) { $className = $parameter->getClass(); if (is_null($className)) { $_GET[$parameter->name] = $params[$parameter->name] = $paramsArray[$index]; $index++; } } } } return $params; } /** * 执行应用 * @return mixed * @throws RouteException */ public function execute() { try { // 绑定模块优先 if ($this->isBindModule) { $this->module = BIND_MODULE; $uri = $this->request->uri(); } else { // 如果没有直接绑定模块,则从链接获取 $rawUri = $this->request->uri(); if (!$rawUri) { $this->module = DEFAULT_MODULE; $uri = $rawUri; } else { // 取第一部分为模块名称 $pos = strpos($rawUri, '/'); if (false !== $pos) { $uri = substr($rawUri, $pos + 1); $this->module = substr($rawUri, 0, $pos); } else { $this->module = $uri = $rawUri; } } } define('CURRENT_MODULE', $this->module()); Application::afterRouter(); // 处理路由 $this->handler($uri); } catch (RouteException $exception) { if (!DEBUG) { // 非调试模式直接404 return \response()->code(404)->send(); } else throw $exception; } // 路由中间件处理 return $this->middleware(function () { return Application::callMethod($this->controllerFullName, $this->method, $this->params); }); } /** * 中间件处理 * @param \Closure $application * @return mixed */ public function middleware(\Closure $application) { // 加载全局配置文件中配置的中间件 $middleware = array_reverse(config('middleware')); // 配置中不执行的中间件 $exceptMiddlewareArray = []; if (isset($this->loadRuleParameters['except_middleware']) && $this->loadRuleParameters['except_middleware'] != '' ) { $exceptMiddlewareArray = $this->loadRuleParameters['except_middleware']; } // 配置中新增的中间件 if (isset($this->loadRuleParameters['accept_middleware']) && $this->loadRuleParameters['accept_middleware'] != '' ) { $acceptMiddlewareArray = $this->loadRuleParameters['accept_middleware']; foreach ($acceptMiddlewareArray as $acceptMiddleware) { if (!in_array($acceptMiddleware, $middleware)) { $middleware[] = $acceptMiddleware; } else continue; } } // 应用打包在在洋葱圈最里层 $next = $application; foreach ($middleware as $value) { if (!in_array($value, $exceptMiddlewareArray)) { $next = function () use ($next, $value) { $middleware = new $value; if ($middleware instanceof MiddlewareIfs) { return $middleware->handler($this->request, $next); } else throw new RouteException('无效的中间件:' . $value); }; } } return $next(); } /** * 处理URI * @param $uri * @throws RouteException */ public function handler($uri) { // 检查模块是否存在 if (!is_dir(APP_PATH . $this->module)) { throw new RouteException('不存在的模块:' . $this->module); } // 如果为空则默认为/ $uri = $uri ? $uri : '/'; $defaultMethod = config('default_method'); $requestMethod = strtolower($this->request->requestMethod()); // 第一次用原始uri去做匹配,第二次带上默认方法去做匹配 if (false === ($rule = $this->parseRouteRule($requestMethod, $uri)) && false === ($rule = $this->parseRouteRule($requestMethod, $uri . '/' . $defaultMethod)) ) { // 如果开启强制路由,则抛异常 if (config('compel_route') === true) { throw new RouteException('不支持的路由规则:' . strtoupper($requestMethod) . ' ' . $uri); } else { // 进行普通处理 $rule = $this->parseRoute($uri); } } $ruleParameters = $rule['parameters']; $this->controllerFullName = $ruleParameters['class']; $this->controller = substr($this->controllerFullName, strrpos($ruleParameters['class'], '\\') + 1); $this->method = $ruleParameters['method']; // 此处还需要检查控制器和方法是否存在 if (!class_exists($this->controllerFullName)) { throw new RouteException('不存在的控制器:' . $this->controllerFullName); } if (!method_exists($this->controllerFullName, $this->method)) { throw new RouteException('不存在的方法:' . $this->method); } $this->params = $this->parseParameters($ruleParameters['class'], $ruleParameters['method'], $rule['rule'], $uri); } /** * 创建路由配置缓存文件 * @return array|mixed */ public function getRouteConfig() { $fileName = './runtime/' . $this->module . '_route_cache.php'; if (!DEBUG && is_file($fileName)) { return require $fileName; } else { $result = []; $controllerPath = APP_PATH . $this->module . '/controller/'; $namespace = APP_NS . '\\' . $this->module . '\\controller'; $files = scandir($controllerPath); for ($i = 2; $i < count($files); $i++) { $className = $namespace . '\\' . pathinfo($files[$i])['filename']; $reflectionClass = Application::getReflectionClass($className); foreach ($reflectionClass->getMethods() as $method) { if ($method->class == $className && substr($method->name, 0, 1) != '_') { $annotation = Annotation::getMethodAnnotation($className, $method->name); $requestMethod = (isset($annotation['requestMethod'])) ? $annotation['requestMethod'] : 'any'; if (isset($annotation['route'])) { $requestUri = $annotation['route']; } else continue; $requestMethod = strtolower($requestMethod); $rule = ($requestUri == '/') ? $requestUri : trim($requestUri, '/'); $result[$requestMethod][$rule] = [ 'class' => $className, 'method' => $method->name, 'except_middleware' => [], 'accept_middleware' => [], ]; if (isset($annotation['exceptMiddleware']) && $annotation['exceptMiddleware'] != '') { foreach (explode('|', $annotation['exceptMiddleware']) as $exceptMiddleware) { $result[$requestMethod][$rule]['except_middleware'][] = $exceptMiddleware; } } if (isset($annotation['acceptMiddleware']) && $annotation['acceptMiddleware'] != '') { foreach (explode('|', $annotation['acceptMiddleware']) as $acceptMiddleware) { $result[$requestMethod][$rule]['accept_middleware'][] = $acceptMiddleware; } } } } } // 加载配置文件中的路由配置 $routeConfigFile = CONFIG_DIR . 'route.php'; if (is_file($routeConfigFile)) { $routeConfig = require $routeConfigFile; foreach ($routeConfig as $key => $value) { if (isset($result[$key])) { // 存在当前请求方法的配置就检查含有的路由配置 foreach ($value as $uri => $config) { $uri = ($uri == '/') ? $uri : trim($uri, '/'); if (isset($result[$key][$uri])) { // 如果已经存在这个路由配置,可能不完全,直接合并覆盖已有项 $result[$key][$uri] = array_merge($result[$key][$uri], $config); } else { $result[$key][$uri] = $config; } } } else { $result[$key] = $value; } } } // 写入文件 ob_start(); var_export($result); $content = ob_get_contents(); ob_clean(); file_put_contents($fileName, "