Passed
Branch 7.x (a5d545)
by Adrien
05:06
created

EnumCaseCollectionTrait::hasCase()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 6
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 13
ccs 0
cts 7
cp 0
crap 20
rs 10
1
<?php
2
3
/**
4
 * Part of SplTypes package.
5
 *
6
 * (c) Adrien Loyant <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Ducks\Component\SplTypes\Reflection\Proxy;
15
16
use Ducks\Component\SplTypes\Reflection\SplReflectionEnumBackedCase;
17
use Ducks\Component\SplTypes\Reflection\SplReflectionEnumUnitCase;
18
use Ducks\Component\SplTypes\SplEnumerable;
19
20
/**
21
 * Used in order to manage constant class as enum case.
22
 */
23
trait EnumCaseCollectionTrait
24
{
25
    /**
26
     * Array of Reflection enum cases, indexed by name.
27
     *
28
     * @var (SplReflectionEnumUnitCase|SplReflectionEnumBackedCase)[]
29
     *
30
     * @phpstan-var array<string, SplReflectionEnumUnitCase|SplReflectionEnumBackedCase>
31
     */
32
    private array $cases = [];
33
34
    /**
35
     * The nameof the case beeing instanciate
36
     *
37
     * @var string|null
38
     */
39
    private ?string $running = null;
40
41
    /**
42
     * Name of the class constant.
43
     *
44
     * @return string
45
     */
46
    abstract public function getName(): string;
47
48
    /**
49
     * Return an array of class constants, indexed by name, that could be use as an enum case.
50
     *
51
     * @return \ReflectionClassConstant[]
52
     *
53
     * @phpstan-return array<string,\ReflectionClassConstant>
54
     *
55
     * @see ConstantCaseCollectionTrait::getConstantCases()
56
     */
57
    abstract public function getConstantCases(): array;
58
59
    /**
60
     * Checks for a constant case on an Enum
61
     *
62
     * @param string $name The case to check for.
63
     *
64
     * @return boolean
65
     *
66
     * @see ConstantCaseCollectionTrait::hasConstantCase()
67
     */
68
    abstract public function hasConstantCase(string $name): bool;
69
70
    /**
71
     * Determines if an Enum is a Backed Enum
72
     *
73
     * @return boolean
74
     *
75
     * @see SplReflectionEnumProxy::isBacked()
76
     */
77
    abstract public function isBacked(): bool;
78
79
    /**
80
     * Gets the backing type of an Enum, if any
81
     *
82
     * @return \ReflectionNamedType|null An instance of ReflectionNamedType, or null if the Enum has no backing type.
83
     *
84
     * @see SplReflectionEnumProxy::getBackingType()
85
     */
86
    abstract public function getBackingType(): ?\ReflectionNamedType;
87
88
    /**
89
     * Init internal cases array.
90
     *
91
     * @return void
92
     *
93
     * @codeCoverageIgnore
94
     */
95
    private function initCases(): void
96
    {
97
        $cases = \array_diff_key($this->getConstantCases(), $this->cases);
98
        $this->addCase(...\array_values($cases));
99
    }
100
101
    /**
102
     * Add a case to internal array
103
     *
104
     * @param \ReflectionClassConstant ...$constants
105
     *
106
     * @return void
107
     */
108
    public function addCase(\ReflectionClassConstant ...$constants): void
109
    {
110
        $className = $this->getName();
111
112
        foreach ($constants as $constant) {
113
            $name = $constant->getName();
114
            if (
115
                !isset($this->cases[$name])
116
                && \is_a($className, SplEnumerable::class, true)
117
            ) {
118
                // Check type
119
                $value = $constant->getValue();
120
121
                // Mandatory in order to prevent infinite loop
122
                $this->running = $name;
123
124
                if (!$this->isBacked() && null === $value) {
125
                    $this->cases[$name] = new SplReflectionEnumUnitCase($className, $name);
126
                    unset($this->running);
127
                    continue;
128
                }
129
130
                $backingType = $this->getBackingType();
131
132
                if (
133
                    $this->isBacked()
134
                    && $backingType instanceof \ReflectionNamedType
135
                    && $backingType->getName() === \gettype($value)
136
                ) {
137
                    $this->cases[$name] = new SplReflectionEnumBackedCase($className, $name);
138
                    unset($this->running);
139
                    continue;
140
                }
141
            }
142
        }
143
    }
144
145
    /**
146
     * Returns a list of all cases on an Enum
147
     *
148
     * @return (SplReflectionEnumUnitCase|SplReflectionEnumBackedCase)[]
149
     *
150
     * @phpstan-return array<string,SplReflectionEnumUnitCase|SplReflectionEnumBackedCase>
151
     *
152
     * @link https://www.php.net/manual/en/reflectionenum.getcases.php
153
     */
154
    public function getCases(): array
155
    {
156
        static $init = false;
157
158
        if (!$init) {
159
            $this->initCases();
160
            $init = true;
161
        }
162
163
        return $this->cases;
164
    }
165
166
    /**
167
     * Returns a specific case of an Enum
168
     *
169
     * @param string $name
170
     *
171
     * @return SplReflectionEnumUnitCase|SplReflectionEnumBackedCase
172
     *
173
     * @throws \ReflectionException If the requested case is not defined
174
     *
175
     * @link https://www.php.net/manual/en/reflectionenum.getcase.php
176
     */
177
    public function getCase(string $name): SplReflectionEnumUnitCase
178
    {
179
        if (!$this->hasCase($name)) {
180
            throw new \ReflectionException($this->getName() . '::' . $name . ' is not a case');
181
        }
182
183
        return $this->cases[$name];
184
    }
185
186
    /**
187
     * Checks for a case on an Enum
188
     *
189
     * @param string $name The case to check for.
190
     *
191
     * @return boolean
192
     *
193
     * @link https://www.php.net/manual/en/reflectionenum.hascase.php
194
     */
195
    public function hasCase(string $name): bool
196
    {
197
        // $this->cases could be empty
198
        if (isset($this->cases[$name]) || $this->running === $name) {
199
            return true;
200
        }
201
202
        if ($this->hasConstantCase($name)) {
203
            $constant = $this->getConstantCase($name);
0 ignored issues
show
Bug introduced by
The method getConstantCase() does not exist on Ducks\Component\SplTypes...EnumCaseCollectionTrait. Did you maybe mean getConstantCases()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

203
            /** @scrutinizer ignore-call */ 
204
            $constant = $this->getConstantCase($name);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
204
            $this->addCase($constant);
205
        }
206
207
        return isset($this->cases[$name]);
208
    }
209
}
210