toobo/pipepie

0.1.1 2015-02-12 00:00 UTC

This package is auto-updated.

Last update: 2024-08-29 04:14:26 UTC


README

travis-ci

简介

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() 的初始值
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 文件。