neu / pipe7
一个用于在PHP中轻松且一致地进行数据处理的开源库。
Requires
- php: >=7.3
- symfony/polyfill-php80: ^1.23
Requires (Dev)
- pestphp/pest: ^1.21
- phpstan/phpstan: ^0.12.88
README
pipe7是一个基于Iterator的数据处理库。它旨在解决使用PHP内置的常规高阶函数(如array_filter、array_map和array_reduce)时可能遇到的一些问题。
PHP文档
PHPDocumentor生成的文档可以在以下链接找到:https://docs.pipe7.joern-neumeyer.de/。
安装
composer require neu/pipe7
问题陈述
PHP已经提供了一些函数来对数组进行数据转换。然而,这些API并不一致,并且仅适用于数组。如果需要(或必须)使用Generator或Iterator,则不能使用这些内置函数。
此外,像array_map这样的函数会立即返回结果,即使可能对数组执行更多操作。它们可能会使用比必要的更多内存,因为中间数组在处理链的下一步中没有被使用。
解决方案
pipe7提供了一个一致且可预测的API来工作,因此代码更容易理解。它还支持Iterator作为数据源(这包括Generator)。
事实上,整个处理机制都是基于Iterator构建的。这允许pipe7在处理源数据结构的长时间转换链时具有很低的内存占用。这不仅有助于计算最终结果集(如转换为数组),而且还允许你仅在需要时触发计算,例如在foreach循环中。如果你只需要处理结果集中的前五个元素,pipe7将只请求足够从原始数据源获取这五个结果元素。因此,如果不需要,就不会将任何元素传递给回调(如array_map)。
还有...
有很多常见的操作你可能想在你的数据上执行(如平均计算、分组元素或将它们转换为字符串)。对于这些常见的用例,pipe7在Mappers和Reducers类中提供了一系列的辅助函数。
要查看所有可用的辅助函数,请访问文档或使用phpDocumentor自行构建。
性能
pipe7是一个权衡。在CPU时间方面,它的性能不如标准数组函数。但这是重点。CPU时间被交换为内存效率。
此存储库包含一个基准测试,显示了pipe7与常规数组函数之间的性能差异。作为参考,数组函数被用作基准线。
表中给出的值仅是近似值。然而,很明显,pipe7在CPU效率上有所牺牲,但在内存效率上更加高效。正如所看到的,当使用Generator作为数据源时,RAM使用量特别低。
由于Generator(或通用的Iterator)与像array_map这样的函数不直接兼容,因此必须首先将其转换为数组,或者需要额外的数据处理机制。
因此,当pipe7可以与Generator一起使用时,它真正地大放异彩。
使用方法
创建一个新的 CollectionPipe
use Neu\Pipe7\CollectionPipe; $data = [1,2,3]; $p = CollectionPipe::from($data); // or with the shorthand $p = pipe($data);
转换元素
$doubled = $p->map(function($x){ return $x * 2; })->toArray(); // if you're using php7.4 or later, // better use arrow functions for more concise code $doubled = $p->map(fn($x) => $x * 2)->toArray();
过滤元素
$even = $p->filter(fn($x) => $x % 2 === 0)->toArray();
组合元素
$total = $p->reduce(fn($carry, $x) => $carry + $x, 0);
如上所示,方法 map 和 filter 总是返回一个新的 CollectionPipe,结果必须从其中收集(例如通过 toArray 方法)。也可以在 foreach 循环中迭代新的 CollectionPipe 实例。
相比之下,reduce 只有在其第三个参数设置为 true 并且 reduce 操作的 carry 是一个 Iterator 时才返回一个新的 CollectionPipe。如果第三个参数是 false 或未设置,则直接返回一个减少后的 array/Iterator。如果第三个参数是 true,并且减少的值不是一个 array/Iterator,将抛出一个 UnprocessableObject 异常。
有关更多信息,请参阅文档。
有状态的运算符
有些操作不容易用一个单一函数表达。例如,可以是一个限制,在给定的管道中处理项目。因此,如果您需要实现这样的操作,请创建一个实现 interface Neu\Pipe7\StatefulOperator 的 class。
如果您想省去一些样板代码,考虑扩展 class Neu\Pipe7\CallableOperator。通过这样做,您将获得 __invoke 方法的实现,如果您的操作不需要任何特定的重置,还将获得 rewind 方法的空实现。
有状态的运算符可以作为参数传递给管道,就像您传递一个 Closure 一样。
传递的操作如何被评估?
根据特定管道需要完成的任务,了解管道的逻辑如何执行可能是很重要的。
映射器
签名:function(mixed $value, mixed $key, Neu\Pipe7\CollectionPipe $this)
如果您创建了一个管道,该管道应该转换传入的元素,那么当调用管道上的 current 方法时,将执行该逻辑。
过滤器
签名:function(mixed $value, mixed $key, Neu\Pipe7\CollectionPipe $this)
当调用管道上的 next 方法时,将调用过滤器,因为它们确定元素是否应该由管道发出。
减少器
签名:function(mixed #carry, mixed $value, mixed $key, Neu\Pipe7\CollectionPipe $this)
减少器的签名与映射器和过滤器不同,因为它们的任务是创建一个新值。因此,它们可以用来计算总和或类似的结果。
许可证
pipe7 在版本 3.0 或更新的 GNU Lesser General Public License 条件下提供使用。