Test Failed
Push — ft/states ( eebc9c )
by Ben
29:42 queued 08:49
created

StateMachine::validateTransitions()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 15
rs 8.4444
cc 8
nc 7
nop 0
1
<?php
2
3
namespace Thinktomorrow\Chief\States\State;
4
5
abstract class StateMachine
6
{
7
    /**
8
     * States and transitions should be set on the specific state Machine.
9
     *
10
     * @var array
11
     */
12
    protected $states = [];
13
    protected $transitions = [];
14
15
    /**
16
     * @var StatefulContract
17
     */
18
    protected $statefulContract;
19
20
    public function __construct(StatefulContract $statefulContract)
21
    {
22
        // TODO: add event dispatcher cause here we want to add loads of events no?
23
        // NO! WE SHOULD BETTER TO THIS ON THE AGGREGATE
24
        $this->statefulContract = $statefulContract;
25
26
        $this->validateTransitions();
27
    }
28
29
    public function apply($transition)
30
    {
31
        // Check valid transition request
32
        if (!array_key_exists($transition, $this->transitions)) {
33
            throw StateException::invalidTransitionKey($transition, $this);
34
        }
35
36
        if (!in_array($this->statefulContract->state(), $this->transitions[$transition]['from'])) {
37
            throw StateException::invalidTransition($transition, $this->statefulContract->state(), $this);
38
        }
39
40
        $state = $this->transitions[$transition]['to'];
41
42
        $this->statefulContract->changeState($state);
43
    }
44
45
    /**
46
     * assert the integrity of the new state.
47
     *
48
     * @param StatefulContract $statefulContract
49
     * @param $state
50
     *
51
     * @throws StateException
52
     */
53
    public static function assertNewState(StatefulContract $statefulContract, $state)
54
    {
55
        $machine = new static($statefulContract);
56
57
        if (!$machine->canTransitionTo($state)) {
58
            throw StateException::invalidState($state, $statefulContract->state(), $machine);
59
        }
60
    }
61
62
    /**
63
     * Verify the new state is valid.
64
     *
65
     * @param $state
66
     *
67
     * @return bool
68
     */
69
    public function canTransitionTo($state)
70
    {
71
        if (!in_array($state, $this->states)) {
72
            return false;
73
        }
74
75
        foreach ($this->transitions as $transition) {
76
            if (!in_array($this->statefulContract->state(), $transition['from'])) {
77
                continue;
78
            }
79
80
            if ($transition['to'] == $state) {
81
                return true;
82
            }
83
        }
84
85
        return false;
86
    }
87
88
    private function validateTransitions()
89
    {
90
        foreach ($this->transitions as $transitionKey => $transition) {
91
            if (!isset($transition['from']) || !isset($transition['to']) || !is_array($transition['from'])) {
92
                throw StateException::malformedTransition($transitionKey, $this);
93
            }
94
95
            foreach ($transition['from'] as $fromState) {
96
                if (!in_array($fromState, $this->states)) {
97
                    throw StateException::invalidTransitionState($transitionKey, $fromState, $this);
98
                }
99
            }
100
101
            if (!in_array($transition['to'], $this->states)) {
102
                throw StateException::invalidTransitionState($transitionKey, $transition['to'], $this);
103
            }
104
        }
105
    }
106
}
107