Test Failed
Push — fix-build-20220718 ( be153b )
by Carlos C
02:14
created

Enum::currentEntries()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 12
ccs 5
cts 5
cp 1
rs 10
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Eclipxe\Enum;
6
7
use Eclipxe\Enum\Exceptions\BadMethodCallException;
8
use Eclipxe\Enum\Exceptions\EnumConstructTypeError;
9
use Eclipxe\Enum\Exceptions\IndexNotFoundException;
10
use Eclipxe\Enum\Exceptions\ValueNotFoundException;
11
use Eclipxe\Enum\Internal\Entries;
12
use Eclipxe\Enum\Internal\EntriesPopulator;
13
use Eclipxe\Enum\Internal\Entry;
14
use Throwable;
15
16
abstract class Enum
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_ENUM, expecting T_STRING on line 16 at column 15
Loading history...
17
{
18
    /** @var Entry */
19
    private $content;
20
21
    final public function value(): string
22
    {
23
        return $this->content->value();
24
    }
25
26
    final public function index(): int
27
    {
28
        return $this->content->index();
29
    }
30
31
    final public function __toString(): string
32
    {
33
        return $this->content->value();
34
    }
35
36
    /**
37
     * Enum constructor.
38
     *
39
     * @param string|int|mixed $valueOrIndex
40
     *
41
     * @throws IndexNotFoundException if received argument is an integer and it was not found in indices
42
     * @throws ValueNotFoundException if received argument is a string and it was not found in values
43
     * @throws EnumConstructTypeError if received argument is not an integer, string, scalar or object
44
     */
45
    final public function __construct($valueOrIndex)
46
    {
47
        // convert to string if object
48
        if (is_object($valueOrIndex)) {
49
            try {
50
                $valueOrIndex = strval($valueOrIndex);
51
            } catch (Throwable $exception) {
52
                throw EnumConstructTypeError::create(static::class, $exception);
53
            }
54
        }
55
56
        if (is_int($valueOrIndex)) { // is index
57
            $entry = static::currentEntries()->findEntryByIndex($valueOrIndex);
58
            if (null === $entry) {
59
                throw IndexNotFoundException::create(static::class, strval($valueOrIndex));
60
            }
61
        } elseif (is_string($valueOrIndex)) { // is value
62
            $entry = static::currentEntries()->findEntryByValue($valueOrIndex);
63
            if (null === $entry) {
64
                throw ValueNotFoundException::create(static::class, $valueOrIndex);
65
            }
66
        } else {
67
            throw EnumConstructTypeError::create(static::class);
68
        }
69
70
        $this->content = $entry;
71
    }
72
73
    /**
74
     * @param string $name
75
     * @param mixed[] $arguments
76
     * @return mixed
77
     */
78
    public function __call(string $name, array $arguments)
79
    {
80
        if (strlen($name) > 2 && 'is' === substr($name, 0, 2)) {
81
            $entry = static::currentEntries()->findEntryByName(substr($name, 2));
82
            return (null !== $entry && $this->content->equals($entry));
83
        }
84
85
        throw BadMethodCallException::create(static::class, $name);
86
    }
87
88
    /**
89
     * @param string $name
90
     * @param mixed[] $arguments
91
     * @return static
92
     */
93
    public static function __callStatic(string $name, array $arguments)
94
    {
95
        $entry = static::currentEntries()->findEntryByName($name);
96
        if (null !== $entry) {
97
            return new static($entry->index());
98
        }
99
100
        throw BadMethodCallException::create(static::class, $name);
101
    }
102
103
    /**
104
     * Obtain the list of registered possible values as an array of indices and values
105
     *
106
     * @return array<int, string>
107
     */
108
    final public static function toArray(): array
109
    {
110
        return static::currentEntries()->toIndexValueArray();
111
    }
112
113
    /**
114
     * Method to override values
115
     *
116
     * It must return an array with key as the name declared in docblock
117
     * and value as the string value to use.
118
     *
119
     * Example:
120
     * method self extraSmall()
121
     * return ['extraSmall' => 'xs', ...];
122
     *
123
     * @return array<string, string>
124
     */
125
    protected static function overrideValues(): array
126
    {
127
        return [];
128
    }
129
130
    /**
131
     * Method to override indices
132
     *
133
     * It must return an array with key as the name declared in docblock
134
     * and value as the integer index to use.
135
     *
136
     * Example:
137
     * method self second()
138
     * return ['second' => 2, ...];
139
     *
140
     * @return array<string, int>
141
     */
142
    protected static function overrideIndices(): array
143
    {
144
        return [];
145
    }
146
147
    final protected static function currentEntries(): Entries
148
    {
149
        /*
150
         * $cache contains the list of current entries as [<class> => <entries>]
151
         * Store the information in this way because to prevent collision with parent classes
152
         */
153
        /** @var array<string, Entries> $cache */
154
        static $cache = [];
155
        if (! isset($cache[static::class])) {
156
            $cache[static::class] = self::createEntries();
157
        }
158
        return $cache[static::class];
159
    }
160
161
    final protected static function createEntries(): Entries
162
    {
163
        $populator = new EntriesPopulator(
164
            static::class,
165
            static::overrideValues(),
166
            static::overrideIndices(),
167
            static::parentEntries()
168
        );
169
        $entries = new Entries();
170
        $populator->populate($entries);
171
        return $entries;
172
    }
173
174
    final protected static function parentEntries(): Entries
175
    {
176
        $parentClass = strval(get_parent_class(static::class));
177
        // if does not have a parent class or is the base template (Enum class)
178
        if ('' === $parentClass || self::class === $parentClass) {
179
            return new Entries();
180
        }
181
        /** @var Enum $parentClass */
182
        return $parentClass::currentEntries();
183
    }
184
}
185