• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

voku / Simple-PHP-Code-Parser / 24276985596

11 Apr 2026 06:47AM UTC coverage: 82.886% (+0.03%) from 82.857%
24276985596

Pull #79

github

web-flow
Merge fd40afd96 into 3fcaa1a81
Pull Request #79: Fix PHP 8 type resolution order and safe autoloading for newer syntax

75 of 128 new or added lines in 11 files covered. (58.59%)

3 existing lines in 1 file now uncovered.

1545 of 1864 relevant lines covered (82.89%)

91.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.38
/src/voku/SimplePhpParser/Model/PHPEnum.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace voku\SimplePhpParser\Model;
6

7
use PhpParser\Node\Stmt\Enum_;
8
use PhpParser\Node\Stmt\EnumCase;
9
use ReflectionClass;
10
use ReflectionEnum;
11
use voku\SimplePhpParser\Parsers\Helper\Utils;
12

13
class PHPEnum extends BasePHPClass
14
{
15
    /**
16
     * @phpstan-var class-string
17
     */
18
    public string $name;
19

20
    /**
21
     * Backing type of the enum (e.g. 'string', 'int'), or null for unit enums.
22
     */
23
    public ?string $scalarType = null;
24

25
    /**
26
     * @var string[]
27
     *
28
     * @phpstan-var class-string[]
29
     */
30
    public array $interfaces = [];
31

32
    /**
33
     * Enum cases, keyed by case name. Value is the backing value (string|int) or null for unit enum cases.
34
     *
35
     * @var array<string, string|int|null>
36
     */
37
    public array $cases = [];
38

39
    /**
40
     * @param Enum_ $node
41
     * @param null  $dummy
42
     *
43
     * @return $this
44
     */
45
    public function readObjectFromPhpNode($node, $dummy = null): self
46
    {
47
        $this->prepareNode($node);
48✔
48

49
        $this->name = static::getFQN($node);
48✔
50

51
        if ($node->scalarType !== null) {
48✔
52
            $this->scalarType = $node->scalarType->toString();
40✔
53
        }
54

55
        // Extract PHP 8.0+ attributes
56
        if (!empty($node->attrGroups)) {
48✔
57
            $this->attributes = Utils::extractAttributesFromAstNode($node->attrGroups);
×
58
        }
59

60
        $enumExists = false;
48✔
61
        try {
62
            if (\class_exists($this->name, true) || \enum_exists($this->name, true)) {
48✔
63
                $enumExists = true;
48✔
64
            }
65
        } catch (\Throwable $e) {
×
66
            // nothing
67
        }
68
        if ($enumExists) {
48✔
69
            $reflectionEnum = Utils::createClassReflectionInstance($this->name);
40✔
70
            $this->readObjectFromReflection($reflectionEnum);
40✔
71
        }
72

73
        $this->collectTags($node);
48✔
74

75
        // Extract enum cases from AST
76
        foreach ($node->stmts as $stmt) {
48✔
77
            if ($stmt instanceof EnumCase) {
48✔
78
                $caseName = $stmt->name->name;
48✔
79
                $caseValue = null;
48✔
80
                if ($stmt->expr !== null) {
48✔
81
                    $caseValue = Utils::getPhpParserValueFromNode($stmt->expr);
40✔
82
                    if ($caseValue === Utils::GET_PHP_PARSER_VALUE_FROM_NODE_HELPER) {
40✔
83
                        $caseValue = null;
×
84
                    }
85
                }
86
                $this->cases[$caseName] = $caseValue;
48✔
87
            }
88
        }
89

90
        if (!empty($node->implements)) {
48✔
91
            foreach ($node->implements as $interfaceObject) {
×
92
                $interfaceFQN = $interfaceObject->toString();
×
93
                /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */
94
                /** @var class-string $interfaceFQN */
95
                $interfaceFQN = $interfaceFQN;
×
96
                $this->interfaces[$interfaceFQN] = $interfaceFQN;
×
97
            }
98
        }
99

100
        foreach ($node->getMethods() as $method) {
48✔
101
            $methodNameTmp = $method->name->name;
40✔
102

103
            if (isset($this->methods[$methodNameTmp])) {
40✔
104
                $this->methods[$methodNameTmp] = $this->methods[$methodNameTmp]->readObjectFromPhpNode($method, $this->name);
32✔
105
            } else {
106
                $this->methods[$methodNameTmp] = (new PHPMethod($this->parserContainer))->readObjectFromPhpNode($method, $this->name);
8✔
107
            }
108

109
            if (!$this->methods[$methodNameTmp]->file) {
40✔
110
                $this->methods[$methodNameTmp]->file = $this->file;
8✔
111
            }
112
        }
113

114
        foreach ($node->getConstants() as $constNode) {
48✔
115
            foreach ($constNode->consts as $const) {
×
116
                $constNameTmp = $const->name->name;
×
117

118
                if (isset($this->constants[$constNameTmp])) {
×
119
                    $this->constants[$constNameTmp] = $this->constants[$constNameTmp]->readObjectFromPhpNode($const);
×
120
                } else {
121
                    $this->constants[$constNameTmp] = (new PHPConst($this->parserContainer))->readObjectFromPhpNode($const);
×
122
                }
123

124
                if (!$this->constants[$constNameTmp]->file) {
×
125
                    $this->constants[$constNameTmp]->file = $this->file;
×
126
                }
127
            }
128
        }
129

130
        return $this;
48✔
131
    }
132

133
    /**
134
     * @param ReflectionClass<object> $clazz
135
     *
136
     * @return $this
137
     */
138
    public function readObjectFromReflection($clazz): self
139
    {
140
        $this->name = $clazz->getName();
40✔
141

142
        if (!$this->line) {
40✔
143
            $lineTmp = $clazz->getStartLine();
×
144
            if ($lineTmp !== false) {
×
145
                $this->line = $lineTmp;
×
146
            }
147
        }
148

149
        $file = $clazz->getFileName();
40✔
150
        if ($file) {
40✔
151
            $this->file = $file;
40✔
152
        }
153

154
        $this->is_final = $clazz->isFinal();
40✔
155

156
        // Extract PHP 8.0+ attributes
157
        $this->attributes = Utils::extractAttributesFromReflection($clazz);
40✔
158

159
        if ($clazz instanceof ReflectionEnum) {
40✔
160
            $backingType = $clazz->getBackingType();
×
161
            if ($backingType !== null) {
×
NEW
162
                $this->scalarType = $backingType->getName();
×
163
            }
164

165
            foreach ($clazz->getCases() as $case) {
×
166
                $caseName = $case->getName();
×
167
                $caseValue = null;
×
168
                if ($clazz->isBacked() && \method_exists($case, 'getBackingValue')) {
×
169
                    $caseValue = $case->getBackingValue();
×
170
                }
171
                $this->cases[$caseName] = $caseValue;
×
172
            }
173
        }
174

175
        foreach ($clazz->getInterfaceNames() as $interfaceName) {
40✔
176
            /** @noinspection PhpSillyAssignmentInspection - hack for phpstan */
177
            /** @var class-string $interfaceName */
178
            $interfaceName = $interfaceName;
40✔
179
            $this->interfaces[$interfaceName] = $interfaceName;
40✔
180
        }
181

182
        foreach ($clazz->getMethods() as $method) {
40✔
183
            $methodNameTmp = $method->getName();
40✔
184

185
            $this->methods[$methodNameTmp] = (new PHPMethod($this->parserContainer))->readObjectFromReflection($method);
40✔
186

187
            if (!$this->methods[$methodNameTmp]->file) {
40✔
188
                $this->methods[$methodNameTmp]->file = $this->file;
40✔
189
            }
190
        }
191

192
        foreach ($clazz->getReflectionConstants() as $constant) {
40✔
193
            $constantNameTmp = $constant->getName();
40✔
194

195
            $this->constants[$constantNameTmp] = (new PHPConst($this->parserContainer))->readObjectFromReflection($constant);
40✔
196

197
            if (!$this->constants[$constantNameTmp]->file) {
40✔
198
                $this->constants[$constantNameTmp]->file = $this->file;
×
199
            }
200
        }
201

202
        return $this;
40✔
203
    }
204
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc