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

voku / Simple-PHP-Code-Parser / 24276710420

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

Pull #79

github

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

98 of 128 new or added lines in 11 files covered. (76.56%)

12 existing lines in 5 files now uncovered.

1545 of 1864 relevant lines covered (82.89%)

45.52 hits per line

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

70.0
/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
    /**
16
     * @var array<string, PHPMethod>
17
     */
18
    public array $methods = [];
19

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

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

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

37
    public ?bool $is_final = null;
38

39
    public ?bool $is_abstract = null;
40

41
    public ?bool $is_readonly = null;
42

43
    public ?bool $is_anonymous = null;
44

45
    public ?bool $is_cloneable = null;
46

47
    public ?bool $is_instantiable = null;
48

49
    public ?bool $is_iterable = null;
50

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

61
        if (\PHP_VERSION_ID < self::PHP_VERSION_8_3_0 && self::containsPHP83PlusSyntax($node)) {
8✔
NEW
62
            return false;
×
63
        }
64

65
        return true;
8✔
66
    }
67

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

82
        if ($node instanceof \PhpParser\Node\UnionType) {
2✔
NEW
83
            foreach ($node->types as $innerType) {
×
NEW
84
                if ($innerType instanceof \PhpParser\Node\IntersectionType) {
×
NEW
85
                    return true;
×
86
                }
87
            }
88
        }
89

90
        if ($node instanceof \PhpParser\Node\Identifier) {
2✔
91
            $typeName = \strtolower($node->name);
2✔
92

93
            // Standalone null/true/false are represented as Identifier nodes too,
94
            // so they are only PHP 8.2+ when they are not part of a union type.
95
            if (
96
                ($typeName === 'null' || $typeName === 'true' || $typeName === 'false')
2✔
97
                &&
98
                !($node->getAttribute('parent') instanceof \PhpParser\Node\UnionType)
2✔
99
            ) {
NEW
100
                return true;
×
101
            }
102
        }
103

104
        foreach ($node->getSubNodeNames() as $subNodeName) {
2✔
105
            $subNode = $node->{$subNodeName};
2✔
106

107
            if ($subNode instanceof \PhpParser\Node && self::containsPHP82PlusSyntax($subNode)) {
2✔
NEW
108
                return true;
×
109
            }
110

111
            if (!\is_array($subNode)) {
2✔
112
                continue;
2✔
113
            }
114

115
            foreach ($subNode as $subNodeInner) {
2✔
116
                if ($subNodeInner instanceof \PhpParser\Node && self::containsPHP82PlusSyntax($subNodeInner)) {
2✔
NEW
117
                    return true;
×
118
                }
119
            }
120
        }
121

122
        return false;
2✔
123
    }
124

125
    /**
126
     * Detect PHP 8.3-only syntax within a class-like AST such as typed class constants.
127
     */
128
    private static function containsPHP83PlusSyntax(\PhpParser\Node $node): bool
129
    {
130
        if (
131
            $node instanceof \PhpParser\Node\Stmt\ClassConst
4✔
132
            &&
133
            $node->type !== null
4✔
134
        ) {
NEW
135
            return true;
×
136
        }
137

138
        foreach ($node->getSubNodeNames() as $subNodeName) {
4✔
139
            $subNode = $node->{$subNodeName};
4✔
140

141
            if ($subNode instanceof \PhpParser\Node && self::containsPHP83PlusSyntax($subNode)) {
4✔
NEW
142
                return true;
×
143
            }
144

145
            if (!\is_array($subNode)) {
4✔
146
                continue;
4✔
147
            }
148

149
            foreach ($subNode as $subNodeInner) {
4✔
150
                if ($subNodeInner instanceof \PhpParser\Node && self::containsPHP83PlusSyntax($subNodeInner)) {
4✔
NEW
151
                    return true;
×
152
                }
153
            }
154
        }
155

156
        return false;
4✔
157
    }
158
}
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