Passed
Push — master ( 8a3186...13a545 )
by Константин
04:30
created

Enum::getPropertyValue()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 0
loc 16
ccs 10
cts 10
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Grachevko\Enum;
4
5
use BadMethodCallException;
6
use InvalidArgumentException;
7
use LogicException;
8
use ReflectionClass;
9
10
/**
11
 * @author Konstantin Grachev <[email protected]>
12
 */
13
abstract class Enum implements \Serializable
14
{
15
    /**
16
     * @var int
17
     */
18
    private $id;
19
20
    /**
21
     * @var ReflectionClass[]
22
     */
23
    private static $reflections = [];
24
25
    /**
26
     * @param int $id
27
     *
28
     * @throws InvalidArgumentException
29
     */
30 19
    public function __construct(int $id)
31
    {
32 19
        if (!\in_array($id, self::getReflection()->getConstants(), true)) {
33 1
            throw new InvalidArgumentException(sprintf('Undefined enum "%s" of class "%s"', $id, static::class));
34
        }
35
36 16
        $this->id = $id;
37 16
    }
38
39
    /**
40
     * @return string
41
     */
42
    public function __toString(): string
43
    {
44
        return (string) $this->getId();
45
    }
46
47
    /**
48
     * @param string $name
49
     * @param array  $arguments
50
     *
51
     * @throws BadMethodCallException
52
     * @throws InvalidArgumentException
53
     * @throws LogicException
54
     *
55
     * @return bool|string
56
     */
57 7
    public function __call(string $name, array $arguments)
58
    {
59 7
        $id = $this->getId();
60 7
        $reflectionClass = self::getReflection();
61 7
        $constants = $reflectionClass->getConstants();
62
63 7
        if (0 === strpos($name, 'is') && ctype_upper($name[2])) {
64 4
            $const = Utils::stringToConstant(substr($name, 2, \strlen($name)));
65
66 4
            if (!array_key_exists($const, $constants)) {
67 1
                throw new InvalidArgumentException(sprintf(
68 1
                        'Undefined constant "%s" in class "%s" to use method "%s"', $const, static::class, $name)
69
                );
70
            }
71
72 4
            return $this->eq(new static($constants[$const]));
73
        }
74
75 3
        if (0 === strpos($name, 'get') && ctype_upper($name[3])) {
76 3
            $property = lcfirst(substr($name, 3));
77
78 3
            if ($value = $this->getPropertyValue($property)) {
79 2
                return $value;
80
            }
81
82 1
            throw new LogicException(sprintf(
83 1
                'Undefined value in property "%s" for "%s" constant',
84 1
                $property,
85 1
                array_flip($constants)[$id]
86
            ));
87
        }
88
89
        throw new BadMethodCallException(sprintf('Undefined method "%s" in class "%s"', $name, static::class));
90
    }
91
92
    /**
93
     * @param string $name
94
     * @param array  $arguments
95
     *
96
     * @throws BadMethodCallException
97
     *
98
     * @return static
99
     */
100 15
    public static function __callStatic(string $name, array $arguments)
101
    {
102 15
        $const = Utils::stringToConstant($name);
103 15
        $constants = self::getReflection()->getConstants();
104
105 15
        if (array_key_exists($const, $constants)) {
106 15
            return new static($constants[$const]);
107
        }
108
109 1
        throw new BadMethodCallException(sprintf('Undefined method "%s" in class "%s"', $name, static::class));
110
    }
111
112
    /**
113
     * @return int
114
     */
115 13
    public function getId(): int
116
    {
117 13
        return $this->id;
118
    }
119
120
    /**
121
     * @return string
122
     */
123 4
    public function getName(): string
124
    {
125 4
        return $this->getPropertyValue('name')
126 4
            ?? strtolower(array_flip(self::getReflection()->getConstants())[$this->getId()]);
127
    }
128
129
    /**
130
     * @return string
131
     */
132 4
    public function getReadableName(): string
133
    {
134 4
        return mb_convert_case(str_replace('_', ' ', $this->getName()), MB_CASE_TITLE);
135
    }
136
137
    /**
138
     * @param array $ids
139
     * @param bool  $reverse
140
     *
141
     * @return array
142
     */
143 1
    public static function all(array $ids = [], $reverse = false): array
144
    {
145 1
        $all = array_values(self::getReflection()->getConstants());
146
147 1
        if (!$ids) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $ids of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
148 1
            $ids = $all;
149
        } else {
150 1
            $ids = $reverse ? array_diff($all, $ids) : $ids;
151
        }
152
153 1
        return array_map(function (int $id) {
154 1
            return new static($id);
155 1
        }, $ids);
156
    }
157
158
    /**
159
     * @param array $array
160
     *
161
     * @return bool
162
     */
163
    public function in(array $array): bool
164
    {
165
        return \in_array($this->getId(), $array, true);
166
    }
167
168
    /**
169
     * @param int $id
170
     *
171
     * @return bool
172
     */
173
    public function is(int $id): bool
174
    {
175
        return $this->getId() === $id;
176
    }
177
178
    /**
179
     * @param Enum $enum
180
     *
181
     * @return bool
182
     */
183 6
    public function eq(Enum $enum): bool
184
    {
185 6
        return $this instanceof $enum && $enum->getId() === $this->getId();
186
    }
187
188
    /**
189
     * @return string
190
     */
191 1
    public function serialize(): string
192
    {
193 1
        return (string) $this->getId();
194
    }
195
196
    /**
197
     * @param string $serialized
198
     */
199 1
    public function unserialize($serialized): void
200
    {
201 1
        $this->id = (int) $serialized;
202 1
    }
203
204
    /**
205
     * @return array
206
     */
207 1
    public function toArray(): array
208
    {
209 1
        return [$this->getId() => $this];
210
    }
211
212
    /**
213
     * @throws LogicException
214
     *
215
     * @return ReflectionClass
216
     */
217 19
    private static function getReflection(): ReflectionClass
218
    {
219 19
        $class = static::class;
220
221 19
        if (array_key_exists($class, self::$reflections)) {
222 16
            return self::$reflections[$class];
223
        }
224
225 7
        self::$reflections[$class] = $reflection = new ReflectionClass($class);
226
227 7
        $constants = $reflection->getConstants();
228 7
        if ([] === $constants) {
229 1
            throw new LogicException(sprintf('Class %s must define Constants', static::class));
230
        }
231
232 6
        foreach ($constants as $value) {
233 6
            if (!\is_int($value)) {
234 6
                throw new LogicException('All enum constants must be in integer type');
235
            }
236
        }
237
238 5
        return $reflection;
239
    }
240
241 7
    private function getPropertyValue(string $property)
242
    {
243 7
        $reflectionClass = self::getReflection();
244
245 7
        $values = [];
246 7
        foreach ($reflectionClass->getProperties() as $reflectionProperty) {
247 4
            if ($reflectionProperty->getName() === $property) {
248 4
                $reflectionProperty->setAccessible(true);
249 4
                $values = $reflectionProperty->getValue();
250 4
                $reflectionProperty->setAccessible(false);
251
252 4
                break;
253
            }
254
        }
255
256 7
        return $values[$this->getId()] ?? null;
257
    }
258
}
259