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

voku / Simple-PHP-Code-Parser / 24698292519

21 Apr 2026 12:55AM UTC coverage: 84.815%. Remained the same
24698292519

push

github

web-flow
Merge pull request #87 from voku/renovate/actions-cache-5.x

Update actions/cache action to v5.0.5

1765 of 2081 relevant lines covered (84.81%)

165.91 hits per line

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

93.55
/src/voku/SimplePhpParser/Model/BasePHPClass.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace voku\SimplePhpParser\Model;
6

7
abstract class BasePHPClass extends BasePHPElement
8
{
9
    use PHPDocElement;
10

11
    private const PHP_VERSION_8_2_0 = 80200;
12

13
    private const PHP_VERSION_8_3_0 = 80300;
14

15
    private const PHP_VERSION_8_4_0 = 80400;
16

17
    /**
18
     * @var array<string, PHPMethod>
19
     */
20
    public array $methods = [];
21

22
    /**
23
     * @var array<string, PHPProperty>
24
     */
25
    public array $properties = [];
26

27
    /**
28
     * @var array<string, PHPConst>
29
     */
30
    public array $constants = [];
31

32
    /**
33
     * PHP 8.0+ attributes on this class/interface/trait/enum.
34
     *
35
     * @var PHPAttribute[]
36
     */
37
    public array $attributes = [];
38

39
    public ?bool $is_final = null;
40

41
    public ?bool $is_abstract = null;
42

43
    public ?bool $is_readonly = null;
44

45
    public ?bool $is_anonymous = null;
46

47
    public ?bool $is_cloneable = null;
48

49
    public ?bool $is_instantiable = null;
50

51
    public ?bool $is_iterable = null;
52

53
    /**
54
     * Check if the parsed class-like node can be safely autoloaded on the
55
     * current runtime without triggering fatal syntax errors from newer PHP features.
56
     */
57
    protected static function canAutoloadFromPhpNode(\PhpParser\Node $node): bool
58
    {
59
        if (\PHP_VERSION_ID < self::PHP_VERSION_8_2_0 && self::containsPHP82PlusSyntax($node)) {
551✔
60
            return false;
10✔
61
        }
62

63
        if (\PHP_VERSION_ID < self::PHP_VERSION_8_3_0 && self::containsPHP83PlusSyntax($node)) {
547✔
64
            return false;
12✔
65
        }
66

67
        if (\PHP_VERSION_ID < self::PHP_VERSION_8_4_0 && self::containsPHP84PlusSyntax($node)) {
547✔
68
            return false;
18✔
69
        }
70

71
        return true;
541✔
72
    }
73

74
    /**
75
     * Detect PHP 8.2-only syntax within a class-like AST such as readonly classes,
76
     * DNF types, and standalone null/true/false types.
77
     */
78
    private static function containsPHP82PlusSyntax(\PhpParser\Node $node): bool
79
    {
80
        if (
81
            $node instanceof \PhpParser\Node\Stmt\Class_
103✔
82
            &&
83
            $node->isReadonly()
103✔
84
        ) {
85
            return true;
2✔
86
        }
87

88
        if ($node instanceof \PhpParser\Node\UnionType) {
103✔
89
            foreach ($node->types as $innerType) {
14✔
90
                if ($innerType instanceof \PhpParser\Node\IntersectionType) {
14✔
91
                    return true;
6✔
92
                }
93
            }
94
        }
95

96
        if ($node instanceof \PhpParser\Node\Identifier) {
103✔
97
            $typeName = \strtolower($node->name);
103✔
98

99
            // Standalone null/true/false are represented as Identifier nodes too,
100
            // so they are only PHP 8.2+ when they are not part of a union type.
101
            if (
102
                ($typeName === 'null' || $typeName === 'true' || $typeName === 'false')
103✔
103
                &&
104
                !($node->getAttribute('parent') instanceof \PhpParser\Node\UnionType)
103✔
105
            ) {
106
                return true;
10✔
107
            }
108
        }
109

110
        foreach ($node->getSubNodeNames() as $subNodeName) {
103✔
111
            $subNode = $node->{$subNodeName};
103✔
112

113
            if ($subNode instanceof \PhpParser\Node && self::containsPHP82PlusSyntax($subNode)) {
103✔
114
                return true;
10✔
115
            }
116

117
            if (!\is_array($subNode)) {
103✔
118
                continue;
103✔
119
            }
120

121
            foreach ($subNode as $subNodeInner) {
103✔
122
                if ($subNodeInner instanceof \PhpParser\Node && self::containsPHP82PlusSyntax($subNodeInner)) {
101✔
123
                    return true;
10✔
124
                }
125
            }
126
        }
127

128
        return false;
103✔
129
    }
130

131
    /**
132
     * Detect PHP 8.3-only syntax within a class-like AST such as typed class constants.
133
     */
134
    private static function containsPHP83PlusSyntax(\PhpParser\Node $node): bool
135
    {
136
        if (
137
            $node instanceof \PhpParser\Node\Stmt\ClassConst
208✔
138
            &&
139
            $node->type !== null
208✔
140
        ) {
141
            return true;
12✔
142
        }
143

144
        foreach ($node->getSubNodeNames() as $subNodeName) {
208✔
145
            $subNode = $node->{$subNodeName};
208✔
146

147
            if ($subNode instanceof \PhpParser\Node && self::containsPHP83PlusSyntax($subNode)) {
208✔
148
                return true;
×
149
            }
150

151
            if (!\is_array($subNode)) {
208✔
152
                continue;
208✔
153
            }
154

155
            foreach ($subNode as $subNodeInner) {
208✔
156
                if ($subNodeInner instanceof \PhpParser\Node && self::containsPHP83PlusSyntax($subNodeInner)) {
204✔
157
                    return true;
12✔
158
                }
159
            }
160
        }
161

162
        return false;
208✔
163
    }
164

165
    /**
166
     * Detect PHP 8.4-only syntax within a class-like AST such as property hooks
167
     * and asymmetric visibility modifiers.
168
     */
169
    protected static function containsPHP84PlusSyntax(\PhpParser\Node $node): bool
170
    {
171
        // Property hooks (PHP 8.4+)
172
        if ($node instanceof \PhpParser\Node\Stmt\Property && !empty($node->hooks)) {
321✔
173
            return true;
15✔
174
        }
175

176
        // Asymmetric visibility on properties (PHP 8.4+)
177
        if (
178
            $node instanceof \PhpParser\Node\Stmt\Property
321✔
179
            && self::getAsymmetricSetVisibility($node) !== ''
321✔
180
        ) {
181
            return true;
×
182
        }
183

184
        // Property hooks on promoted constructor parameters (PHP 8.4+)
185
        if ($node instanceof \PhpParser\Node\Param && !empty($node->hooks)) {
321✔
186
            return true;
12✔
187
        }
188

189
        // Asymmetric visibility on promoted constructor parameters (PHP 8.4+)
190
        if (
191
            $node instanceof \PhpParser\Node\Param
321✔
192
            && self::getAsymmetricSetVisibility($node) !== ''
321✔
193
        ) {
194
            return true;
×
195
        }
196

197
        foreach ($node->getSubNodeNames() as $subNodeName) {
321✔
198
            $subNode = $node->{$subNodeName};
321✔
199

200
            if ($subNode instanceof \PhpParser\Node && self::containsPHP84PlusSyntax($subNode)) {
321✔
201
                return true;
×
202
            }
203

204
            if (!\is_array($subNode)) {
321✔
205
                continue;
321✔
206
            }
207

208
            foreach ($subNode as $subNodeInner) {
321✔
209
                if ($subNodeInner instanceof \PhpParser\Node && self::containsPHP84PlusSyntax($subNodeInner)) {
315✔
210
                    return true;
18✔
211
                }
212
            }
213
        }
214

215
        return false;
321✔
216
    }
217
}
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