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

voku / Simple-PHP-Code-Parser / 29085974904

10 Jul 2026 10:10AM UTC coverage: 85.103% (+0.3%) from 84.815%
29085974904

push

github

moellekenl
[+]: Add support for source-range information and enhance PHP model elements

- Introduced `endLine`, `startFilePos`, and `endFilePos` properties to model elements for precise source navigation.
- Added `PHPFileInfo` class for lightweight file summaries and improved AST access methods.
- Enhanced trait handling in class-like models to include trait uses and adaptations.
- Updated method and parameter models to include `is_returned_by_ref` and `is_promoted` properties.
- Improved PHPDoc context handling for better type resolution in models.

269 of 316 new or added lines in 16 files covered. (85.13%)

1 existing line in 1 file now uncovered.

1988 of 2336 relevant lines covered (85.1%)

175.85 hits per line

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

98.18
/src/voku/SimplePhpParser/Model/PHPFileInfo.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace voku\SimplePhpParser\Model;
6

7
use PhpParser\Node;
8
use PhpParser\Node\Stmt\Declare_;
9
use PhpParser\Node\Stmt\GroupUse;
10
use PhpParser\Node\Stmt\Namespace_;
11
use PhpParser\Node\Stmt\Use_;
12
use voku\SimplePhpParser\Parsers\Helper\Utils;
13

14
/**
15
 * Compact file-scope metadata for consumers that need to resolve a symbol or
16
 * PHPDoc without reading the source header again.
17
 */
18
final class PHPFileInfo
19
{
20
    /**
21
     * @param list<array{
22
     *     name: string,
23
     *     line: int|null,
24
     *     endLine: int|null,
25
     *     imports: list<array{name: string, alias: string, kind: 'class'|'const'|'function', line: int|null, endLine: int|null}>,
26
     *     declares: array<string, bool|float|int|string|null>
27
     * }> $namespaces
28
     */
29
    public function __construct(
30
        public ?string $file,
31
        public array $namespaces,
32
    ) {
33
    }
10✔
34

35
    /**
36
     * @param array<int, Node> $ast
37
     */
38
    public static function fromAst(array $ast, ?string $file = null): self
39
    {
40
        $namespaces = [];
10✔
41
        $globalStatements = [];
10✔
42

43
        foreach ($ast as $statement) {
10✔
44
            if ($statement instanceof Namespace_) {
10✔
45
                $namespaces[] = self::scope($statement->name?->toString() ?? '', $statement->stmts, $statement);
10✔
46

47
                continue;
10✔
48
            }
49

50
            $globalStatements[] = $statement;
10✔
51
        }
52

53
        if ($globalStatements !== []) {
10✔
54
            \array_unshift($namespaces, self::scope('', $globalStatements));
10✔
55
        }
56

57
        return new self($file, $namespaces);
10✔
58
    }
59

60
    /**
61
     * @param array<int, Node> $statements
62
     *
63
     * @return array{
64
     *     name: string,
65
     *     line: int|null,
66
     *     endLine: int|null,
67
     *     imports: list<array{name: string, alias: string, kind: 'class'|'const'|'function', line: int|null, endLine: int|null}>,
68
     *     declares: array<string, bool|float|int|string|null>
69
     * }
70
     */
71
    private static function scope(string $name, array $statements, ?Namespace_ $namespace = null): array
72
    {
73
        $imports = [];
10✔
74
        $declares = [];
10✔
75

76
        foreach ($statements as $statement) {
10✔
77
            if ($statement instanceof Use_) {
10✔
78
                $imports = \array_merge($imports, self::imports($statement->uses, $statement->type));
10✔
79

80
                continue;
10✔
81
            }
82

83
            if ($statement instanceof GroupUse) {
10✔
84
                $imports = \array_merge($imports, self::imports($statement->uses, $statement->type, $statement->prefix->toString()));
10✔
85

86
                continue;
10✔
87
            }
88

89
            if (!$statement instanceof Declare_) {
10✔
90
                continue;
10✔
91
            }
92

93
            foreach ($statement->declares as $declare) {
10✔
94
                $value = Utils::getPhpParserValueFromNode($declare->value);
10✔
95
                if ($value === Utils::GET_PHP_PARSER_VALUE_FROM_NODE_HELPER || !\is_scalar($value) && $value !== null) {
10✔
NEW
96
                    continue;
×
97
                }
98

99
                $declares[$declare->key->toString()] = $value;
10✔
100
            }
101
        }
102

103
        return [
10✔
104
            'name'     => $name,
10✔
105
            'line'     => $namespace === null ? null : self::line($namespace, 'getStartLine'),
10✔
106
            'endLine'  => $namespace === null ? null : self::line($namespace, 'getEndLine'),
10✔
107
            'imports'  => $imports,
10✔
108
            'declares' => $declares,
10✔
109
        ];
10✔
110
    }
111

112
    /**
113
     * @param array<int, \PhpParser\Node\UseItem> $uses
114
     *
115
     * @return list<array{name: string, alias: string, kind: 'class'|'const'|'function', line: int|null, endLine: int|null}>
116
     */
117
    private static function imports(array $uses, int $parentType, string $prefix = ''): array
118
    {
119
        $imports = [];
10✔
120

121
        foreach ($uses as $use) {
10✔
122
            $type = $use->type === Use_::TYPE_UNKNOWN ? $parentType : $use->type;
10✔
123
            $name = $use->name->toString();
10✔
124
            if ($prefix !== '') {
10✔
125
                $name = $prefix . '\\' . $name;
10✔
126
            }
127

128
            $imports[] = [
10✔
129
                'name'    => $name,
10✔
130
                'alias'   => $use->getAlias()->toString(),
10✔
131
                'kind'    => self::importKind($type),
10✔
132
                'line'    => self::line($use, 'getStartLine'),
10✔
133
                'endLine' => self::line($use, 'getEndLine'),
10✔
134
            ];
10✔
135
        }
136

137
        return $imports;
10✔
138
    }
139

140
    /**
141
     * @return 'class'|'const'|'function'
142
     */
143
    private static function importKind(int $type): string
144
    {
145
        if ($type === Use_::TYPE_FUNCTION) {
10✔
146
            return 'function';
10✔
147
        }
148

149
        if ($type === Use_::TYPE_CONSTANT) {
10✔
150
            return 'const';
10✔
151
        }
152

153
        return 'class';
10✔
154
    }
155

156
    private static function line(Node $node, string $method): ?int
157
    {
158
        $line = $node->{$method}();
10✔
159

160
        return $line >= 0 ? $line : null;
10✔
161
    }
162
}
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