A PHP pipeline plug-in League \ pipeline is recommended

Time:2021-8-22

A PHP pipeline plug-in League \ pipeline is recommended

Pipeline design pattern

The water pipe is too long. As long as one part is broken, it will leak, and it is not conducive to the use in complex environment. Therefore, we will divide the water pipe into very short sections, and then maximize the size of the pipe, adjust measures to local conditions, and assemble it to meet a variety of different needs.

It is concluded that the design pattern of pipeline is to cut complex and lengthy processes into small processes and small tasks. Each minimum quantitative task can be reused, and different small tasks can be assembled to form complex and diverse processes.

Finally, the “input” is introduced into the pipeline, the input is operated (processed and filtered) according to each small task, and finally the required results are output.

Today, I will mainly study “pipeline”. By the way, I recommend a PHP plug-in:league/pipeline

gulp

The first time I know the concept of “pipe” comes fromgulpUse of.

A PHP pipeline plug-in League \ pipeline is recommended

gulpIs based onNodeJSAutomatic task runner, she can complete it automaticallyJavascriptsasslessTest, check, merge, compress, format, browser automatic refresh, deploy file generation, and listen for files. Repeat the specified steps after changes. In terms of implementation, she draws lessons fromUnixThe pipeline idea of the operating system, the output of the previous level directly becomes the input of the next level, which makes the operation very simple.

var gulp = require('gulp');
var less = require('gulp-less');
var minifyCSS = require('gulp-csso');
var concat = require('gulp-concat');
var sourcemaps = require('gulp-sourcemaps');

gulp.task('css', function(){
  return gulp.src('client/templates/*.less')
    .pipe(less())
    .pipe(minifyCSS())
    .pipe(gulp.dest('build/css'))
});

gulp.task('js', function(){
  return gulp.src('client/javascript/*.js')
    .pipe(sourcemaps.init())
    .pipe(concat('app.min.js'))
    .pipe(sourcemaps.write())
    .pipe(gulp.dest('build/js'))
});

gulp.task('default', [ 'html', 'css', 'js' ]);

The two abovetaskMainlyless, alljsProcess operations such as file parsing, compression and output, and then save them to the corresponding folder; The output of each operation is the input of the next operation, just like the running water of the pipeline.

IlluminatePipeline

The middleware in the laravel framework uses Illuminate\PipelineI originally wanted to write my interpretation of the source code of “laravel middleware”, but I found that many posts on the Internet have expressed it, so this article briefly talks about how to use itIlluminate\Pipeline

Write a demo

public function demo(Request $request)
{
    $pipe1 = function ($payload, Closure $next) {
        $payload = $payload + 1;
        return $next($payload);
    };

    $pipe2 = function ($payload, Closure $next) {
        $payload = $payload * 3;
        return $next($payload);
    };

    $data = $request->input('data', 0);

    $pipeline = new Pipeline();

    return $pipeline
        ->send($data)
        ->through([$pipe1, $pipe2])
        ->then(function ($data) {
            return $data;
        });
}

A PHP pipeline plug-in League \ pipeline is recommended

A PHP pipeline plug-in League \ pipeline is recommended

For the analysis of the source code, you can recommend this article. The analysis is very thorough:

Implementation of laravel pipeline componenthttps://www.insp.top/article/realization-of-pipeline-component-for-laravel

LeaguePipeline

Top rightgulpandIlluminate\PipelineThe simple use of “pipeline” only tells us that “pipeline” is widely used. If we are allowed to write a similar plug-in, I think it should not be very difficult.

I’ll take it nextLeague\PipelinePlug in to pick up its source code and see how to implement it.

sketch

This package provides a plug and play implementation of the Pipeline Pattern. It’s an architectural pattern which encapsulates sequential processes. When used, it allows you to mix and match operation, and pipelines, to create new execution chains. The pipeline pattern is often compared to a production line, where each stage performs a certain operation on a given payload/subject. Stages can act on, manipulate, decorate, or even replace the payload.

If you find yourself passing results from one function to another to complete a series of tasks on a given subject, you might want to convert it into a pipeline.

https://pipeline.thephpleague.com/

Install plug-ins

composer require league/pipeline

Write a demo

use League\Pipeline\Pipeline;

//Create two closure functions
$pipe1 = function ($payload) {
    return $payload + 1;
};

$pipe2 = function ($payload) {
    return $payload * 3;
};

$route->map(
    'GET',
    '/demo',
    function (ServerRequestInterface $request, ResponseInterface $response
    ) use ($service, $pipe1, $pipe2) {
        $params = $request->getQueryParams();

        //Normal use
        $pipeline1 = (new Pipeline)
            ->pipe($pipe1)
            ->pipe($pipe2);

        $callback1 = $pipeline1->process($params['data']);

        $response - > getbody() - > write ("< H1 > normal use < / H1 >");
        $response - > getbody() - > write ("< p > result: $callback 1 < / P >");

        //Use magic methods
        $pipeline2 = (new Pipeline())
            ->pipe($pipe1)
            ->pipe($pipe2);

        $callback2 = $pipeline2($params['data']);

        $response - > getbody() - > write ("< H1 > use magic method < / H1 >");
        $response - > getbody() - > write ("< p > result: $callback 2 < / P >");

        //Using builder
        $builder = new PipelineBuilder();
        $pipeline3 = $builder
            ->add($pipe1)
            ->add($pipe2)
            ->build();

        $callback3 = $pipeline3($params['data']);

        $response - > getbody() - > write ("< H1 > use builder < / H1 >");
        $response - > getbody() - > write ("< p > result: $callback 3 < / P >");
        return $response;
    }
);

Operation results

A PHP pipeline plug-in League \ pipeline is recommended

A PHP pipeline plug-in League \ pipeline is recommended

Interpretation of source code

The whole plug-in contains these files:

A PHP pipeline plug-in League \ pipeline is recommended

PipelineInterface

<?php
declare(strict_types=1);

namespace League\Pipeline;

interface PipelineInterface extends StageInterface
{
    /**
     * Create a new pipeline with an appended stage.
     *
     * @return static
     */
    public function pipe(callable $operation): PipelineInterface;
}

interface StageInterface
{
    /**
     * Process the payload.
     *
     * @param mixed $payload
     *
     * @return mixed
     */
    public function __invoke($payload);
}

The interface mainly uses the idea of chain programming to continuously add a pipe, and then add a magic method to make the incoming parameters run.

Let’s take a look at the function of this magic method:

mixed __invoke ([ $... ] )
When you try to call an object by calling a function__ The invoke () method is called automatically.

Reference from:http://php.net/manual/zh/language.oop5.magic.php
For example:

<?php
class CallableClass 
{
    function __invoke($x) {
        var_dump($x);
    }
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));
?>

Return result:

int(5)
bool(true)

Pipeline

<?php
declare(strict_types=1);

namespace League\Pipeline;

class Pipeline implements PipelineInterface
{
    /**
     * @var callable[]
     */
    private $stages = [];

    /**
     * @var ProcessorInterface
     */
    private $processor;

    public function __construct(ProcessorInterface $processor = null, callable ...$stages)
    {
        $this->processor = $processor ?? new FingersCrossedProcessor;
        $this->stages = $stages;
    }

    public function pipe(callable $stage): PipelineInterface
    {
        $pipeline = clone $this;
        $pipeline->stages[] = $stage;

        return $pipeline;
    }

    public function process($payload)
    {
        return $this->processor->process($payload, ...$this->stages);
    }

    public function __invoke($payload)
    {
        return $this->process($payload);
    }
}

Core classPipelineThe main functions of are two:

  1. Add and assemble each pipe “pipe”;
  2. After assembly, divert water to flow, execute process ($payload), and output the results.

Processor

After connecting all kinds of pipes, it is necessary to “lead water into the canal”. The plug-in provides two basic execution classes, which are relatively simple and can be understood directly by reading the code.

//According to the $stages array, the pipeline method is traversed and executed smoothly, and then the result is passed to the next pipeline to make the "water" flow layer by layer
class FingersCrossedProcessor implements ProcessorInterface
{
    public function process($payload, callable ...$stages)
    {
        foreach ($stages as $stage) {
            $payload = $stage($payload);
        }

        return $payload;
    }
}

//An additional "filter screen" is added. The results after passing through each pipeline need to be checked. Once they are met, they will be terminated and the results will be output directly.
class InterruptibleProcessor implements ProcessorInterface
{
    /**
     * @var callable
     */
    private $check;

    public function __construct(callable $check)
    {
        $this->check = $check;
    }

    public function process($payload, callable ...$stages)
    {
        $check = $this->check;

        foreach ($stages as $stage) {
            $payload = $stage($payload);

            if (true !== $check($payload)) {
                return $payload;
            }
        }

        return $payload;
    }
}

interface ProcessorInterface
{
    /**
     * Process the payload using multiple stages.
     *
     * @param mixed $payload
     *
     * @return mixed
     */
    public function process($payload, callable ...$stages);
}

We can also use this interface to implement our method to assemble pipes and “filters”.

PipelineBuilder

Finally, a builder is provided, which is also well understood:

class PipelineBuilder implements PipelineBuilderInterface
{
    /**
     * @var callable[]
     */
    private $stages = [];

    /**
     * @return self
     */
    public function add(callable $stage): PipelineBuilderInterface
    {
        $this->stages[] = $stage;

        return $this;
    }

    public function build(ProcessorInterface $processor = null): PipelineInterface
    {
        return new Pipeline($processor, ...$this->stages);
    }
}

interface PipelineBuilderInterface
{
    /**
     * Add an stage.
     *
     * @return self
     */
    public function add(callable $stage): PipelineBuilderInterface;

    /**
     * Build a new Pipeline object.
     */
    public function build(ProcessorInterface $processor = null): PipelineInterface;
}

summary

No matter the horizontal understanding of different technologies, or based on laravel or some open source plug-ins, we can learn the general principles and methods based on technology. Then these principles and methods will react on our actual code development.

I have nothing to do recently. I refer to laravel to write a simple framework, which will alsoLeague\PipelineIntroduced into the framework for use.

“Unfinished to be continued”

Recommended Today

Java Engineer Interview Questions

The content covers: Java, mybatis, zookeeper, Dubbo, elasticsearch, memcached, redis, mysql, spring, spring boot, springcloud, rabbitmq, Kafka, Linux, etcMybatis interview questions1. What is mybatis?1. Mybatis is a semi ORM (object relational mapping) framework. It encapsulates JDBC internally. During development, you only need to pay attention to the SQL statement itself, and you don’t need to […]