Completed
Push — master ( 32ae8c...b47dfe )
by Brent
15s
created

Enum::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace Spatie\Enum;
4
5
use TypeError;
6
use ReflectionClass;
7
use JsonSerializable;
8
use ReflectionMethod;
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
    public static function getKeys(): array
104
    {
105
        return array_keys(self::resolve());
106
    }
107
108
    public static function getValues(): array
109
    {
110
        return array_values(self::resolve());
111
    }
112
113
    protected static function resolve(): array
114
    {
115
        $class = static::class;
116
117
        if (isset(self::$cache[$class])) {
118
            return self::$cache[$class];
119
        }
120
121
        $enumValues = [];
122
123
        $staticReflection = new ReflectionClass(static::class);
124
125
        foreach (self::resolveValuesFromStaticMethods($staticReflection) as $value => $name) {
126
            $enumValues[$value] = $name;
127
        }
128
129
        foreach (self::resolveFromDocblocks($staticReflection) as $value => $name) {
130
            $enumValues[$value] = $name;
131
        }
132
133
        self::$cache[$class] = $enumValues;
134
135
        return self::$cache[$class];
136
    }
137
138
    protected static function resolveValuesFromStaticMethods(ReflectionClass $staticReflection): array
139
    {
140
        $enumValues = [];
141
142
        $selfReflection = new ReflectionClass(self::class);
143
144
        $selfStaticMethods = [];
145
146
        foreach ($selfReflection->getMethods(ReflectionMethod::IS_STATIC) as $method) {
147
            $selfStaticMethods[$method->name] = $method->name;
148
        }
149
150
        foreach ($staticReflection->getMethods(ReflectionMethod::IS_STATIC) as $method) {
151
            $methodName = $method->getName();
152
153
            if (isset($selfStaticMethods[$methodName])) {
154
                continue;
155
            }
156
157
            $enumValues[$methodName] = static::$map[$methodName] ?? $methodName;
158
        }
159
160
        return $enumValues;
161
    }
162
163
    protected static function resolveFromDocblocks(ReflectionClass $staticReflection): array
164
    {
165
        $enumValues = [];
166
167
        $docComment = $staticReflection->getDocComment();
168
169
        preg_match_all('/\@method static self ([\w]+)\(\)/', $docComment, $matches);
170
171
        foreach ($matches[1] ?? [] as $valueName) {
172
            $enumValues[$valueName] = static::$map[$valueName] ?? $valueName;
173
        }
174
175
        return $enumValues;
176
    }
177
178
    protected function resolveValueFromStaticCall(): ?string
179
    {
180
        if (strpos(get_class($this), 'class@anonymous') === 0) {
181
            $backtrace = debug_backtrace();
182
183
            return $backtrace[2]['function'];
184
        }
185
186
        return null;
187
    }
188
}
189