andydune/pipeline

提供了一种管道模式。基于中间件方法。

v2.0.0 2019-04-23 08:54 UTC

README

Build Status Software License Packagist Version Total Downloads

此包提供了一个管道模式实现。它基于中间件方法。

需求

PHP版本 >= 7.1

对于php 5.6使用版本1

"require" : {
     "andydune/pipeline": "1.*"
}

安装

使用composer安装

composer require andydune/pipeline 

或者如果尚未全局安装composer

php composer.phar require andydune/pipeline

或者编辑您的 composer.json

"require" : {
     "andydune/pipeline": "^2"
}

并执行命令

php composer.phar update

用法

管道阶段中的操作可以是满足callable类型提示的任何内容。所以闭包和任何可调用的内容都是好的。

use AndyDune\Pipeline\Pipeline;
 
$pipeline = new Pipeline();

$stages = [
    function($contest, $next) {
        $contest += 100;
        return $next($contest);    
    },
    function($contest, $next) {
        $contest += 10;
        return $next($contest);    
    }
];
$result = $pipeline->through($stages)->send(1)
->then(function ($context) {
            $context += 1000;
            return $context;});
$result // 1111

阶段类型

基本上,每个阶段都可以是Closure

$pipeline = new Pipeline();
$pipeline->pipe(function ($context, $next) {
    $context['closure'] = 'was here';
    return $next($context); 
});

它可以是一个具有可调用接口的类的实例

$instance = new class() {
    public function __invoke($context, $next) 
    {
        $context['invoke'] = 'was here';
        return $next($context); 
    }
}

$pipeline = new Pipeline();
$pipeline->pipe($instance);

它可以是一个具有任何方法的类的实例

$instance = new class() {
    public function doIt($context, $next) 
    {
        $context['invoke'] = 'was here';
        return $next($context); 
    }
}

$pipeline = new Pipeline();
$pipeline->pipe($instance, 'doIt');

它可以是一个具有__invoke方法或您描述的任何方法的类名

class Trim
{
    public function __invoke($context, $next) 
    {
        $context['class_invoke'] = 'was here';
        return $next($context); 
    }
}

$pipeline = new Pipeline();
$pipeline->pipe(Trim::class);

它可以是一个您描述的任何方法的类名

class Trim
{
    public function handle($context, $next) 
    {
        $context['class_invoke'] = 'was here';
        return $next($context); 
    }
}

$pipeline = new Pipeline();
$pipeline->pipe(Trim::class,  'handle');

对于没有中间件接口的阶段,请使用对象

您可以使用不执行$next函数的方法。它获取一些数据并返回结果。存在特殊方法:'pipeForContainer'

示例类。

namespace AndyDune\Pipeline\Example;

class Methods
{
    // return result - no calling next() 
    public function addBraceLeft($string)
    {
        return '(' . $string;
    }

    // It has middleware interface
    public function addBraceRight($string, callable $next)
    {
        $string =  $string . ')';
        return $next($string);
    }
}
$instance = new Methods();

$pipeline = new Pipeline();
$pipeline->send('puh');

$pipeline->pipeForContainer($instance, 'addBraceLeft');

$pipeline->pipe(Methods::class, 'addBraceRight');
$result = $pipeline->execute();

$result == '(puh)';

容器提供者(服务提供者)

可以将字符串作为管道阶段传递。默认情况下,它是一个类名。这是通过使用默认提供者AndyDune\Pipeline\PipeIsClassName实现的。
您可以为需要注入的依赖项设置自己的提供者。您的类必须实现接口:Interop\Container\ContainerInterface

如果使用您的提供者系统找不到阶段,将尝试使用默认提供者。存在提供者栈。

将您的提供者作为管道构造函数的参数使用

use Interop\Container\ContainerInterface;

class SpecialPipelineContainer implements ContainerInterface
{
    /**
     * @var  \Rzn\ObjectBuilder
     */
    protected $objectBuilder;

    public function get($name)
    {
        /*
        do anything to build object using $name
        */
        return $object;
    }
    
    public function has($name)
    {
        /*
        do anything to check required object can be build  
        */
        return $exist;
    }
}

$container = new SpecialPipelineContainer();
$pipeLine = new Pipeline($container);

$pipeLine->pipe('any string to get stage from container');

阶段的其他参数

有时您需要在描述管道阶段时传递任何数量的附加参数。这对于测试非常有用,并且对于阶段的更多灵活性也非常好。它增加了类的复用性。

让我们看看例子。这是一个非常简单的例子。

以下是阶段示例类

namespace AndyDune\Pipeline\Example;
class PowerOfNumber
{
    public function __invoke($data, callable $next, $power = 2)
    {
        if (is_array($power)) {
            array_walk($power, function (&$value, $key) use ($data) {
                $value = pow($data, $value);
            });
            return $next($power);
        }
        $data = $this->handle($data, $power);
        return $next($data);
    }
    protected function handle($number, $power)
    {
        return pow($number, $power);
    }
}

让我们使用它

    use use AndyDune\Pipeline\Pipeline;
    use AndyDune\Pipeline\Example;
    
    $pipeline = new Pipeline();
    $pipeline->send(2);
    $pipeline->pipe(PowerOfNumber::class);
    $result = $pipeline->execute(); // == 4

    $pipeline = new Pipeline();
    $pipeline->send(2);
    $pipeline->pipe(PowerOfNumber::class, null, 3);
    $result = $pipeline->execute(); // == 8
    
    $pipeline = new Pipeline();
    $pipeline->send(2);
    $pipeline->pipe(PowerOfNumber::class, null, 4);
    $result = $pipeline->execute(); // == 16

依赖注入

您可以使用默认的管道阶段创建者将服务注入到阶段对象中或从外部注入任何对象。

存在接口的注入。

    use use AndyDune\Pipeline\Pipeline;
    
    $pipeline = new Pipeline();
    
    $pipeline->addInitializer(function($stageObject) use ($someService) {
        if ($stageObject instanceof SomeServiceAwareInterface) {
            $stageObject->setSomeService($someService)
        }     
    });
    
    $pipeline->pipe(ClassHaveInterface::class);
    $result = $pipeline->execute();

这使用方法addInitializer,该方法接受可调用参数。

异常

包没有集成异常捕获支持。您可以在管道阶段中简单地包含异常try-catch块。

    use AndyDune\Pipeline\Pipeline;
    $pipeline = new Pipeline();
    $pipeline->send(['zub' => 'kovoy']);
    $pipeline->pipe(function ($context, $next) {
        try {
            return $next($context);
        } catch (Exception $e) {
            $context['exception'] = 'caught';
        }
        return $context;
    });

    $pipeline->pipe(function ($context, $next) {
        $context['action'] = 'before_exception';
        throw new Exception();
        return $next($context); // it will be never execute
    });
     
    // This stage will never be executed
    $pipeline->pipe(function ($context, $next) {
        $context['after_exception'] = 'ignored';
        return $next($context);
    });

    $result = $pipeline->execute();
    array_key_exists('zub', $result);       // true
    array_key_exists('exception', $result); // true
    array_key_exists('action', $result);    // false
    array_key_exists('after_exception', $result); // false

有一个类您可能将其用作管道中的阶段来捕获此包中的异常。

 
    $pipeline = new Pipeline();
    $pipeline->send(['zub' => 'kovoy']);
    $pipeline->pipe(AndyDune\Pipeline\Stage\ExceptionCatch::class);
    $pipeline->pipe(function ($context, $next) {
        $context['action'] = 'before_exception';
        throw new Exception('jump');
    });
    $result = $pipeline->execute();
    
    $result instancheof \Exception // true

示例

缓存

您有一个用于检索数据的服务。您不需要更改其代码。只需将其作为管道中的阶段使用即可。

// Description
$pipeline = new Pipeline();
$pipeline->pipe(function($key, $next) {
    /*
    * Cache adapter with (PSR-16) interface
    * Ones a have used Symfony Cache Component [https://github.com/symfony/cache] 
    * It's for example
    */
    $cache = new FilesystemCache();
    if ($cache->has($key)) {
        return $cache->get($key);
    }
    $data = $next($catId);
    $cache->set($key, $data);
    return $data;
});
$pipeline->pipe(DataRetrieveClass::class, 'getImportantData');
 
// Execute 
$results = $pipeline->send($key)->execute();

在Zend FW 3中使用

默认情况下,作为管道阶段的字符串表示该类名。如果您没有参数创建管道对象,服务容器实现了AndyDune\Pipeline\PipeIsClassName。它仅创建给定类的实例并返回它。如果此包是作为Zend FW 3的一部分使用,您可以使用Zend的服务来检索实例。

首先,您必须使用composer安装包。然后,将文件vendor/andydune/pipeline/config/pipeline.global.php复制到config/autoload/pipeline.global.php

在工厂中描述您的管道

use Zend\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
class DunhillFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $pipeline = $container->get('pipeline');
        $pipeline->pipe('service name within Zend3');
        $pipeline->pipe(function($data, $next) {
            return $next($data); 
        });
        return $pipeline;
    }
}

如果您不使用Zend服务,请直接使用管道。

类似项

Laravel管道 - 它几乎实现了我所想要的功能,但我需要更多测试工具。而且我不喜欢Laravel的服务管理器,它没有通用的接口。我的管道可能会使用基于container-interop和PSR-11接口的服务管理器。

League\Pipeline - 它有良好的文档。但是有一个不好的特性——工作流程控制差。要停止执行,需要使用异常。