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 捎带手给解决了. ...
随机推荐
- 牛客练习赛63 牛牛的斐波那契字符串 矩阵乘法 KMP
LINK:牛牛的斐波那契字符串 虽然sb的事实没有改变 但是 也不会改变. 赛时 看了E和F题 都不咋会写 所以弃疗了. 中午又看了一遍F 发现很水 差分了一下就过了. 这是下午和古队长讨论+看题解的 ...
- QT学习笔记(day01)
QT中的对象树 一定程度上简化了内存回收机制:当创建的对象 指定的父亲是由QObject或者Object派生的类时候,这个对象被加载到对象树上,当窗口关闭掉时候,树上的对象也都会被释放掉 信号和槽 通 ...
- electron开发 - mac平台的打包和签名 - 针对electron-builder的自动更新
配合自动更新 - 安装包签名(mac)(待补充) 前提:实现自动更新需要签名 一.本地开发调试mac签名(无需具有权限的appple账号) 打开xcode,xcode -> preference ...
- Python自动化运维 技术与最佳实践PDF高清完整版免费下载|百度云盘|Python基础教程免费电子书
点击获取提取码:7bl4 一.内容简介 <python自动化运维:技术与最佳实践>一书在中国运维领域将有"划时代"的重要意义:一方面,这是国内第一本从纵.深和实践角度探 ...
- 无版号的ios手游下架 TF签名才是正确选择?
2020年8月1日开始,无版号的ios手游就要全部下架appstore了,在这样关键的时刻,TF签名成了ios手游的最后一根救命稻草.因为被下架或者根本无法通过appstore的上架审核,ios手游的 ...
- 自定义 Mybatis 框架
分析流程 1. 引入dom4j <dependencies> <!--<dependency> <groupId>org.mybatis</groupI ...
- 学Java必看!零基础小白再也不用退缩了
程序员们!请往这儿看 对于JAVA的学习,可能你还会有许多的顾虑 不要担心 接着往下看吧 学Java前 一.数学差,英语也不好是不是学不好Java? 答案是:是~ 因为你在问这个问题的时候说明你对自己 ...
- ios 浅谈一下UITextFiled UITextView 在tableview的cell上边展示
最近在项目中.要做到在tableview的cell上边加一个输入框.允许用户输入. 1.我首先选的是在uitextView 然后在通知键盘出现的时候,将tableview的内容设置在键盘的上边.但是 ...
- Android常用布局和控件
一.Android常用布局属性 1. LinearLayout的特有属性 android:orientation:设置布局排列方式 android:layout_weight:设置所占布局的权重 ...
- “随手记”开发记录day09
今天完成了关于我们页面和更新查找页面 效果