Calculator

The example shows how workflow process can interact with a user through WEB.

Setup


mkdir ~/pvm-calculator
cd ~/pvm-calculator

# the dep on symfony is optional
composer req formapro/pvm:0.4.x-dev makasim/values:0.5.x-dev symfony/http-foundation:^4.1

# create such files with the code below
php -S localhost:8000
# open in browser http://localhost:8000/calculator.php

Result

calculator.php


<?php
use Formapro\Pvm\DefaultBehaviorRegistry;
use 
Formapro\Pvm\Exception\WaitExecutionException;
use 
Formapro\Pvm\ProcessBuilder;
use 
Formapro\Pvm\ProcessEngine;
use 
Formapro\Pvm\Token;
use 
Formapro\Pvm\FileDAL;
use 
Formapro\Pvm\TokenTransition;
use 
Symfony\Component\HttpFoundation\Request;
use 
Symfony\Component\HttpFoundation\Response;

require_once 
__DIR__.'/vendor/autoload.php';

$httpRequest Request::createFromGlobals();

$fileDal = new FileDAL(__DIR__.'/store');

$process null;
if (
$httpRequest->get('token')) {
    try {
        
$token $fileDal->getToken($httpRequest->get('token'));
        
$process $token->getProcess();
    } catch (\
InvalidArgumentException $e) {
    }
}

if (
false == $process) {
    
$process = (new ProcessBuilder())
        ->
createNode('provide_a''a')->end()
        ->
createNode('provide_b''b')->end()
        ->
createNode('provide_operator''operator')->end()
        ->
createNode('result''result')->end()
        ->
createTransition('provide_a''provide_b')->end()
        ->
createTransition('provide_b''provide_operator')->end()
        ->
createTransition('provide_operator''result')->end()
        ->
createStartTransition('provide_a')->end()

        ->
getProcess()
    ;

    
$token $fileDal->createProcessToken($process);
    
$token->addTransition(TokenTransition::createFor($process->getStartTransition(), 1));
}

$token->httpRequest $httpRequest;

$registry = new DefaultBehaviorRegistry([
    
'a' => function(Token $token) {
        
/** @var Request $httpRequest */
        
$httpRequest $token->httpRequest;

        if (
$token->getCurrentTransition()->isWaiting() && Request::METHOD_POST === $httpRequest->getMethod()) {
            
$token->setValue('a'$httpRequest->request->getInt('a'));

            return;
        }

        
$token->httpResponse = new Response('
            <form method="post" action="'
.$_SERVER['REQUEST_URI'].'?token='.$token->getId().'">
                <label for="a">Select A:</label>
                <input name="a" value="1" type="number" />
                <input type="submit" title="Submit" />
            </form>
        '
);

        throw new 
WaitExecutionException();
    },
    
'b' => function(Token $token) {
        
/** @var Request $httpRequest */
        
$httpRequest $token->httpRequest;

        if (
$token->getCurrentTransition()->isWaiting() && Request::METHOD_POST === $httpRequest->getMethod()) {
            
$token->setValue('b'$httpRequest->request->getInt('b'));

            return;
        }

        
$token->httpResponse = new Response('
            <form method="post" action="'
.$_SERVER['REQUEST_URI'].'">
                <label for="b">Select B:</label>
                <input name="b" value="2" type="number" />
                <input type="submit" title="Submit" />
            </form>
        '
);

        throw new 
WaitExecutionException();
    },
    
'operator' => function(Token $token) {
        
/** @var Request $httpRequest */
        
$httpRequest $token->httpRequest;

        if (
$token->getCurrentTransition()->isWaiting() && Request::METHOD_POST === $httpRequest->getMethod()) {
            
$token->setValue('operator'$httpRequest->request->get('operator'));

            return;
        }

        
$token->httpResponse = new Response('
            <form method="post" action="'
.$_SERVER['REQUEST_URI'].'">
                <fieldset>
                    <legend>Select an operator:</legend>
            
                    <div>
                        <input type="radio" name="operator" value="+" checked />
                        <label for="huey">Addition "+"</label>
                    </div>
            
                    <div>
                        <input type="radio" name="operator" value="-" />
                        <label for="dewey">Subtraction "-"</label>
                    </div>
            
                    <div>
                        <input type="radio" name="operator" value="*" />
                        <label for="louie">Multiplication "*"</label>
                    </div>
                </fieldset>
                <input type="submit" title="Submit" />
            </form>
        '
);

        throw new 
WaitExecutionException();
    },
    
'result' => function(Token $token) {
        
$result $token->getValue('result');
        
$operator $token->getValue('operator');
        
$a $token->getValue('a');
        
$b $token->getValue('b');

        if (
false == $result) {
            switch (
$operator) {
                case 
'+':
                    
$result $a $b;
                    break;
                case 
'-':
                    
$result $a $b;
                    break;
                case 
'*':
                    
$result $a $b;
                    break;
                default:
                    throw new \
LogicException('Invalid operator');
            }

            
$token->setValue('result'$result);
        }

        
$token->httpResponse = new Response("
            <p>The result of 
$a $operator $b equals <b>$result</b></p> 
        "
);

        throw new 
WaitExecutionException();
    },
]);

$engine = new ProcessEngine($registry$fileDal);

$waitTokens $engine->proceed($token);

/** @var Response $httpResponse */
$httpResponse $waitTokens[0]->httpResponse;

echo 
$httpResponse->getContent();
{
    "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Process.json",
    "id": "06e6f437-63ec-4dd7-bd7e-166877e97a13",
    "nodes": {
        "provide_a": {
            "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Node.json",
            "id": "provide_a",
            "behavior": "a"
        },
        "provide_b": {
            "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Node.json",
            "id": "provide_b",
            "behavior": "b"
        },
        "provide_operator": {
            "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Node.json",
            "id": "provide_operator",
            "behavior": "operator"
        },
        "result": {
            "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Node.json",
            "id": "result",
            "behavior": "result"
        }
    },
    "transitions": {
        "808e178b-9efa-4167-b1ae-8f66b241241a": {
            "id": "808e178b-9efa-4167-b1ae-8f66b241241a",
            "weight": 1,
            "async": false,
            "active": true,
            "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Transition.json",
            "from": "provide_a",
            "to": "provide_b"
        },
        "936b3787-33e6-4048-b5ad-ec0cea12911c": {
            "id": "936b3787-33e6-4048-b5ad-ec0cea12911c",
            "weight": 1,
            "async": false,
            "active": true,
            "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Transition.json",
            "from": "provide_b",
            "to": "provide_operator"
        },
        "5b3599e6-ddb5-4f82-b143-7018741dacb0": {
            "id": "5b3599e6-ddb5-4f82-b143-7018741dacb0",
            "weight": 1,
            "async": false,
            "active": true,
            "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Transition.json",
            "from": "provide_operator",
            "to": "result"
        },
        "a32f2794-77f4-4be9-b4ba-a10c74d0597f": {
            "id": "a32f2794-77f4-4be9-b4ba-a10c74d0597f",
            "weight": 1,
            "async": false,
            "active": true,
            "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Transition.json",
            "to": "provide_a"
        }
    },
    "outTransitions": {
        "provide_a": [
            "808e178b-9efa-4167-b1ae-8f66b241241a"
        ],
        "provide_b": [
            "936b3787-33e6-4048-b5ad-ec0cea12911c"
        ],
        "provide_operator": [
            "5b3599e6-ddb5-4f82-b143-7018741dacb0"
        ]
    },
    "inTransitions": {
        "provide_b": [
            "808e178b-9efa-4167-b1ae-8f66b241241a"
        ],
        "provide_operator": [
            "936b3787-33e6-4048-b5ad-ec0cea12911c"
        ],
        "result": [
            "5b3599e6-ddb5-4f82-b143-7018741dacb0"
        ],
        "provide_a": [
            "a32f2794-77f4-4be9-b4ba-a10c74d0597f"
        ]
    },
    "tokens": {
        "796d132e-bd04-4125-84ee-575b3a58b367": {
            "schema": "http:\/\/pvm.forma-pro.com\/schemas\/Token.json",
            "id": "796d132e-bd04-4125-84ee-575b3a58b367",
            "transitions": [
                {
                    "schema": "http:\/\/pvm.forma-pro.com\/schemas\/TokenTransition.json",
                    "id": "70e32c1f-3b52-4f56-a03e-18b0c401d342",
                    "transitionId": "a32f2794-77f4-4be9-b4ba-a10c74d0597f",
                    "weight": 1,
                    "state": "opened",
                    "time": 15421486254673
                },
                {
                    "schema": "http:\/\/pvm.forma-pro.com\/schemas\/TokenTransition.json",
                    "id": "e9dd2ed7-c97f-48b2-9595-b4b498eedc6a",
                    "transitionId": "a32f2794-77f4-4be9-b4ba-a10c74d0597f",
                    "weight": 1,
                    "state": "waiting",
                    "time": 15421486254684,
                    "reason": ""
                }
            ]
        }
    }
}