>PHP 프레임워크 >ThinkPHP >ThinkPHP6.0 파이프라인 모드 및 미들웨어 구현 분석

ThinkPHP6.0 파이프라인 모드 및 미들웨어 구현 분석

藏色散人
藏色散人앞으로
2019-11-04 13:29:254180검색

Description

ThinkPHP 6.0 RC5에서는 파이프라인 모드를 사용하여 미들웨어를 구현하기 시작했는데, 이는 이전 버전의 구현보다 더 간결하고 질서정연해졌습니다. 이 기사에서는 구현 세부 사항을 분석합니다.

먼저 항목 파일 public/index.php에서 시작합니다. $http = (new App())->http;

http 클래스의 인스턴스 가져오기 그리고 이를 실행 메소드라고 부릅니다: $response = $http->run(); 그런 다음 실행 메소드는 runWithRequest 메소드를 호출합니다:

protected function runWithRequest(Request $request)
{
    .
    .
    .
    return $this->app->middleware->pipeline()
        ->send($request)
        ->then(function ($request) {
            return $this->dispatchToRoute($request);
        });
}

미들웨어 실행은 최종 return 문에 있습니다.

pipeline, 통해, 전송 방법

$this->app->middleware->pipeline() 的 pipeline 方法:
public function pipeline(string $type = 'global')
{
    return (new Pipeline())  
           // array_map将所有中间件转换成闭包,闭包的特点:
          // 1. 传入参数:$request,请求实例; $next,一个闭包
          // 2. 返回一个Response实例
        ->through(array_map(function ($middleware) {
            return function ($request, $next) use ($middleware) {
                list($call, $param) = $middleware;
                if (is_array($call) && is_string($call[0])) {
                    $call = [$this->app->make($call[0]), $call[1]];
                }
                 // 该语句执行中间件类实例的handle方法,传入的参数是外部传进来的$request和$next
                 // 还有一个$param是中间件接收的参数
                $response = call_user_func($call, $request, $next, $param);
                if (!$response instanceof Response) {
                    throw new LogicException('The middleware must return Response instance');
                }
                return $response;
            };
            // 将中间件排序
        }, $this->sortMiddleware($this->queue[$type] ?? [])))
        ->whenException([$this, 'handleException']);
}

방법 코드를 통해:

public function through($pipes)
{
    $this->pipes = is_array($pipes) ? $pipes : func_get_args();
    return $this;
}
#🎜 🎜#through에 대한 이전 호출은 미들웨어를 클로저로 캡슐화하는 수신 array_map(...)이며 through는 이러한 클로저를 Pipeline 클래스의 $pipes 속성에 저장합니다.

PHP의 array_map 메소드 서명:

array_map ( callable $callback , array $array1 [, array $... ] ) : array

$callback은 $array의 각 요소를 반복하고 새 값을 반환합니다. 따라서 $pipes에 있는 각 클로저의 최종 형식 특성은 다음과 같습니다(의사 코드):

function ($request, $next) {
    $response = handle($request, $next, $param);
    return $response;
}

이 클로저는 두 개의 매개변수를 받습니다. 하나는 요청 인스턴스이고 다른 하나는 콜백 함수입니다. 메서드는 처리 후 응답을 받아 반환합니다.

through는 Pipeline 클래스의 인스턴스를 반환한 다음 전송 메서드를 호출합니다.

public function send($passable)
{
    $this->passable = $passable;
    return $this;
}

이 메서드는 매우 간단합니다. 들어오는 요청 인스턴스를 $passable 멤버에 저장하기만 하면 됩니다. 변수, 그리고 마지막으로 Pipeline 클래스의 인스턴스도 반환하므로 Pipeline 클래스의 다른 메서드를 체인에서 호출할 수 있습니다.

then, carry 메서드

send 메서드를 호출한 다음 then 메서드를 호출합니다.

return $this->app->middleware->pipeline()
            ->send($request)
            ->then(function ($request) {
                return $this->dispatchToRoute($request);
            });

여기에서 수신합니다. 클로저는 매개변수로 사용되며 이 클로저는 실제로 컨트롤러 작업의 실행 코드를 포함합니다.

then 메소드 코드:

public function then(Closure $destination)
{
    $pipeline = array_reduce(
        //用于迭代的数组(中间件闭包),这里将其倒序
        array_reverse($this->pipes),
        // array_reduce需要的回调函数
        $this->carry(),
        //这里是迭代的初始值
        function ($passable) use ($destination) {
            try {
                return $destination($passable);
            } catch (Throwable | Exception $e) {
                return $this->handleException($passable, $e);
            }
        });
    return $pipeline($this->passable);
}

캐리 코드:

protected function carry()
{
    // 1. $stack 上次迭代得到的值,如果是第一次迭代,其值是后面的「初始值
    // 2. $pipe 本次迭代的值
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            try {
                return $pipe($passable, $stack);
            } catch (Throwable | Exception $e) {
                return $this->handleException($passable, $e);
            }
        };
    };
}

for 원리를 더 쉽게 분석할 수 있도록 carry 메소드를 then에 인라인하고 오류 캡처 코드를 제거하여 다음을 얻습니다.

public function then(Closure $destination)
{
    $pipeline = array_reduce(
        array_reverse($this->pipes),
        function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
                return $pipe($passable, $stack);
            };
        },
        function ($passable) use ($destination) {
            return $destination($passable);
        });
    return $pipeline($this->passable);
}

여기서 핵심은 array_reduce 및 $pipeline($this-> 합격) 실행 과정, 이 두 과정은 '양파 감싸기'와 '양파 껍질 벗기기' 과정에 비유할 수 있습니다.

array_reduce 첫 번째 반복, $stack의 초기 값은 다음과 같습니다.

(A)

function ($passable) use ($destination) {
    return $destination($passable);
});

Callback 함수 반환 값은 다음과 같습니다.

(B)

function ($passable) use ($stack, $pipe) {
    return $pipe($passable, $stack);
};

A를 B로 대체하여 첫 번째 반복 후 $stack 값을 얻습니다.

(C)

function ($passable) use ($stack, $pipe) {
    return $pipe($passable, 
        function ($passable) use ($destination) {
            return $destination($passable);
        })
    );
};

두 번째 반복에서는 마찬가지로 C를 B로 대체하여 다음을 얻습니다.

#🎜🎜 #

(D)

// 伪代码
// 每一层的$pipe都代表一个中间件闭包
function ($passable) use ($stack, $pipe) {
    return $pipe($passable,  //倒数第二层中间件
        function ($passable) use ($stack, $pipe) {
            return $pipe($passable,  //倒数第一层中间件
                function ($passable) use ($destination) {
                    return $destination($passable);  //包含控制器操作的闭包
                })
            );
        };
    );
};
비유하자면 미들웨어가 있는 만큼 최대한 많이 대체하고 마지막으로 $stack을 얻었을 때 $pipeline으로 돌아왔습니다. 미들웨어 클로저는 이전 순서가 반전되었기 때문에 앞의 클로저가 내부 레이어에 싸여 있으므로 역순 이후의 클로저는 바깥쪽에 나중에 있고 순방향의 관점에서는 더 빠른 클로저가 됩니다. 미들웨어는 가장 바깥쪽 레이어입니다.

클로저를 레이어별로 래핑한 후 양파 구조와 유사한 "슈퍼" 클로저 D를 얻습니다. 이 클로저의 구조는 위의 코드 주석에 표시된 대로입니다. 마지막으로 $request 객체를 이 클로저에 전달하고 실행합니다: $pipeline($this->passable); 그러면 양파 껍질을 벗기는 것과 유사한 프로세스가 시작됩니다. 다음으로 양파 껍질을 벗기는 방법을 살펴보겠습니다.

양파 껍질 벗기기 과정 분석

array_map(...) 각 미들웨어 클래스를 이 구조와 유사한 클로저로 처리합니다. :

function ($request, $next) {
    $response = handle($request, $next, $param);
    return $response;
}

손잡이는 미들웨어의 입구이며 그 구조적 특징은 다음과 같습니다.

public function handle($request, $next, $param) {
    // do sth ------ M1-1 / M2-1
    $response = $next($request);
    // do sth ------ M1-2 / M2-2
    return $response;
}

위의 "양파"는 총 2개의 레이어만 있습니다. M1-1과 M1-2가 각각 첫 번째 미들웨어 처리 방법의 접두사 및 사후 값 작업 지점이라고 가정하면 계층 미들웨어가 닫힙니다. 두 번째 미들웨어인 M2-1과 M2-2도 마찬가지입니다. . 이제 프로그램이 $pipeline($this->passable)을 실행하고 확장하도록 합니다. 즉, 다음을 실행합니다:

// 伪代码
function ($passable) use ($stack, $pipe) {
    return $pipe($passable,  
        function ($passable) use ($stack, $pipe) {
            return $pipe($passable,  
                function ($passable) use ($destination) {
                    return $destination($passable);  
                })
            );
        };
    );
}($this->passable)

이 때 프로그램은 다음에서 반환해야 합니다:

return $pipe($passable,  
    function ($passable) use ($stack, $pipe) {
        return $pipe($passable,  
            function ($passable) use ($destination) {
                return $destination($passable);  
            })
        );
    };
);
# 🎜🎜# 값, 즉 첫 번째 미들웨어 클로저를 실행하기 위한 $passable은 핸들 메소드의 $request 매개변수에 해당하고, 다음 레벨 클로저

function ($passable) use ($stack, $pipe) {
    return $pipe($passable,  
        function ($passable) use ($destination) {
            return $destination($passable);  
        })
    );
}

는 의 $next 매개변수에 해당합니다. 핸들 방식.

첫 번째 클로저를 실행하기 위한, 즉 첫 번째 클로저의 핸들 메소드를 실행하기 위한 과정은 먼저 M1-1 지점, 즉 전처리(pre-Operation)의 코드를 실행하고, 그런 다음 $ response = $next($request);를 실행하면 프로그램이 다음 클로저를 실행하기 위해 시작되고 $next($request)가 확장됩니다. 즉:

function ($passable) use ($stack, $pipe) {
    return $pipe($passable,  
        function ($passable) use ($destination) {
            return $destination($passable);  
        })
    );
}($request)

등이 실행됩니다. 클로저, 즉 두 번째 미들웨어의 핸들 메소드를 실행합니다. 이때 M2-1 지점을 먼저 실행한 후 $response = $next($request)를 실행합니다. 이때 $next 클로저는 #🎜🎜 #
function ($passable) use ($destination) {
    return $destination($passable);  
})
#🎜🎜 #양파의 핵심에 속합니다. 가장 안쪽 레이어는 컨트롤러 작업이 포함된 클로저로 확장합니다.

function ($passable) use ($destination) {
    return $destination($passable);  
})($request)

最终,我们从 return $destination($passable) 中返回一个 Response 类的实例,也就是,第二层的 $response = $next($request) 语句成功得到了结果,接着执行下面的语句,也就是 M2-2 点位,最后第二层闭包返回结果,也就是第一层闭包的 $response = $next($request) 语句成功得到了结果,然后执行这一层闭包该语句后面的语句,即 M1-2 点位,该点位之后,第一层闭包也成功返回结果,于是,then 方法最终得到了返回结果。

整个过程过来,程序经过的点位顺序是这样的:M1-1→M2-1→控制器操作→M2-2→M1-2→返回结果。

总结

整个过程看起来虽然复杂,但不管中间件有多少层,只要理解了前后两层中间件的这种递推关系,洋葱是怎么一层层剥开又一层层返回的,来多少层都不在话下。

위 내용은 ThinkPHP6.0 파이프라인 모드 및 미들웨어 구현 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

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