Completed
Pull Request — master (#39)
by Cédric
01:48
created

Enum::getCachedInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 1
cts 1
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
crap 1
1
<?php
2
3
/*
4
 * This file is part of the "elao/enum" package.
5
 *
6
 * Copyright (C) Elao
7
 *
8
 * @author Elao <[email protected]>
9
 */
10
11
namespace Elao\Enum;
12
13
use Elao\Enum\Exception\InvalidValueException;
14
use Elao\Enum\Exception\LogicException;
15
use Elao\Enum\Exception\NotSerializableException;
16
17
abstract class Enum implements EnumInterface, \Serializable
18
{
19
    /**
20
     * Cached array of enum instances by enum type (FQCN).
21
     * This cache is used in order to make single enums values act as singletons.
22
     * This means you'll always get the exact same instance for a same enum value.
23
     *
24
     * @var array
25
     */
26
    private static $instances;
27
28
    /** @var mixed */
29
    protected $value;
30
31
    /**
32
     * The constructor is private and cannot be overridden: use the static get method instead.
33
     *
34
     * @param mixed $value The raw value of an enumeration
35 9
     */
36
    final private function __construct($value)
37 9
    {
38
        $this->value = $value;
39 9
40 9
        $enumType = static::class;
41
        $identifier = serialize($value);
42 9
43
        if (isset(self::$instances[$enumType][$identifier])) {
44
            throw new LogicException(
45
                '"__construct" should not be called when an instance already exists for this enum value.'
46
            );
47
        }
48 9
49 3
        if (!isset(self::$instances[$enumType])) {
50
            self::$instances[$enumType] = [];
51
        }
52 9
53 9
        self::$instances[$enumType][$identifier] = $this;
54
    }
55
56
    /**
57
     * {@inheritdoc}
58
     *
59
     * @return static The enum instance for given value
60 69
     */
61
    public static function get($value): EnumInterface
62 69
    {
63 69
        // Return the cached instance for given value if it already exists:
64
        if (null !== $instance = self::getCachedInstance($value)) {
65
            return $instance;
66 69
        }
67 64
68
        if (!static::accepts($value)) {
69
            throw new InvalidValueException($value, static::class);
70 13
        }
71 3
72
        return new static($value);
73
    }
74 9
75
    /**
76
     * Instantiates a new enumeration.
77
     *
78
     * @param string $name      The name of a particular enumerated constant
79
     * @param array  $arguments
80
     *
81
     * @throws \BadMethodCallException On invalid constant name
82
     *
83
     * @return static When $name is an existing constant for this enumeration type
84
     */
85
    public static function __callStatic($name, $arguments = []): EnumInterface
86
    {
87 2
        $value = @constant('static::' . $name);
88
        if (null === $value) {
89 2
            throw new \BadMethodCallException(sprintf(
90 2
                'No constant named "%s" exists in class "%s"',
91 1
                $name,
92 1
                static::class
93
            ));
94 1
        }
95
96
        return static::get($value);
97
    }
98 1
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public static function accepts($value): bool
103
    {
104 19
        return in_array($value, static::values(), true);
105
    }
106 19
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public static function instances(): array
111
    {
112
        return array_map(function ($value) {
113
            return static::get($value);
114 19
        }, static::values());
115 19
    }
116 19
117
    private static function getCachedInstance($value)
118
    {
119
        $enumType = static::class;
120
        $identifier = serialize($value);
121
122 40
        return self::$instances[$enumType][$identifier] ?? null;
123
    }
124 40
125
    /**
126
     * {@inheritdoc}
127
     */
128
    public function getValue()
129
    {
130 2
        return $this->value;
131
    }
132 2
133
    /**
134
     * {@inheritdoc}
135
     */
136
    public function equals(EnumInterface $enum): bool
137
    {
138 3
        return get_class($this) === get_class($enum) && $this->value === $enum->getValue();
139
    }
140 3
141
    /**
142
     * {@inheritdoc}
143
     */
144
    public function is($value): bool
145
    {
146
        return $this->getValue() === $value;
147
    }
148
149
    /**
150
     * {@inheritdoc}
151
     * @throws NotSerializableException When Enum value type is neither "string" not "int"
152
     */
153
    public function serialize(): string
154
    {
155
        if(!is_string($this->getValue()) && !is_int($this->getValue())) {
156
            throw new NotSerializableException('Only values of type "string" or "int" can be serialized');
157
        }
158
159
        return (string)$this->getValue();
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165
    public function unserialize($serialized)
166
    {
167
        if(static::accepts($serialized)) {
168
            $this->value = $serialized;
169
            return;
170
        }
171
        if(is_numeric($serialized) && static::accepts(intval($serialized))) {
172
            $this->value = intval($serialized);
173
            return;
174
        }
175
176
        throw new InvalidValueException($serialized, static::class);
177
    }
178
}
179