Completed
Pull Request — master (#94)
by Maxime
01:58
created

EnumTrait::get()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
c 0
b 0
f 0
rs 9.8333
cc 3
nc 3
nop 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
16
/**
17
 * @see EnumInterface which this trait implements
18
 */
19
trait EnumTrait
20
{
21
    /**
22
     * Cached array of enum instances by enum type (FQCN).
23
     * This cache is used in order to make single enums values act as singletons.
24
     * This means you'll always get the exact same instance for a same enum value.
25
     *
26
     * @var array
27
     *
28
     * @internal
29
     */
30
    private static $instances;
31
32
    /** @var int|string */
33
    protected $value;
34
35
    /**
36
     * The constructor is private and MUST NOT be overridden: use the static get method instead.
37
     *
38
     * @param int|string $value The raw value of an enumeration
39
     *
40
     * @internal Use {@see EnumInterface::get()} instead.
41
     * @final It must not be overridden in classes using this trait
42
     */
43
    final private function __construct($value)
44
    {
45
        $this->value = $value;
46
47
        $enumType = static::class;
48
        $identifier = serialize($value);
49
50
        if (isset(self::$instances[$enumType][$identifier])) {
51
            throw new LogicException(
52
                '"__construct" should not be called when an instance already exists for this enum value.'
53
            );
54
        }
55
56
        if (!isset(self::$instances[$enumType])) {
57
            self::$instances[$enumType] = [];
58
        }
59
60
        self::$instances[$enumType][$identifier] = $this;
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     *
66
     * @return static The enum instance for given value
67
     */
68
    public static function get($value): EnumInterface
69
    {
70
        // Return the cached instance for given value if it already exists:
71
        if (null !== $instance = self::$instances[static::class][serialize($value)] ?? null) {
72
            return $instance;
73
        }
74
75
        if (!static::accepts($value)) {
76
            throw new InvalidValueException($value, static::class);
77
        }
78
79
        return new static($value);
80
    }
81
82
    /**
83
     * Instantiates a new enumeration.
84
     *
85
     * @param string $name      The name of a particular enumerated constant
86
     * @param array  $arguments
87
     *
88
     * @throws \BadMethodCallException On invalid constant name
89
     *
90
     * @return static When $name is an existing constant for this enumeration type
91
     */
92
    public static function __callStatic($name, $arguments = []): EnumInterface
93
    {
94
        if (!\defined('static::' . $name)) {
95
            throw new \BadMethodCallException(sprintf(
96
                'No constant named "%s" exists in class "%s"',
97
                $name,
98
                static::class
99
            ));
100
        }
101
102
        return static::get(\constant('static::' . $name));
103
    }
104
105
    /**
106
     * {@inheritdoc}
107
     */
108
    public static function accepts($value): bool
109
    {
110
        return \in_array($value, static::values(), true);
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116
    public static function instances(): array
117
    {
118
        return array_map(static function ($value) {
119
            return static::get($value);
120
        }, static::values());
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function getValue()
127
    {
128
        return $this->value;
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function equals(EnumInterface $enum): bool
135
    {
136
        return \get_class($this) === \get_class($enum) && $this->value === $enum->getValue();
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142
    public function is($value): bool
143
    {
144
        return $this->getValue() === $value;
145
    }
146
}
147