Completed
Push — master ( 28b5f4...a0bd05 )
by Brent
01:16
created

Enum   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 169
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 0

Importance

Changes 0
Metric Value
wmc 29
lcom 2
cbo 0
dl 0
loc 169
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A from() 0 8 2
A __construct() 0 12 3
A __callStatic() 0 10 2
A equals() 0 16 4
A isOneOf() 0 11 3
A __toString() 0 4 1
A jsonSerialize() 0 4 1
A toArray() 0 4 1
A resolve() 0 24 4
A resolveValuesFromStaticMethods() 0 24 4
A resolveFromDocblocks() 0 14 2
A resolveValueFromStaticCall() 0 10 2
1
<?php
2
3
namespace Spatie\Enum;
4
5
use JsonSerializable;
6
use ReflectionMethod;
7
use TypeError;
8
use ReflectionClass;
9
10
abstract class Enum implements JsonSerializable
11
{
12
    /** @var array */
13
    protected static $cache = [];
14
15
    /** @var array */
16
    protected static $map = [];
17
18
    /** @var string */
19
    protected $value;
20
21
    public static function from(string $value): Enum
22
    {
23
        if (method_exists(static::class, $value)) {
24
            return forward_static_call(static::class . '::' . $value);
25
        }
26
27
        return new static($value);
28
    }
29
30
    public function __construct(string $value = null)
31
    {
32
        if ($value === null) {
33
            $value = $this->resolveValueFromStaticCall();
34
        }
35
36
        if (! in_array($value, self::resolve())) {
37
            throw new TypeError("Value {$value} not available in enum ".static::class);
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with "Value {$value} not avai... enum " . static::class.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
38
        }
39
40
        $this->value = $value;
41
    }
42
43
    public static function __callStatic($name, $arguments): Enum
44
    {
45
        $enumValues = self::resolve();
46
47
        if (! isset($enumValues[$name])) {
48
            throw new TypeError("Method {$name} not available in enum ".static::class);
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with "Method {$name} not avai... enum " . static::class.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
49
        }
50
51
        return new static($enumValues[$name]);
52
    }
53
54
    /**
55
     * @param string|\Spatie\Enum\Enum $enum
56
     *
57
     * @return bool
58
     */
59
    public function equals($enum): bool
60
    {
61
        if (is_string($enum)) {
62
            $enum = static::from($enum);
63
        }
64
65
        if (! $enum instanceof $this) {
66
            return false;
67
        }
68
69
        if ($enum->value !== $this->value) {
70
            return false;
71
        }
72
73
        return true;
74
    }
75
76
    public function isOneOf(array $enums): bool
77
    {
78
        /** @var \Spatie\Enum\Enum $enum */
79
        foreach ($enums as $enum) {
80
            if ($this->equals($enum)) {
81
                return true;
82
            }
83
        }
84
85
        return false;
86
    }
87
88
    public function __toString(): string
89
    {
90
        return $this->value;
91
    }
92
93
    public function jsonSerialize()
94
    {
95
        return $this->value;
96
    }
97
98
    public static function toArray(): array
99
    {
100
        return self::resolve();
101
    }
102
103
    protected static function resolve(): array
104
    {
105
        $class = static::class;
106
107
        if (isset(self::$cache[$class])) {
108
            return self::$cache[$class];
109
        }
110
111
        $enumValues = [];
112
113
        $staticReflection = new ReflectionClass(static::class);
114
115
        foreach (self::resolveValuesFromStaticMethods($staticReflection) as $value => $name) {
116
            $enumValues[$value] = $name;
117
        }
118
119
        foreach (self::resolveFromDocblocks($staticReflection) as $value => $name) {
120
            $enumValues[$value] = $name;
121
        }
122
123
        self::$cache[$class] = $enumValues;
124
125
        return self::$cache[$class];
126
    }
127
128
    protected static function resolveValuesFromStaticMethods(ReflectionClass $staticReflection): array
129
    {
130
        $enumValues = [];
131
132
        $selfReflection = new ReflectionClass(self::class);
133
134
        $selfStaticMethods = [];
135
136
        foreach ($selfReflection->getMethods(ReflectionMethod::IS_STATIC) as $method) {
137
            $selfStaticMethods[$method->name] = $method->name;
138
        }
139
140
        foreach ($staticReflection->getMethods(ReflectionMethod::IS_STATIC) as $method) {
141
            $methodName = $method->getName();
142
143
            if (isset($selfStaticMethods[$methodName])) {
144
                continue;
145
            }
146
147
            $enumValues[$methodName] = static::$map[$methodName] ?? $methodName;
148
        }
149
150
        return $enumValues;
151
    }
152
153
    protected static function resolveFromDocblocks(ReflectionClass $staticReflection): array
154
    {
155
        $enumValues = [];
156
157
        $docComment = $staticReflection->getDocComment();
158
159
        preg_match_all('/\@method static self ([\w]+)\(\)/', $docComment, $matches);
160
161
        foreach ($matches[1] ?? [] as $valueName) {
162
            $enumValues[$valueName] = static::$map[$valueName] ?? $valueName;
163
        }
164
165
        return $enumValues;
166
    }
167
168
    protected function resolveValueFromStaticCall(): ?string
169
    {
170
        if (strpos(get_class($this), 'class@anonymous') === 0) {
171
            $backtrace = debug_backtrace();
172
173
            return $backtrace[2]['function'];
174
        }
175
176
        return null;
177
    }
178
}
179