GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 02ca20...d0e3db )
by Rolf
05:30 queued 03:14
created

Adapter::setState()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 6
Bugs 0 Features 0
Metric Value
c 6
b 0
f 0
dl 0
loc 13
ccs 5
cts 5
cp 1
rs 9.4285
cc 2
eloc 6
nc 2
nop 3
crap 2
1
<?php
2
namespace izzum\statemachine\persistence;
3
use izzum\statemachine\Identifier;
4
use izzum\statemachine\Exception;
5
use izzum\statemachine\State;
6
use izzum\statemachine\Transition;
7
use izzum\statemachine\utils\Utils;
8
9
/**
10
 * The abstract class Adapter is responsible for adapting any code written to access
11
 * different persistence backends to the targeted php extensions and vendor databases. 
12
 * In essence subclasses will be data access objects (https://en.wikipedia.org/wiki/Data_access_object)
13
 * 
14
 * This class serves as a base class for access to different type of persistence
15
 * layers we might want to use to store the states for stateful entities.
16
 * for example: relational (postgres/mysql) databases, nosql databases, php
17
 * sessions, files, memory, mongodb, redis etc..
18
 *
19
 * It also acts as a central place to store logic related to this persistance
20
 * layer, which might be useful when you want to get statistics from the
21
 * persistence layer and make it available to your application code.
22
 *
23
 * all data access logic should be centralized in a subclass of adapter
24
 * logic such as:
25
 * - history of all transitions (when did an entity for a statemachine
26
 * transition?)
27
 * - the retrieval of the definition of transitions
28
 * - the retrieval of data (like state, rules and commands) to be able to check/execute them
29
 *      via a form (with input of machine and entity_id). see the persistence/Tooling interface.
30
 *
31
 * A specific type of persistence adapter can be coupled (via the
32
 * AbstractFactory) to a specific implementation of a statemachine.
33
 *
34
 * This abstract Adapter class has no side effects (like
35
 * database/file/session/memory writes etc). Subclasses will probably have side
36
 * effects.
37
 *
38
 * Adapter is a base class that defines the algorithm outline for reading and
39
 * writing a state. It implements hooks to allow subclasses to alter the base
40
 * implementation.
41
 *
42
 * This class is a helper class for Context. Context delegates reading and
43
 * writing states to this class and it's subclasses.
44
 * 
45
 * @link https://en.wikipedia.org/wiki/Data_access_object
46
 * 
47
 * @author Rolf Vreijdenberger
48
 */
49
abstract class Adapter {
50
51
    /**
52
     * Get all the entity id's for a specific statemachine that have been persisted
53
     * All entity id's in all states are returned unless a specific
54
     * state is given via the optional parameter.
55
     *
56
     * This method will be highly useful when you want to select a batch of
57
     * entities to feed to the statemachine, for example in a cron job or a
58
     * message queue.
59
     *
60
     * @param string $machine
61
     *            the name of the machine
62
     * @param string $state
63
     *            optional: if provided, only those entities in the specific state
64
     * @return string[] an array of entity_id's
65
     */
66
    abstract public function getEntityIds($machine, $state = null);
67
68
    /**
69
     * A template method to be able to process the setting of the current state.
70
     * saves an object to a storage facility (either insert or update).
71
     * Implement this method for specifying how you want to set a state in the
72
     * storage facility.
73
     *
74
     * A storage facility could store a timestamp and the state the transition
75
     * was made to, for extra statistical information.
76
     * 
77
     * this method is public to be able to call it via the ReaderWriterDelegator
78
     *
79
     * @param Identifier $identifier            
80
     * @param string $state            
81
     * @return boolean true if just added to storage, false if stored before
82
     */
83 19
    public function processSetState(Identifier $identifier, $state, $message = null) 
84
    {
85 19
        if ($this->isPersisted($identifier)) {
86 15
            $this->addHistory($identifier, $state, $message);
87 15
            $this->updateState($identifier, $state, $message);
88 15
            return false;
89
        } else {
90 14
            $this->addHistory($identifier, $state, $message);
91 14
            $this->insertState($identifier, $state, $message);
92 14
            return true;
93
        }
94
    }
95
    
96
    /**
97
     * Adds a history record for a transition
98
     *
99
     * @param Identifier $identifier
100
     * @param string $state
101
     * @param mixed $message string or array/object with relevant fields.
102
     *            an optional message (which might be exception data or not).
103
     * @param boolean $is_exception
104
     *            an optional value, specifying if there was something
105
     *            exceptional or not.
106
     *            this can be used to signify an exception for storage in the
107
     *            backend so we can analyze the history
108
     *            for regular transitions and failed transitions
109
     * @throws Exception
110
     */
111
    protected function addHistory(Identifier $identifier, $state, $message = null, $is_exception = false)
112
    {
113
        //override in subclasses if needed
114
    }
115
    
116
    /**
117
     * insert state for Identifier into persistance layer.
118
     * 
119
     * @param Identifier $identifier            
120
     * @param string $state            
121
     */
122
    protected function insertState(Identifier $identifier, $state, $message = null)
123
    {
124
        //override in subclasses
125
    }
126
    
127
    /**
128
     * update state for statemachine/entity into persistance layer
129
     * @param Identifier $identifier
130
     * @param string $state
131
     * @throws Exception
132
     */
133
     protected function updateState(Identifier $identifier, $state, $message = null)
134
     {
135
         //override in subclasses
136
     }
137
138
    /**
139
     * A hook to be able to process the getting of the current state.
140
     * Implement this method for specifying how you want to get a state from a
141
     * storage facility.
142
     * 
143
     * this method is public to be able to call it via the ReaderWriterDelegator
144
     *
145
     * @param Identifier $identifier            
146
     * @return string the current state of the entity represented in the context
147
     */
148
    abstract public function processGetState(Identifier $identifier);
149
    
150
    /**
151
     * is the state information already persisted?
152
     * 
153
     * @param Identifier $identifier            
154
     * @return boolean
155
     * @throws Exception
156
     */
157
    abstract public function isPersisted(Identifier $identifier);
158
159
    /**
160
     * Adds state information to the persistence layer so
161
     * it can be manipulated by the statemachine package.
162
     * It adds a record to the underlying implementation about when the stateful
163
     * object was first generated/manipulated.
164
     *
165
     * This method can safely be called multiple times. It will only add data
166
     * when there is no state information already present.
167
     *
168
     * This template method creates a record in the underlying persistence layer where
169
     * it's initial state is set.
170
     * It can then be manipulated via other methods via this Adapter or via
171
     * the statemachine itself eg: via 'getEntityIds' etc.
172
     *
173
     * @param Identifier $identifier            
174
     * @param string $state
175
     *            the initial state to set, which should be known to
176
     *            the client of the statemachine the first time a machine is
177
     *            created.
178
     *            this can also be retrieved via a loaded statemachine:
179
     *            $machine->getInitialState()->getName()
180
     * @param string $message optional message. this can be used by the persistence adapter
181
     *          to be part of the transition history to provide extra information about the transition.
182
     * @return boolean true if it was added, false if it was already there.
183
     * @throws Exception
184
     */
185 8
    public function add(Identifier $identifier, $state, $message = null)
186
    {
187 8
        if ($this->isPersisted($identifier)) {
188 5
            return false;
189
        }
190 8
        $this->addHistory($identifier, $state, $message);
191 8
        $this->insertState($identifier, $state, $message);
192 8
        return true;
193
    }
194
195
    /**
196
     * Get the current state for an Identifier (machine/id)
197
     * 
198
     * A template method.
199
     *
200
     * @param Identifier $identifier            
201
     * @return string the state
202
     * @throw Exception
203
     */
204 23
    public function getState(Identifier $identifier)
205
    {
206
        try {
207
            // execute a hook that should be implemented in a subclass.
208
            // the subclass could return STATE_UNKNOWN if it is not already
209
            // added to the storage backend or it might throw an exception warning the 
210
            //end user that he should 'StateMachine::add' to the backend first
211 23
            $state = $this->processGetState($identifier);
212 22
            return $state;
213 1
        } catch(\Exception $e) {
214 1
            $e = Utils::wrapToStateMachineException($e, Exception::IO_FAILURE_GET);
215 1
            throw $e;
216
        }
217
    }
218
219
    /**
220
     * Sets the new state for an Identifier in the storage facility.
221
     * Will only be called by the statemachine.
222
     * A template method.
223
     *
224
     * @param Identifier $identifier
225
     *            (old state can be retrieved via the identifier and this class)
226
     * @param string $state
227
     *            this is the new state
228
     * @param string $message optional message. this can be used by the persistence adapter
229
     *          to be part of the transition history to provide extra information about the transition.
230
     * @return boolan false if already stored before, true if just added
231
     * @throws Exception
232
     */
233 21
    public function setState(Identifier $identifier, $state, $message = null)
234
    {
235
        try {
236
            // a subclass could map a state to
237
            // something else that is used internally in legacy
238
            // systems (eg: order.order_status)
239 21
            return $this->processSetState($identifier, $state, $message);
240 1
        } catch(\Exception $e) {
241
            // a possible lowlevel nonstatemachine exception, wrap it and throw
242 1
            $e = Utils::wrapToStateMachineException($e, Exception::IO_FAILURE_SET);
243 1
            throw $e;
244
        }
245
    }
246
247
    /**
248
     * A template method that Stores a failed transition in the storage facility for
249
     * historical/analytical purposes.
250
     *
251
     * @param Identifier $identifier            
252
     * @param Transition $transition            
253
     * @param \Exception $e            
254
     */
255 3
    public function setFailedTransition(Identifier $identifier, Transition $transition, \Exception $e)
256
    {
257
        // check if it is persisted, otherwise we cannot get the current state
258 3
        $message = new \stdClass();
259 3
        $message->code = $e->getCode();
260 3
        $message->transition = $transition->getName();
261 3
        $message->message = $e->getMessage();
262 3
        $message->file = $e->getFile();
263 3
        $message->line = $e->getLine();
264 3
        if ($this->isPersisted($identifier)) {
265
            /*
266
            a transition can fail even after a state has been set in the transition process,
267
            for example when executing the code in the entry action of the new state,
268
            making the transition partly failed.
269
            the history will then show a succesful transition to the new state first,
270
            and here we will then add the failure of the transition with the current state (which is the 'to' state of the transition)
271
            and with the failure message.
272
            In case that the transition failed before the state has been set
273
            then this will be put in the history of transitions with the 'from' state as the current state.
274
            */
275 1
            $state = $this->getState($identifier);
276 1
        } else {
277
            //no current state available in persistence layer.
278
            //this is exceptional and should not happen when configured correctly and
279
            //if the machine has been 'added' or if a transition has been (partly) mande.
280
            //therefore, it must be the from state.. 
281 2
            $state = $transition->getStateFrom()->getName();
282
        }
283 3
        $message->state = $state;
284 3
        $this->addHistory($identifier, $state, $message, true);
285 3
    }
286
287 3
    public function toString()
288
    {
289 3
        return get_class($this);
290
    }
291
292 1
    public function __toString()
293
    {
294 1
        return $this->toString();
295
    }
296
}
297