Completed
Push — master ( acfce2...80801b )
by Peter
10:12
created

ReflectionEnum::byValue()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 0
cts 10
cp 0
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 1
crap 12
1
<?php
2
3
/**
4
 * GpsLab component.
5
 *
6
 * @author    Peter Gribanov <[email protected]>
7
 * @copyright Copyright (c) 2011, Peter Gribanov
8
 * @license   http://opensource.org/licenses/MIT
9
 */
10
11
namespace GpsLab\Component\Enum;
12
13
use GpsLab\Component\Enum\Exception\BadMethodCallException;
14
use GpsLab\Component\Enum\Exception\OutOfEnumException;
15
16
abstract class ReflectionEnum implements Enum, \Serializable
17
{
18
    /**
19
     * @var mixed
20
     */
21
    private $value = '';
22
23
    /**
24
     * @var Enum[]
25
     */
26
    private static $instances = [];
27
28
    /**
29
     * @var mixed[][]
30
     */
31
    private static $create_methods = [];
32
33
    /**
34
     * @var mixed[][]
35
     */
36
    private static $is_methods = [];
37
38
    /**
39
     * @var mixed[][]
40
     */
41
    private static $constants = [];
42
43
    /**
44
     * @param mixed $value
45
     */
46
    final private function __construct($value)
47
    {
48
        $this->value = $value;
49
    }
50
51
    /**
52
     * @param mixed $value
53
     *
54
     * @return Enum
55
     */
56
    final public static function byValue($value)
57
    {
58
        $class = get_called_class();
59
        self::detectConstants($class);
60
61
        $method = array_search($value, self::$create_methods[$class], true);
62
63
        if ($method === false) {
64
            throw OutOfEnumException::create($value, $class);
65
        }
66
67
        // limitation of count object instances
68
        if (!isset(self::$instances[$class][$method])) {
69
            self::$instances[$class][$method] = new static($value);
70
        }
71
72
        return self::$instances[$class][$method];
73
    }
74
75
    /**
76
     * @return mixed
77
     */
78
    final public function value()
79
    {
80
        return $this->value;
81
    }
82
83
    /**
84
     * Available values.
85
     *
86
     * @return Enum[]
87
     */
88
    final public static function values()
89
    {
90
        $values = [];
91
        foreach (self::constants() as $constant => $value) {
92
            $values[$constant] = self::byValue($value);
93
        }
94
95
        return $values;
96
    }
97
98
    /**
99
     * @param Enum $enum
100
     *
101
     * @return bool
102
     */
103
    final public function equals(Enum $enum)
104
    {
105
        return $this === $enum || ($this->value() === $enum->value() && get_called_class() == get_class($enum));
106
    }
107
108
    /**
109
     * Is value supported.
110
     *
111
     * @param mixed $value
112
     *
113
     * @return bool
114
     */
115
    final public static function isValid($value)
116
    {
117
        return in_array($value, self::constants(), true);
118
    }
119
120
    /**
121
     * Get choices for radio group.
122
     *
123
     * <code>
124
     * {
125
     *   value1: 'Readable value 1',
126
     *   value2: 'Readable value 2',
127
     * }
128
     * </code>
129
     *
130
     * @return array
131
     */
132
    final public static function choices()
133
    {
134
        $choices = [];
135
        foreach (self::constants() as $value) {
136
            $choices[$value] = (string) self::byValue($value);
137
        }
138
139
        return $choices;
140
    }
141
142
    /**
143
     * Return readable value.
144
     *
145
     * @return string
146
     */
147
    public function __toString()
148
    {
149
        return $this->constant();
150
    }
151
152
    final public function __clone()
153
    {
154
        throw new \LogicException('Enumerations are not cloneable');
155
    }
156
157
    /**
158
     * @return mixed
159
     */
160
    public function serialize()
161
    {
162
        return serialize($this->value);
163
    }
164
165
    /**
166
     * @param mixed $data
167
     */
168
    public function unserialize($data)
169
    {
170
        self::byValue($this->value = unserialize($data));
171
    }
172
173
    /**
174
     * @param string $class
175
     */
176
    private static function detectConstants($class)
177
    {
178
        if (!isset(self::$create_methods[$class])) {
179
            self::$create_methods[$class] = [];
180
            self::$is_methods[$class] = [];
181
            self::$constants[$class] = [];
182
183
            $constants = [];
184
            $reflection = new \ReflectionClass($class);
185
186 View Code Duplication
            if (PHP_VERSION_ID >= 70100) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
187
                // Since PHP-7.1 visibility modifiers are allowed for class constants
188
                // for enumerations we are only interested in public once.
189
                foreach ($reflection->getReflectionConstants() as $refl_constant) {
190
                    if ($refl_constant->isPublic()) {
191
                        $constants[$refl_constant->getName()] = $refl_constant->getValue();
192
                    }
193
                }
194
            } else {
195
                // In PHP < 7.1 all class constants were public by definition
196
                foreach ($reflection->getConstants() as $constant => $constant_value) {
197
                    $constants[$constant] = $constant_value;
198
                }
199
            }
200
201
            foreach ($constants as $constant => $constant_value) {
202
                self::$constants[$class][$constant] = $constant_value;
203
204
                // second parameter of ucwords() is not supported on HHVM
205
                $constant = str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($constant))));
206
207
                self::$is_methods[$class]['is'.$constant] = $constant_value;
208
                self::$create_methods[$class][lcfirst($constant)] = $constant_value;
209
            }
210
        }
211
    }
212
213
    /**
214
     * @return array
215
     */
216
    private static function constants()
217
    {
218
        $class = get_called_class();
219
        self::detectConstants($class);
220
221
        return self::$constants[$class];
222
    }
223
224
    /**
225
     * @return string
226
     */
227
    private function constant()
228
    {
229
        return array_search($this->value(), self::constants());
230
    }
231
232
    /**
233
     * @param string $method
234
     * @param array  $arguments
235
     *
236
     * @return bool
237
     */
238
    public function __call($method, array $arguments = [])
239
    {
240
        $class = get_called_class();
241
        self::detectConstants($class);
242
243
        if (!isset(self::$is_methods[$class][$method])) {
244
            throw BadMethodCallException::noMethod($method, $class);
245
        }
246
247
        return $this->value === self::$is_methods[$class][$method];
248
    }
249
250
    /**
251
     * @param string $method
252
     * @param array  $arguments
253
     *
254
     * @return Enum
255
     */
256
    public static function __callStatic($method, array $arguments = [])
257
    {
258
        $class = get_called_class();
259
        if (isset(self::$instances[$class][$method])) {
260
            return self::$instances[$class][$method];
261
        }
262
263
        self::detectConstants($class);
264
265
        if (!isset(self::$create_methods[$class][$method])) {
266
            throw BadMethodCallException::noStaticMethod($method, $class);
267
        }
268
269
        return self::$instances[$class][$method] = new static(self::$create_methods[$class][$method]);
270
    }
271
}
272