Completed
Push — master ( 47d960...e61af3 )
by Peter
03:38
created

ReflectionEnum::__clone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

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
eloc 2
nc 1
nop 0
crap 1
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 2
    final private function __construct($value)
47
    {
48 2
        $this->value = $value;
49 2
    }
50
51
    /**
52
     * @param mixed $value
53
     *
54
     * @return Enum
55
     */
56 30
    final public static function byValue($value)
57
    {
58 30
        $class = get_called_class();
59 30
        self::detectConstants($class);
60
61 30
        $method = array_search($value, self::$create_methods[$class], true);
62
63 30
        if ($method === false) {
64 2
            throw OutOfEnumException::create($value, $class);
65
        }
66
67
        // limitation of count object instances
68 28
        if (!isset(self::$instances[$class][$method])) {
69 2
            self::$instances[$class][$method] = new static($value);
70 2
        }
71
72 28
        return self::$instances[$class][$method];
73
    }
74
75
    /**
76
     * @return mixed
77
     */
78 20
    final public function value()
79
    {
80 20
        return $this->value;
81
    }
82
83
    /**
84
     * Available values.
85
     *
86
     * @return Enum[]
87
     */
88 2
    final public static function values()
89
    {
90 2
        $values = [];
91 2
        foreach (self::constants() as $constant => $value) {
92 2
            $values[$constant] = self::byValue($value);
93 2
        }
94
95 2
        return $values;
96
    }
97
98
    /**
99
     * @param Enum $enum
100
     *
101
     * @return bool
102
     */
103 8
    final public function equals(Enum $enum)
104
    {
105 8
        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 8
    final public static function isValid($value)
116
    {
117 8
        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 2
    final public static function choices()
133
    {
134 2
        $choices = [];
135 2
        foreach (self::constants() as $value) {
136 2
            $choices[$value] = (string) self::byValue($value);
137 2
        }
138
139 2
        return $choices;
140
    }
141
142
    /**
143
     * Return readable value.
144
     *
145
     * @return string
146
     */
147 10
    public function __toString()
148
    {
149 10
        return $this->constant();
150
    }
151
152 2
    final public function __clone()
153
    {
154 2
        throw new \LogicException('Enumerations are not cloneable');
155
    }
156
157
    /**
158
     * @return mixed
159
     */
160 8
    public function serialize()
161
    {
162 8
        return serialize($this->value);
163
    }
164
165
    /**
166
     * @param mixed $data
167
     */
168 8
    public function unserialize($data)
169
    {
170 8
        self::byValue($this->value = unserialize($data));
171 8
    }
172
173
    /**
174
     * @param string $class
175
     */
176 42
    private static function detectConstants($class)
177
    {
178 42
        if (!isset(self::$create_methods[$class])) {
179 2
            self::$create_methods[$class] = [];
180 2
            self::$is_methods[$class] = [];
181 2
            self::$constants[$class] = [];
182
183 2
            $constants = [];
184 2
            $reflection = new \ReflectionClass($class);
185
186 2 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 2
                foreach ($reflection->getConstants() as $constant => $constant_value) {
197 2
                    $constants[$constant] = $constant_value;
198 2
                }
199
            }
200
201 2
            foreach ($constants as $constant => $constant_value) {
202 2
                self::$constants[$class][$constant] = $constant_value;
203
204
                // second parameter of ucwords() is not supported on HHVM
205 2
                $constant = str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($constant))));
206
207 2
                self::$is_methods[$class]['is'.$constant] = $constant_value;
208 2
                self::$create_methods[$class][lcfirst($constant)] = $constant_value;
209 2
            }
210 2
        }
211 42
    }
212
213
    /**
214
     * @return array
215
     */
216 12
    private static function constants()
217
    {
218 12
        $class = get_called_class();
219 12
        self::detectConstants($class);
220
221 12
        return self::$constants[$class];
222
    }
223
224
    /**
225
     * @return string
226
     */
227 10
    private function constant()
228
    {
229 10
        return array_search($this->value(), self::constants());
230
    }
231
232
    /**
233
     * @param string $method
234
     * @param array  $arguments
235
     *
236
     * @return bool
237
     */
238 10
    public function __call($method, array $arguments = [])
239
    {
240 10
        $class = get_called_class();
241 10
        self::detectConstants($class);
242
243 10
        if (!isset(self::$is_methods[$class][$method])) {
244 2
            throw BadMethodCallException::noMethod($method, $class);
245
        }
246
247 8
        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 14
    public static function __callStatic($method, array $arguments = [])
257
    {
258 14
        $class = get_called_class();
259 14
        if (isset(self::$instances[$class][$method])) {
260 12
            return self::$instances[$class][$method];
261
        }
262
263 2
        self::detectConstants($class);
264
265 2
        if (!isset(self::$create_methods[$class][$method])) {
266 2
            throw BadMethodCallException::noStaticMethod($method, $class);
267
        }
268
269
        return self::$instances[$class][$method] = new static(self::$create_methods[$class][$method]);
270
    }
271
}
272