管道模式
Testing Is Documentation
tests/Pipeline/PipelineTest.php
QueryPHP 提供了一个管道模式组件 \Leevel\Pipeline\Pipeline
对象。
QueryPHP 管道模式提供的几个 API 命名参考了 Laravel,底层核心采用迭代器实现。
管道就像流水线,将复杂的问题分解为一个个小的单元,依次传递并处理,前一个单元的处理结果作为第二个单元的输入。
Uses
<?php
use Closure;
use Leevel\Di\Container;
use Leevel\Pipeline\Pipeline;
管道模式基本使用方法
fixture 定义
Tests\Pipeline\First
namespace Tests\Pipeline;
class First
{
public function handle(Closure $next, $send)
{
$_SERVER['test.first'] = 'i am in first handle and get the send:'.$send;
$next($send);
}
}
Tests\Pipeline\Second
namespace Tests\Pipeline;
class Second
{
public function handle(Closure $next, $send)
{
$_SERVER['test.second'] = 'i am in second handle and get the send:'.$send;
$next($send);
}
}
public function testPipelineBasic(): void
{
$result = (new Pipeline(new Container()))
->send(['hello world'])
->through([First::class, Second::class])
->then();
$this->assertSame('i am in first handle and get the send:hello world', $_SERVER['test.first']);
$this->assertSame('i am in second handle and get the send:hello world', $_SERVER['test.second']);
unset($_SERVER['test.first'], $_SERVER['test.second']);
}
then 执行管道工序并返回响应结果
public function testPipelineWithThen(): void
{
$thenCallback = function (Closure $next, $send) {
$_SERVER['test.then'] = 'i am end and get the send:'.$send;
};
$result = (new Pipeline(new Container()))
->send(['foo bar'])
->through([First::class, Second::class])
->then($thenCallback);
$this->assertSame('i am in first handle and get the send:foo bar', $_SERVER['test.first']);
$this->assertSame('i am in second handle and get the send:foo bar', $_SERVER['test.second']);
$this->assertSame('i am end and get the send:foo bar', $_SERVER['test.then']);
unset($_SERVER['test.first'], $_SERVER['test.second'], $_SERVER['test.then']);
}
管道工序支持返回值
public function testPipelineWithReturn(): void
{
$pipe1 = function (Closure $next, $send) {
$result = $next($send);
$this->assertSame($result, 'return 2');
$_SERVER['test.1'] = '1 and get the send:'.$send;
return 'return 1';
};
$pipe2 = function (Closure $next, $send) {
$result = $next($send);
$this->assertNull($result);
$_SERVER['test.2'] = '2 and get the send:'.$send;
return 'return 2';
};
$result = (new Pipeline(new Container()))
->send(['return test'])
->through([$pipe1, $pipe2])
->then();
$this->assertSame('1 and get the send:return test', $_SERVER['test.1']);
$this->assertSame('2 and get the send:return test', $_SERVER['test.2']);
$this->assertSame('return 1', $result);
unset($_SERVER['test.1'], $_SERVER['test.2']);
}
then 管道工序支持依赖注入
fixture 定义
Tests\Pipeline\DiConstruct
namespace Tests\Pipeline;
class DiConstruct
{
protected $testClass;
public function __construct(TestClass $testClass)
{
$this->testClass = $testClass;
}
public function handle(Closure $next, $send)
{
$_SERVER['test.DiConstruct'] = 'get class:'.get_class($this->testClass);
$next($send);
}
}
Tests\Pipeline\TestClass
namespace Tests\Pipeline;
class TestClass
{
}
public function testPipelineWithDiConstruct(): void
{
$result = (new Pipeline(new Container()))
->send(['hello world'])
->through([DiConstruct::class])
->then();
$this->assertSame('get class:'.TestClass::class, $_SERVER['test.DiConstruct']);
unset($_SERVER['test.DiConstruct']);
}
管道工序无参数
public function testPipelineWithSendNoneParams(): void
{
$pipe = function (Closure $next) {
$this->assertCount(1, func_get_args());
};
$result = (new Pipeline(new Container()))
->through([$pipe])
->then();
}
send 管道工序通过 send 传递参数
public function testPipelineWithSendMoreParams(): void
{
$pipe = function (Closure $next, $send1, $send2, $send3, $send4) {
$this->assertSame($send1, 'hello world');
$this->assertSame($send2, 'foo');
$this->assertSame($send3, 'bar');
$this->assertSame($send4, 'wow');
};
$result = (new Pipeline(new Container()))
->send(['hello world'])
->send(['foo', 'bar', 'wow'])
->through([$pipe])
->then();
}
through 设置管道中的执行工序支持多次添加
public function testPipelineWithThroughMore(): void
{
$_SERVER['test.Through.count'] = 0;
$pipe = function (Closure $next) {
$_SERVER['test.Through.count']++;
$next();
};
$result = (new Pipeline(new Container()))
->through([$pipe])
->through([$pipe, $pipe, $pipe])
->through([$pipe, $pipe])
->then();
$this->assertSame(6, $_SERVER['test.Through.count']);
unset($_SERVER['test.Through.count']);
}
管道工序支持参数传入
fixture 定义
Tests\Pipeline\WithArgs
namespace Tests\Pipeline;
class WithArgs
{
public function handle(Closure $next, $one, $two)
{
$_SERVER['test.WithArgs'] = [$one, $two];
$next();
}
}
public function testPipelineWithPipeArgs(): void
{
$params = ['one', 'two'];
$result = (new Pipeline(new Container()))
->through([WithArgs::class.':'.implode(',', $params)])
->then();
$this->assertSame($params, $_SERVER['test.WithArgs']);
unset($_SERVER['test.WithArgs']);
}
管道工序支持自定义入口方法
fixture 定义
Tests\Pipeline\WithAtMethod
namespace Tests\Pipeline;
class WithAtMethod
{
public function run(Closure $next, $send)
{
$_SERVER['test.at.method'] = 'i am in at.method handle and get the send:'.$send;
$next($send);
}
}
public function testStageWithAtMethod(): void
{
(new Pipeline(new Container()))
->send(['hello world'])
->through([WithAtMethod::class.'@run'])
->then();
$this->assertSame('i am in at.method handle and get the send:hello world', $_SERVER['test.at.method']);
unset($_SERVER['test.at.method']);
}