Dispatcher.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. <?php
  2. namespace Heanup\Frame;
  3. use Heanup\Config\Route;
  4. use Heanup\Frame\Logger;
  5. class Dispatcher
  6. {
  7. const CONFIG_PREFIX = '\\Heanup\\Config';
  8. const CONTROLLER_ERROR = 'Error';
  9. const CONTROLLER_DEFAULT = 'Index';
  10. const METHOD_DEFAULT = 'main';
  11. /**
  12. * 使用方法
  13. * @param $customPrefix
  14. * @param string $pathInfo
  15. */
  16. public static function run($customPrefix, $pathInfo = '')
  17. {
  18. // 获取前缀
  19. $prefix = '\Heanup\\App\\' . self::convertToClassName($customPrefix) . "\\Controller";
  20. if (!$pathInfo) {
  21. $pathInfo = self::getPathInfo();
  22. }
  23. $pathInfo = self::convertToClassName($pathInfo);
  24. $pathInfo = explode('\\', $pathInfo);
  25. if (count($pathInfo) > 1) {
  26. $method = array_pop($pathInfo);
  27. } else {
  28. $method = self::METHOD_DEFAULT;
  29. }
  30. $controller = $prefix . '\\' . implode('\\', $pathInfo);
  31. // 加入一次出错重试的机会
  32. for ($i = 0; $i < 2; $i++) {
  33. try {
  34. // 判断是否是公开方法
  35. $reflection = new \ReflectionMethod($controller, $method);
  36. if ($reflection->isPublic() == false) {
  37. throw new \Exception($controller . "::$method is not public");
  38. }
  39. $controller = new $controller();
  40. $controller->$method();
  41. break;
  42. } catch (\Exception $ex) {
  43. Logger::emerg($ex, 'dispatcher.run');
  44. $controller = $prefix . '\\' . self::CONTROLLER_ERROR;
  45. $method = self::METHOD_DEFAULT;
  46. }
  47. }
  48. }
  49. /**
  50. * 分发请求
  51. * @param string $customPrefix
  52. * @param string $pathInfo
  53. */
  54. public static function dispatch($customPrefix, $pathInfo = '')
  55. {
  56. // 获取前缀
  57. $prefix = '\Heanup\\App\\' . self::convertToClassName($customPrefix) . "\\Controller";
  58. if (!$pathInfo) {
  59. $pathInfo = self::getPathInfo();
  60. }
  61. $pathInfo = self::convertToClassName($pathInfo);
  62. try {
  63. $path = explode('\\', $pathInfo);
  64. $id = array_pop($path);
  65. if (is_numeric($id)) {
  66. $_GET['id'] = $id;
  67. $pathInfo = implode('\\', $path);
  68. }
  69. $controller = $prefix . '\\' . $pathInfo;
  70. // 如果类不存在则使用默认的错误类
  71. if (!class_exists($controller)) {
  72. //类不存在则尝试通过自定义路由解析
  73. $url = $_SERVER["REQUEST_URI"];
  74. // 去除 ?后面的部分
  75. $request = parse_url($url);
  76. $url = $request['path'];
  77. $route = Route::getData($customPrefix);
  78. foreach ($route as $val) {
  79. $pattern = $val["pattern"];
  80. $params = self::checkUrl(strtolower($url), strtolower($pattern));
  81. if ($params != null) {
  82. $controller = $val["action"];
  83. $count = count($params);
  84. for ($i = 0; $i < $count; $i++) {
  85. $_GET['args_' . $i] = $params[$i];
  86. }
  87. break;// 寻找到第一个匹配的路由即执行,然后返回
  88. }
  89. }
  90. if (!class_exists($controller)) {
  91. Logger::error($controller, 'dispatcher.controller_not_exist');
  92. throw new \Exception($controller . ' not exist');
  93. }
  94. }
  95. // 判断主方法
  96. if (!method_exists($controller, 'run')) {
  97. throw new \Exception($controller . ' has no method called "run"');
  98. }
  99. $controller = new $controller();
  100. $controller->run();
  101. } catch (\Exception $ex) {
  102. $method = strtolower($_SERVER['REQUEST_METHOD']);
  103. if ($method == 'options') {
  104. header("HTTP/1.1 200");
  105. header("Access-Control-Allow-Origin: *");
  106. header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE");
  107. header("Access-Control-Allow-Headers: lang,token,content-type");
  108. die();
  109. }
  110. Response::sendResponse(404, [], $ex->getMessage());
  111. Logger::emerg($ex, 'dispatcher.dispatch');
  112. }
  113. }
  114. /**
  115. * 正则匹配检查,并提取出参数
  116. * @param $str
  117. * @param $pattern
  118. * @return array|null
  119. */
  120. private static function checkUrl($str, $pattern)
  121. {
  122. $ma = array();
  123. $pattern = ltrim(rtrim($pattern, "/"));
  124. $pattern = "/" . str_replace("/", "\/", $pattern) . "\/?$/";
  125. $pattern = str_replace(":s", "([^\/]+)", $pattern);
  126. if (preg_match($pattern, $str, $ma) > 0) {
  127. array_shift($ma);
  128. return $ma;
  129. }
  130. return null;
  131. }
  132. /**
  133. * 获取pathInfo, 兼容Apache, Nginx
  134. * 如果使用Apache的话可以直接使用 $_SERVER['PATH_INFO']的
  135. * @return string
  136. */
  137. protected static function getPathInfo()
  138. {
  139. //去掉uri中的PHP_SELF 信息
  140. $length = strlen($_SERVER['PHP_SELF']);
  141. if (substr($_SERVER['REQUEST_URI'], 0, $length) == $_SERVER['PHP_SELF']) {
  142. $request = substr($_SERVER['REQUEST_URI'], $length);
  143. } else {
  144. $request = $_SERVER['REQUEST_URI'];
  145. }
  146. // 去除 ?后面的部分
  147. $request = parse_url($request);
  148. $request = $request['path'];
  149. $request = urldecode($request);
  150. $request = trim($request, '/_ ');
  151. // 去掉 '.' 后缀
  152. if (strpos($request, '.') !== false) {
  153. $request = explode('.', $request);
  154. $request = array_shift($request);
  155. }
  156. if (empty($request)) {
  157. $request = self::CONTROLLER_DEFAULT;
  158. }
  159. return $request;
  160. }
  161. /**
  162. * 将字符串转成带命名空间的类名
  163. * @param $string
  164. * @return string
  165. */
  166. public static function convertToClassName($string)
  167. {
  168. $string = trim($string, '\\/_ ');
  169. $string = strtolower($string);
  170. $string = str_replace('/', '\\', $string);
  171. $string = str_replace(' ', '\\', ucwords(str_replace('\\', ' ', $string)));
  172. $string = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
  173. return $string;
  174. }
  175. }