Completed
Push — master ( b304e8...044dfd )
by Steve
02:05
created

Transaction::add()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
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
        $this->logger = $logger ?? new NullLogger();
30
    }
31
32
33
    public function add(...$args): Transaction
34
    {
35
        $step = StepBuilder::build(...$args);
36
        return $this->addStep($step);
37
    }
38
39
    /**
40
     * Runs each step in the transaction
41
     *
42
     * @param mixed $startingState the state to pass in to the first step
43
     * @return TransactionResult
44
     */
45
    public function run($startingState = null): TransactionResult
46
    {
47
        $this->logger->debug("Running transaction");
48
        $state = $startingState;
49
        $completedSteps = [];
50
        foreach ($this->steps as $key => $step) {
51
            try {
52
                $this->logger->debug("Executing {$this->stepName($step)} step [$key]");
53
                $state = $step->execute($state);
54
                $completedSteps[] = new CompletedStep($step, $state, $key);
55
                $this->logger->debug("Execution of {$this->stepName($step)} step [$key] complete");
56
            } catch (\Throwable $failure) {
57
                $this->logger->debug("Failed executing {$this->stepName($step)} step [$key]");
58
                $this->revertCompletedSteps($completedSteps);
59
                $this->logger->debug("Finished compensating all previous steps");
60
                return new Failure($failure);
61
            }
62
        }
63
        $this->finaliseSteps($completedSteps);
64
        return new Success($state);
65
    }
66
67
    private function addStep(Step $step): Transaction
68
    {
69
        $this->logger->debug("Adding {$this->stepName($step)} to transaction definition");
70
        $this->steps[] = $step;
71
        return $this;
72
    }
73
74
    /**
75
     * @param CompletedStep[] $completedSteps
76
     */
77
    private function revertCompletedSteps(array $completedSteps)
78
    {
79
        foreach (array_reverse($completedSteps) as $completedStep) {
80
            $step = $completedStep->step;
81
            $stepId = $completedStep->stepId;
82
            $this->logger->debug("Compensating for step {$this->stepName($step)} [{$stepId}]");
83
            $step->compensate($completedStep->state);
84
            $this->logger->debug("Compensation complete for step {$this->stepName($step)} [{$stepId}]");
85
        }
86
    }
87
88
    /**
89
     * @param CompletedStep[] $completedSteps
90
     */
91
    private function finaliseSteps($completedSteps)
92
    {
93
        foreach ($completedSteps as $completedStep) {
94
            $step = $completedStep->step;
95
            if ($step instanceof FinalisingStep) {
96
                $stepId = $completedStep->stepId;
97
                $this->logger->debug("Finalising step {$this->stepName($step)} [{$stepId}]");
98
                $step->finalise($completedStep->state);
99
                $this->logger->debug("Finalising step {$this->stepName($step)} [{$stepId}]");
100
            }
101
        }
102
    }
103
104
    private function stepName(Step $step): string
105
    {
106
        if ($step instanceof NamedStep) {
107
            return "`{$step->stepName()}`";
108
        }
109
        return "anonymous step";
110
    }
111
}
112