skrip42/node-processor

v1.0 2020-01-28 08:52 UTC

This package is auto-updated.

Last update: 2024-05-28 20:20:55 UTC


README

Description

Node Process is a small procedural like program executable on top of php.

Installing

composer require skrip42/node-processor

Documentation

Node Process

Node Process is a small procedural like program executable on top of php. The process consists of nodes and the connections between them, as well as the state of these nodes An important consequence of this: At any time, the process can be stopped, saved with the current state and continue to execute from the same point. Processes can be dynamically created using PHP, serialize, deserialize.

Base usage

Add

use Skrip42\NodeProcessor\Process;

on top of your file. You can also add all the nodes that you intend to use:

use Skrip42\NodeProcessor\Node\Other\ValueNode;
use Skrip42\NodeProcessor\Node\Logical\CompareNode;
Create process

This section is presented to understand the processes, the recommended way to create processes is process builder.

$process = new Process; //create new process;

//create all necessary nodes:
// as syntax: process->createNode($nodeClassName, ...$values) : NodeAbstract
$valNode1 = $process->createNode(ValueNode::class, 1); //you can pass start parameters to the node if required 
$valNode2 = $process->createNode(ValueNode::class, 5);
$compareNode = $process->createNode(ValueNode::class);
//You do not need to create a start and end node, they are already included in the process

//link nodes:
// as syntax: process->linkNode($outputNodeName, $outputNode, $inputNodeName, $inputNode)
$process->linkNode('out1', $process->getBeginNode(), 'emit', $valNode1); // you can get begin and end node
                                                                        // from getBeginNode and getEndNode methods
$process->linkNode('out2', $process->getBeginNode(), 'emit', $valNode2);
$process->linkNode('out', $valNode1, 'in1', $compareNode);
$process->linkNode('out', $valNode2, 'in2', $compareNode);
$process->linkNode('more', $compareNode, 'more', $process->getEndNode()); // end node has dynamically input name
$process->linkNode('less', $compareNode, 'less', $process->getEndNode());
//You can always leave output nodes empty; can input be left blank determined by node policy

The resulting process can be represented graphically:

                        ┌───────────────┐         
                  ┌─emit┤ ValueNode = 1 ├out─┐    ┌─────────────┐            ┌─────────┐
┌───────────┐     │     └───────────────┘    └─in1┤             ├more────more┤         │
│           ├out1─┘                               │             │            │ EndNode │
│ BeginNode │                                     │ CompareNode ├less────less┤         │
│           ├out2─┐                               │             │            └─────────┘
└───────────┘     │     ┌───────────────┐    ┌─in2┤             ├equal─X
                  └─emit┤ ValueNode = 5 ├out─┘    └─────────────┘
                        └───────────────┘         

Most nodes have one or more inputs and outputs. The names of the inputs and outputs of standard bonds can be found in the documentation

Running process and debug

For start process:

$result = $process->run(); // running process
$state = $process->getState(); // you also can get current process state for debug you script

You will get array of the form:

[
    'status' => currentProcessStatus,
    'output' => [ .. array of end node inputs .. ]
    'requests' =>  [ .. array of requests .. ]
]

status is number indicating the current state of the process

  • Process::STATUS_REDY - redy to run
  • Process::STATUS_RUNN - running
  • Process::STATUS_ERROR - complete with error
  • Process::STATUS_WAIT_RESPONSE - wait response (see Request/Response)
  • Process::STATUS_COMPLETE - complete

output is array of EndNode inputs. Input name map to array key.

requests is array of request (see Request/Response)

$process->getState() - return current state of process, all process nodes, all nodes inputs and outputs After the node identifier and the names of its inputs and outputs, you can see the number of starts of the nodes, how many times each input received a signal and how many times each output emitted a signal

Serialize/deserialize process

You can serialize and deserialize process as default php tools:

$serialized = serialize($process);
$process = unserialize($serialized);

You should not include other objects in the nodes or pass from as parameters if you do not want to serialize them!

Process builder

A more convenient way to create a process is through the process builder. The previous process could be created like this:

$process = Process::build(
    [
        'node' => [
            'vn1' => [ValueNode::class, 1],
            'vn2' => [ValueNode::class, 5],
            'cn' => [CompareNode::class],
        ],
        'link' => [
            ['out1', 'begin', 'emit', 'vn1'], //begin is the predefined start node name
            ['out2', 'begin', 'emit', 'vn2'],
            ['out', 'vn1', 'in1', 'cn'],
            ['out', 'vn2', 'in2', 'cn'],
            ['less', 'cn', 'less', 'end'], //end is the predefined end node name
            ['more', 'vn2', 'more', 'end'], 
        ]
    ]
);

using the builder you can also parameterize your process:

$process = Process::build(
    [
        'node' => [
            'vn1' => [ValueNode::class, '{$val1}'], // template syntax: {$valueName}
            'vn2' => [ValueNode::class, '{$val2}'],
            'cn' => [CompareNode::class],
        ],
        'link' => [
            ['out1', 'begin', 'emit', 'vn1'], //begin is the predefined start node name
            ['out2', 'begin', 'emit', 'vn2'],
            ['out', 'vn1', 'in1', 'cn'],
            ['out', 'vn2', 'in2', 'cn'],
            ['less', 'cn', 'less', 'end'], //end is the predefined end node name
            ['more', 'vn2', 'more', 'end'], 
        ]
    ],
    [ //value list
        'val1' => 1,
        'val2' => 5
    ]
);

Request/Response

To communicate external code in the processes provided request/response system Some node can send request to process. Then the process will return with the status “waiting for a response” and as a result there will be a list of requests of the form:

[
    ['uniqIdOfRequest' => 'request data'],
    .......
]

You can send an answer like:

$process->setResponse('uniqIdOfRequest', 'response data');

Then the process will continue to execute from the node that sent the request (from node "setResponse" method)

Nodes

Nodes are the buildeing blocks of processes. Most nodes have one or more inputs and outputs. Input can be 'required' then node cannot run without this input received. Input and output can be dynamically declaration (as pattern or free). Output can have default value. Input and output sored their values. Input and output have a start counter. Nodes run after all required input received. Nodes run everytime when any input received if all required input has value. Nodes can be force running from eval() method (no recommended). Nodes can emit request and receive response.

The module provides a sume number of base nodes:

ComonNodes

Manager nodes

BeginNode
Description:

Start node, executed when the process starts

Parametrs:
  • mixed value = true
Grapical:
 ┌─────────────┐
 │             ├─ out1
 │             │
 │             │
 │             ├─ out2
 │  BeginNode  │
 │             │
 │             ├─ ...
 │             │
 │             │
 │             ├─ out$n
 └─────────────┘
Inputs:

none

Outputs:
  • one or more outputs by pattern out{number}
Requests:

none

Logic:

emit $value from all outputs

EndNode
Description:

End node, collect result of process

Parametrs:

none

Grapical:
      ┌───────────────┐
in1  ─┤               │
      │               │
in2  ─┤               │
      │    EndNode    │
...  ─┤               │
      │               │
in$n ─┤               │
      └───────────────┘
Inputs:
  • some count of any value with any names
Outputs:

none

Requests:

none

Logic:

Collect all input value and map it to process result array. Input can haw any names, this names map to result array key.

SplitterNode
Description:

Forked process trates

Parametrs:

none

Grapical:
     ┌───────────────┐
     │               ├─ out1
     │               │
     │               ├─ out2
in  ─┤  SplitterNode │
     │               ├─ ...
     │               │
     │               ├─ out$n
     └───────────────┘
Inputs:
  • in - required any type
Outputs:
  • eny count of value with name like pattern: out{number}
Requests:

none

Logic:

emit input value to all outputs out1 = out2 = ... = out$n = in

SingleNode
Description:

singlify input signals

Parametrs:

none

Grapical:
       ┌───────────────┐
in    ─┤               │
       │   SingleNode  ├─ out
reset ─┤               │
       └───────────────┘
Inputs:
  • in - required any type
  • reset - any value to reset input lock
Outputs:
  • out - equal first of in
Requests:

none

Logic:

emit output signal only for first time input signal

WaitingNode
Description:

Waiting emit signal for continue

Parametrs:

none

Grapical:
      ┌───────────────┐
in   ─┤               │
      │  WaitingNode  ├─ out
emit ─┤               │
      └───────────────┘
Inputs:
  • in - required any type
  • reset - required bool
Outputs:
  • out - equal in
Requests:

none

Logic:

emit outputs only if 'emit' signal = true

TriggerNode
Description:

emit additional signals when input signal received

Parametrs:

none

Grapical:
      ┌───────────────┐
      │               ├─ before
      │               │
in   ─┤  TriggerNode  ├─ out
      │               │
      │               ├─ after
      └───────────────┘
Inputs:
  • in - required any type
Outputs:
  • before - true
  • out - equal in
  • after - true
Requests:

none

Logic:

emit 'before'=true output when input received, before output emitted emit 'out' = 'in' emit 'after'=true output when input received, after output emitted

IterateNode
Description:

iterate array for each 'emit' signal received

Parametrs:

none

Grapical:
        ┌───────────────┐
array  ─┤               ├─ out
        │               │
emit   ─┤  IterateNode  ├─ complete
        │               │
reset  ─┤               ├─ count
        └───────────────┘
Inputs:
  • array - required array
  • emit - required any type
  • reset - reset control
Outputs:
  • out - any type value (emit for each elements of 'array')
  • complete - true (emit when array is end)
  • count - count of 'array' elements (emit when 'array' signal received)
Requests:

none

Logic:

emit 'count' = count(array) signal when 'array' signal received emit 'out' = array[$n] for each 'emit' signal received emit 'complete' = true when 'array' is end

PauseNode
Description:

Stop current thread until a response is received

Parametrs:

none

Grapical:
      ┌───────────────┐
      │               │
 in  ─┤   PauseNode   ├─ out
      │               │
      └───────────────┘
Inputs:
  • in - required any type
Outputs:
  • out - any type value equal 'in'
Requests:
  • 'pause': waiting eny response
Logic:

Stop current thread until a response is received then forward input to output

RepeatNode
Description:

Repeat 'in' sume time

Parametrs:

$value - count of repeat time

Grapical:
      ┌───────────────┐
      │               ├─ out
 in  ─┤   RepeatNode  │
      │               ├─ complete
      └───────────────┘
Inputs:
  • in - required any type
Outputs:
  • out - any type value equal 'in'
  • complete - true (emit when repeat end)
Requests:

none

Logic:

Forward input to output and repeat it '$value' time. Emit complete = true when repeat time is end

SubprocessNode
Description:

Run subprocess (another instance of Process) inside node

Parametrs:

$subprocessScheme like process builder

Grapical:
       ┌─────────────────────┐
 in1  ─┤                     ├─ out1
       │                     │
 in2  ─┤                     ├─ out2
       │    SubProcessNode   │
 ...  ─┤                     ├─ ...
       │                     │
 in$n ─┤                     ├─ ount$n
       └─────────────────────┘
Inputs:

Depending on the scheme Map inputs to scheme parameters

Outputs:

Depending on the scheme Map subprocess output to node output

Requests:

Forward request from subprocess Forward response to subprocess

Logic:

Run subprocess (instance of Process) inside SubProcessNode, Map inputs to subprocess scheme parameters and map output of subprocess to current output Forward subprocess request and response Emit all output when subprocess status is Process::STATUS_COMPLETE

MultiSubProcessNode
Description:

Run sume fork of subprocess (another instance of Process) inside node

Parametrs:

$subprocessScheme like process builder

Grapical:
       ┌─────────────────────┐
 in1  ─┤                     ├─ out1
       │                     │
 in2  ─┤                     ├─ out2
       │ MultiSubProcessNode │
 ...  ─┤                     ├─ ...
       │                     │
 in$n ─┤                     ├─ ount$n
       └─────────────────────┘
Inputs:

Depending on the scheme Map simple inputs to scheme parameters Map each element of array inputs to appropriate number of subprocess

Outputs:

Depending on the scheme Map collected subprocesses outputs (as array)

Requests:

Forward request from subprocesses Forward response to subprocesses

Logic:

Run subprocess instance for each array element of greatest input value Collect all subprocesses outputs as appropriate arrays and map it to output forward all subprocesses request to node request forward all node response to subprocesses response emit all output when all subprocesses status is Process::STATUS_COMPLETE

Logical nodes

AndNode
Description:

Logical AND node

Parametrs:

none

Grapical:
       ┌───────────────┐
 in1  ─┤               │
       │               ├─ out
 in2  ─┤               │
       │    AndNode    │
 ...  ─┤               │
       │               ├─ iout
 in$n ─┤               │
       └───────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required boolean
Outputs:
  • out - logical AND of all inputs
  • iout - inverted out
Requests:

none

Logic:

'out' = 'in1' && 'in2' && ... && 'in$n' 'iout' = !'out'

NotNode
Description:

Logical NOT node

Parametrs:

none

Grapical:
      ┌───────────────┐
      │               │
 in  ─┤   CountNode   ├─ out
      │               │
      └───────────────┘
Inputs:
  • in - required boolean
Outputs:
  • out - logical NOT of input
Requests:

none

Logic:

'out' = !'in'

OrNode
Description:

Logical OR node

Parametrs:

none

Grapical:
       ┌───────────────┐
 in1  ─┤               │
       │               ├─ out
 in2  ─┤               │
       │     OrNode    │
 ...  ─┤               │
       │               ├─ iout
 in$n ─┤               │
       └───────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required boolean
Outputs:
  • out - logical AND of all inputs
  • iout - inverted out
Requests:

none

Logic:

'out' = 'in1' || 'in2' || ... || 'in$n'

XorNode
Description:

Logical XOR node

Parametrs:

none

Grapical:
       ┌───────────────┐
 in1  ─┤               │
       │               ├─ out
 in2  ─┤               │
       │    XorNode    │
 ...  ─┤               │
       │               ├─ iout
 in$n ─┤               │
       └───────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required boolean
Outputs:
  • out - logical XOR of all inputs
  • iout - inverted out
Requests:

none

Logic:

out = (in1 || in2 || .. || in$n) && !(in1 && in2) && !(in2 && in3) && !(in1 && in3) ..... only one value must be true iout = !out

CompareNode
Description:

Logical XOR node

Parametrs:

none

Grapical:
       ┌───────────────┐
       │               ├─ more
 in1  ─┤               │
       │               │
       │  CompareNode  ├─ less
       │               │
 in2  ─┤               │
       │               ├─ equal
       └───────────────┘
Inputs:
  • in1 - required some compared value
  • in2 - required some compared value
Outputs:
  • more - true when in1 > in2
  • less - true when in1 < in2
  • equal - true when in1 == in2
Requests:

none

Logic:

more = in1 > in2 less = in1 < in2 equal = in1 == in2

Arithmetical nodes

DivideNode
Description:

Ariphmetic devide node

Parametrs:

none

Grapical:
       ┌───────────────┐
 in1  ─┤               │
       │               │
 in2  ─┤               │
       │   DivideNode  ├─ out
 ...  ─┤               │
       │               │
 in$n ─┤               │
       └───────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required number
Outputs:
  • out - divide in1 to all in$n inputs
Requests:

none

Logic:

out = in1 / in2 / .. / in$n

ModNode
Description:

Ariphmetic mod node

Parametrs:

none

Grapical:
       ┌───────────────┐
 in1  ─┤               │
       │               │
 in2  ─┤               │
       │    ModNode    ├─ out
 ...  ─┤               │
       │               │
 in$n ─┤               │
       └───────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required number
Outputs:
  • out - mod of divide in1 to all in$n inputs
Requests:

none

Logic:

out = in1 % in2 % .. % in$n

MultiptyNode
Description:

Ariphmetic multiply node

Parametrs:

none

Grapical:
       ┌──────────────┐
 in1  ─┤              │
       │              │
 in2  ─┤              │
       │ MultiplyNode ├─ out
 ...  ─┤              │
       │              │
 in$n ─┤              │
       └──────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required number
Outputs:
  • out - multiply all in$n inputs
Requests:

none

Logic:

out = in1 * in2 * .. * in$n

SubstractNode
Description:

Ariphmetic substract node

Parametrs:

none

Grapical:
       ┌───────────────┐
 in1  ─┤               │
       │               │
 in2  ─┤               │
       │ SubstractNode ├─ out
 ...  ─┤               │
       │               │
 in$n ─┤               │
       └───────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required number
Outputs:
  • out - substract in1 and all in$n inputs
Requests:

none

Logic:

out = in1 - in2 - .. - in$n

SumNode
Description:

Ariphmetic sum node

Parametrs:

none

Grapical:
       ┌───────────────┐
 in1  ─┤               │
       │               │
 in2  ─┤               │
       │    SumNode    ├─ out
 ...  ─┤               │
       │               │
 in$n ─┤               │
       └───────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required number
Outputs:
  • out - sum of all inputs
Requests:

none

Logic:

out = in1 + in2 + .. + in$n

String nodes

ConcatNode
Description:

String concatination node

Parametrs:

none

Grapical:
       ┌───────────────┐
 in1  ─┤               │
       │               │
 in2  ─┤               │
       │   ConcatNode  ├─ out
 ...  ─┤               │
       │               │
 in$n ─┤               │
       └───────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required string
Outputs:
  • out - concatination of all inputs
Requests:

none

Logic:

'out' = in1 . in2 . ... . in$n

MatchNode
Description:

String match node

Parametrs:

$pattern - regexp string for matching

Grapical:
      ┌───────────────┐
      │               ├─ out
 in  ─┤   MatchNode   │
      │               ├─ iout
      └───────────────┘
Inputs:
  • in - required string
Outputs:
  • out - true if match success
  • iout - inverted out
Requests:

none

Logic:

Emit 'out' if 'in' match pattern Emit 'iout' if 'in' don't match pattern

ReplaceNode
Description:

Replace string or string part

Parametrs:

$pattern - regexp string for matching $replacement - string for replace

Grapical:
      ┌───────────────┐
      │               │
 in  ─┤  ReplaceNode  ├─ out
      │               │
      └───────────────┘
Inputs:
  • in - required string
Outputs:
  • out - result string
Requests:

none

Logic:

equal preg_replace($pattern, $replacement, 'in');

Array nodes

CollectNode
Description:

Collect input signals to array

Parametrs:

none

Grapical:
       ┌───────────────┐
 in   ─┤               │
       │  CollectNode  ├─ out
 emit ─┤               │
       └───────────────┘
Inputs:
  • in - required mixed
  • emit - required true
Outputs:
  • out - array of input values
Requests:

none

Logic:

Collect all 'in' signals to array emit this array when 'emit' received

CombineNode
Description:

Collect all inputs to single array

Parametrs:

none

Grapical:
       ┌───────────────┐
 in1  ─┤               │
       │               │
 in2  ─┤               │
       │  CombineNode  ├─ out
 ...  ─┤               │
       │               │
 in$n ─┤               │
       └───────────────┘
Inputs:
  • some inputs names like pattern 'in{$number}' - required mixed
Outputs:
  • out - array of inputs values
Requests:

none

Logic:

Combine all inputs to array like ['in1' => $valueOfIn1, 'in2' => $valueOfIn2 , .....]

CountNode
Description:

Collect input signals to array

Parametrs:

none

Grapical:
        ┌───────────────┐
        │               │
 array ─┤   CountNode   ├─ out
        │               │
        └───────────────┘
Inputs:
  • array - required array
Outputs:
  • out - count of array values
Requests:

none

Logic:

emit count of 'array' elements

EachNode
Description:

Emit output for each element of input array

Parametrs:

none

Grapical:
        ┌──────────────┐
        │              ├─ out
        │              │
 array ─┤   EachNode   ├─ key
        │              │
        │              ├─ complete
        └──────────────┘
Inputs:
  • array - required array
Outputs:
  • out - single value of 'array'
  • key - single key of 'arra'
  • complete - true when array end
Requests:

none

Logic:

Emit 'out' and 'key' for each 'array' elements Emit 'complete' when array is end

ExtractNode
Description:

Exctract single value from array by key

Parametrs:

$key - key

Grapical:
        ┌───────────────┐
        │               │
 array ─┤  ExtractNode  ├─ out
        │               │
        └───────────────┘
Inputs:
  • array - required array
Outputs:
  • out - emit single value of array
Requests:

none

Logic:

Emit array[$key]

Other nodes

CounterNode
Description:

Calculate count of input signals

Parametrs:

none

Grapical:
      ┌───────────────┐
      │               │
 in  ─┤  CounterNode  ├─ out
      │               │
      └───────────────┘
Inputs:
  • in - required mixed
Outputs:
  • out - emit count of input signals
Requests:

none

Logic:

calculate input signal count emit 'out' everytime when 'in' received

DumpNode
Description:

Dump input values

Parametrs:

none

Grapical:
      ┌───────────────┐
      │               │
 in  ─┤    DumpNode   ├─ out
      │               │
      └───────────────┘
Inputs:
  • in - required mixed
Outputs:
  • out - equal 'in'
Requests:

none

Logic:

forward input to output call dump('in') if dump function available call var_export if running in cli mode call var_dump if running in fpm mode

RandNode
Description:

Random value emiter

Parametrs:

$min - min value of random $max - max value of random

Grapical:
       ┌───────────────┐
       │               │
 emit ─┤    RandNode   ├─ out
       │               │
       └───────────────┘
Inputs:
  • emit - required mixed
Outputs:
  • out - random number value
Requests:

none

Logic:

when 'emit' received emit random($min=0, $max=1) value to 'out'

RangeNode
Description:

Range generator

Parametrs:

$start - start valut of range $end - end value of range $step - step of range

Grapical:
       ┌───────────────┐
       │               │
 emit ─┤    RageNode   ├─ out
       │               │
       └───────────────┘
Inputs:
  • emit - required mixed
Outputs:
  • out - array
Requests:

none

Logic:

equal range($start, $end, $step=1) emit array when 'emit' received

ValueNode
Description:

value emiter

Parametrs:

$value - emitted value

Grapical:
       ┌───────────────┐
       │               │
 emit ─┤   ValueNode   ├─ out
       │               │
       └───────────────┘
Inputs:
  • emit - required mixed
Outputs:
  • out - value
Requests:

none

Logic:

emit value when 'out' received

Creating user node

To create user node type, create a class extended from NodeAbstract and define public eval() method

Input/output defenition

Input and output policy declarated in static propery $scheme

static propery $scheme = [
    'input' => [                //input declaration
        'optionalInput' => [       //optional input named as 'optionalInput'
        ],
        'requiredInput' => [       //required input named as 'requiredInput'
            'required' => true,    //set 'required' => true to make input required
        ]
    ],
    'user_input' => [           //dynamic input declarated
        'pattern' => '~in\d+~', //pattern for available input names
        'property' => [
            'required' => true  //also can be required
        ]
    ],
    'output' => [                       //output declaration
        'outputWithDefaultData' => [    //output named as 'outputWithDefaultData'
            'data' => 'default data'        //define 'data' property to define default data
        ],
        'outputWithoutDefaultData' => [ //output named as 'outputWithoutDefaultData'
        ]
    ],
    'user_output' => [  //dynamic output declarated
    ],                  //if pattern property is null, all names available
]

Output emited only when all required input received

Read input/output, emit signals

You can get access to input and output as

$value = $this->input['inputName']['data']; //get value from 'inputName' input
$this->output['outputName']['data'] = $value; //set value to 'outputName' output (not emitted)

To emit output print:

$this->emit('outputName');
//or you can set value and output:
$this->emit('ouputName', $sumeValue);

Request/response processing

You can emit request from node:

$this->sendRequest($data, $id=null); //if $id == null $id get automaticaly uniq identifier

To process response in node extend setResponse method

public function setResponse($id, $data)
{
    ....do somethind
}