MultiApp.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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. return $this->app->getAppPath() . 'route' . DIRECTORY_SEPARATOR;
  72. }
  73. /**
  74. * 解析多应用
  75. * @return bool
  76. */
  77. protected function parseMultiApp(): bool
  78. {
  79. $scriptName = $this->getScriptName();
  80. $defaultApp = $this->app->config->get('app.default_app') ?: 'index';
  81. if ($this->name || ($scriptName && !in_array($scriptName, ['index', 'router', 'think']))) {
  82. $appName = $this->name ?: $scriptName;
  83. $this->app->http->setBind();
  84. } else {
  85. // 自动多应用识别
  86. $this->app->http->setBind(false);
  87. $appName = null;
  88. $this->appName = '';
  89. $bind = $this->app->config->get('app.domain_bind', []);
  90. if (!empty($bind)) {
  91. // 获取当前子域名
  92. $subDomain = $this->app->request->subDomain();
  93. $domain = $this->app->request->host(true);
  94. if (isset($bind[$domain])) {
  95. $appName = $bind[$domain];
  96. $this->app->http->setBind();
  97. } elseif (isset($bind[$subDomain])) {
  98. $appName = $bind[$subDomain];
  99. $this->app->http->setBind();
  100. } elseif (isset($bind['*'])) {
  101. $appName = $bind['*'];
  102. $this->app->http->setBind();
  103. }
  104. }
  105. if (!$this->app->http->isBind()) {
  106. $path = $this->app->request->pathinfo();
  107. $map = $this->app->config->get('app.app_map', []);
  108. $deny = $this->app->config->get('app.deny_app_list', []);
  109. $name = current(explode('/', $path));
  110. if (strpos($name, '.')) {
  111. $name = strstr($name, '.', true);
  112. }
  113. if (isset($map[$name])) {
  114. if ($map[$name] instanceof Closure) {
  115. $result = call_user_func_array($map[$name], [$this->app]);
  116. $appName = $result ?: $name;
  117. } else {
  118. $appName = $map[$name];
  119. }
  120. } elseif ($name && (false !== array_search($name, $map) || in_array($name, $deny))) {
  121. throw new HttpException(404, 'app not exists:' . $name);
  122. } elseif ($name && isset($map['*'])) {
  123. $appName = $map['*'];
  124. } else {
  125. $appName = $name ?: $defaultApp;
  126. $appPath = $this->path ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR;
  127. if (!is_dir($appPath)) {
  128. $express = $this->app->config->get('app.app_express', false);
  129. if ($express) {
  130. $this->setApp($defaultApp);
  131. return true;
  132. } else {
  133. return false;
  134. }
  135. }
  136. }
  137. if ($name) {
  138. $this->app->request->setRoot('/' . $name);
  139. $this->app->request->setPathinfo(strpos($path, '/') ? ltrim(strstr($path, '/'), '/') : '');
  140. }
  141. }
  142. }
  143. $this->setApp($appName ?: $defaultApp);
  144. return true;
  145. }
  146. /**
  147. * 获取当前运行入口名称
  148. * @access protected
  149. * @codeCoverageIgnore
  150. * @return string
  151. */
  152. protected function getScriptName(): string
  153. {
  154. if (isset($_SERVER['SCRIPT_FILENAME'])) {
  155. $file = $_SERVER['SCRIPT_FILENAME'];
  156. } elseif (isset($_SERVER['argv'][0])) {
  157. $file = realpath($_SERVER['argv'][0]);
  158. }
  159. return isset($file) ? pathinfo($file, PATHINFO_FILENAME) : '';
  160. }
  161. /**
  162. * 设置应用
  163. * @param string $appName
  164. */
  165. protected function setApp(string $appName): void
  166. {
  167. $this->appName = $appName;
  168. $this->app->http->name($appName);
  169. $appPath = $this->path ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR;
  170. $this->app->setAppPath($appPath);
  171. // 设置应用命名空间
  172. $this->app->setNamespace($this->app->config->get('app.app_namespace') ?: 'app\\' . $appName);
  173. if (is_dir($appPath)) {
  174. $this->app->setRuntimePath($this->app->getRuntimePath() . $appName . DIRECTORY_SEPARATOR);
  175. $this->app->http->setRoutePath($this->getRoutePath());
  176. //加载应用
  177. $this->loadApp($appName, $appPath);
  178. }
  179. }
  180. /**
  181. * 加载应用文件
  182. * @param string $appName 应用名
  183. * @return void
  184. */
  185. protected function loadApp(string $appName, string $appPath): void
  186. {
  187. if (is_file($appPath . 'common.php')) {
  188. include_once $appPath . 'common.php';
  189. }
  190. $files = [];
  191. $files = array_merge($files, glob($appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
  192. foreach ($files as $file) {
  193. $this->app->config->load($file, pathinfo($file, PATHINFO_FILENAME));
  194. }
  195. if (is_file($appPath . 'event.php')) {
  196. $this->app->loadEvent(include $appPath . 'event.php');
  197. }
  198. if (is_file($appPath . 'middleware.php')) {
  199. $this->app->middleware->import(include $appPath . 'middleware.php', 'app');
  200. }
  201. if (is_file($appPath . 'provider.php')) {
  202. $this->app->bind(include $appPath . 'provider.php');
  203. }
  204. // 加载应用默认语言包
  205. $this->app->loadLangPack($this->app->lang->defaultLangSet());
  206. }
  207. }