日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

laravel 管道设计模式

發(fā)布時(shí)間:2024/6/21 综合教程 38 生活家
生活随笔 收集整理的這篇文章主要介紹了 laravel 管道设计模式 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

laravel中的管道(Pipeline)是什么?

  所謂管道(Pipeline)設(shè)計(jì)模式,就是把數(shù)據(jù)傳遞給一個(gè)任務(wù)隊(duì)列,由任務(wù)隊(duì)列按次序依次對(duì)數(shù)據(jù)進(jìn)行加工處理。在laravel框架中,這里的數(shù)據(jù)就是http請(qǐng)求,任務(wù)隊(duì)列包含了一個(gè)又一個(gè)的中間件。

  類比1:以流水線或流水管道作類比,流水線上的產(chǎn)品(http請(qǐng)求),依次經(jīng)過一個(gè)又一個(gè)的加工單元(對(duì)應(yīng)一個(gè)又一個(gè)的中間件)進(jìn)行處理,最后生成產(chǎn)品(http響應(yīng))。

  類比2:同樣的,也可以與linux下的管道作類比,

cat helloworld.txt | grep "hello world" | rev | > output.txt

  不過,差異的地方是,linux shell管道中任務(wù)隊(duì)列中的單元是一個(gè)又一個(gè)的進(jìn)程。而laravel框架中的Pipeline是運(yùn)行在一個(gè)進(jìn)程中的一個(gè)又一個(gè)的程序塊,或者說邏輯片。
laravel中如何使用pipeline?
  Laravel 在框架中的很多地方使用了管道設(shè)計(jì)模式,最常見的就是中間件的實(shí)現(xiàn)。
  當(dāng)請(qǐng)求最終到達(dá)控制器動(dòng)作被處理前,會(huì)先經(jīng)過一系列的中間件。每個(gè)中間價(jià)都有一個(gè)獨(dú)立的職責(zé),例如,設(shè)置 Cookie、判斷是否登錄以及阻止 CSRF 攻擊等等。每個(gè)階段都會(huì)對(duì)請(qǐng)求進(jìn)行處理,如果請(qǐng)求通過就會(huì)被傳遞給下一個(gè)處理,不通過就會(huì)返回相應(yīng)的 HTTP 響應(yīng)。
  這種機(jī)制使得我們很容易在請(qǐng)求最終到達(dá)應(yīng)用代碼前添加處理操作,當(dāng)然如果不需要這個(gè)處理操作你也可以隨時(shí)移除而不影響請(qǐng)求的生命周期。
Pipeline有什么優(yōu)點(diǎn)?

1. 將復(fù)雜的處理流程分解成獨(dú)立的子任務(wù),從而方便測試每個(gè)子任務(wù);
2. 被分解的子任務(wù)可以被不同的處理進(jìn)程復(fù)用,避免代碼冗余。(這里說的不同的處理進(jìn)程是指,針對(duì)不同的http請(qǐng)求,采用不同的子任務(wù)組合來處理)
3. 在復(fù)雜進(jìn)程中添加、移除和替換子任務(wù)非常輕松,對(duì)已存在的進(jìn)程沒有任何影響。

Pipeline有什么缺點(diǎn)?

1. 雖然每個(gè)子任務(wù)變得簡單了,但是當(dāng)你再度嘗試將這些子任務(wù)組合成完整進(jìn)程時(shí)有一定復(fù)雜性;
2. 你還需要保證獨(dú)立子任務(wù)測試通過后整體的流程能正常工作,這有一定的不確定性。(因?yàn)樵诠艿乐辛鲃?dòng)的是http請(qǐng)求,并且子任務(wù)可以修改http請(qǐng)求,這樣就存在前一次的修改內(nèi)容導(dǎo)致下一個(gè)子任務(wù)的執(zhí)行失敗的可能性)
3. 當(dāng)你看到的都是一個(gè)個(gè)子任務(wù)時(shí),對(duì)理解整體流程帶來困難(盲人摸象的故事想必大家很熟悉,正是此理)。

代碼理解
這里只局部分析管道實(shí)現(xiàn)的三個(gè)文件,它們并沒有組成一個(gè)完整的工作流程。想要了解完整流程,還需要研究后面的幾個(gè)文件。

分析的文件:
1. laravel/vendor/laravel/framework/src/Illuminate/Contracts/Pipeline/Pipeline.php
2. laravel/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php
3. laravel/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php

了解完整流程,還需要看以下文件:
1. laravel/public/index.php
2. laravel/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
3. laravel/vendor/laravel/framework/src/Illuminate/Foundation/Application.php

下面的部分是對(duì)Pipeline接口的定義,定義了send、through、via、then四個(gè)方法。(這里不知道via方法具體是干嘛的)

laravelvendor/laravel/framework/src/Illuminate/Contracts/Pipeline/Pipeline.php

 1 <?php
 2 
 3 namespace IlluminateContractsPipeline;
 4 
 5 use Closure;
 6 
 7 interface Pipeline
 8 {
 9     /**
10      * Set the traveler object being sent on the pipeline.
11      *
12      * @param  mixed  $traveler
13      * @return $this
14      */
15     public function send($traveler);
16 
17     /**
18      * Set the stops of the pipeline.
19      *
20      * @param  dynamic|array  $stops
21      * @return $this
22      */
23     public function through($stops);
24 
25     /**
26      * Set the method to call on the stops.
27      *
28      * @param  string  $method
29      * @return $this
30      */
31     public function via($method);
32 
33     /**
34      * Run the pipeline with a final destination callback.
35      *
36      * @param  Closure  $destination
37      * @return mixed
38      */
39     public function then(Closure $destination);
40 }

View Code

   接下來,是對(duì)管道的實(shí)現(xiàn).

protected $container;  //保存服務(wù)容器的實(shí)例

protected $passable;  //保存?zhèn)魅氲膆ttp請(qǐng)求

protected $pipes = [];  //保存子任務(wù)隊(duì)列,子任務(wù)可以使閉包函數(shù),也可以使類名與參數(shù)名的字符串組合

protected $method = 'handle';  //當(dāng)子任務(wù)是類名+參數(shù)的字符串組合時(shí),$method指定在管道處理到該類子任務(wù)時(shí),該類子任務(wù)用來處理http請(qǐng)求的方法名。$method默認(rèn)是handle,但是可以通過                  via()方法修改

在一次http的請(qǐng)求過程中,以下方法的被調(diào)用的過程是:send() -> through() -> then() 。

public function send($passable){}  //傳入初始的http請(qǐng)求

public function through($pipes){}  //設(shè)置管道的子任務(wù)隊(duì)列

public function via($method){}  //設(shè)置$method的值

public function then(Closure $destination){}  //啟動(dòng)管道,用設(shè)定的子任務(wù)隊(duì)列去處理http請(qǐng)求

protected function getSlice(){}      //本函數(shù)返回array_reduce()中所需要的第二個(gè)參數(shù),callback函數(shù)

protected function getInitialSlice(Closure $destination){}    //本函數(shù)對(duì)應(yīng)array_reduce()中所需要的第三個(gè)參數(shù),初始化值

protected function parsePipeString($pipe){}    //針對(duì)子任務(wù)是類名+參數(shù)名的字符串組合,提取類名和參數(shù)

由上面可知,整個(gè)管道的處理邏輯主要集中在then()方法中。

then()方法中,最難懂的是下面這句話:

1         return call_user_func(
2             array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
3         );

結(jié)合getSlice()方法,array_reduce()的處理過程實(shí)際上是,利用了閉包函數(shù)的特點(diǎn),用閉包函數(shù)保存了局部作用域中的參數(shù)$stack和$pipe,并將保存了局部scope的閉包函數(shù)作為對(duì)象,壓如由$stack保存的堆棧中,當(dāng)將整個(gè)逆序的子任務(wù)隊(duì)列的執(zhí)行函數(shù)的閉包函數(shù)形式壓入棧中后,再通過call\_user\_func(),傳入$this->passable(即http請(qǐng)求),從棧中依次彈出閉包函數(shù)處理請(qǐng)求。

laravelvendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php

  1 <?php
  2 
  3 namespace IlluminatePipeline;
  4 
  5 use Closure;
  6 use IlluminateContractsContainerContainer;
  7 use IlluminateContractsPipelinePipeline as PipelineContract;
  8 
  9 class Pipeline implements PipelineContract
 10 {
 11     /**
 12      * The container implementation.
 13      *
 14      * @var IlluminateContractsContainerContainer
 15      */
 16     protected $container;
 17 
 18     /**
 19      * The object being passed through the pipeline.
 20      *
 21      * @var mixed
 22      */
 23     protected $passable;
 24 
 25     /**
 26      * The array of class pipes.
 27      *
 28      * @var array
 29      */
 30     protected $pipes = [];
 31 
 32     /**
 33      * The method to call on each pipe.
 34      *
 35      * @var string
 36      */
 37     protected $method = 'handle';
 38 
 39     /**
 40      * Create a new class instance.
 41      *
 42      * @param  IlluminateContractsContainerContainer  $container
 43      * @return void
 44      */
 45     public function __construct(Container $container)
 46     {
 47         $this->container = $container;
 48     }
 49 
 50     /**
 51      * Set the object being sent through the pipeline.
 52      *
 53      * @param  mixed  $passable
 54      * @return $this
 55      */
 56     public function send($passable)
 57     {
 58         $this->passable = $passable;
 59 
 60         return $this;
 61     }
 62 
 63     /**
 64      * Set the array of pipes.
 65      *
 66      * @param  array|mixed  $pipes
 67      * @return $this
 68      */
 69     public function through($pipes)
 70     {
 71         $this->pipes = is_array($pipes) ? $pipes : func_get_args();
 72 
 73         return $this;
 74     }
 75 
 76     /**
 77      * Set the method to call on the pipes.
 78      *
 79      * @param  string  $method
 80      * @return $this
 81      */
 82     public function via($method)
 83     {
 84         $this->method = $method;
 85 
 86         return $this;
 87     }
 88 
 89     /**
 90      * Run the pipeline with a final destination callback.
 91      *
 92      * @param  Closure  $destination
 93      * @return mixed
 94      */
 95     public function then(Closure $destination)
 96     {
 97         $firstSlice = $this->getInitialSlice($destination);
 98 
 99         $pipes = array_reverse($this->pipes);
100 
101         return call_user_func(
102             array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
103         );
104     }
105 
106     /**
107      * Get a Closure that represents a slice of the application onion.
108      *
109      * @return Closure
110      */
111     protected function getSlice()
112     {
113         return function ($stack, $pipe) {
114             return function ($passable) use ($stack, $pipe) {
115                 // If the pipe is an instance of a Closure, we will just call it directly but
116                 // otherwise we'll resolve the pipes out of the container and call it with
117                 // the appropriate method and arguments, returning the results back out.
118                 if ($pipe instanceof Closure) {
119                     return call_user_func($pipe, $passable, $stack);
120                 } else {
121                     list($name, $parameters) = $this->parsePipeString($pipe);
122 
123                     return call_user_func_array([$this->container->make($name), $this->method],
124                             array_merge([$passable, $stack], $parameters));
125                 }
126             };
127         };
128     }
129 
130     /**
131      * Get the initial slice to begin the stack call.
132      *
133      * @param  Closure  $destination
134      * @return Closure
135      */
136     protected function getInitialSlice(Closure $destination)
137     {
138         return function ($passable) use ($destination) {
139             return call_user_func($destination, $passable);
140         };
141     }
142 
143     /**
144      * Parse full pipe string to get name and parameters.
145      *
146      * @param  string $pipe
147      * @return array
148      */
149     protected function parsePipeString($pipe)
150     {
151         list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []);
152 
153         if (is_string($parameters)) {
154             $parameters = explode(',', $parameters);
155         }
156 
157         return [$name, $parameters];
158     }
159 }

View Code

下面是對(duì)管道添加了異常處理的實(shí)現(xiàn)。

laravelvendor/laravel/framework/src/Illuminate/Routing/Pipeline.php

 1 <?php
 2 
 3 namespace IlluminateRouting;
 4 
 5 use Closure;
 6 use Throwable;
 7 use Exception;
 8 use IlluminateHttpRequest;
 9 use IlluminateContractsDebugExceptionHandler;
10 use IlluminatePipelinePipeline as BasePipeline;
11 use SymfonyComponentDebugExceptionFatalThrowableError;
12 
13 /**
14  * This extended pipeline catches any exceptions that occur during each slice.
15  *
16  * The exceptions are converted to HTTP responses for proper middleware handling.
17  */
18 class Pipeline extends BasePipeline
19 {
20     /**
21      * Get a Closure that represents a slice of the application onion.
22      *
23      * @return Closure
24      */
25     protected function getSlice()
26     {
27         return function ($stack, $pipe) {
28             return function ($passable) use ($stack, $pipe) {
29                 try {
30                     $slice = parent::getSlice();
31 
32                     return call_user_func($slice($stack, $pipe), $passable);
33                 } catch (Exception $e) {
34                     return $this->handleException($passable, $e);
35                 } catch (Throwable $e) {
36                     return $this->handleException($passable, new FatalThrowableError($e));
37                 }
38             };
39         };
40     }
41 
42     /**
43      * Get the initial slice to begin the stack call.
44      *
45      * @param  Closure  $destination
46      * @return Closure
47      */
48     protected function getInitialSlice(Closure $destination)
49     {
50         return function ($passable) use ($destination) {
51             try {
52                 return call_user_func($destination, $passable);
53             } catch (Exception $e) {
54                 return $this->handleException($passable, $e);
55             } catch (Throwable $e) {
56                 return $this->handleException($passable, new FatalThrowableError($e));
57             }
58         };
59     }
60 
61     /**
62      * Handle the given exception.
63      *
64      * @param  mixed  $passable
65      * @param  Exception  $e
66      * @return mixed
67      *
68      * @throws Exception
69      */
70     protected function handleException($passable, Exception $e)
71     {
72         if (! $this->container->bound(ExceptionHandler::class) || ! $passable instanceof Request) {
73             throw $e;
74         }
75 
76         $handler = $this->container->make(ExceptionHandler::class);
77 
78         $handler->report($e);
79 
80         $response = $handler->render($passable, $e);
81 
82         if (method_exists($response, 'withException')) {
83             $response->withException($e);
84         }
85 
86         return $response;
87     }
88 }

View Code

參考文獻(xiàn):

1. Laravel 中管道設(shè)計(jì)模式的使用 —— 中間件實(shí)現(xiàn)原理探究

2. 不依賴于任何框架的管道

總結(jié)

以上是生活随笔為你收集整理的laravel 管道设计模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。