-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathStateMachine.php
More file actions
127 lines (106 loc) · 3.61 KB
/
StateMachine.php
File metadata and controls
127 lines (106 loc) · 3.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<?php declare(strict_types=1);
/**
* This file is part of the php-state project.
*
* (c) Yannick Voyer (http://github.com/yvoyer)
*/
namespace Star\Component\State;
use Closure;
use Star\Component\State\Callbacks\AlwaysThrowExceptionOnFailure;
use Star\Component\State\Callbacks\TransitionCallback;
use Star\Component\State\Event\StateEventStore;
use Star\Component\State\Event\TransitionWasFailed;
use Star\Component\State\Event\TransitionWasSuccessful;
use Star\Component\State\Event\TransitionWasRequested;
final class StateMachine
{
private EventRegistry $listeners;
private StateRegistry $states;
private string $currentState;
public function __construct(
string $currentState,
StateRegistry $states,
EventRegistry $listeners
) {
$this->listeners = $listeners;
$this->states = $states;
$this->setCurrentState($currentState);
}
/**
* @param string $transitionName The transition name
* @param string|object $context
* @param TransitionCallback|null $callback
*
* @return string The next state to store on your context
* @throws InvalidStateTransitionException
* @throws NotFoundException
*/
public function transit(
string $transitionName,
mixed $context,
?TransitionCallback $callback = null
): string {
if (!$callback) {
$callback = new AlwaysThrowExceptionOnFailure();
}
$this->listeners->dispatch(
StateEventStore::BEFORE_TRANSITION,
new TransitionWasRequested($transitionName)
);
$transition = $this->states->getTransition($transitionName);
$callback->beforeStateChange($context, $this);
$newState = $transition->getDestinationState();
$allowed = $this->states->transitionStartsFrom($transitionName, $this->currentState);
if (!$allowed) {
$exception = InvalidStateTransitionException::notAllowedTransition(
$transitionName,
$context,
$this->currentState
);
$this->listeners->dispatch(
StateEventStore::FAILURE_TRANSITION,
new TransitionWasFailed($transitionName, $exception)
);
$newState = $callback->onFailure($exception, $context, $this);
}
$this->setCurrentState($newState);
$callback->afterStateChange($context, $this);
$this->listeners->dispatch(
StateEventStore::AFTER_TRANSITION,
new TransitionWasSuccessful($transitionName)
);
return $this->currentState;
}
/**
* @param string $stateName
* @return bool
* @throws NotFoundException
*/
public function isInState(string $stateName): bool
{
if (!$this->states->hasState($stateName)) {
throw NotFoundException::stateNotFound($stateName);
}
return $this->currentState === $stateName;
}
public function hasAttribute(string $attribute): bool
{
return $this->states->hasAttribute($this->currentState, $attribute);
}
public function addListener(string $event, Closure $listener): void
{
$this->listeners->addListener($event, $listener);
}
public function acceptTransitionVisitor(TransitionVisitor $visitor): void
{
$this->states->acceptTransitionVisitor($visitor);
}
public function acceptStateVisitor(StateVisitor $visitor): void
{
$this->states->acceptStateVisitor($visitor);
}
private function setCurrentState(string $state): void
{
$this->currentState = $state;
}
}