Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
HasTransitions
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
8 / 8
12
100.00% covered (success)
100.00%
1 / 1
 transitionMap
n/a
0 / 0
n/a
0 / 0
0
 initialStateCases
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 allowedTransitions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 canTransitionTo
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 assertCanTransitionTo
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 isTerminal
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isInitial
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 initialStates
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 terminalStates
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/**
6 * Ergonomic utilities for PHP enums, including names, values, lookups, and option maps.
7 *
8 * This file is part of fast-forward/enum project.
9 *
10 * @author   Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
11 * @license  https://opensource.org/licenses/MIT MIT License
12 *
13 * @see      https://github.com/php-fast-forward/enum
14 * @see      https://github.com/php-fast-forward/enum/issues
15 * @see      https://php-fast-forward.github.io/enum/
16 * @see      https://datatracker.ietf.org/doc/html/rfc2119
17 */
18
19namespace FastForward\Enum\StateMachine;
20
21trait HasTransitions
22{
23    /**
24     * @return array<string, list<self>>
25     */
26    abstract protected static function transitionMap(): array;
27
28    /**
29     * @return list<self>
30     */
31    protected static function initialStateCases(): array
32    {
33        return [];
34    }
35
36    /**
37     * @return list<self>
38     */
39    public function allowedTransitions(): array
40    {
41        return self::transitionMap()[$this->name] ?? [];
42    }
43
44    /**
45     * @param self $target
46     *
47     * @return bool
48     */
49    public function canTransitionTo(self $target): bool
50    {
51        return \in_array($target, $this->allowedTransitions(), true);
52    }
53
54    /**
55     * @param self $target
56     *
57     * @return void
58     */
59    public function assertCanTransitionTo(self $target): void
60    {
61        if (! $this->canTransitionTo($target)) {
62            throw InvalidTransitionException::between($this, $target);
63        }
64    }
65
66    /**
67     * @return bool
68     */
69    public function isTerminal(): bool
70    {
71        return [] === $this->allowedTransitions();
72    }
73
74    /**
75     * @return bool
76     */
77    public function isInitial(): bool
78    {
79        return \in_array($this, self::initialStates(), true);
80    }
81
82    /**
83     * @return list<self>
84     */
85    public static function initialStates(): array
86    {
87        $initialStates = static::initialStateCases();
88
89        if ([] !== $initialStates) {
90            return $initialStates;
91        }
92
93        $incoming = [];
94
95        foreach (self::transitionMap() as $targets) {
96            foreach ($targets as $target) {
97                $incoming[$target->name] = true;
98            }
99        }
100
101        return array_values(array_filter(
102            self::cases(),
103            static fn(self $case): bool => ! isset($incoming[$case->name]),
104        ));
105    }
106
107    /**
108     * @return list<self>
109     */
110    public static function terminalStates(): array
111    {
112        return array_values(array_filter(self::cases(), static fn(self $case): bool => $case->isTerminal()));
113    }
114}