>  기사  >  PHP 프레임워크  >  ThinkPHP6 로딩 미들웨어 및 다중 애플리케이션 분석

ThinkPHP6 로딩 미들웨어 및 다중 애플리케이션 분석

藏色散人
藏色散人앞으로
2021-07-09 14:48:453373검색

1. 미들웨어 로딩

전에 쓴 글에서 Http 클래스 확장의 run() 메소드에서 호출되는 애플리케이션의 초기화를 분석했습니다. runWithRequest() 메소드의 첫 번째 코드 라인인 $this->initialize()를 분석합니다. runWithRequest() 메서드의 처음 몇 줄을 다시 살펴보겠습니다.

protected function runWithRequest(Request $request)
{
    $this->initialize();

    // 加载全局中间件
    $this->loadMiddleware();
    .
    .
    .
애플리케이션이 초기화된 후 다음 단계는 미들웨어를 처리하는 것입니다.

미들웨어 클래스 초기화

loadMiddleware 방법:

protected function loadMiddleware(): void
{
    if (is_file($this->app->getBasePath() . 'middleware.php')) {
        $this->app->middleware->import(include $this->app->getBasePath() . 'middleware.php');
    }
}
는 여전히 일반적인 루틴입니다. $this->app->middleware를 사용하여 미들웨어를 인스턴스화하고 해당 인스턴스를 얻습니다.

Import middleware

$this->app->middleware를 통해 Middleware 클래스의 인스턴스를 가져온 후 프로그램은 가져오기 메소드를 호출하고 "middleware.php" 파일에서 읽은 파일을 " app" 디렉토리. 데이터. 파일의 원본 내용은 다음과 같습니다(모든 댓글은 원래 주석 처리되었습니다):

return [
    // 全局请求缓存
    // \think\middleware\CheckRequestCache::class,
    // 多语言加载
     \think\middleware\LoadLangPack::class,
    // Session初始化
    // \think\middleware\SessionInit::class,
    // 页面Trace调试
     \think\middleware\TraceDebug::class,
];
미들웨어가 어떻게 로드되는지 연구하려면 먼저 주석 두 개를 제거합니다. 즉, 미들웨어 두 개를 추가합니다. 다음으로 가져오기 방법을 살펴보세요.

public function import(array $middlewares = [], string $type = 'global'): void
{
    foreach ($middlewares as $middleware) {
        $this->add($middleware, $type);
    }
}
이 방법은 미들웨어 배열과 미들웨어 유형을 전달합니다. 기본값은 내부의 추가 방법입니다. 추가 메소드로 이동:

public function add($middleware, string $type = 'route'): void
{
    if (is_null($middleware)) {
        return;
    }

    $middleware = $this->buildMiddleware($middleware, $type);

    if ($middleware) {
        $this->queue[$type][] = $middleware;
        // 去除重复
        $this->queue[$type]   = array_unique($this->queue[$type], SORT_REGULAR);
    }
}
실제로 실제 작업은 buildMiddleware 메소드입니다. 다음으로 직접 이동하세요.

protected function buildMiddleware($middleware, string $type): array
{
    // 是否是数组
    if (is_array($middleware)) {
        // 列出中间件及其参数
        // 这里说明我们可以给中间件传入参数,且形式为 [中间件, 参数]
        list($middleware, $param) = $middleware;
    }
    // 是否是一个闭包
    // 说明中间件可以是一个闭包
    if ($middleware instanceof \Closure) {
        //返回闭包和参数
        return [$middleware, $param ?? null];
    }
    // 排除了上面几种类型,且不是字符串,抛出错误
    if (!is_string($middleware)) {
        throw new InvalidArgumentException('The middleware is invalid');
    }

    //中间件别名检查
     $alias = $this->app->config->get('middleware.alias', []);

     if (isset($alias[$middleware])) {
        $middleware = $alias[$middleware];
    }

    //如果中间件有包含中间件(说明中间件可以嵌套)
    //再走一遍「import」递归解析
    if (is_array($middleware)) {
        $this->import($middleware, $type);
        return [];
    }
    //返回解析结果
    return [[$middleware, 'handle'], $param ?? null];
}
자세한 분석은 위의 코드 주석을 참조하세요. 최종 반환된 결과는 add 메서드에서 $ this->queue[$type][] = $middleware;를 실행하여 대기열에 추가됩니다. 최종 구문 분석 결과는 아마도 다음과 같습니다(app/middleware.php는 일부 미들웨어 주석을 제거합니다):

ThinkPHP6 로딩 미들웨어 및 다중 애플리케이션 분석

이 시점에서 전역 미들웨어가 로드됩니다.

2. 다중 애플리케이션 구문 분석

미들웨어를 로드한 후 다음 단계는 다중 애플리케이션 구문 분석입니다(ThinkPHP 6는 다중 애플리케이션 모드를 지원하기 시작합니다).

if ($this->multi) {
    $this->parseMultiApp();
}
Http 클래스의 생성자:

public function __construct(App $app)
{
    $this->app   = $app;
    //多应用解析,通过判断「app」目录下有无「controller」目录,没有就是多应用模式
    $this->multi = is_dir($this->app->getBasePath() . 'controller') ? false : true;
}
프로그램이 "app" 디렉터리에 "controller" 디렉터리가 있는지 판단하여 다중 응용 프로그램 모드인지 여부를 결정하는 것을 볼 수 있습니다.

그런 다음 기본 메서드인 parseMultiApp을 살펴보세요.

protected function parseMultiApp(): void
{
    // 虽然在「Http」的构造函数自动判断过是否开启多应用
    //如果没有controller目录,$this->multi为true,就会来到本方法
    // 接着还要看配置文件是否有配置
    if ($this->app->config->get('app.auto_multi_app', false)) {
        // 自动多应用识别
        $this->bindDomain = false;
        // 获取域名绑定
        $bind = $this->app->config->get('app.domain_bind', []);
        // 如果有域名绑定
        if (!empty($bind)) {
            // 获取当前子域名
            $subDomain = $this->app->request->subDomain();
            $domain    = $this->app->request->host(true);

            //完整域名绑定
            if (isset($bind[$domain])) {
                $appName          = $bind[$domain];
                $this->bindDomain = true;
                //子域名绑定
            } elseif (isset($bind[$subDomain])) {
                $appName          = $bind[$subDomain];
                $this->bindDomain = true;
                //二级泛域名绑定
            } elseif (isset($bind['*'])) {
                $appName          = $bind['*'];
                $this->bindDomain = true;
            }
        }
        //如果没有域名绑定
        if (!$this->bindDomain) {
            //获取别名映射
            $map  = $this->app->config->get('app.app_map', []);
            //获取禁止URL访问目录
            $deny = $this->app->config->get('app.deny_app_list', []);
            //获取当前请求URL的pathinfo信息(含URL后缀)
            // 比如 index/index/index
            $path = $this->app->request->pathinfo();
            // 比如,从index/index/index获取得index
            $name = current(explode('/', $path));
            //解析别名映射
            if (isset($map[$name])) {
                //如果这个别名映射到的是一个闭包
                //这样不知有啥用
                if ($map[$name] instanceof Closure) {
                    $result  = call_user_func_array($map[$name], [$this]);
                    $appName = $result ?: $name;
                    //直接取得应用名
                } else {
                    $appName = $map[$name];
                }
                //$name不为空且$name在$map数组中作为KEY,或者$name是禁止URL方位的目录
            } elseif ($name && (false !== array_search($name, $map) || in_array($name, $deny))) {
                throw new HttpException(404, 'app not exists:' . $name);
            } elseif ($name && isset($map['*'])) {
                $appName = $map['*'];
            } else {
                $appName = $name;
            }

            if ($name) {
                $this->app->request->setRoot('/' . $name);
                $this->app->request->setPathinfo(strpos($path, '/') ? ltrim(strstr($path, '/'), '/') : '');
            }
        }
    } else {
        $appName = $this->name ?: $this->getScriptName();
    }

    $this->loadApp($appName ?: $this->app->config->get('app.default_app', 'index'));
}
"pathinfo" 정보의 첫 번째 섹션이 index/index/index/의 index와 같은 애플리케이션 이름으로 구문 분석되는 것을 볼 수 있습니다. 메서드 마지막에는 loadApp 메서드도 호출됩니다. 수행되는 작업은 로드된 파일이 모두 애플리케이션의 디렉터리에 있다는 점을 제외하면 이전 애플리케이션의 초기화와 유사합니다.

이전 버전과 비교하면 ThinkPHP 6에서는 원본 모듈을 다중 애플리케이션으로 변형한 것으로 보입니다. 다중 애플리케이션의 경우 pathinfo의 첫 번째 섹션에서 애플리케이션 이름과 이전 모듈 이름을 파싱하고, 새 문서에서도 모듈의 내용을 보지 못했습니다.

관련 추천:

최신 10개 thinkphp 비디오 튜토리얼

위 내용은 ThinkPHP6 로딩 미들웨어 및 다중 애플리케이션 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제