Completed
Push — master ( d22687...58c98c )
by Peter
05:41
created

ReflectionEnum::constants()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 3
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 0
crap 2
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 create($value)
57
    {
58
        self::detectConstants(static::class);
59
60
        $method = array_search($value, self::$create_methods[static::class], true);
61
62
        if ($method === false) {
63
            throw OutOfEnumException::create($value, static::class);
64
        }
65
66
        // limitation of count object instances
67 View Code Duplication
        if (!isset(self::$instances[static::class][$method])) {
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...
68
            self::$instances[static::class][$method] = new static($value);
69
        }
70
71
        return self::$instances[static::class][$method];
72
    }
73
74
    /**
75
     * @return mixed
76
     */
77
    final public function value()
78
    {
79
        return $this->value;
80
    }
81
82
    /**
83
     * Available values.
84
     *
85
     * @return Enum[]
86
     */
87
    final public static function values()
88
    {
89
        $values = [];
90
        foreach (self::constants() as $constant => $value) {
91
            $values[$constant] = static::create($value);
92
        }
93
94
        return $values;
95
    }
96
97
    /**
98
     * @param Enum $enum
99
     *
100
     * @return bool
101
     */
102
    final public function equals(Enum $enum)
103
    {
104
        return $this === $enum || ($this->value() === $enum->value() && static::class == get_class($enum));
105
    }
106
107
    /**
108
     * Is value supported.
109
     *
110
     * @param mixed $value
111
     *
112
     * @return bool
113
     */
114
    final public static function isValid($value)
115
    {
116
        return in_array($value, self::constants(), true);
117
    }
118
119
    /**
120
     * Get choices for radio group.
121
     *
122
     * <code>
123
     * {
124
     *   value1: 'Readable value 1',
125
     *   value2: 'Readable value 2',
126
     * }
127
     * </code>
128
     *
129
     * @return array
130
     */
131
    final public static function choices()
132
    {
133
        $choices = [];
134
        foreach (self::constants() as $value) {
135
            $choices[$value] = (string) static::create($value);
136
        }
137
138
        return $choices;
139
    }
140
141
    /**
142
     * Return readable value.
143
     *
144
     * @return string
145
     */
146
    public function __toString()
147
    {
148
        return $this->constant();
1 ignored issue
show
Comprehensibility Best Practice introduced by
The expression $this->constant(); of type false|integer|string adds false to the return on line 148 which is incompatible with the return type declared by the interface GpsLab\Component\Enum\Enum::__toString of type string. It seems like you forgot to handle an error condition.
Loading history...
149
    }
150
151
    final public function __clone()
152
    {
153
        throw new \LogicException('Enumerations are not cloneable');
154
    }
155
156
    /**
157
     * @return mixed
158
     */
159
    public function serialize()
160
    {
161
        return $this->value;
162
    }
163
164
    /**
165
     * @param mixed $data
166
     */
167
    public function unserialize($data)
168
    {
169
        static::create($this->value = $data);
170
    }
171
172
    /**
173
     * @param string $class
174
     */
175
    private static function detectConstants($class)
176
    {
177
        if (!isset(self::$create_methods[$class])) {
178
            self::$create_methods[$class] = [];
179
            self::$is_methods[$class] = [];
180
            self::$constants[$class] = [];
181
182
            $constants = [];
183
            $reflection = new \ReflectionClass($class);
184
185 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...
186
                // Since PHP-7.1 visibility modifiers are allowed for class constants
187
                // for enumerations we are only interested in public once.
188
                foreach ($reflection->getReflectionConstants() as $refl_constant) {
189
                    if ($refl_constant->isPublic()) {
190
                        $constants[$refl_constant->getName()] = $refl_constant->getValue();
191
                    }
192
                }
193
            } else {
194
                // In PHP < 7.1 all class constants were public by definition
195
                foreach ($reflection->getConstants() as $constant => $constant_value) {
196
                    $constants[$constant] = $constant_value;
197
                }
198
            }
199
200
            foreach ($constants as $constant => $constant_value) {
201
                self::$constants[$class][$constant] = $constant_value;
202
203
                // second parameter of ucwords() is not supported on HHVM
204
                $constant = str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($constant))));
205
206
                self::$is_methods[$class]['is'.$constant] = $constant_value;
207
                self::$create_methods[$class][lcfirst($constant)] = $constant_value;
208
            }
209
        }
210
    }
211
212
    /**
213
     * @return array
214
     */
215
    private static function constants()
216
    {
217
        self::detectConstants(static::class);
218
219
        return self::$constants[static::class];
220
    }
221
222
    /**
223
     * @return string
224
     */
225
    private function constant()
226
    {
227
        return array_search($this->value(), self::constants());
228
    }
229
230
    /**
231
     * @param string $method
232
     * @param array  $arguments
233
     *
234
     * @return bool
235
     */
236
    public function __call($method, array $arguments = [])
237
    {
238
        self::detectConstants(static::class);
239
240
        if (!isset(self::$is_methods[static::class][$method])) {
241
            throw BadMethodCallException::noMethod($method, static::class);
242
        }
243
244
        return $this->value === self::$is_methods[static::class][$method];
245
    }
246
247
    /**
248
     * @param string $method
249
     * @param array  $arguments
250
     *
251
     * @return Enum
252
     */
253
    public static function __callStatic($method, array $arguments = [])
254
    {
255 View Code Duplication
        if (isset(self::$instances[static::class][$method])) {
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...
256
            return self::$instances[static::class][$method];
257
        }
258
259
        self::detectConstants(static::class);
260
261
        if (!isset(self::$create_methods[static::class][$method])) {
262
            throw BadMethodCallException::noStaticMethod($method, static::class);
263
        }
264
265
        return self::$instances[static::class][$method] = new static(self::$create_methods[static::class][$method]);
266
    }
267
}
268