管道模式

Testing Is Documentation

tests/Pipeline/PipelineTest.php管道模式 - 图1

QueryPHP 提供了一个管道模式组件 \Leevel\Pipeline\Pipeline 对象。

QueryPHP 管道模式提供的几个 API 命名参考了 Laravel,底层核心采用迭代器实现。

管道就像流水线,将复杂的问题分解为一个个小的单元,依次传递并处理,前一个单元的处理结果作为第二个单元的输入。

Uses

  1. <?php
  2. use Closure;
  3. use Leevel\Di\Container;
  4. use Leevel\Pipeline\Pipeline;

管道模式基本使用方法

fixture 定义

Tests\Pipeline\First

  1. namespace Tests\Pipeline;
  2. class First
  3. {
  4. public function handle(Closure $next, $send)
  5. {
  6. $_SERVER['test.first'] = 'i am in first handle and get the send:'.$send;
  7. $next($send);
  8. }
  9. }

Tests\Pipeline\Second

  1. namespace Tests\Pipeline;
  2. class Second
  3. {
  4. public function handle(Closure $next, $send)
  5. {
  6. $_SERVER['test.second'] = 'i am in second handle and get the send:'.$send;
  7. $next($send);
  8. }
  9. }
  1. public function testPipelineBasic(): void
  2. {
  3. $result = (new Pipeline(new Container()))
  4. ->send(['hello world'])
  5. ->through([First::class, Second::class])
  6. ->then();
  7. $this->assertSame('i am in first handle and get the send:hello world', $_SERVER['test.first']);
  8. $this->assertSame('i am in second handle and get the send:hello world', $_SERVER['test.second']);
  9. unset($_SERVER['test.first'], $_SERVER['test.second']);
  10. }

then 执行管道工序并返回响应结果

  1. public function testPipelineWithThen(): void
  2. {
  3. $thenCallback = function (Closure $next, $send) {
  4. $_SERVER['test.then'] = 'i am end and get the send:'.$send;
  5. };
  6. $result = (new Pipeline(new Container()))
  7. ->send(['foo bar'])
  8. ->through([First::class, Second::class])
  9. ->then($thenCallback);
  10. $this->assertSame('i am in first handle and get the send:foo bar', $_SERVER['test.first']);
  11. $this->assertSame('i am in second handle and get the send:foo bar', $_SERVER['test.second']);
  12. $this->assertSame('i am end and get the send:foo bar', $_SERVER['test.then']);
  13. unset($_SERVER['test.first'], $_SERVER['test.second'], $_SERVER['test.then']);
  14. }

管道工序支持返回值

  1. public function testPipelineWithReturn(): void
  2. {
  3. $pipe1 = function (Closure $next, $send) {
  4. $result = $next($send);
  5. $this->assertSame($result, 'return 2');
  6. $_SERVER['test.1'] = '1 and get the send:'.$send;
  7. return 'return 1';
  8. };
  9. $pipe2 = function (Closure $next, $send) {
  10. $result = $next($send);
  11. $this->assertNull($result);
  12. $_SERVER['test.2'] = '2 and get the send:'.$send;
  13. return 'return 2';
  14. };
  15. $result = (new Pipeline(new Container()))
  16. ->send(['return test'])
  17. ->through([$pipe1, $pipe2])
  18. ->then();
  19. $this->assertSame('1 and get the send:return test', $_SERVER['test.1']);
  20. $this->assertSame('2 and get the send:return test', $_SERVER['test.2']);
  21. $this->assertSame('return 1', $result);
  22. unset($_SERVER['test.1'], $_SERVER['test.2']);
  23. }

then 管道工序支持依赖注入

fixture 定义

Tests\Pipeline\DiConstruct

  1. namespace Tests\Pipeline;
  2. class DiConstruct
  3. {
  4. protected $testClass;
  5. public function __construct(TestClass $testClass)
  6. {
  7. $this->testClass = $testClass;
  8. }
  9. public function handle(Closure $next, $send)
  10. {
  11. $_SERVER['test.DiConstruct'] = 'get class:'.get_class($this->testClass);
  12. $next($send);
  13. }
  14. }

Tests\Pipeline\TestClass

  1. namespace Tests\Pipeline;
  2. class TestClass
  3. {
  4. }
  1. public function testPipelineWithDiConstruct(): void
  2. {
  3. $result = (new Pipeline(new Container()))
  4. ->send(['hello world'])
  5. ->through([DiConstruct::class])
  6. ->then();
  7. $this->assertSame('get class:'.TestClass::class, $_SERVER['test.DiConstruct']);
  8. unset($_SERVER['test.DiConstruct']);
  9. }

管道工序无参数

  1. public function testPipelineWithSendNoneParams(): void
  2. {
  3. $pipe = function (Closure $next) {
  4. $this->assertCount(1, func_get_args());
  5. };
  6. $result = (new Pipeline(new Container()))
  7. ->through([$pipe])
  8. ->then();
  9. }

send 管道工序通过 send 传递参数

  1. public function testPipelineWithSendMoreParams(): void
  2. {
  3. $pipe = function (Closure $next, $send1, $send2, $send3, $send4) {
  4. $this->assertSame($send1, 'hello world');
  5. $this->assertSame($send2, 'foo');
  6. $this->assertSame($send3, 'bar');
  7. $this->assertSame($send4, 'wow');
  8. };
  9. $result = (new Pipeline(new Container()))
  10. ->send(['hello world'])
  11. ->send(['foo', 'bar', 'wow'])
  12. ->through([$pipe])
  13. ->then();
  14. }

through 设置管道中的执行工序支持多次添加

  1. public function testPipelineWithThroughMore(): void
  2. {
  3. $_SERVER['test.Through.count'] = 0;
  4. $pipe = function (Closure $next) {
  5. $_SERVER['test.Through.count']++;
  6. $next();
  7. };
  8. $result = (new Pipeline(new Container()))
  9. ->through([$pipe])
  10. ->through([$pipe, $pipe, $pipe])
  11. ->through([$pipe, $pipe])
  12. ->then();
  13. $this->assertSame(6, $_SERVER['test.Through.count']);
  14. unset($_SERVER['test.Through.count']);
  15. }

管道工序支持参数传入

fixture 定义

Tests\Pipeline\WithArgs

  1. namespace Tests\Pipeline;
  2. class WithArgs
  3. {
  4. public function handle(Closure $next, $one, $two)
  5. {
  6. $_SERVER['test.WithArgs'] = [$one, $two];
  7. $next();
  8. }
  9. }
  1. public function testPipelineWithPipeArgs(): void
  2. {
  3. $params = ['one', 'two'];
  4. $result = (new Pipeline(new Container()))
  5. ->through([WithArgs::class.':'.implode(',', $params)])
  6. ->then();
  7. $this->assertSame($params, $_SERVER['test.WithArgs']);
  8. unset($_SERVER['test.WithArgs']);
  9. }

管道工序支持自定义入口方法

fixture 定义

Tests\Pipeline\WithAtMethod

  1. namespace Tests\Pipeline;
  2. class WithAtMethod
  3. {
  4. public function run(Closure $next, $send)
  5. {
  6. $_SERVER['test.at.method'] = 'i am in at.method handle and get the send:'.$send;
  7. $next($send);
  8. }
  9. }
  1. public function testStageWithAtMethod(): void
  2. {
  3. (new Pipeline(new Container()))
  4. ->send(['hello world'])
  5. ->through([WithAtMethod::class.'@run'])
  6. ->then();
  7. $this->assertSame('i am in at.method handle and get the send:hello world', $_SERVER['test.at.method']);
  8. unset($_SERVER['test.at.method']);
  9. }