Laravel Pipeline原理及使用
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原理及使用的更多相关文章
- Laravel Facade原理及使用
Laravel Facade原理及使用 laravel过于庞大,加之笔者水平有限,所以后面的源码解读会按模块功能介绍,希望能帮大家稍微捋顺下思路,即使能够帮助大家回顾几个函数也好.如发现错误,还望指正 ...
- drone的pipeline原理与代码分析
最近的一个项目,需要实现一个工作任务流(task pipeline),基于之前CICD的经验,jenkins pipeline和drone的pipeline进入候选. drone是基于go的cicd解 ...
- Redis Pipeline原理分析
转载请注明出处:http://www.cnblogs.com/jabnih/ 1. 基本原理 1.1 为什么会出现Pipeline Redis本身是基于Request/Response协议的,正常情况 ...
- Laravel 认证原理及完全自定义认证
Laravel 默认的 auth 功能已经是很全面了,但是我们也经常会碰到一些需要自定义的一些情况,比如验证的字段和默认的不匹配,比如需要能够同时满足 user name 和 email 认证等等.如 ...
- Laravel底层原理系列
Laravel 从学徒到工匠精校版 地址:https://laravelacademy.org/laravel-from-appreciate-to-artisan Advanced Applicat ...
- 1.pipeline原理
redis基本语法:https://www.cnblogs.com/xiaonq/p/7919111.html redis四篇:https://www.cnblogs.com/xiaonq/categ ...
- LARAVEL 路由原理分析
<?php class App { protected $routes = []; protected $responseStatus = '200 OK'; protecte ...
- 2016 版 Laravel 系列入门教程(二)【最适合中国人的 Laravel 教程】
本教程示例代码见: https://github.com/johnlui/Learn-Laravel-5 在任何地方卡住,最快的办法就是去看示例代码. 本篇文章中,我将跟宝宝们一起学习 Laravel ...
- Laravel 系列入门教程(二)【最适合中国人的 Laravel 教程】
本篇文章中,我将跟大家一起体验 Laravel 框架最重要的部分——路由系统. 如果你读过 2015 版的教程,你会发现那篇文章里大书特书的 Auth 系统构建已经被 Laravel 捎带手给解决了. ...
随机推荐
- 题解 [SHOI2002]滑雪
记忆化搜索$||dp||$剪枝 先讲方法,代码待会上 方法一:记忆化搜索 这个方法不怎么解释,就是每搜索完一个高度的最长路径记录一下,以后搜索其他的点时如果走到了这条路就直接用记录的值计算就是了 方法 ...
- 云小课 | IPv4枯了,IPv6来了
摘要:本篇主要分享IPv4与IPv6的区别和IPv6的应用场景,助您快速理解华为云IPv6双栈及IPv6 EIP. IPv6的由来 上节课我们讲了公网IP与私网IP,了解了IP地址的一些知识. 我们一 ...
- 实验11——java线程模拟卖票
package cn.tedu.demo; /** * @author 赵瑞鑫 E-mail:1922250303@qq.com * @version 1.0 * @创建时间:2020年7月31日 下 ...
- 程序员必须了解!IntelliJ IDEA 2020.2的新增功能
IDEA 因为之前破解过,所以家里的电脑都是19版本的,用的也比较顺手,也就一直懒得去动他,但是,程序猿的好奇心可能真的挺重,猎奇心里,在网上也看到了很多关于2020版本的idea的各种好处,于是,闲 ...
- 【模式识别与机器学习】——PCA主成分分析
基本思想 其基本思想就是设法提取数据的主成分(或者说是主要信息),然后摒弃冗余信息(或次要信息),从而达到压缩的目的.本文将从更深的层次上讨论PCA的原理,以及Kernel化的PCA. 引子 首先我们 ...
- java 启动Tomcat报错:The specified JRE installation does not exist
启动TomCat服务报错: The specified JRE installation does not exist 解决方法: Eclipse:window->perferences-> ...
- 2020-06-20:一句话总结ZK?
福哥答案2020-06-20: 这道题价值不大,但是面试题里有这道题. 分布式协调服务,注册服务和发现,树形结构,监听机制,过半机制. ZooKeeper是源代码开放的分布式协调服务,由雅虎公司创建, ...
- 2020-04-10:有一个 API 服务,后端只使用了数据库来持久化数据,平时在 API 网关上监控到响应时间平均值大约为10ms,现在突然上涨到 5s,而且一直居高不下。请简单描述一下你排查这个问题的思路。
福哥答案2020-04-11: 1 排查api服务 是否是有大量请求 2 查看mysql的系统情况 cpu 磁盘io 连接数 还是要先定位问题出现在哪个环节
- css如何让子元素在父元素中水平垂直居中
方法一: display:flex <!DOCTYPE html> <html lang="en"> <head> <meta chars ...
- 关于什么时候用,怎么用:ExecuteNonQuery(),还有其它返回值
ExecuteScalar方法返回的类型是object类型,这个方法返回sql语句执行后的第一行第一列的值,由于不知到sql语句到底是什么样的结构(有可能是int,有可能是char等等),所以Exec ...