MultiApp.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. public function __construct(App $app)
  26. {
  27. $this->app = $app;
  28. }
  29. /**
  30. * 多应用解析
  31. * @access public
  32. * @param Request $request
  33. * @param Closure $next
  34. * @return Response
  35. */
  36. public function handle($request, Closure $next)
  37. {
  38. if (!$this->parseMultiApp()) {
  39. return $next($request);
  40. }
  41. return $this->app->middleware->pipeline('app')
  42. ->send($request)
  43. ->then(function ($request) use ($next) {
  44. return $next($request);
  45. });
  46. }
  47. /**
  48. * 获取路由目录
  49. * @access protected
  50. * @return string
  51. */
  52. protected function getRoutePath(): string
  53. {
  54. return $this->app->getAppPath() . 'route' . DIRECTORY_SEPARATOR;
  55. }
  56. /**
  57. * 解析多应用
  58. * @return bool
  59. */
  60. protected function parseMultiApp(): bool
  61. {
  62. $scriptName = $this->getScriptName();
  63. $defaultApp = $this->app->config->get('app.default_app') ?: 'index';
  64. $appName = $this->app->http->getName();
  65. if ($appName || ($scriptName && !in_array($scriptName, ['index', 'router', 'think']))) {
  66. $appName = $appName ?: $scriptName;
  67. $this->app->http->setBind();
  68. } else {
  69. // 自动多应用识别
  70. $this->app->http->setBind(false);
  71. $appName = null;
  72. $bind = $this->app->config->get('app.domain_bind', []);
  73. if (!empty($bind)) {
  74. // 获取当前子域名
  75. $subDomain = $this->app->request->subDomain();
  76. $domain = $this->app->request->host(true);
  77. if (isset($bind[$domain])) {
  78. $appName = $bind[$domain];
  79. $this->app->http->setBind();
  80. } elseif (isset($bind[$subDomain])) {
  81. $appName = $bind[$subDomain];
  82. $this->app->http->setBind();
  83. } elseif (isset($bind['*'])) {
  84. $appName = $bind['*'];
  85. $this->app->http->setBind();
  86. }
  87. }
  88. if (!$this->app->http->isBind()) {
  89. $path = $this->app->request->pathinfo();
  90. $map = $this->app->config->get('app.app_map', []);
  91. $deny = $this->app->config->get('app.deny_app_list', []);
  92. $name = current(explode('/', $path));
  93. if (strpos($name, '.')) {
  94. $name = strstr($name, '.', true);
  95. }
  96. if (isset($map[$name])) {
  97. if ($map[$name] instanceof Closure) {
  98. $result = call_user_func_array($map[$name], [$this->app]);
  99. $appName = $result ?: $name;
  100. } else {
  101. $appName = $map[$name];
  102. }
  103. } elseif ($name && (false !== array_search($name, $map) || in_array($name, $deny))) {
  104. throw new HttpException(404, 'app not exists:' . $name);
  105. } elseif ($name && isset($map['*'])) {
  106. $appName = $map['*'];
  107. } else {
  108. $appName = $name ?: $defaultApp;
  109. $appPath = $this->app->http->getPath() ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR;
  110. if (!is_dir($appPath)) {
  111. $express = $this->app->config->get('app.app_express', false);
  112. if ($express) {
  113. $this->setApp($defaultApp);
  114. return true;
  115. } else {
  116. return false;
  117. }
  118. }
  119. }
  120. if ($name) {
  121. $this->app->request->setRoot('/' . $name);
  122. $this->app->request->setPathinfo(strpos($path, '/') ? ltrim(strstr($path, '/'), '/') : '');
  123. }
  124. }
  125. }
  126. $this->setApp($appName ?: $defaultApp);
  127. return true;
  128. }
  129. /**
  130. * 获取当前运行入口名称
  131. * @access protected
  132. * @codeCoverageIgnore
  133. * @return string
  134. */
  135. protected function getScriptName(): string
  136. {
  137. if (isset($_SERVER['SCRIPT_FILENAME'])) {
  138. $file = $_SERVER['SCRIPT_FILENAME'];
  139. } elseif (isset($_SERVER['argv'][0])) {
  140. $file = realpath($_SERVER['argv'][0]);
  141. }
  142. return isset($file) ? pathinfo($file, PATHINFO_FILENAME) : '';
  143. }
  144. /**
  145. * 设置应用
  146. * @param string $appName
  147. */
  148. protected function setApp(string $appName): void
  149. {
  150. $this->app->http->name($appName);
  151. $appPath = $this->app->http->getPath() ?: $this->app->getBasePath() . $appName . DIRECTORY_SEPARATOR;
  152. $this->app->setAppPath($appPath);
  153. // 设置应用命名空间
  154. $this->app->setNamespace($this->app->config->get('app.app_namespace') ?: 'app\\' . $appName);
  155. if (is_dir($appPath)) {
  156. $this->app->setRuntimePath($this->app->getRuntimePath() . $appName . DIRECTORY_SEPARATOR);
  157. $this->app->http->setRoutePath($this->getRoutePath());
  158. //加载应用
  159. $this->loadApp($appName, $appPath);
  160. }
  161. }
  162. /**
  163. * 加载应用文件
  164. * @param string $appName 应用名
  165. * @return void
  166. */
  167. protected function loadApp(string $appName, string $appPath): void
  168. {
  169. if (is_file($appPath . 'common.php')) {
  170. include_once $appPath . 'common.php';
  171. }
  172. $files = [];
  173. $files = array_merge($files, glob($appPath . 'config' . DIRECTORY_SEPARATOR . '*' . $this->app->getConfigExt()));
  174. foreach ($files as $file) {
  175. $this->app->config->load($file, pathinfo($file, PATHINFO_FILENAME));
  176. }
  177. if (is_file($appPath . 'event.php')) {
  178. $this->app->loadEvent(include $appPath . 'event.php');
  179. }
  180. if (is_file($appPath . 'middleware.php')) {
  181. $this->app->middleware->import(include $appPath . 'middleware.php', 'app');
  182. }
  183. if (is_file($appPath . 'provider.php')) {
  184. $this->app->bind(include $appPath . 'provider.php');
  185. }
  186. // 加载应用默认语言包
  187. $this->app->loadLangPack($this->app->lang->defaultLangSet());
  188. }
  189. }