Dispatcher.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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. header("HTTP/1.1 200");
  57. header("Access-Control-Allow-Origin: *");
  58. header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE");
  59. header("Access-Control-Allow-Headers: *");
  60. // 获取前缀
  61. $prefix = '\Heanup\\App\\' . self::convertToClassName($customPrefix) . "\\Controller";
  62. if (!$pathInfo) {
  63. $pathInfo = self::getPathInfo();
  64. }
  65. $pathInfo = self::convertToClassName($pathInfo);
  66. try {
  67. $path = explode('\\', $pathInfo);
  68. $id = array_pop($path);
  69. if (is_numeric($id)) {
  70. $_GET['id'] = $id;
  71. $pathInfo = implode('\\', $path);
  72. }
  73. $controller = $prefix . '\\' . $pathInfo;
  74. // 如果类不存在则使用默认的错误类
  75. if (!class_exists($controller)) {
  76. //类不存在则尝试通过自定义路由解析
  77. $url = $_SERVER["REQUEST_URI"];
  78. // 去除 ?后面的部分
  79. $request = parse_url($url);
  80. $url = $request['path'];
  81. $route = Route::getData($customPrefix);
  82. foreach ($route as $val) {
  83. $pattern = $val["pattern"];
  84. $params = self::checkUrl(strtolower($url), strtolower($pattern));
  85. if ($params != null) {
  86. $controller = $val["action"];
  87. $count = count($params);
  88. for ($i = 0; $i < $count; $i++) {
  89. $_GET['args_' . $i] = $params[$i];
  90. }
  91. break;// 寻找到第一个匹配的路由即执行,然后返回
  92. }
  93. }
  94. if (!class_exists($controller)) {
  95. Logger::error($controller, 'dispatcher.controller_not_exist');
  96. throw new \Exception($controller . ' not exist');
  97. }
  98. }
  99. // 判断主方法
  100. if (!method_exists($controller, 'run')) {
  101. throw new \Exception($controller . ' has no method called "run"');
  102. }
  103. $controller = new $controller();
  104. $controller->run();
  105. } catch (\Exception $ex) {
  106. $method = strtolower($_SERVER['REQUEST_METHOD']);
  107. if ($method == 'options') {
  108. header("HTTP/1.1 200");
  109. header("Access-Control-Allow-Origin: *");
  110. header("Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE");
  111. header("Access-Control-Allow-Headers: lang,token,content-type");
  112. die();
  113. }
  114. Response::sendResponse(404, [], $ex->getMessage());
  115. Logger::emerg($ex, 'dispatcher.dispatch');
  116. }
  117. }
  118. /**
  119. * 正则匹配检查,并提取出参数
  120. * @param $str
  121. * @param $pattern
  122. * @return array|null
  123. */
  124. private static function checkUrl($str, $pattern)
  125. {
  126. $ma = array();
  127. $pattern = ltrim(rtrim($pattern, "/"));
  128. $pattern = "/" . str_replace("/", "\/", $pattern) . "\/?$/";
  129. $pattern = str_replace(":s", "([^\/]+)", $pattern);
  130. if (preg_match($pattern, $str, $ma) > 0) {
  131. array_shift($ma);
  132. return $ma;
  133. }
  134. return null;
  135. }
  136. /**
  137. * 获取pathInfo, 兼容Apache, Nginx
  138. * 如果使用Apache的话可以直接使用 $_SERVER['PATH_INFO']的
  139. * @return string
  140. */
  141. protected static function getPathInfo()
  142. {
  143. //去掉uri中的PHP_SELF 信息
  144. $length = strlen($_SERVER['PHP_SELF']);
  145. if (substr($_SERVER['REQUEST_URI'], 0, $length) == $_SERVER['PHP_SELF']) {
  146. $request = substr($_SERVER['REQUEST_URI'], $length);
  147. } else {
  148. $request = $_SERVER['REQUEST_URI'];
  149. }
  150. // 去除 ?后面的部分
  151. $request = parse_url($request);
  152. $request = $request['path'];
  153. $request = urldecode($request);
  154. $request = trim($request, '/_ ');
  155. // 去掉 '.' 后缀
  156. if (strpos($request, '.') !== false) {
  157. $request = explode('.', $request);
  158. $request = array_shift($request);
  159. }
  160. if (empty($request)) {
  161. $request = self::CONTROLLER_DEFAULT;
  162. }
  163. return $request;
  164. }
  165. /**
  166. * 将字符串转成带命名空间的类名
  167. * @param $string
  168. * @return string
  169. */
  170. public static function convertToClassName($string)
  171. {
  172. $string = trim($string, '\\/_ ');
  173. $string = strtolower($string);
  174. $string = str_replace('/', '\\', $string);
  175. $string = str_replace(' ', '\\', ucwords(str_replace('\\', ' ', $string)));
  176. $string = str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
  177. return $string;
  178. }
  179. }