<?php
declare(strict_types=1);
* +----------------------------------------------------------------------
* | ThinkAdmin Plugin for ThinkAdmin
* +----------------------------------------------------------------------
* | 版权所有 2014~2026 ThinkAdmin [ thinkadmin.top ]
* +----------------------------------------------------------------------
* | 官方网站: https://thinkadmin.top
* +----------------------------------------------------------------------
* | 开源协议 ( https://mit-license.org )
* | 免责声明 ( https://thinkadmin.top/disclaimer )
* | 会员特权 ( https://thinkadmin.top/vip-introduce )
* +----------------------------------------------------------------------
* | gitee 代码仓库:https://gitee.com/zoujingli/ThinkAdmin
* | github 代码仓库:https://github.com/zoujingli/ThinkAdmin
* +----------------------------------------------------------------------
*/
namespace think\admin\support\middleware;
use think\admin\extend\ToolsExtend;
use think\admin\Library;
use think\admin\Plugin;
use think\admin\service\NodeService;
use think\admin\service\SystemService;
use think\App;
use think\exception\HttpException;
use think\Request;
use think\Response;
* 多应用调度中间键.
* @class MultAccess
*/
class MultAccess
{
* 应用实例.
* @var App
*/
private $app;
* 应用路径.
* @var string
*/
private $appPath;
* 应用空间.
* @var string
*/
private $appSpace;
* App constructor.
*/
public function __construct(App $app)
{
$this->app = $app;
}
* 多应用解析.
*/
public function handle(Request $request, \Closure $next): Response
{
[$this->appPath, $this->appSpace] = ['', ''];
if (!$this->parseMultiApp()) {
return $next($request);
}
return $this->app->middleware->pipeline('app')->send($request)->then(function ($request) use ($next) {
return $next($request);
});
}
* 解析多应用.
*/
private function parseMultiApp(): bool
{
$defaultApp = $this->app->config->get('route.default_app') ?: 'index';
[$script, $pathinfo] = [$this->scriptName(), $this->app->request->pathinfo()];
if ($script && !in_array($script, ['index', 'router', 'think'])) {
$this->app->request->setPathinfo(preg_replace("#^{$script}\\.php(/|\\.|$)#i", '', $pathinfo) ?: '/');
return $this->setMultiApp($script, true);
}
$domains = $this->app->config->get('app.domain_bind', []);
if (!empty($domains)) {
foreach ([$this->app->request->host(true), $this->app->request->subDomain(), '*'] as $key) {
if (isset($domains[$key])) {
return $this->setMultiApp($domains[$key], true);
}
}
}
$name = current(explode('/', $pathinfo));
if (strpos($name, '.')) {
$name = strstr($name, '.', true);
}
$addons = Plugin::get();
$appmap = $this->app->config->get('app.app_map', []);
if (isset($appmap[$name])) {
$appName = $appmap[$name] instanceof \Closure ? (call_user_func_array($appmap[$name], [$this->app]) ?: $name) : $appmap[$name];
} elseif ($name && (in_array($name, $appmap) || in_array($name, $this->app->config->get('app.deny_app_list', [])))) {
throw new HttpException(404, "app not exists: {$name}");
} elseif ($name && isset($appmap['*'])) {
$appName = $appmap['*'];
} else {
$appName = $name ?: $defaultApp;
if (!isset($addons[$appName]) && !is_dir($this->app->getBasePath() . $appName)) {
return $this->app->config->get('app.app_express', false) && $this->setMultiApp($defaultApp, false);
}
}
if (isset($addons[$appName])) {
[$this->appPath, $this->appSpace] = [$addons[$appName]['path'], $addons[$appName]['space']];
}
if ($name) {
$this->app->request->setRoot('/' . $name);
$this->app->request->setPathinfo(strpos($pathinfo, '/') ? ltrim(strstr($pathinfo, '/'), '/') : '');
}
return $this->setMultiApp($appName ?? $defaultApp, $this->app->http->isBind());
}
* 获取当前运行入口名称.
* @codeCoverageIgnore
*/
private function scriptName(): string
{
$file = $_SERVER['SCRIPT_FILENAME'] ?? ($_SERVER['argv'][0] ?? '');
return empty($file) ? '' : pathinfo($file, PATHINFO_FILENAME);
}
* 设置应用参数.
* @param string $appName 应用名称
* @param bool $appBind 应用绑定
*/
private function setMultiApp(string $appName, bool $appBind): bool
{
sysvar('CurrentPluginCode', $appName);
if (is_dir($this->appPath = $this->appPath ?: syspath("app/{$appName}/"))) {
$this->app->setNamespace($this->appSpace ?: NodeService::space($appName))->setAppPath($this->appPath);
$this->app->http->setBind($appBind)->name($appName)->path($this->appPath)->setRoutePath($this->appPath . 'route' . DIRECTORY_SEPARATOR);
$uris = array_merge($this->app->config->get('view.tpl_replace_string', []), SystemService::uris());
$this->app->config->set(['view_path' => $this->appPath . 'view' . DIRECTORY_SEPARATOR, 'tpl_replace_string' => $uris], 'view');
return $this->loadMultiApp($this->appPath);
}
return false;
}
* 加载应用文件.
* @param string $appPath 应用路径
* @codeCoverageIgnore
*/
private function loadMultiApp(string $appPath): bool
{
[$ext, $fmaps] = [$this->app->getConfigExt(), []];
if (is_file($file = "{$appPath}common{$ext}")) {
Library::load($file);
}
ToolsExtend::find($appPath . 'config', 1, function (\SplFileInfo $info) use ($ext) {
if ($info->isFile() && strtolower(".{$info->getExtension()}") === $ext) {
$this->app->config->load($info->getPathname(), $info->getBasename($ext));
}
});
if (in_array('route', $fmaps) && method_exists($this->app->route, 'reload')) {
$this->app->route->reload();
}
if (is_file($file = "{$appPath}provider{$ext}")) {
$this->app->bind(include $file);
}
if (is_file($file = "{$appPath}event{$ext}")) {
$this->app->loadEvent(include $file);
}
if (is_file($file = "{$appPath}middleware{$ext}")) {
$this->app->middleware->import(include $file, 'app');
}
if (method_exists($this->app->lang, 'switchLangSet')) {
$this->app->lang->switchLangSet($this->app->lang->getLangSet());
}
return true;
}
}