Completed
Push — master ( 75ec1b...02ab52 )
by Carlos C
02:40
created

Enum::overrideIndices()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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