| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- <?php
- namespace Heanup\Frame;
- use Heanup\Config\Route;
- use Heanup\Frame\Logger;
- class Dispatcher
- {
- const CONFIG_PREFIX = '\\Heanup\\Config';
- const CONTROLLER_ERROR = 'Error';
- const CONTROLLER_DEFAULT = 'Index';
- const METHOD_DEFAULT = 'main';
- /**
- * 使用方法
- * @param $customPrefix
- * @param string $pathInfo
- */
- public static function run($customPrefix, $pathInfo = '')
- {
- // 获取前缀
- $prefix = '\Heanup\\App\\' . self::convertToClassName($customPrefix) . "\\Controller";
- if (!$pathInfo) {
- $pathInfo = self::getPathInfo();
- }
- $pathInfo = self::convertToClassName($pathInfo);
- $pathInfo = explode('\\', $pathInfo);
- if (count($pathInfo) > 1) {
- $method = array_pop($pathInfo);
- } else {
- $method = self::METHOD_DEFAULT;
- }
- $controller = $prefix . '\\' . implode('\\', $pathInfo);
- // 加入一次出错重试的机会
- for ($i = 0; $i < 2; $i++) {
- try {
- // 判断是否是公开方法
- $reflection = new \ReflectionMethod($controller, $method);
- if ($reflection->isPublic() == false) {
- throw new \Exception($controller . "::$method is not public");
- }
- $controller = new $controller();
- $controller->$method();
- break;
- } catch (\Exception $ex) {
- Logger::emerg($ex, 'dispatcher.run');
- $controller = $prefix . '\\' . self::CONTROLLER_ERROR;
- $method = self::METHOD_DEFAULT;
- }
- }
- }
- /**
- * 分发请求
- * @param string $customPrefix
- * @param string $pathInfo
- */
- public static function dispatch($customPrefix, $pathInfo = '')
- {
- header("HTTP/1.1 200");
- header("Access-Control-Allow-Origin: *");
- header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE");
- header("Access-Control-Allow-Headers: *");
- // 获取前缀
- $prefix = '\Heanup\\App\\' . self::convertToClassName($customPrefix) . "\\Controller";
- if (!$pathInfo) {
- $pathInfo = self::getPathInfo();
- }
- $pathInfo = self::convertToClassName($pathInfo);
- try {
- $path = explode('\\', $pathInfo);
- $id = array_pop($path);
- if (is_numeric($id)) {
- $_GET['id'] = $id;
- $pathInfo = implode('\\', $path);
- }
- $controller = $prefix . '\\' . $pathInfo;
- // 如果类不存在则使用默认的错误类
- if (!class_exists($controller)) {
- //类不存在则尝试通过自定义路由解析
- $url = $_SERVER["REQUEST_URI"];
- // 去除 ?后面的部分
- $request = parse_url($url);
- $url = $request['path'];
- $route = Route::getData($customPrefix);
- foreach ($route as $val) {
- $pattern = $val["pattern"];
- $params = self::checkUrl(strtolower($url), strtolower($pattern));
- if ($params != null) {
- $controller = $val["action"];
- $count = count($params);
- for ($i = 0; $i < $count; $i++) {
- $_GET['args_' . $i] = $params[$i];
- }
- break;// 寻找到第一个匹配的路由即执行,然后返回
- }
- }
- if (!class_exists($controller)) {
- Logger::error($controller, 'dispatcher.controller_not_exist');
- throw new \Exception($controller . ' not exist');
- }
- }
- // 判断主方法
- if (!method_exists($controller, 'run')) {
- throw new \Exception($controller . ' has no method called "run"');
- }
- $controller = new $controller();
- $controller->run();
- } catch (\Exception $ex) {
- $method = strtolower($_SERVER['REQUEST_METHOD']);
- if ($method == 'options') {
- header("HTTP/1.1 200");
- header("Access-Control-Allow-Origin: *");
- header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE");
- header("Access-Control-Allow-Headers: lang,token,content-type");
- die();
- }
- Response::sendResponse(404, [], $ex->getMessage());
- Logger::emerg($ex, 'dispatcher.dispatch');
- }
- }
- /**
- * 正则匹配检查,并提取出参数
- * @param $str
- * @param $pattern
- * @return array|null
- */
- private static function checkUrl($str, $pattern)
- {
- $ma = array();
- $pattern = ltrim(rtrim($pattern, "/"));
- $pattern = "/" . str_replace("/", "\/", $pattern) . "\/?$/";
- $pattern = str_replace(":s", "([^\/]+)", $pattern);
- if (preg_match($pattern, $str, $ma) > 0) {
- array_shift($ma);
- return $ma;
- }
- return null;
- }
- /**
- * 获取pathInfo, 兼容Apache, Nginx
- * 如果使用Apache的话可以直接使用 $_SERVER['PATH_INFO']的
- * @return string
- */
- protected static function getPathInfo()
- {
- //去掉uri中的PHP_SELF 信息
- $length = strlen($_SERVER['PHP_SELF']);
- if (substr($_SERVER['REQUEST_URI'], 0, $length) == $_SERVER['PHP_SELF']) {
- $request = substr($_SERVER['REQUEST_URI'], $length);
- } else {
- $request = $_SERVER['REQUEST_URI'];
- }
- // 去除 ?后面的部分
- $request = parse_url($request);
- $request = $request['path'];
- $request = urldecode($request);
- $request = trim($request, '/_ ');
- // 去掉 '.' 后缀
- if (strpos($request, '.') !== false) {
- $request = explode('.', $request);
- $request = array_shift($request);
- }
- if (empty($request)) {
- $request = self::CONTROLLER_DEFAULT;
- }
- return $request;
- }
- /**
- * 将字符串转成带命名空间的类名
- * @param $string
- * @return string
- */
- public static function convertToClassName($string)
- {
- $string = trim($string, '\\/_ ');
- $string = strtolower($string);
- $string = str_replace('/', '\\', $string);
- $string = str_replace(' ', '\\', ucwords(str_replace('\\', ' ', $string)));
- $string = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
- return $string;
- }
- }
|