Completed
Push — master ( a43bdb...27ad3e )
by Steve
02:42
created

Transaction::finaliseSteps()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 8
nc 2
nop 1
1
<?php
2
3
namespace MeadSteve\Tale;
4
5
use MeadSteve\Tale\Execution\CompletedStep;
6
use MeadSteve\Tale\Execution\Failure;
7
use MeadSteve\Tale\Execution\Success;
8
use MeadSteve\Tale\Execution\TransactionResult;
9
use MeadSteve\Tale\Steps\FinalisingStep;
10
use MeadSteve\Tale\Steps\NamedStep;
11
use MeadSteve\Tale\Steps\Step;
12
use Psr\Log\LoggerInterface;
13
use Psr\Log\NullLogger;
14
15
class Transaction
16
{
17
    /**
18
     * @var Step[]
19
     */
20
    private $steps = [];
21
22
    /**
23
     * @var LoggerInterface
24
     */
25
    private $logger;
26
27
    public function __construct(LoggerInterface $logger = null)
28
    {
29
        if ($logger === null) {
30
            $logger = new NullLogger();
31
        }
32
        $this->logger = $logger;
33
    }
34
35
    public function addStep(Step $step): Transaction
36
    {
37
        $this->logger->debug("Adding {$this->stepName($step)} to transaction definition");
38
        $this->steps[] = $step;
39
        return $this;
40
    }
41
42
    /**
43
     * Runs each step in the transaction
44
     *
45
     * @param mixed $startingState the state to pass in to the first step
46
     * @return TransactionResult
47
     */
48
    public function run($startingState = null): TransactionResult
49
    {
50
        $this->logger->debug("Running transaction");
51
        $state = $startingState;
52
        $completedSteps = [];
53
        foreach ($this->steps as $key => $step) {
54
            try {
55
                $this->logger->debug("Executing {$this->stepName($step)} step [$key]");
56
                $state = $step->execute($state);
57
                $completedSteps[] = new CompletedStep($step, $state, $key);
58
                $this->logger->debug("Execution of {$this->stepName($step)} step [$key] complete");
59
            } catch (\Throwable $failure) {
60
                $this->logger->debug("Failed executing {$this->stepName($step)} step [$key]");
61
                $this->revertCompletedSteps($completedSteps);
62
                $this->logger->debug("Finished compensating all previous steps");
63
                return new Failure($failure);
64
            }
65
        }
66
        $this->finaliseSteps($completedSteps);
67
        return new Success($state);
68
    }
69
70
    /**
71
     * @param CompletedStep[] $completedSteps
72
     */
73
    private function revertCompletedSteps(array $completedSteps)
74
    {
75
        foreach (array_reverse($completedSteps) as $completedStep) {
76
            $step = $completedStep->step;
77
            $stepId = $completedStep->stepId;
78
            $this->logger->debug("Compensating for step {$this->stepName($step)} [{$stepId}]");
79
            $step->compensate($completedStep->state);
80
            $this->logger->debug("Compensation complete for step {$this->stepName($step)} [{$stepId}]");
81
        }
82
    }
83
84
    /**
85
     * @param CompletedStep[] $completedSteps
86
     */
87
    private function finaliseSteps($completedSteps)
88
    {
89
        $stepsToFinalise = array_filter($completedSteps, function (CompletedStep $x) {
90
            return $x->step instanceof FinalisingStep;
91
        });
92
        foreach ($stepsToFinalise as $completedStep) {
93
            $step = $completedStep->step;
94
            $stepId = $completedStep->stepId;
95
            $this->logger->debug("Finalising step {$this->stepName($step)} [{$stepId}]");
96
            $step->finalise($completedStep->state);
97
            $this->logger->debug("Finalising step {$this->stepName($step)} [{$stepId}]");
98
        }
99
    }
100
101
    private function stepName(Step $step): string
102
    {
103
        if ($step instanceof NamedStep) {
104
            return "`{$step->stepName()}`";
105
        }
106
        return "anonymous step";
107
    }
108
}
109