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 중국어 웹사이트의 기타 관련 기사를 참조하세요!