Passed
Push — 7.x ( 57acf3...a5d545 )
by Adrien
03:55
created

getFirstCaseConstant()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 6
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\SplBackedEnum;
17
18
/**
19
 * Used in order to manage constant class as enum case.
20
 */
21
trait ConstantCaseCollectionTrait
22
{
23
    /**
24
     * Array of constants class, indexed by name, as enum cases.
25
     *
26
     * @var \ReflectionClassConstant[]
27
     *
28
     * @phpstan-var array<string,\ReflectionClassConstant>
29
     */
30
    private array $constantCases = [];
31
32
    /**
33
     * Name of the class constant.
34
     *
35
     * @return string
36
     */
37
    abstract public function getName(): string;
38
39
    /**
40
     * Gets constants.
41
     *
42
     * @return mixed[] An array of constants,
43
     * where the keys hold the name and the values the value of the constants.
44
     *
45
     * @phpstan-return array<string, mixed>
46
     */
47
    abstract public function getConstants(): array;
48
49
    /**
50
     * Gets a ReflectionClassConstant for a class's property
51
     *
52
     * @param string $name The class constant name.
53
     *
54
     * @return \ReflectionClassConstant|null
55
     */
56
    abstract public function getReflectionConstant(string $name): ?\ReflectionClassConstant;
57
58
    /**
59
     * Init internal constant cases array.
60
     *
61
     * @return void
62
     *
63
     * @codeCoverageIgnore
64
     */
65
    private function initConstantCases(): void
66
    {
67
        $constants = \array_diff_key($this->getConstants(), $this->constantCases);
68
        foreach (\array_keys($constants) as $name) {
69
            $constant = $this->getReflectionConstant($name);
70
            if ($constant instanceof \ReflectionClassConstant) {
71
                $this->addConstantCase($constant);
72
            }
73
        }
74
    }
75
76
    /**
77
     * Add a constant case to internal array
78
     *
79
     * @param \ReflectionClassConstant ...$constants
80
     *
81
     * @return void
82
     *
83
     * @no-named-arguments
84
     */
85
    public function addConstantCase(\ReflectionClassConstant ...$constants): void
86
    {
87
        $className = $this->getName();
88
89
        foreach ($constants as $constant) {
90
            $name = $constant->getName();
91
            if (
92
                !isset($this->constantCases[$name])
93
                && $constant->isPublic()
94
                // Check consistency because of polyfilling or other bad overrides
95
                && $constant->getDeclaringClass()->getName() === $className
96
                // Do not use isBacked method because of infinite loop possibility
97
                // Add if not BackedEnum or Backed but valid type
98
                && (
99
                    !\is_a($className, SplBackedEnum::class, true)
100
                    || (
101
                        \is_a($className, SplBackedEnum::class, true)
102
                        && (\is_int($constant->getValue()) || \is_string($constant->getValue()))
103
                    )
104
                )
105
            ) {
106
                $this->constantCases[$name] = $constant;
107
            }
108
        }
109
    }
110
111
    /**
112
     * Return an array of class constants, indexed by name, that could be use as an enum case.
113
     *
114
     * @return \ReflectionClassConstant[]
115
     *
116
     * @phpstan-return array<string,\ReflectionClassConstant>
117
     */
118
    public function getConstantCases(): array
119
    {
120
        static $init = false;
121
122
        if (!$init) {
123
            $this->initConstantCases();
124
            $init = true;
125
        }
126
127
        return $this->constantCases;
128
    }
129
130
    /**
131
     * Return a class constant for a case name if exists
132
     *
133
     * @param string $name
134
     *
135
     * @return \ReflectionClassConstant
136
     *
137
     * @throws \ReflectionException If the requested constant case is not defined
138
     */
139
    public function getConstantCase(string $name): \ReflectionClassConstant
140
    {
141
        if (!$this->hasConstantCase($name)) {
142
            throw new \ReflectionException($this->getName() . '::' . $name . ' is not a constant case');
143
        }
144
145
        return $this->constantCases[$name];
146
    }
147
148
    /**
149
     * Checks for a constant case on an Enum
150
     *
151
     * @param string $name The case to check for.
152
     *
153
     * @return boolean
154
     */
155
    public function hasConstantCase(string $name): bool
156
    {
157
        if (isset($this->constantCases[$name])) {
158
            return true;
159
        }
160
161
        $constant = $this->getReflectionConstant($name);
162
        if ($constant instanceof \ReflectionClassConstant) {
163
            $this->addConstantCase($constant);
164
        }
165
166
        return isset($this->constantCases[$name]);
167
    }
168
169
    /**
170
     * Return the first defined constant as possible enum case.
171
     *
172
     * @return \ReflectionClassConstant|null
173
     */
174
    public function getFirstCaseConstant(): ?\ReflectionClassConstant
175
    {
176
        return \current($this->getConstantCases()) ?: null;
177
    }
178
}
179