Test Failed
Push — ft/states ( ef32c7...d6dd80 )
by Ben
07:05 queued 19s
created

StateMachine::allowedTransitions()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 11
rs 10
cc 3
nc 3
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
    public function allowedTransitions(): array
89
    {
90
        $transitions = [];
91
92
        foreach($this->transitions as $transitionKey => $transition){
93
            if (false !== array_search($this->statefulContract->state(), $transition['from'])) {
94
                $transitions[] = $transitionKey;
95
            }
96
        }
97
98
        return $transitions;
99
    }
100
101
    private function validateTransitions()
102
    {
103
        foreach ($this->transitions as $transitionKey => $transition) {
104
            if (!isset($transition['from']) || !isset($transition['to']) || !is_array($transition['from'])) {
105
                throw StateException::malformedTransition($transitionKey, $this);
106
            }
107
108
            foreach ($transition['from'] as $fromState) {
109
                if (!in_array($fromState, $this->states)) {
110
                    throw StateException::invalidTransitionState($transitionKey, $fromState, $this);
111
                }
112
            }
113
114
            if (!in_array($transition['to'], $this->states)) {
115
                throw StateException::invalidTransitionState($transitionKey, $transition['to'], $this);
116
            }
117
        }
118
    }
119
}
120