MultiApp.php 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types = 1);
  12. namespace think\app;
  13. use Closure;
  14. use think\App;
  15. use think\exception\HttpException;
  16. use think\Request;
  17. use think\Response;
  18. /**
  19. * 多应用模式支持
  20. */
  21. class MultiApp
  22. {
  23. /** @var App */
  24. protected $app;
  25. /**
  26. * 应用名称
  27. * @var string
  28. */
  29. protected $name;
  30. /**
  31. * 应用名称
  32. * @var string
  33. */
  34. protected $appName;
  35. /**
  36. * 应用路径
  37. * @var string
  38. */
  39. protected $path;
  40. public function __construct(App $app)
  41. {
  42. $this->app = $app;
  43. $this->name = $this->app->http->getName();
  44. $this->path = $this->app->http->getPath();
  45. }
  46. /**
  47. * 多应用解析
  48. * @access public
  49. * @param Request $request
  50. * @param Closure $next
  51. * @return Response
  52. */
  53. public function handle($request, Closure $next)
  54. {
  55. if (!$this->parseMultiApp()) {
  56. return $next($request);
  57. }
  58. return $this->app->middleware->pipeline('app')
  59. ->send($request)
  60. ->then(function ($request) use ($next) {
  61. return $next($request);
  62. });
  63. }
  64. /**
  65. * 获取路由目录
  66. * @access protected
  67. * @return string
  68. */
  69. protected function getRoutePath(): string
  70. {
  71. if (is_dir($this->app->getAppPath() . 'route')) {
  72. return $this->app->getAppPath() . 'route' . DIRECTORY_SEPARATOR;
  73. }
  74. return $this->app->getRootPath() . 'route' . DIRECTORY_SEPARATOR . $this->appName . DIRECTORY_SEPARATOR;
  75. }
  76. /**
  77. * 解析多应用
  78. * @return bool
  79. */
  80. protected function parseMultiApp(): bool
  81. {
  82. $scriptName = $this->getScriptName();
  83. $defaultApp = $this->app->config->get('app.default_app') ?: 'index';
  84. if ($this->name || ($scriptName && !in_array($scriptName, ['index', 'router', 'think']))) {
  85. $appName = $this->name ?: $scriptName;
  86. $this->app->http->setBind();
  87. } else {
  88. // 自动多应用识别
  89. $this->app->http->setBind(false);
  90. $appName = null;
  91. $this->appName = '';
  92. $bind = $this->app->config->get('app.domain_bind', []);
  93. if (!empty($bind)) {
  94. // 获取当前子域名
  95. $subDomain = $this->app->request->subDomain();
  96. $domain = $this->app->request->host(true);
  97. if (isset($bind[$domain])) {
  98. $appName = $bind[$domain];
  99. $this->app->http->setBind();
  100. } elseif (isset($bind[$subDomain])) {
  101. $appName = $bind[$subDomain];
  102. $this->app->http->setBind();
  103. } elseif (isset($bind['*'])) {
  104. $appName = $bind['*'];
  105. $this->app->http->setBind();
  106. }
  107. }
  108. if (!$this->app->http->isBind()) {
  109. $path = $this->app->request->pathinfo();
  110. $map = $this->app->config->get('app.app_map', []);
  111. $deny = $this->app->config->get('app.deny_app_list', []);
  112. $name = current(explode('/', $path));
  113. if (strpos($name, '.')) {
  114. $name = strstr($name, '.', true);
  115. }
  116. if (isset($map[$name])) {
  117. if ($map[$name] instanceof Closure) {
  118. $result = call_user_func_array($map[$name], [$this->app]);
  119. $appName = $result ?: $name;
  120. } else {
  121. $appName = $map[$name];
  122. }
  123. } elseif ($name && (false !== array_search($name, $map) || in_array($name, $deny))) {
  124. throw new HttpException(404, 'app not exists:' . $name);
  125. } elseif ($name && isset($map['*'])) {
  126. $appName = $map['*'];
  127. } else {
  128. $appName = $name ?: $defaultApp;
  129. $appPath = $this->path ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR;
  130. if (!is_dir($appPath)) {
  131. $express = $this->app->config->get('app.app_express', false);
  132. if ($express) {
  133. $this->setApp($defaultApp);
  134. return true;
  135. } else {
  136. return false;
  137. }
  138. }
  139. }
  140. if ($name) {
  141. $this->app->request->setRoot('/' . $name);
  142. $this->app->request->setPathinfo(strpos($path, '/') ? ltrim(strstr($path, '/'), '/') : '');
  143. }
  144. }
  145. }
  146. $this->setApp($appName ?: $defaultApp);
  147. return true;
  148. }
  149. /**
  150. * 获取当前运行入口名称
  151. * @access protected
  152. * @codeCoverageIgnore
  153. * @return string
  154. */
  155. protected function getScriptName(): string
  156. {
  157. if (isset($_SERVER['SCRIPT_FILENAME'])) {
  158. $file = $_SERVER['SCRIPT_FILENAME'];
  159. } elseif (isset($_SERVER['argv'][0])) {
  160. $file = realpath($_SERVER['argv'][0]);
  161. }
  162. return isset($file) ? pathinfo($file, PATHINFO_FILENAME) : '';
  163. }
  164. /**
  165. * 设置应用
  166. * @param string $appName
  167. */
  168. protected function setApp(string $appName): void
  169. {
  170. $this->appName = $appName;
  171. $this->app->http->name($appName);
  172. $appPath = $this->path ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR;
  173. $this->app->setAppPath($appPath);
  174. // 设置应用命名空间
  175. $this->app->setNamespace($this->app->config->get('app.app_namespace') ?: 'app\\' . $appName);
  176. if (is_dir($appPath)) {
  177. $this->app->setRuntimePath($this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . $appName . DIRECTORY_SEPARATOR);
  178. $this->app->http->setRoutePath($this->getRoutePath());
  179. //加载应用
  180. $this->loadApp($appName, $appPath);
  181. }
  182. }
  183. /**
  184. * 加载应用文件
  185. * @param string $appName 应用名
  186. * @return void
  187. */
  188. protected function loadApp(string $appName, string $appPath): void
  189. {
  190. if (is_file($appPath . 'common.php')) {
  191. include_once $appPath . 'common.php';
  192. }
  193. $configPath = $this->app->getConfigPath();
  194. $files = [];
  195. if (is_dir($appPath . 'config')) {
  196. $files = array_merge($files, glob($appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
  197. } elseif (is_dir($configPath . $appName)) {
  198. $files = array_merge($files, glob($configPath . $appName . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
  199. }
  200. foreach ($files as $file) {
  201. $this->app->config->load($file, pathinfo($file, PATHINFO_FILENAME));
  202. }
  203. if (is_file($appPath . 'event.php')) {
  204. $this->app->loadEvent(include $appPath . 'event.php');
  205. }
  206. if (is_file($appPath . 'middleware.php')) {
  207. $this->app->middleware->import(include $appPath . 'middleware.php', 'app');
  208. }
  209. if (is_file($appPath . 'provider.php')) {
  210. $this->app->bind(include $appPath . 'provider.php');
  211. }
  212. // 加载应用默认语言包
  213. $this->app->loadLangPack($this->app->lang->defaultLangSet());
  214. }
  215. }