everyman/plumber

创建处理管道的简单库

dev-master 2012-08-31 23:35 UTC

This package is not auto-updated.

Last update: 2024-09-14 13:30:21 UTC


README

易于创建处理链的库。

TinkerPop Pipes 启发和影响

作者:Josh Adell josh.adell@gmail.com
版权 (c) 2011-2012

Build Status

用法

$msg = array(72,0,101,108,108,111,44,32,0,87,111,114,108,0,100,33);

$pipeline = new Everyman\Plumber\Pipeline();
$pipeline->filter()->transform(function ($v) { return chr($v); });

foreach ($pipeline($msg) as $c) {
  echo $c;
}

安装

Composer 是安装 Plumber 的推荐方式。将以下内容添加到您的 composer.json

{
    "require": {
        "everyman/plumber": "dev-master"
    }
}

目的

很多时候,需要遍历列表、过滤掉不需要的值,并对剩余的值执行一个或多个转换。一个很好的例子是从数据库中读取和格式化记录

$users = // code to retrieve user records from a database as an array or Iterator...

$names = array();
foreach ($users as $user) {
    if (!$user['first_name'] || !$user['last_name']) {
        continue;
    }

    $name = $user['first_name'] . ' ' . $user['last_name'];
    $name = ucwords($name);
    $name = htmlentities($name);
    $names[] = $name;
}

// later on, display the names
foreach ($names as $name) {
    echo "$name<br>";
}

这个过程有几个缺点

  • 多次遍历列表
  • 一次将整个数据集加载到内存中
  • 处理步骤不可重用

使用“延迟处理管道”,值仅在需要时才进行转换,按需转换

$users = // code to retrieve user records from a database...

$names = new Everyman\Plumber\Pipeline();
$names->filter(function ($user) { return $user['first_name'] && $user['last_name']; })
    ->transform(function ($user) { return $user['first_name'] . ' ' . $user['last_name']; })
    ->transform('ucwords')
    ->transform('htmlentities');

// later on, display the names
foreach ($names($users) as $name) {
    echo "$name<br>";
}

内置管道

Plumber 包含几个预构建的管道,可以立即使用。

过滤器

filter 管道从数据中移除值以避免进一步处理。如果没有提供过滤器函数,过滤器管道会删除转换为布尔值 false 的值

$pipeline->filter();
foreach ($pipeline(array(0, 1, false, true, '', 'abc', null, array(), array())) as $value) {
    echo $value.' ';
}
// Output: 1 1 abc Array

您可以提供一个过滤器函数来使用。该函数应接受两个参数,当前元素的值和键。如果函数返回一个真值,则该元素将继续到下一个处理步骤

$pipeline->filter(function ($value, $key) {
    return $key % 2;
});
foreach ($pipeline(array(0, 1, 2, 9, 10, 67)) as $value) {
    echo $value.' ';
}
// Output: 0 2 10

过滤器管道是其他几个内置管道的基础。

唯一

unique 管道过滤掉在处理过程中之前已看到的任何值

$pipeline->unique();
foreach ($pipeline(array('foo', 'bar', 'baz', 'foo', 'baz', 'qux')) as $value) {
    echo $value.' ';
}
// Output: foo bar baz qux

切片

slice 管道返回给定偏移量之后和给定长度之前的值

$pipeline->slice(2,3);
foreach ($pipeline(array('foo', 'bar', 'baz', 'qux', 'lorem', 'ipsum')) as $value) {
    echo $value.' ';
}
// Output: baz quz lorem

如果省略第二个参数,则返回偏移量之后的所有值

$pipeline->slice(1);
foreach ($pipeline(array('foo', 'bar', 'baz', 'qux', 'lorem', 'ipsum')) as $value) {
    echo $value.' ';
}
// Output: bar baz quz lorem ipsum

随机

random 管道根据阈值随机发出值。阈值应在 0 到 100 之间,表示值被发出的 100 中的机会

$pipeline->random(40);
foreach ($pipeline(array('foo', 'bar', 'baz', 'qux', 'lorem', 'ipsum')) as $value) {
    echo $value.' ';
}
// Possible output: bar ipsum

转换

transform 管道操纵传入的值并发出操纵的输出。如果没有提供转换函数,管道将按原样发出每个值

$pipeline->transform();
foreach ($pipeline(array('foo', 'bar', 'baz', 'qux', 'lorem', 'ipsum')) as $value) {
    echo $value.' ';
}
// Output: foo bar baz qux lorem ipsum

转换函数应接受两个参数,当前值和管道中的键

$pipeline->transform(function ($value, $key) {
    return strrev($value);
});
foreach ($pipeline(array('foo', 'bar', 'baz', 'qux', 'lorem', 'ipsum')) as $value) {
    echo $value.' ';
}
// Output: oof rab zab xuq merol muspi

提取

pluck 管道从数组或对象中发出单个值(或值的数组)

$pipeline->pluck('id');
$data = array(
    array('id' => 123, 'foo' => 'bar'),
    array('id' => 456, 'baz' => 'qux'),
    array('lorem' => 'ipsum'),
);
foreach ($pipeline($data) as $key => $value) {
    echo $key.':'.$value.' ';
}
// Output: 0:123 1:456 2:

如果提供键的数组,则发出的值将是一个包含每个键及其值的数组的数组。任何未找到的键的值将是 null

如果省略第三个回调(“否则”回调),则如果条件不满足,则值将“按原样”传递

条件判断

$pipeline->ifElse(function ($value, $key) { return strlen($value) < 4; },
    function ($value, $key) { return -1; },
    function ($value, $key) { return strlen($value); }
);
foreach ($pipeline(array('zero', 'one', 'two', 'three', 'four', 'five', 'six')) as $value) {
    echo $value.' ';
}
// Output: 4 -1 -1 5 4 4 -1

ifElse 管道用于在满足某个条件时发出一个值,如果不满足条件,则发出不同的值

$pipeline->ifElse(function ($value, $key) { return strlen($value) < 4; },
    function ($value, $key) { return -1; }
);
foreach ($pipeline(array('zero', 'one', 'two', 'three', 'four', 'five', 'six')) as $value) {
    echo $value.' ';
}
// Output: zero -1 -1 three four five -1

自定义管道

可以构建自己的管道以执行自定义逻辑。管道应扩展内置管道之一,通常是 Everyman\Plumber\Pipe\TransformPipeEveryman\Plumber\Pipe\FilterPipe。如果您不扩展内置管道,则必须扩展 Everyman\Plumber\Pipe

class MyCustomPipe extends Everyman\Plumber\Pipe\TransformPipe
{
    public function __construct()
    {
        parent::__construct(function ($value, $key) use () {
            // do custom logic
            return $customValue;
        });
    }
}

$pipeline = new Everyman\Plumber\Pipeline();
$pipeline->appendPipe(new MyCustomPipe());

还可以注册自定义管道类,以便它们可以与流畅管道接口一起使用

Everyman\Plumber\Helper::registerPipe('custom', 'MyCustomPipe');

$pipeline = new Everyman\Plumber\Pipeline();
$pipeline->custom();

请注意,管道的名称不必与类名匹配。此外,注册的类名必须是完全限定的类名。

如果多个自定义管道都在同一命名空间下,并且每个管道类的名称都以 "Pipe" 结尾,则可以注册整个命名空间

namespace My\Project\Pipes;

class MyCustomPipe extends Everyman\Plumber\Pipe\TransformPipe { ... }

class AnotherPipe extends Everyman\Plumber\Pipe\TransformPipe { ... }

Everyman\Plumber\Helper::registerNamespace('My\Project\Pipes');

$pipeline = new Everyman\Plumber\Pipeline();
$pipeline->myCustom()->another();

请注意,在注册命名空间时,对管道调用的方法名称是类名,小驼峰命名法,且不带 'Pipe' 后缀。如果命名空间中的所有管道都有除 'Pipe' 之外的后缀,则可以将该后缀作为第二个参数传递给 registerNamespace。如果不存在公共后缀,则第二个参数可以是空字符串。