Passed
Push — master ( 4da179...4cded1 )
by Yannick
02:33
created

StateMachine::hasAttribute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the php-state project.
4
 *
5
 * (c) Yannick Voyer <[email protected]> (http://github.com/yvoyer)
6
 */
7
8
namespace Star\Component\State;
9
10
use Star\Component\State\Callbacks\AlwaysThrowExceptionOnFailure;
11
use Star\Component\State\Callbacks\TransitionCallback;
12
use Star\Component\State\Event\StateEventStore;
13
use Star\Component\State\Event\TransitionWasFailed;
14
use Star\Component\State\Event\TransitionWasSuccessful;
15
use Star\Component\State\Event\TransitionWasRequested;
16
use Webmozart\Assert\Assert;
17
18
final class StateMachine
19
{
20
    /**
21
     * @var EventRegistry
22
     */
23
    private $listeners;
24
25
    /**
26
     * @var StateRegistry
27
     */
28
    private $states;
29
30
    /**
31
     * @var string
32
     */
33
    private $currentState;
34
35
    /**
36
     * @param string $currentState
37
     * @param StateRegistry $states
38
     * @param EventRegistry $listeners
39
     */
40 46
    public function __construct(
41
        $currentState,
42
        StateRegistry $states,
43
        EventRegistry $listeners
44
    ) {
45 46
        Assert::string($currentState);
46 46
        $this->listeners = $listeners;
47 46
        $this->states = $states;
48 46
        $this->setCurrentState($currentState);
49 46
    }
50
51
    /**
52
     * @param string $transitionName The transition name
53
     * @param mixed $context
54
     * @param TransitionCallback|null $callback
55
     *
56
     * @return string The next state to store on your context
57
     * @throws InvalidStateTransitionException
58
     * @throws NotFoundException
59
     */
60 30
    public function transit($transitionName, $context, TransitionCallback $callback = null)
61
    {
62 30
        if (! $callback) {
63 29
            $callback = new AlwaysThrowExceptionOnFailure();
64
        }
65
66 30
        $this->listeners->dispatch(
67 30
            StateEventStore::BEFORE_TRANSITION,
68 30
            new TransitionWasRequested($transitionName)
69
        );
70
71 30
        Assert::string($transitionName);
72 30
        $transition = $this->states->getTransition($transitionName);
73 29
        $callback->beforeStateChange($context, $this);
74
75 29
        $newState = $transition->getDestinationState();
76 29
        $allowed = $this->states->transitionStartsFrom($transitionName, $this->currentState);
77 29
        if (! $allowed) {
78 11
            $exception = InvalidStateTransitionException::notAllowedTransition(
79 11
                $transitionName,
80 11
                $context,
81 11
                $this->currentState
82
            );
83
84 11
            $this->listeners->dispatch(
85 11
                StateEventStore::FAILURE_TRANSITION,
86 11
                new TransitionWasFailed($transitionName, $exception)
87
            );
88
89 11
            $newState = $callback->onFailure($exception, $context, $this);
90
        }
91 19
        Assert::string($newState);
92
93 19
        $this->setCurrentState($newState);
94
95 19
        $callback->afterStateChange($context, $this);
96
97 19
        $this->listeners->dispatch(
98 19
            StateEventStore::AFTER_TRANSITION,
99 19
            new TransitionWasSuccessful($transitionName)
100
        );
101
102 19
        return $this->currentState;
103
    }
104
105
    /**
106
     * @param string $stateName
107
     * @return bool
108
     * @throws NotFoundException
109
     */
110 34
    public function isInState($stateName)
111
    {
112 34
        Assert::string($stateName);
113 34
        if (! $this->states->hasState($stateName)) {
114 1
            throw NotFoundException::stateNotFound($stateName);
115
        }
116
117 33
        return $this->currentState === $stateName;
118
    }
119
120
    /**
121
     * @param string $attribute
122
     *
123
     * @return bool
124
     */
125 9
    public function hasAttribute($attribute)
126
    {
127 9
        Assert::string($attribute);
128 9
        return $this->states->hasAttribute($this->currentState, $attribute);
129
    }
130
131
    /**
132
     * @param string $event
133
     * @param \Closure $listener
134
     */
135
    public function addListener($event, \Closure $listener)
136
    {
137
        Assert::string($event);
138
        $this->listeners->addListener($event, $listener);
139
    }
140
141
    /**
142
     * @param TransitionVisitor $visitor
143
     */
144 3
    public function acceptTransitionVisitor(TransitionVisitor $visitor)
145
    {
146 3
        $this->states->acceptTransitionVisitor($visitor);
147 3
    }
148
149
    /**
150
     * @param StateVisitor $visitor
151
     */
152 1
    public function acceptStateVisitor(StateVisitor $visitor)
153
    {
154 1
        $this->states->acceptStateVisitor($visitor);
155 1
    }
156
157
    /**
158
     * @param string $state
159
     */
160 46
    private function setCurrentState($state)
161
    {
162 46
        Assert::string($state);
163 46
        $this->currentState = $state;
164 46
    }
165
}
166