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 — develop ( 50cef7...02ca20 )
by Rolf
01:54
created

Transition::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
namespace izzum\statemachine;
3
use izzum\command\NullCommand;
4
use izzum\rules\TrueRule;
5
use izzum\statemachine\Exception;
6
use izzum\statemachine\utils\Utils;
7
use izzum\statemachine\Context;
8
use izzum\rules\Rule;
9
use izzum\rules\AndRule;
10
use izzum\rules\izzum\rules;
11
use izzum\rules\IRule;
12
13
/**
14
 * Transition class
15
 * An abstraction for everything that is needed to make an allowed and succesful
16
 * transition between states.
17
 *
18
 * It has functionality to accept a Rule (guard logic) and a Command (transition
19
 * logic) as well as callables for the guard logic and transition logic .
20
 * callables are: closures, anonymous functions, user defined functions,
21
 * instance methods, static methods etc. see the php manual.
22
 *
23
 * The guards are used to check whether a transition can take place (Rule and callable)
24
 * The logic parts are used to execute the transition logic (Command and callable)
25
 *
26
 * Rules and commands should be able to be found/autoloaded by the application
27
 *
28
 * If transitions share the same states (both to and from) then they should
29
 * point to the same object reference (same states should share the exact same state
30
 * configuration).
31
 *
32
 * @link https://php.net/manual/en/language.types.callable.php
33
 * @link https://en.wikipedia.org/wiki/Command_pattern
34
 * @author Rolf Vreijdenberger
35
 *
36
 */
37
class Transition {
38
    const RULE_TRUE = '\izzum\rules\TrueRule';
39
    const RULE_FALSE = '\izzum\rules\FalseRule';
40
    const RULE_EMPTY = '';
41
    const COMMAND_NULL = '\izzum\command\NullCommand';
42
    const COMMAND_EMPTY = '';
43
    const CALLABLE_NULL = null;
44
45
    /**
46
     * the state this transition starts from
47
     *
48
     * @var State
49
     */
50
    protected $state_from;
51
52
    /**
53
     * the state this transition points to
54
     *
55
     * @var State
56
     */
57
    protected $state_to;
58
59
    /**
60
     * an event code that can trigger this transitions
61
     *
62
     * @var string
63
     */
64
    protected $event;
65
66
    /**
67
     * The fully qualified Rule class name of the
68
     * Rule to be applied to check if we can transition.
69
     * This can actually be a ',' seperated string of multiple rules.
70
     *
71
     * @var string
72
     */
73
    protected $rule;
74
75
    /**
76
     * the fully qualified Command class name of the Command to be
77
     * executed as part of the transition logic.
78
     * This can actually be a ',' seperated string of multiple commands.
79
     *
80
     * @var string
81
     */
82
    protected $command;
83
84
    /**
85
     * the callable to call as part of the transition logic
86
     * @var callable
87
     */
88
    protected $callable_transition;
89
90
    /**
91
     * the callable to call as part of the transition guard (should return a boolean)
92
     * @var callable
93
     */
94
    protected $callable_guard;
95
96
    /**
97
     * a description for the state
98
     *
99
     * @var string
100
     */
101
    protected $description;
102
103
    /**
104
     *
105
     * @param State $state_from
106
     * @param State $state_to
107
     * @param string $event
108
     *            optional: an event name by which this transition can be
109
     *            triggered
110
     * @param string $rule
111
     *            optional: one or more fully qualified Rule (sub)class name(s)
112
     *            to check to see if we are allowed to transition.
113
     *            This can actually be a ',' seperated string of multiple rules
114
     *            that will be applied as a chained 'and' rule.
115
     * @param string $command
116
     *            optional: one or more fully qualified Command (sub)class
117
     *            name(s) to execute for a transition.
118
     *            This can actually be a ',' seperated string of multiple
119
     *            commands that will be executed as a composite.
120
     * @param callable $callable_guard
121
     *            optional: a php callable to call. eg: "function(){echo 'closure called';};"
122
     * @param callable $callable_transition
123
     *            optional: a php callable to call. eg: "izzum\MyClass::myStaticMethod"
124
     */
125 63
    public function __construct(State $state_from, State $state_to, $event = null, $rule = self::RULE_EMPTY, $command = self::COMMAND_EMPTY, $callable_guard = self::CALLABLE_NULL, $callable_transition = self::CALLABLE_NULL)
126
    {
127 63
        $this->state_from = $state_from;
128 63
        $this->state_to = $state_to;
129 63
        $this->setRuleName($rule);
130 63
        $this->setCommandName($command);
131 63
        $this->setGuardCallable($callable_guard);
0 ignored issues
show
Bug introduced by
It seems like $callable_guard defined by parameter $callable_guard on line 125 can also be of type null; however, izzum\statemachine\Transition::setGuardCallable() does only seem to accept callable, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
132 63
        $this->setTransitionCallable($callable_transition);
0 ignored issues
show
Bug introduced by
It seems like $callable_transition defined by parameter $callable_transition on line 125 can also be of type null; however, izzum\statemachine\Trans...setTransitionCallable() does only seem to accept callable, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
133
        // setup bidirectional relationship with state this transition
134
        // originates from. only if it's not a regex or final state type
135 63
        if (!$state_from->isRegex() && !$state_from->isFinal()) {
136 63
            $state_from->addTransition($this);
137 63
        }
138
        // set and sanitize event name
139 63
        $this->setEvent($event);
140 63
    }
141
142
    /**
143
     * the callable to call as part of the transition logic
144
     * @param callable $callable
145
     */
146 63
    public function setTransitionCallable($callable) {
147 63
        $this->callable_transition = $callable;
148 63
        return $this;
149
    }
150
151
    /**
152
     * returns the callable for the transition logic.
153
     * @return callable or null
154
     */
155 34
    public function getTransitionCallable()
156
    {
157 34
        return $this->callable_transition;
158
    }
159
160
    /**
161
     * the callable to call as part of the transition guard
162
     * @param callable $callable
163
     */
164 63
    public function setGuardCallable($callable) {
165 63
        $this->callable_guard = $callable;
166 63
        return $this;
167
    }
168
169
    /**
170
     * returns the callable for the guard logic.
171
     * @return callable or null
172
     */
173 32
    public function getGuardCallable()
174
    {
175 32
        return $this->callable_guard;
176
    }
177
178
    /**
179
     * Can this transition be triggered by a certain event?
180
     * This also matches on the transition name.
181
     *
182
     * @param string $event
183
     * @return boolean
184
     */
185 12
    public function isTriggeredBy($event)
186
    {
187 12
        return ($this->event === $event || $this->getName() === $event) && $event !== null && $event !== '';
188
    }
189
190
    /**
191
     * is a transition possible? Check the guard Rule with the domain object
192
     * injected.
193
     *
194
     * @param Context $context
195
     * @return boolean
196
     */
197 22
    public function can(Context $context)
198
    {
199
        try {
200 22
            if(!$this->getRule($context)->applies()) {
201 5
                return false;
202
            }
203 19
            return $this->callCallable($this->getGuardCallable(), $context);
204 3
        } catch(\Exception $e) {
205 3
            $e = new Exception($this->toString() . ' '. $e->getMessage(), Exception::RULE_APPLY_FAILURE, $e);
206 3
            throw $e;
207
        }
208
    }
209
210
    /**
211
     * Process the transition for the statemachine and execute the associated
212
     * Command with the domain object injected.
213
     *
214
     * @param Context $context
215
     * @return void
216
     */
217 22
    public function process(Context $context)
218
    {
219
        // execute, we do not need to check if we 'can' since this is done
220
        // by the statemachine itself
221
        try {
222 22
            $this->getCommand($context)->execute();
223 21
            $this->callCallable($this->getTransitionCallable(), $context);
224 22
        } catch(\Exception $e) {
225
            // command failure
226 1
            $e = new Exception($e->getMessage(), Exception::COMMAND_EXECUTION_FAILURE, $e);
227 1
            throw $e;
228
        }
229 21
    }
230
231
    /**
232
     * calls the $callable as part of the transition
233
     * @param callable $callable
234
     * @param Context $context
235
     */
236 23
    protected function callCallable($callable, Context $context) {
237
        //in case it is a guard callable we need to return true/false
238 23
        if($callable != self::CALLABLE_NULL && is_callable($callable)){
239 8
            return (boolean) call_user_func($callable, $context->getEntity());
240
        }
241 21
        return true;
242
    }
243
244
    /**
245
     * returns the associated Rule for this Transition,
246
     * configured with a 'reference' (stateful) object
247
     *
248
     * @param Context $context
249
     *            the associated Context for a our statemachine
250
     * @return IRule a Rule or chained AndRule if the rule input was a ','
251
     *         seperated string of rules.
252
     * @throws Exception
253
     */
254 29
    public function getRule(Context $context)
255
    {
256
        // if no rule is defined, just allow the transition by default
257 29
        if ($this->rule === '' || $this->rule === null) {
258 12
            return new TrueRule();
259
        }
260
261 22
        $entity = $context->getEntity();
262
263
        // a rule string can be made up of multiple rules seperated by a comma
264 22
        $all_rules = explode(',', $this->rule);
265 22
        $rule = new TrueRule();
266 22
        foreach ($all_rules as $single_rule) {
267
268
            // guard clause to check if rule exists
269 22
            if (!class_exists($single_rule)) {
270 1
                $e = new Exception(sprintf("failed rule creation, class does not exist: (%s) for Context (%s).", $this->rule, $context->toString()), Exception::RULE_CREATION_FAILURE);
271 1
                throw $e;
272
            }
273
274
            try {
275 21
                $and_rule = new $single_rule($entity);
276
                // create a chain of rules that need to be true
277 20
                $rule = new AndRule($rule, $and_rule);
278 21
            } catch(\Exception $e) {
279 1
                $e = new Exception(sprintf("failed rule creation, class objects to construction with entity: (%s) for Context (%s). message: %s", $this->rule, $context->toString(), $e->getMessage()), Exception::RULE_CREATION_FAILURE);
280 1
                throw $e;
281
            }
282 20
        }
283 20
        return $rule;
284
    }
285
286
287
    /**
288
     * returns the associated Command for this Transition.
289
     * the Command will be configured with the 'reference' of the stateful
290
     * object.
291
     * In case there have been multiple commands as input (',' seperated), this
292
     * method will return a Composite command.
293
     *
294
     * @param Context $context
295
     * @return izzum\command\ICommand
296
     * @throws Exception
297
     */
298 29
    public function getCommand(Context $context)
299
    {
300 29
        return Utils::getCommand($this->command, $context);
301
    }
302
303
    /**
304
     *
305
     * @return string
306
     */
307 7
    public function toString()
308
    {
309 7
        return get_class($this) . " '" . $this->getName() . "' [event]: '" . $this->event . "'" . " [rule]: '" . $this->rule . "' [command]: '" . $this->command . "'";
310
    }
311
312
    /**
313
     * get the state this transition points from
314
     *
315
     * @return State
316
     */
317 63
    public function getStateFrom()
318
    {
319 63
        return $this->state_from;
320
    }
321
322
    /**
323
     * get the state this transition points to
324
     *
325
     * @return State
326
     */
327 63
    public function getStateTo()
328
    {
329 63
        return $this->state_to;
330
    }
331
332
    /**
333
     * get the transition name.
334
     * the transition name is always unique for a statemachine
335
     * since it constists of <state_from>_to_<state_to>
336
     *
337
     * @return string
338
     */
339 63
    public function getName()
340
    {
341 63
        $name = Utils::getTransitionName($this->getStateFrom()->getName(), $this->getStateTo()->getName());
342 63
        return $name;
343
    }
344
345
    /**
346
     * return the command name(s).
347
     * one or more fully qualified command (sub)class name(s) to execute for a
348
     * transition.
349
     * This can actually be a ',' seperated string of multiple commands that
350
     * will be executed as a composite.
351
     *
352
     * @return string
353
     */
354 21
    public function getCommandName()
355
    {
356 21
        return $this->command;
357
    }
358
359 63
    public function setCommandName($command)
360
    {
361 63
        $this->command = trim($command);
362 63
        return $this;
363
    }
364
365 21
    public function getRuleName()
366
    {
367 21
        return $this->rule;
368
    }
369
370 63
    public function setRuleName($rule)
371
    {
372 63
        $this->rule = trim($rule);
373 63
        return $this;
374
    }
375
376
    /**
377
     * set the description of the transition (for uml generation for example)
378
     *
379
     * @param string $description
380
     */
381 23
    public function setDescription($description)
382
    {
383 23
        $this->description = $description;
384 23
        return $this;
385
    }
386
387
    /**
388
     * get the description for this transition (if any)
389
     *
390
     * @return string
391
     */
392 22
    public function getDescription()
393
    {
394 22
        return $this->description;
395
    }
396
397
    /**
398
     * set the event name by which this transition can be triggered.
399
     * In case the event name is null or an empty string, it defaults to the
400
     * transition name.
401
     *
402
     * @param string $event
403
     */
404 63
    public function setEvent($event)
405
    {
406 63
        if ($event === null || $event === '') {
407 43
            $event = $this->getName();
408 43
        }
409 63
        $this->event = $event;
410 63
        return $this;
411
    }
412
413
    /**
414
     * get the event name by which this transition can be triggered
415
     *
416
     * @return string
417
     */
418 22
    public function getEvent()
419
    {
420 22
        return $this->event;
421
    }
422
423
    /**
424
     * for transitions that contain regex states, we need to be able to copy an
425
     * existing (subclass of this) transition with all it's fields.
426
     * We need to instantiate it with a different from and to state since either
427
     * one of those states can be the regex states. All other fields need to be
428
     * copied.
429
     *
430
     * Override this method in a subclass to add other fields. By using 'new
431
     * static' we are already instantiating a possible subclass.
432
     *
433
     *
434
     * @param State $from
435
     * @param State $to
436
     * @return Transition
437
     */
438 19
    public function getCopy(State $from, State $to)
439
    {
440 19
        $copy = new static($from, $to, $this->getEvent(), $this->getRuleName(), $this->getCommandName(), $this->getGuardCallable(), $this->getTransitionCallable());
441 19
        $copy->setDescription($this->getDescription());
442 19
        return $copy;
443
    }
444
445
    /**
446
     *
447
     * @return string
448
     */
449 1
    public function __toString()
450
    {
451 1
        return $this->getName();
452
    }
453
}