toobo / pipepie
回调管道
Requires (Dev)
- phpunit/phpunit: ~4.4
This package is auto-updated.
Last update: 2024-08-29 04:14:26 UTC
README
简介
PipePie 是一个通用工具,用于将一组回调应用于某些输入数据以获取一些输出数据。
use Toobo\PipePie\Pipeline; $pipeline = (new Pipeline()) ->pipe(function($carry) { return $carry.'B'; })->pipe(function($carry) { return $carry.'C'; }); echo $pipeline->applyTo('A'); // 'ABC'
所以
Pipeline::pipe()
用于向管道添加回调Pipeline::applyTo()
用于将回调管道应用于某些数据
管道中的每个回调都接收前一个回调返回的值作为第一个参数。第一个回调接收输入数据。
请注意,回调可以是 PHP 中评估为 callable
的任何内容,因此不仅仅是闭包。
例如,可以使用 可调用对象(即具有 __invoke()
方法的对象)来提高代码的 可重用性。
初始值访问
管道中回调接收的第二个参数始终是初始值,即传递给 Pipeline::applyTo()
的值。
use Toobo\PipePie\Pipeline; $pipeline = (new Pipeline()) ->pipe(function($carry, $initial) { return $initial % 2 === 0 ? $carry * 2 : $carry; })->pipe(function($carry, $initial) { return $initial % 2 === 0 ? $carry * 2 : $carry; }); echo $pipeline->applyTo(2); // 8 echo $pipeline->applyTo(1); // 1
这样,管道中的每个回调都可以在知道初始值的情况下完成其工作。
DTO
DTO 代表 "数据传输对象"。
PipePie 使用此类对象来使得在回调之间传递数据成为可能。
实际上,回调接收的第三个参数是 Toobo\PipePie\DTO
类的实例,这是一个非常简单的对象,实现了 ArrayAccess
接口。
一旦这个对象实例在管道中的所有回调中都是相同的,就可以使用它来从回调传递数据到下一个。
use Toobo\PipePie\Pipeline; use Toobo\PipePie\DTO; $pipeline = (new Pipeline()) ->pipe(function($carry, $initial, DTO $dto) { $dto['ai'] = new ArrayIterator(); $dto['ai']->append('bar'); })->pipe(function($carry, $initial, DTO $dto) { $dto['ai']->append('baz'); })->pipe(function($carry, $initial, DTO $dto) { return $carry.implode(',' $dto['ai']->getArrayCopy()); }); echo $pipeline->applyTo('foo,'); // 'foo,bar,baz'
为了确保数据完整性,DTO 以这种方式实现 ArrayAccess
- 在回调内部无法删除先前回调设置的 DTO 值
- 如果该值是对象,则无法在回调内部覆盖先前回调设置的 DTO 值
上下文
如上所示,DTO 是将数据从回调传递到其他回调的好方法。
有时可能希望在管道级别将一些 上下文 传递给 所有 回调。
可以通过将上下文数据作为 Pipeline 构造函数的第一个参数来执行此操作。之后,可以通过在传递给回调的 DTO 实例上调用 DTO::context()
方法来在回调内部访问上下文。
use Toobo\PipePie\Pipeline; use Toobo\PipePie\DTO; // Context here is a string, but can be anything $pipeline = (new Pipeline('I am the context ')) ->pipe(function($carry, $initial, DTO $dto) { return $carry.$dto->context(); })->pipe(function($carry, $initial, DTO $dto) { return $carry.$dto->context(); }); echo $pipeline->applyTo('foo '); // 'foo I am the context I am the context'
DTO "新鲜度"
每次在相同的 Pipeline 实例上调用 Pipeline::applyTo()
时,传递给回调的 DTO 实例都是 新鲜的,即 DTO 状态不会在 applyTo()
调用之间保持。
只有 DTO::context()
返回相同的值,因为它是设置在 Pipeline 级别的。
use Toobo\PipePie\Pipeline; use Toobo\PipePie\DTO; $pipeline = (new Pipeline('Call: ')) ->pipe(function($carry, $initial, DTO $dto) { $dto['random'] = rand(1, 100); return $carry; })->pipe(function($carry, $initial, DTO $dto) { return $carry.$dto->context().$dto['random']; }); echo $pipeline->applyTo('First '); // 'First Call: 5' echo $pipeline->applyTo('Second '); // 'Second Call: 72'
自动类型转换
可能希望确保管道中的每个回调都返回特定的数据类型。PipePie 允许以两种方式做到这一点
- 通过 Pipeline 标志
- 通过自定义回调
通过标志进行类型转换
Pipeline 类有一些常量
Pipeline::TO_ARRAY
Pipeline::TO_OBJECT
Pipeline::TO_STRING
Pipeline::TO_INT
Pipeline::TO_BOOL
Pipeline::TO_FLOAT
将它们之一作为 Pipeline 构造函数的第二个参数传递,可以确保所有回调返回的类型是期望的类型。
use Toobo\PipePie\Pipeline; $pipeline = (new Pipeline(null, Pipeline::TO_ARRAY)) ->pipe(function ($carry) { return $carry; })->pipe(function (array $carry) { // type casting flag ensures $carry is an array $carry[] = 'bar'; return $carry; }); return $pipeline->applyTo('foo'); // ['foo', 'bar']
请注意,默认情况下,传递给第一个回调的初始值不会进行类型转换(但其返回值会转换)。
然而,在将初始值传递给第一个回调之前,也可以对其进行类型转换。这可以通过将 true
作为 Pipeline 构造函数的第三个参数来实现。
use Toobo\PipePie\Pipeline; $pipeline = (new Pipeline(null, Pipeline::TO_ARRAY, true)) ->pipe(function (array $carry) { // Even 1st callback receives type-casted value return $carry; }); return $pipeline->applyTo('foo'); // ['foo']
通过自定义回调进行类型转换
可以将回调传递给 Pipeline 构造函数作为第二个参数,用于类型转换 Pipeline 中所有回调返回的值。
use Toobo\PipePie\Pipeline; /** * Ensures returned value is an array of unique integers */ $caster = function ($data) { if (! is_array($data)) { $data = (array) $data; } return array_values(array_unique(array_filter($data, 'is_int'))); }; $pipeline = (new Pipeline(null, $caster)) ->pipe(function (array $carry) { return array_merge($carry, ['a', 1, 'b', 2, false, 3]); })->pipe(function (array $carry) { return array_merge($carry, ['foo', 4, 'bar', 3, false, 2, 1]); }); return $pipeline->applyTo(['foo' => 0, 'bar' => 'baz'])); // [0, 1, 2, 3, 4]
尽管类型转换自定义回调的主要作用是转换 Pipeline 回调的结果,但实际上它可以做任何事。
回调的额外参数
上述示例展示了 Pipeline 回调至少接收 3 个参数
- 第一个参数将是管道中先前回调的结果(或第一个回调的初始值);
- 第二个参数始终是初始数据;
- 第三个参数将是一个 DTO 实例。
可以在回调的基础上传递额外的参数。
这是通过将参数数组作为 Pipeline::pipe()
方法的第二个参数来实现的
use Toobo\PipePie\Pipeline; $pipeline = (new Pipeline())->pipe( function ($carry, $initial, $dto, $foo, $bar) { // 1st arg is the callback return $carry.$foo.', '.$bar; }, ['"foo"', '"bar"'] // 2nd arg is the additional arguments array ); echo $pipeline->applyTo('Args: '); // 'Args: "foo", "bar"'
嵌套管道
如果您喜欢使用可调用的对象作为回调来提高代码的可重用性,那么您会喜欢 Pipeline 类实现了 __invoke()
方法,这允许将任何 Pipeline 作为回调添加到 "父" Pipeline 中。
use Toobo\PipePie\Pipeline; $child1 = (new Pipeline())->pipe(function ($carry) { return $carry.'Inner 1/1, '; })->pipe(function ($carry) { return $carry.'Inner 1/2, '; }); $child2 = (new Pipeline())->pipe(function ($carry) { return $carry.'Inner 2/1, '; })->pipe(function ($carry) { return $carry.'Inner 2/2.'; }); $parent = (new Pipeline()) ->pipe($child1) ->pipe(function ($carry) { return $carry.'Parent, '; }); ->pipe($child2); echo $pipeline->applyTo('Called: '); // 'Called: Inner 1/1, Inner 1/2, Parent, Inner 2/1, Inner 2/2.'
请注意,当使用嵌套管道时,与上述代码一样,DTO 在管道之间是共享的,即 DTO 实例对于父管道和子管道中的回调是相同的。
调试辅助工具
Pipeline 类有一个方法:info()
,它可以提供有关
- Pipeline 的上下文(无论是传递给构造函数的第一个参数)的信息
- 对于 Pipeline 实例上的每个
applyTo()
调用,该方法返回一个数组,其中包含- 键 'started_at':包含在调用
applyTo()
时的 Unix 时间戳(通过microtime(true)
获取) - 键 'transported':与
applyTo()
调用相关的 DTO 对象内容的数组表示 - 键 'input':传递给
applyTo()
的初始值
- 键 'started_at':包含在调用
use Toobo\PipePie\Pipeline; $pipeline = (new Pipeline('I am the context')) ->pipe(function ($carry, $initial, $dto) { $dto['callback'] = 'First Callback'; $dto['random'] = rand(1, 100); return $carry; }); $pipeline->applyTo('One'); $pipeline->applyTo('Two'); $pipeline->applyTo('Three'); return $pipeline->info(); /* [ 'context' => 'I am the context' [ 'input' => 'Three', 'transported' => [ 'callback' => 'First Callback', 'random' => 72 ], 'started_at' => 1423689928.5802 ], [ 'input' => 'Two', 'transported' => [ 'callback' => 'First Callback', 'random' => 21 ], 'started_at' => 1423689928.5801 ], [ 'input' => 'One', 'transported' => [ 'callback' => 'First Callback', 'random' => 89 ], 'started_at' => 1423689928.5800 ], ) */
注意
- 关于各种
applyTo()
调用的信息以倒序显示:较晚的调用首先显示; info()
调用会 刷新 信息,因此后续调用该方法时,它只会返回自上次info()
调用以来发生的applyTo()
调用的数据。
要求
- PHP 5.4+
- 使用 Composer 进行安装
安装
- PipePie 是一个可在 Packagist 上找到的 Composer 包,可以通过运行以下命令进行安装:
composer require toobo/pipepie:~0.1
单元测试
PipePie 仓库包含一些为 PHPUnit 编写的单元测试。
要运行测试,请从控制台导航到仓库文件夹并运行
phpunit
许可证
PipePie 在 MIT 许可下发布,有关更多信息,请参阅 LICENSE 文件。