Laravel Pipeline原理及使用

开发中可能遇到非常冗长的逻辑,以至于我们想将针对性逻辑拆分出来,但是又拿不准该如何拆分才能实现较高的扩展性并保证较高的维护性,或者说不知道如何优雅的将待处理的数据在多个逻辑中传递,那么面向切面编程(AOP)可能会帮助到你。本文讲解laravel中一个AOP的良好实现Pipeline。 不了解array_reduce的请先查看手册

直接上简易代码

<?php

interface MyFilter
{
public static function handle(array $request, callable $next);
} class FilterScum implements MyFilter
{
public static function handle($request, $next)
{
// 前置中间件一
echo '前置中间件一执行', PHP_EOL;
// some logic inc
array_push($request, 'handled by scum filter');
$next($request);
}
} class FilterPieces implements MyFilter
{
public static function handle($request, $next)
{
// 前置中间件二
echo '前置中间件二执行', PHP_EOL;
array_push($request, 'handled by piece filter');
$next($request);
}
} class FilterDot implements MyFilter
{
public static function handle($request, $next)
{
$request = $next($request);
echo '后置中间件一执行', PHP_EOL;
array_push($request, 'handled by dot filter');
var_dump($request);
}
} // 主逻辑闭包
$mainLogic = function ($request) {
echo 'echo from main logic', PHP_EOL;
array_push($request, 'through main logic');
var_dump($request);
echo 'main logic processed', PHP_EOL;
return $request;
}; $filters = [
FilterScum::class,
FilterPieces::class,
FilterDot::class
]; $filters = array_reverse($filters); $callableOnion = array_reduce($filters, function ($destination, $filter) {
return function ($request) use ($destination, $filter) {
return $filter::handle($request, $destination);
};
}, $mainLogic); // $callableOnion(['got no filtered yet']); call_user_func($callableOnion, ['got no filtered yet']); // 执行文件 输出如下
前置中间件一执行
前置中间件二执行
echo from main logic
array(4) {
[0]=>
string(19) "got no filtered yet"
[1]=>
string(22) "handled by scum filter"
[2]=>
string(23) "handled by piece filter"
[3]=>
string(18) "through main logic"
}
main logic processed
后置中间件一执行
array(5) {
[0]=>
string(19) "got no filtered yet"
[1]=>
string(22) "handled by scum filter"
[2]=>
string(23) "handled by piece filter"
[3]=>
string(18) "through main logic"
[4]=>
string(21) "handled by dot filter"
}

callableOnion产生的讲解(伪代码)

执行array_reduce

step1
return function () { FilterDot::handle($mainLogic) }; step2
return function () {
FilterPieces::handle (
function () { Filter::handle($mainLogic) }
)
}; step3 将他移动到FilterDot类中吧 可能会帮助大家理解
return function () {
FilterScum::handle(){
function () {
FilterPieces::handle (
function () { Filter::handle($mainLogic()) }
)
}
}; 当调用step3生成的最终的闭包的时候,先执行了FilterScum的handle方法,其中echo了字符串,向数组中追加了元素,最后调用了传递的闭包,
即在step2生成的闭包,也就是执行了FilterPieces的handle方法,其中echo了字符串,向数组中追加了元素,最后调用了传递的闭包
即在step1生成的闭包, 也就是执行了FilterDot的handle方法,只不过他先调用了主逻辑闭包,后执行了自身的逻辑 希望看到这里大家能够明白为什么要进行一次array_reverse 以上代码中的filter就是主逻辑中拆分出去的切面,将庞大的逻辑拆分成小而针对性强的逻辑,并通过调用闭包的方式,传递各个切面处理后的数据到另外的切面,从而实现庞大的主逻辑,这样可以实现主逻辑的拆分、扩展,同时保证代码的可维护性。

使用过laravel中间件的道友可能会发现和上面的代码非常类似,没错laravel中的中间件就是面向切面编程的良好实现,那么laravel又是如何确定一个请求需要走过哪些中间件,并且最终调用哪段代码逻辑,并生成响应的呢?请出我们本篇的主角 Pipeline,让我们再次感受laravel的优雅吧!

// Illuminate\Foundation\Http\Kernel::sendRequestThroughRouter方法
protected function sendRequestThroughRouter($request)
{
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request'); $this->bootstrap(); // 这段代码真的非常漂亮,实现流接口,面向对象,且人类可读,我们一起结合生活实际感受下
// new Pipeline 首先获得Pipeline实例 对应的实际场景:找到一个管道
// send方法 从管道的一头送入经过管道各个节点处理的数据(laravel 6 中真的可以是任何数据) 对应的实际场景:从管道一头送入包含杂质的原料(包含三角形,方形的杂质和我们真正需要的圆形原料)
// through方法 $this->middleware就是 App\Http\Kernel中的middleware全局中间件数组,表示请求要先经过这些中间件的过滤,若中间件中返回false,则管道停止前进,不会进行路由匹配 对应的实际场景:在管道的各个节点上加上圆形的筛板,只有圆形的原料可以通过,杂质都被剔除掉了
// then方法,经过了前面中间件的过滤,然后进行路由匹配,执行实际的业务逻辑 对应的实际场景:得到了圆形的原材料,生产产品交付用户
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
} // 跳转到send方法
public function send($passable)
{
// 要通过管道依次传递到不同切面(中间件)的任何东西
$this->passable = $passable; return $this;
} // 跳转到through方法
public function through($pipes)
{
// 传递的数据要经过的切面(中间件)
$this->pipes = is_array($pipes) ? $pipes : func_get_args(); return $this;
} // 执行最终的逻辑 产生响应返回给用户
// 结合文章开头的简易代码
public function then(Closure $destination)
{
// 生成一条指向目的地,且搭建好了各个筛板(中间件)的管道
$pipeline = array_reduce(
// 跳转到carry方法 其实就是文章开头array_reduce的第二个闭包
array_reverse($this->pipes()), $this->carry(), $this-
// prepareDestination 将最终要执行的方法 分装成闭包
>prepareDestination($destination)
); // 向管道中投递要处理的数据,生成响应并返回
// 就是本文开头的 call_user_func($callableOnion, ['got no filtered yet']);
return $pipeline($this->passable);
} // 返回供array_reduce迭代使用的闭包
protected function carry()
{
return function ($stack, $pipe) {
return function ($passable) use ($stack, $pipe) {
try {
if (is_callable($pipe)) {
// 本篇之说明最简单的$pipe是个闭包的情况,其他分支会在后面的使用示例中展示
// 迭代的将闭包传递闭包中,并返回
return $pipe($passable, $stack);
} elseif (! is_object($pipe)) {
// dump($pipe);
[$name, $parameters] = $this->parsePipeString($pipe);
$pipe = $this->getContainer()->make($name);
$parameters = array_merge([$passable, $stack], $parameters);
} else {
$parameters = [$passable, $stack];
} $carry = method_exists($pipe, $this->method)
? $pipe->{$this->method}(...$parameters)
: $pipe(...$parameters); return $this->handleCarry($carry);
} catch (Exception $e) {
return $this->handleException($passable, $e);
} catch (Throwable $e) {
return $this->handleException($passable, new FatalThrowableError($e));
}
};
};
}

好了,理论讲解就到这里吧,今天的文章只要搞懂array_reduce方法就差不多能够理解了,下面讲解laravel pipeline的使用 毫无疑问laravel依然为我们准备好了相应的服务。

使用

我们实现一个简单的字符串过滤转换管道

1 创建契约
<?php namespace App\Contracts; use Closure; interface MyPipeline
{
public function myViaFunc($raw, Closure $next);
} 2 创建管道中间件
<?php namespace App\Pipes; use Closure;
use App\Contracts\MyPipeline; class UcFilter implements MyPipeline
{
public function myViaFunc($string, Closure $next)
{
$string = ucfirst($string);
return $next($string);
}
} 3 使用管道
// 其中的via thenReturn方法非常简单,请自行查看
Route::get('pipe', function () {
// $barString = app(\Illuminate\Pipeline\Pipeline::class)
$barString = (new \Illuminate\Pipeline\Pipeline(app('app')))
->via('myViaFunc')
->send('some foo string')
->through([App\Pipes\UcFilter::class])
->thenReturn();
dump($barString); // Some foo string
}); Route::get('pipes', function () {
$barString = app(\Illuminate\Pipeline\Pipeline::class)
->via('myViaFunc')
->send('some foo string')
->through([App\Pipes\UcFilter::class])
->then(function ($raw) {
dump($raw); // Some foo string
return substr($raw, 2);
});
dump($barString); // me foo string
});
// thenReturn和then方法的区别,then方法显示的指定了管道的最后一站而已,也就是说产品生产完交付给用户前的最后一站。如果你查阅源码可以发现thenReturn方法就是调用的then方法。希望你是自己发现的。 // 随便折磨一下管道
Route::get('pipess', function () {
$barString = app(\Illuminate\Pipeline\Pipeline::class)
->via('myViaFunc')
->send('some foo string')
->through(
// 你甚至可以这样折磨管道 完全因为laravel pipeline的强大
// 对应carry方法中的各种分支 当然前提是能够通过容器进行解析的类
function ($passable, $next) {
return $next(ucfirst($passable));
},
SomeFilterClass::class,
new SomeClass()
)
->thenReturn();
dump($barString);
}); // 在实际的应用中可能这样使用更有意义些
public function create(Request $request, Pipeline $pipeline)
{
$something = $pipeline->send($request->someField)
->through([
过滤字符串类1::class,
过滤字符串类2::class,
过滤字符串类3::class
...
])->thenReturn();
SomeModel::doSomeShit([]);
...
}

通过上面的不恰当例子,希望能够帮助大家认识管道的使用情景。

不出意外下篇就是laravel系列的完结了,返回响应给用户。

发现错误,还望指教,感谢!!!

Laravel Pipeline原理及使用的更多相关文章

  1. Laravel Facade原理及使用

    Laravel Facade原理及使用 laravel过于庞大,加之笔者水平有限,所以后面的源码解读会按模块功能介绍,希望能帮大家稍微捋顺下思路,即使能够帮助大家回顾几个函数也好.如发现错误,还望指正 ...

  2. drone的pipeline原理与代码分析

    最近的一个项目,需要实现一个工作任务流(task pipeline),基于之前CICD的经验,jenkins pipeline和drone的pipeline进入候选. drone是基于go的cicd解 ...

  3. Redis Pipeline原理分析

    转载请注明出处:http://www.cnblogs.com/jabnih/ 1. 基本原理 1.1 为什么会出现Pipeline Redis本身是基于Request/Response协议的,正常情况 ...

  4. Laravel 认证原理及完全自定义认证

    Laravel 默认的 auth 功能已经是很全面了,但是我们也经常会碰到一些需要自定义的一些情况,比如验证的字段和默认的不匹配,比如需要能够同时满足 user name 和 email 认证等等.如 ...

  5. Laravel底层原理系列

    Laravel 从学徒到工匠精校版 地址:https://laravelacademy.org/laravel-from-appreciate-to-artisan Advanced Applicat ...

  6. 1.pipeline原理

    redis基本语法:https://www.cnblogs.com/xiaonq/p/7919111.html redis四篇:https://www.cnblogs.com/xiaonq/categ ...

  7. LARAVEL 路由原理分析

    <?php class App {    protected $routes = [];    protected $responseStatus = '200 OK';    protecte ...

  8. 2016 版 Laravel 系列入门教程(二)【最适合中国人的 Laravel 教程】

    本教程示例代码见: https://github.com/johnlui/Learn-Laravel-5 在任何地方卡住,最快的办法就是去看示例代码. 本篇文章中,我将跟宝宝们一起学习 Laravel ...

  9. Laravel 系列入门教程(二)【最适合中国人的 Laravel 教程】

    本篇文章中,我将跟大家一起体验 Laravel 框架最重要的部分——路由系统. 如果你读过 2015 版的教程,你会发现那篇文章里大书特书的 Auth 系统构建已经被 Laravel 捎带手给解决了. ...

随机推荐

  1. BSOJ 5553 wangyurzee的树 prufer序列 容斥

    BSOJ我也不知道在哪. 容易想到容斥. 考虑不合法的方案 想到强制某个点的度数为限制即可. 这样就变成了了总方案-一个不合法+两个不合法-3个......的模型了. 坑点 当强制两个相同的点时 方案 ...

  2. odoo12的视图常见属性和操作

    1.菜单视图属性: 常用属性: name是展示在用户界面中的菜单项标题 action是点击菜单项时运行的窗口操作的XML ID parent是父级菜单项的XML ID.本例中父级项由其它模块创建,因此 ...

  3. 三个技巧帮助Docker镜像瘦身

    在构建Docker容器时,应该尽量想办法获得体积更小的镜像,因为传输和部署体积较小的镜像速度更快. 但RUN语句总是会创建一个新层,而且在生成镜像之前还需要使用很多中间文件,在这种情况下,该如何获得体 ...

  4. centos,linux环境下安装JDK1.8完整

    进入oracle官网下载安装包,cetos一般选择xx-xx-linux-x64.tar.gz.获取到地址后可以点击下载,也可以使用wget命令下载. 在得到下载好的文件后下面就可以开始安装了.比如我 ...

  5. Kaggle-pandas(2)

    Intndexing-selecting-assigning 教程 介绍选择要处理的pandas DataFrame或Series的特定值是几乎将要运行的任何数据操作中的一个隐含步骤,因此在Pytho ...

  6. Qt信号参数中使用QVariantList时编译问题

    今天调试代码时遇到一个奇怪的问题,不过一般感觉比较奇怪的问题,最后查到原因时,原因都比较简单! 编译问题 先来看一下qt的编译错误,提示一堆错误: In file included from D:\Q ...

  7. JS DOM操作案例

    显示隐藏表单文本内容 <input type="text" value="手机"> var text = document.querySelecto ...

  8. Spring Cloud 之分布式配置基础应用

    分布式配置基础应用 配置中心服务 spring-config-server pom.xml <?xml version="1.0" encoding="UTF-8& ...

  9. 算法学习笔记:2-SAT

    SAT 是适定性(Satisfiability)问题的简称.一般形式为 k - 适定性问题,简称 k-SAT.而当 \(k>2\) 时该问题为 NP 完全的.所以我们只研究 \(k=2\) 的情 ...

  10. 小白入门Web前端开发学习一周小结

    说之前还是先说点其他的,简单介绍下自己为何选择web前端开发: 本人之前在一家国企单位从事质检工作,干了3年,工资和待遇还算不错,但由于其工作的流动性导致知识的脱轨以及精神上的空缺,最后还是打算在25 ...