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

歡迎訪問 生活随笔!

生活随笔

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

综合教程

laravel 管道设计模式

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

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

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

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

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

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

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

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

Pipeline有什么缺點?

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

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

分析的文件:
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

下面的部分是對Pipeline接口的定義,定義了send、through、via、then四個方法。(這里不知道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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

下面是對管道添加了異常處理的實現(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

參考文獻:

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

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

總結(jié)

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

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