Skip to content

Commit

Permalink
Add basic loop implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
wol-soft committed Nov 2, 2021
1 parent 75051b9 commit 4e33c32
Show file tree
Hide file tree
Showing 11 changed files with 564 additions and 275 deletions.
6 changes: 3 additions & 3 deletions src/Stage/Next/AllowNextExecuteWorkflow.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ public function executeWorkflow(
);

if ($exception instanceof SkipWorkflowException) {
return new WorkflowResult($workflowState->getWorkflowName(), true, $workflowState);
return $workflowState->close(true);
}

$result = new WorkflowResult($workflowState->getWorkflowName(), false, $workflowState, $exception);
$result = $workflowState->close(false, $exception);

if ($throwOnFailure) {
throw new WorkflowException(
Expand All @@ -65,6 +65,6 @@ public function executeWorkflow(
return $result;
}

return new WorkflowResult($workflowState->getWorkflowName(), true, $workflowState);
return $workflowState->close(true);
}
}
66 changes: 3 additions & 63 deletions src/Stage/Stage.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@

namespace PHPWorkflow\Stage;

use Exception;
use PHPWorkflow\Exception\WorkflowControl\FailStepException;
use PHPWorkflow\Exception\WorkflowControl\SkipStepException;
use PHPWorkflow\Exception\WorkflowControl\SkipWorkflowException;
use PHPWorkflow\State\ExecutionLog\ExecutionLog;
use PHPWorkflow\State\WorkflowState;
use PHPWorkflow\Step\WorkflowStep;
use PHPWorkflow\Step\StepExecutionTrait;
use PHPWorkflow\Workflow;

abstract class Stage
{
use StepExecutionTrait;

protected ?Stage $nextStage = null;
protected Workflow $workflow;

Expand All @@ -24,61 +21,4 @@ public function __construct(Workflow $workflow)
}

abstract protected function runStage(WorkflowState $workflowState): ?Stage;

protected function wrapStepExecution(WorkflowStep $step, WorkflowState $workflowState): void {
try {
($this->resolveMiddleware($step, $workflowState))();
} catch (SkipStepException | FailStepException $exception) {
$workflowState->addExecutionLog(
$step->getDescription(),
$exception instanceof FailStepException ? ExecutionLog::STATE_FAILED : ExecutionLog::STATE_SKIPPED,
$exception->getMessage(),
);

if ($exception instanceof FailStepException) {
// cancel the workflow during preparation
if ($workflowState->getStage() <= WorkflowState::STAGE_PROCESS) {
throw $exception;
}

$workflowState->getExecutionLog()->addWarning(sprintf('Step failed (%s)', get_class($step)), true);
}

return;
} catch (Exception $exception) {
$workflowState->addExecutionLog(
$step->getDescription(),
$exception instanceof SkipWorkflowException ? ExecutionLog::STATE_SKIPPED : ExecutionLog::STATE_FAILED,
$exception->getMessage(),
);

// cancel the workflow during preparation
if ($workflowState->getStage() <= WorkflowState::STAGE_PROCESS) {
throw $exception;
}

if (!($exception instanceof SkipWorkflowException)) {
$workflowState->getExecutionLog()->addWarning(sprintf('Step failed (%s)', get_class($step)), true);
}

return;
}

$workflowState->addExecutionLog($step->getDescription());
}

private function resolveMiddleware(WorkflowStep $step, WorkflowState $workflowState): callable
{
$tip = fn () => $step->run($workflowState->getWorkflowControl(), $workflowState->getWorkflowContainer());

foreach ($workflowState->getMiddlewares() as $middleware) {
$tip = fn () => $middleware(
$tip,
$workflowState->getWorkflowControl(),
$workflowState->getWorkflowContainer(),
);
}

return $tip;
}
}
21 changes: 12 additions & 9 deletions src/State/WorkflowResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,26 @@
namespace PHPWorkflow\State;

use Exception;
use PHPWorkflow\State\ExecutionLog\ExecutionLog;

class WorkflowResult
{
private bool $success;
private WorkflowState $workflowState;
private ?Exception $exception;
private string $workflowName;
private ExecutionLog $executionLog;
private WorkflowContainer $workflowContainer;

public function __construct(
string $workflowName,
bool $success,
WorkflowState $workflowState,
bool $success,
?Exception $exception = null
) {
$this->workflowName = $workflowName;
$this->workflowName = $workflowState->getWorkflowName();
$this->executionLog = $workflowState->getExecutionLog();
$this->workflowContainer = $workflowState->getWorkflowContainer();

$this->success = $success;
$this->workflowState = $workflowState;
$this->exception = $exception;
}

Expand All @@ -46,15 +49,15 @@ public function success(): bool
*/
public function debug(): string
{
return (string) $this->workflowState->getExecutionLog();
return (string) $this->executionLog;
}

/**
* Check if the workflow execution has triggered warnings
*/
public function hasWarnings(): bool
{
return count($this->workflowState->getExecutionLog()->getWarnings()) > 0;
return count($this->executionLog->getWarnings()) > 0;
}

/**
Expand All @@ -65,7 +68,7 @@ public function hasWarnings(): bool
*/
public function getWarnings(): array
{
return $this->workflowState->getExecutionLog()->getWarnings();
return $this->executionLog->getWarnings();
}

/**
Expand All @@ -82,6 +85,6 @@ public function getException(): ?Exception
*/
public function getContainer(): WorkflowContainer
{
return $this->workflowState->getWorkflowContainer();
return $this->workflowContainer;
}
}
16 changes: 16 additions & 0 deletions src/State/WorkflowState.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,27 @@ class WorkflowState
private string $workflowName;
private array $middlewares = [];

private static array $runningWorkflows = [];

public function __construct(WorkflowContainer $workflowContainer)
{
$this->executionLog = new ExecutionLog($this);
$this->workflowControl = new WorkflowControl($this->executionLog);
$this->workflowContainer = $workflowContainer;

self::$runningWorkflows[] = $this;
}

public function close(bool $success, ?Exception $exception = null): WorkflowResult
{
array_pop(self::$runningWorkflows);

return new WorkflowResult($this, $success, $exception);
}

public static function getRunningWorkflow(): ?self
{
return self::$runningWorkflows ? end(self::$runningWorkflows) : null;
}

public function getProcessException(): ?Exception
Expand Down
49 changes: 49 additions & 0 deletions src/Step/Loop.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace PHPWorkflow\Step;

use PHPWorkflow\State\WorkflowContainer;
use PHPWorkflow\State\WorkflowState;
use PHPWorkflow\WorkflowControl;

class Loop implements WorkflowStep
{
use StepExecutionTrait;

protected array $steps = [];
protected LoopControl $loopControl;

public function __construct(LoopControl $loopControl)
{
$this->loopControl = $loopControl;
}

public function addStep(WorkflowStep $step): self
{
$this->steps[] = $step;

return $this;
}

public function getDescription(): string
{
return $this->loopControl->getDescription();
}

public function run(WorkflowControl $control, WorkflowContainer $container)
{
$iteration = 0;

while ($this->loopControl->executeNextIteration($iteration, $control, $container)) {
foreach ($this->steps as $step) {
$this->wrapStepExecution($step, WorkflowState::getRunningWorkflow());
}

WorkflowState::getRunningWorkflow()->addExecutionLog(sprintf("Loop iteration #%s", ++$iteration));
}

$control->attachStepInfo(sprintf("Loop finished after %s iterations", $iteration));
}
}
21 changes: 21 additions & 0 deletions src/Step/LoopControl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace PHPWorkflow\Step;

use PHPWorkflow\State\WorkflowContainer;
use PHPWorkflow\WorkflowControl;

interface LoopControl
{
/**
* Describe in a few words what this loop does
*/
public function getDescription(): string;

/**
* Return true if the next iteration of the loop shall be executed. Return false to break the loop
*/
public function executeNextIteration(int $iteration, WorkflowControl $control, WorkflowContainer $container): bool;
}
72 changes: 72 additions & 0 deletions src/Step/StepExecutionTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace PHPWorkflow\Step;

use Exception;
use PHPWorkflow\Exception\WorkflowControl\FailStepException;
use PHPWorkflow\Exception\WorkflowControl\SkipStepException;
use PHPWorkflow\Exception\WorkflowControl\SkipWorkflowException;
use PHPWorkflow\State\ExecutionLog\ExecutionLog;
use PHPWorkflow\State\WorkflowState;

trait StepExecutionTrait
{
protected function wrapStepExecution(WorkflowStep $step, WorkflowState $workflowState): void {
try {
($this->resolveMiddleware($step, $workflowState))();
} catch (SkipStepException | FailStepException $exception) {
$workflowState->addExecutionLog(
$step->getDescription(),
$exception instanceof FailStepException ? ExecutionLog::STATE_FAILED : ExecutionLog::STATE_SKIPPED,
$exception->getMessage(),
);

if ($exception instanceof FailStepException) {
// cancel the workflow during preparation
if ($workflowState->getStage() <= WorkflowState::STAGE_PROCESS) {
throw $exception;
}

$workflowState->getExecutionLog()->addWarning(sprintf('Step failed (%s)', get_class($step)), true);
}

return;
} catch (Exception $exception) {
$workflowState->addExecutionLog(
$step->getDescription(),
$exception instanceof SkipWorkflowException ? ExecutionLog::STATE_SKIPPED : ExecutionLog::STATE_FAILED,
$exception->getMessage(),
);

// cancel the workflow during preparation
if ($workflowState->getStage() <= WorkflowState::STAGE_PROCESS) {
throw $exception;
}

if (!($exception instanceof SkipWorkflowException)) {
$workflowState->getExecutionLog()->addWarning(sprintf('Step failed (%s)', get_class($step)), true);
}

return;
}

$workflowState->addExecutionLog($step->getDescription());
}

private function resolveMiddleware(WorkflowStep $step, WorkflowState $workflowState): callable
{
$tip = fn () => $step->run($workflowState->getWorkflowControl(), $workflowState->getWorkflowContainer());

foreach ($workflowState->getMiddlewares() as $middleware) {
$tip = fn () => $middleware(
$tip,
$workflowState->getWorkflowControl(),
$workflowState->getWorkflowContainer(),
);
}

return $tip;
}
}
Loading

0 comments on commit 4e33c32

Please sign in to comment.