Passed
Push — master ( 95c142...58f956 )
by Beau
02:43
created

FiniteStateMachine::graph()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Dflydev\FiniteStateMachine;
6
7
use Dflydev\FiniteStateMachine\Contracts\EventDispatcher;
8
use Dflydev\FiniteStateMachine\Contracts\FiniteStateMachine as FiniteStateMachineContract;
9
use Dflydev\FiniteStateMachine\Event\NullEventDispatcher;
10
use Dflydev\FiniteStateMachine\Event\Applied;
11
use Dflydev\FiniteStateMachine\Event\Started;
12
use Dflydev\FiniteStateMachine\Event\TransitionEventDispatcher;
13
use Dflydev\FiniteStateMachine\Graph\Graph;
14
use Dflydev\FiniteStateMachine\Guard\GuardCollection;
15
use Dflydev\FiniteStateMachine\State\State;
16
use Dflydev\FiniteStateMachine\Contracts\ObjectProxy;
17
use Dflydev\FiniteStateMachine\State\StateCollection;
18
use Dflydev\FiniteStateMachine\Transition\Transition;
19
use Dflydev\FiniteStateMachine\Transition\TransitionCollection;
20
21
class FiniteStateMachine implements FiniteStateMachineContract
22
{
23
    private Graph $graph;
24
    private ObjectProxy $objectProxy;
25
    private StateCollection $stateCollection;
26
    private TransitionCollection $transitionCollection;
27
    private GuardCollection $guardCollection;
28
    private EventDispatcher $eventDispatcher;
29
30 19
    public function __construct(
31
        ObjectProxy $objectProxy,
32
        Graph $graph,
33
        ?EventDispatcher $eventDispatcher = null
34
    ) {
35 19
        $this->graph = $graph;
36 19
        $this->objectProxy = $objectProxy;
37 19
        $this->stateCollection = $graph->stateCollection();
38 19
        $this->transitionCollection = $graph->transitionCollection();
39 19
        $this->guardCollection = $graph->guardCollection();
40 19
        $this->eventDispatcher = new TransitionEventDispatcher(
41 19
            $eventDispatcher ?? new NullEventDispatcher(),
42 19
            $graph->transitionEventCollection()
43
        );
44 19
    }
45
46 1
    public function graph(): Graph
47
    {
48 1
        return $this->graph;
49
    }
50
51 5
    public function can(string $transition): bool
52
    {
53 5
        $resolvedTransition = $this->resolveTransition($transition);
54
55 5
        $currentState = $this->currentState();
56
57 5
        if ($resolvedTransition->cannotTransitionFrom($currentState)) {
58
            return false;
59
        }
60
61 5
        $toState = $this->stateCollection->named($resolvedTransition->toStateName());
62
63 5
        if ($this->guardCollection->cannot($this->objectProxy->object(), $resolvedTransition, $currentState, $toState)) {
64 1
            return false;
65
        }
66
67 4
        return true;
68
    }
69
70 8
    public function apply(string $transition): void
71
    {
72 8
        $resolvedTransition = $this->resolveTransition($transition);
73
74 8
        $currentState = $this->currentState();
75
76 8
        if ($resolvedTransition->cannotTransitionFrom($currentState)) {
77
            throw new \RuntimeException(sprintf('Cannot apply transition "%s"', $resolvedTransition->name()));
78
        }
79
80 8
        $toState = $this->stateCollection->named($resolvedTransition->toStateName());
81
82 8
        if ($this->guardCollection->cannot($this->objectProxy->object(), $resolvedTransition, $currentState, $toState)) {
83 1
            throw new \RuntimeException(sprintf('Cannot apply transition "%s"', $resolvedTransition->name()));
84
        }
85
86 7
        $this->eventDispatcher->dispatch(new Started(
87 7
            $this->graph,
88 7
            $this->objectProxy->object(),
89
            $resolvedTransition,
90
            $currentState,
91
            $toState
92
        ));
93
94 7
        $this->objectProxy->apply(
95 7
            $resolvedTransition,
96
            $currentState,
97
            $toState
98
        );
99
100 7
        $this->eventDispatcher->dispatch(new Applied(
101 7
            $this->graph,
102 7
            $this->objectProxy->object(),
103
            $resolvedTransition,
104
            $currentState,
105
            $toState
106
        ));
107 7
    }
108
109 14
    public function currentState(): State
110
    {
111 14
        $currentState = $this->objectProxy->state();
112
113 14
        if (is_null($currentState)) {
114
            throw new \RuntimeException('No state currently set');
115
        }
116
117 14
        return $this->stateCollection->named($currentState);
118
    }
119
120 1
    public function allStates(): StateCollection
121
    {
122 1
        return $this->stateCollection;
123
    }
124
125 1
    public function state(string $name): State
126
    {
127 1
        return $this->stateCollection->named($name);
128
    }
129
130 1
    public function allTransitions(): TransitionCollection
131
    {
132 1
        return $this->transitionCollection;
133
    }
134
135 1
    public function transition(string $name): Transition
136
    {
137 1
        return $this->transitionCollection->named($name);
138
    }
139
140 8
    public function availableTransitions(): TransitionCollection
141
    {
142 8
        return $this->transitionCollection->fromState($this->currentState());
143
    }
144
145
    /**
146
     * @param Transition|string $transitionOrTransitionName
147
     */
148 13
    private function resolveTransition($transitionOrTransitionName): Transition
149
    {
150 13
        if ($transitionOrTransitionName instanceof Transition) {
151
            return $transitionOrTransitionName;
152
        }
153
154 13
        return $this->transitionCollection->named($transitionOrTransitionName);
155
    }
156
}
157