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

voku / Simple-PHP-Code-Parser / 5709606856

pending completion
5709606856

push

github

voku
[+]: ci: run test with PHP 8.2

1305 of 1592 relevant lines covered (81.97%)

7.61 hits per line

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

65.07
/src/voku/SimplePhpParser/Model/PHPTrait.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace voku\SimplePhpParser\Model;
6

7
use PhpParser\Comment\Doc;
8
use PhpParser\Node\Stmt\Trait_;
9
use ReflectionClass;
10
use voku\SimplePhpParser\Parsers\Helper\Utils;
11

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

19
    /**
20
     * @param Trait_ $node
21
     * @param null   $dummy
22
     *
23
     * @return $this
24
     */
25
    public function readObjectFromPhpNode($node, $dummy = null): self
26
    {
27
        $this->prepareNode($node);
2✔
28

29
        $this->name = static::getFQN($node);
2✔
30

31
        if (\trait_exists($this->name, true)) {
2✔
32
            $reflectionClass = Utils::createClassReflectionInstance($this->name);
2✔
33
            $this->readObjectFromReflection($reflectionClass);
2✔
34
        }
35

36
        $this->collectTags($node);
2✔
37

38
        $docComment = $node->getDocComment();
2✔
39
        if ($docComment) {
2✔
40
            $this->readPhpDocProperties($docComment);
2✔
41
        }
42

43
        foreach ($node->getProperties() as $property) {
2✔
44
            $propertyNameTmp = $this->getConstantFQN($property, $property->props[0]->name->name);
2✔
45

46
            if (isset($this->properties[$propertyNameTmp])) {
2✔
47
                $this->properties[$propertyNameTmp] = $this->properties[$propertyNameTmp]->readObjectFromPhpNode($property, $this->name);
2✔
48
            } else {
49
                $this->properties[$propertyNameTmp] = (new PHPProperty($this->parserContainer))->readObjectFromPhpNode($property, $this->name);
×
50
            }
51
        }
52

53
        foreach ($node->getMethods() as $method) {
2✔
54
            $methodNameTmp = $method->name->name;
2✔
55

56
            if (isset($this->methods[$methodNameTmp])) {
2✔
57
                $this->methods[$methodNameTmp] = $this->methods[$methodNameTmp]->readObjectFromPhpNode($method, $this->name);
2✔
58
            } else {
59
                $this->methods[$methodNameTmp] = (new PHPMethod($this->parserContainer))->readObjectFromPhpNode($method, $this->name);
×
60
            }
61

62
            if (!$this->methods[$methodNameTmp]->file) {
2✔
63
                $this->methods[$methodNameTmp]->file = $this->file;
×
64
            }
65
        }
66

67
        return $this;
2✔
68
    }
69

70
    /**
71
     * @param ReflectionClass $clazz
72
     *
73
     * @return $this
74
     */
75
    public function readObjectFromReflection($clazz): self
76
    {
77
        if (!$clazz->isTrait()) {
2✔
78
            return $this;
×
79
        }
80

81
        $this->name = $clazz->getName();
2✔
82

83
        if (!$this->line) {
2✔
84
            $lineTmp = $clazz->getStartLine();
×
85
            if ($lineTmp !== false) {
×
86
                $this->line = $lineTmp;
×
87
            }
88
        }
89

90
        $file = $clazz->getFileName();
2✔
91
        if ($file) {
2✔
92
            $this->file = $file;
2✔
93
        }
94

95
        $this->is_final = $clazz->isFinal();
2✔
96

97
        $this->is_abstract = $clazz->isAbstract();
2✔
98

99
        $this->is_anonymous = $clazz->isAnonymous();
2✔
100

101
        $this->is_cloneable = $clazz->isCloneable();
2✔
102

103
        $this->is_instantiable = $clazz->isInstantiable();
2✔
104

105
        $this->is_iterable = $clazz->isIterable();
2✔
106

107
        foreach ($clazz->getProperties() as $property) {
2✔
108
            $propertyPhp = (new PHPProperty($this->parserContainer))->readObjectFromReflection($property);
2✔
109
            $this->properties[$propertyPhp->name] = $propertyPhp;
2✔
110
        }
111

112
        foreach ($clazz->getMethods() as $method) {
2✔
113
            $methodNameTmp = $method->getName();
2✔
114

115
            $this->methods[$methodNameTmp] = (new PHPMethod($this->parserContainer))->readObjectFromReflection($method);
2✔
116

117
            if (!$this->methods[$methodNameTmp]->file) {
2✔
118
                $this->methods[$methodNameTmp]->file = $this->file;
×
119
            }
120
        }
121

122
        foreach ($clazz->getReflectionConstants() as $constant) {
2✔
123
            $constantNameTmp = $constant->getName();
×
124

125
            $this->constants[$constantNameTmp] = (new PHPConst($this->parserContainer))->readObjectFromReflection($constant);
×
126

127
            if (!$this->constants[$constantNameTmp]->file) {
×
128
                $this->constants[$constantNameTmp]->file = $this->file;
×
129
            }
130
        }
131

132
        return $this;
2✔
133
    }
134

135
    /**
136
     * @param string[] $access
137
     * @param bool     $skipMethodsWithLeadingUnderscore
138
     *
139
     * @return array
140
     *
141
     * @psalm-return array<string, array{type: null|string, typeFromPhpDocMaybeWithComment: null|string, typeFromPhpDoc: null|string, typeFromPhpDocSimple: null|string, typeFromPhpDocExtended: null|string, typeFromDefaultValue: null|string}>
142
     */
143
    public function getPropertiesInfo(
144
        array $access = ['public', 'protected', 'private'],
145
        bool $skipMethodsWithLeadingUnderscore = false
146
    ): array {
147
        // init
148
        $allInfo = [];
1✔
149

150
        foreach ($this->properties as $property) {
1✔
151
            if (!\in_array($property->access, $access, true)) {
1✔
152
                continue;
×
153
            }
154

155
            if ($skipMethodsWithLeadingUnderscore && \strpos($property->name, '_') === 0) {
1✔
156
                continue;
×
157
            }
158

159
            $types = [];
1✔
160
            $types['type'] = $property->type;
1✔
161
            $types['typeFromPhpDocMaybeWithComment'] = $property->typeFromPhpDocMaybeWithComment;
1✔
162
            $types['typeFromPhpDoc'] = $property->typeFromPhpDoc;
1✔
163
            $types['typeFromPhpDocSimple'] = $property->typeFromPhpDocSimple;
1✔
164
            $types['typeFromPhpDocExtended'] = $property->typeFromPhpDocExtended;
1✔
165
            $types['typeFromDefaultValue'] = $property->typeFromDefaultValue;
1✔
166

167
            $allInfo[$property->name] = $types;
1✔
168
        }
169

170
        return $allInfo;
1✔
171
    }
172

173
    /**
174
     * @param string[] $access
175
     * @param bool     $skipDeprecatedMethods
176
     * @param bool     $skipMethodsWithLeadingUnderscore
177
     *
178
     * @return array<mixed>
179
     *
180
     * @psalm-return array<string, array{
181
     *     fullDescription: string,
182
     *     line: null|int,
183
     *     file: null|string,
184
     *     error: string,
185
     *     is_deprecated: bool,
186
     *     is_static: null|bool,
187
     *     is_meta: bool,
188
     *     is_internal: bool,
189
     *     is_removed: bool,
190
     *     paramsTypes: array<string,
191
     *         array{
192
     *              type?: null|string,
193
     *              typeFromPhpDoc?: null|string,
194
     *              typeFromPhpDocExtended?: null|string,
195
     *              typeFromPhpDocSimple?: null|string,
196
     *              typeFromPhpDocMaybeWithComment?: null|string,
197
     *              typeFromDefaultValue?: null|string
198
     *         }
199
     *     >,
200
     *     returnTypes: array{
201
     *         type: null|string,
202
     *         typeFromPhpDoc: null|string,
203
     *         typeFromPhpDocExtended: null|string,
204
     *         typeFromPhpDocSimple: null|string,
205
     *         typeFromPhpDocMaybeWithComment: null|string
206
     *     },
207
     *     paramsPhpDocRaw: array<string, null|string>,
208
     *     returnPhpDocRaw: null|string
209
     *  }>
210
     */
211
    public function getMethodsInfo(
212
        array $access = ['public', 'protected', 'private'],
213
        bool $skipDeprecatedMethods = false,
214
        bool $skipMethodsWithLeadingUnderscore = false
215
    ): array {
216
        // init
217
        $allInfo = [];
1✔
218

219
        foreach ($this->methods as $method) {
1✔
220
            if (!\in_array($method->access, $access, true)) {
1✔
221
                continue;
×
222
            }
223

224
            if ($skipDeprecatedMethods && $method->hasDeprecatedTag) {
1✔
225
                continue;
×
226
            }
227

228
            if ($skipMethodsWithLeadingUnderscore && \strpos($method->name, '_') === 0) {
1✔
229
                continue;
×
230
            }
231

232
            $paramsTypes = [];
1✔
233
            foreach ($method->parameters as $tagParam) {
1✔
234
                $paramsTypes[$tagParam->name]['type'] = $tagParam->type;
×
235
                $paramsTypes[$tagParam->name]['typeFromPhpDocMaybeWithComment'] = $tagParam->typeFromPhpDocMaybeWithComment;
×
236
                $paramsTypes[$tagParam->name]['typeFromPhpDoc'] = $tagParam->typeFromPhpDoc;
×
237
                $paramsTypes[$tagParam->name]['typeFromPhpDocSimple'] = $tagParam->typeFromPhpDocSimple;
×
238
                $paramsTypes[$tagParam->name]['typeFromPhpDocExtended'] = $tagParam->typeFromPhpDocExtended;
×
239
                $paramsTypes[$tagParam->name]['typeFromDefaultValue'] = $tagParam->typeFromDefaultValue;
×
240
            }
241

242
            $returnTypes = [];
1✔
243
            $returnTypes['type'] = $method->returnType;
1✔
244
            $returnTypes['typeFromPhpDocMaybeWithComment'] = $method->returnTypeFromPhpDocMaybeWithComment;
1✔
245
            $returnTypes['typeFromPhpDoc'] = $method->returnTypeFromPhpDoc;
1✔
246
            $returnTypes['typeFromPhpDocSimple'] = $method->returnTypeFromPhpDocSimple;
1✔
247
            $returnTypes['typeFromPhpDocExtended'] = $method->returnTypeFromPhpDocExtended;
1✔
248

249
            $paramsPhpDocRaw = [];
1✔
250
            foreach ($method->parameters as $tagParam) {
1✔
251
                $paramsPhpDocRaw[$tagParam->name] = $tagParam->phpDocRaw;
×
252
            }
253

254
            $infoTmp = [];
1✔
255
            $infoTmp['fullDescription'] = \trim($method->summary . "\n\n" . $method->description);
1✔
256
            $infoTmp['paramsTypes'] = $paramsTypes;
1✔
257
            $infoTmp['returnTypes'] = $returnTypes;
1✔
258
            $infoTmp['paramsPhpDocRaw'] = $paramsPhpDocRaw;
1✔
259
            $infoTmp['returnPhpDocRaw'] = $method->returnPhpDocRaw;
1✔
260
            $infoTmp['line'] = $method->line ?? $this->line;
1✔
261
            $infoTmp['file'] = $method->file ?? $this->file;
1✔
262
            $infoTmp['error'] = \implode("\n", $method->parseError);
1✔
263
            foreach ($method->parameters as $parameter) {
1✔
264
                $infoTmp['error'] .= ($infoTmp['error'] ? "\n" : '') . \implode("\n", $parameter->parseError);
×
265
            }
266
            $infoTmp['is_deprecated'] = $method->hasDeprecatedTag;
1✔
267
            $infoTmp['is_static'] = $method->is_static;
1✔
268
            $infoTmp['is_meta'] = $method->hasMetaTag;
1✔
269
            $infoTmp['is_internal'] = $method->hasInternalTag;
1✔
270
            $infoTmp['is_removed'] = $method->hasRemovedTag;
1✔
271

272
            $allInfo[$method->name] = $infoTmp;
1✔
273
        }
274

275
        \asort($allInfo);
1✔
276

277
        return $allInfo;
1✔
278
    }
279

280
    /**
281
     * @param Doc|string $doc
282
     */
283
    private function readPhpDocProperties($doc): void
284
    {
285
        if ($doc instanceof Doc) {
2✔
286
            $docComment = $doc->getText();
2✔
287
        } else {
288
            $docComment = $doc;
×
289
        }
290
        if ($docComment === '') {
2✔
291
            return;
×
292
        }
293

294
        try {
295
            $phpDoc = Utils::createDocBlockInstance()->create($docComment);
2✔
296

297
            $parsedPropertyTags = $phpDoc->getTagsByName('property')
2✔
298
                               + $phpDoc->getTagsByName('property-read')
2✔
299
                               + $phpDoc->getTagsByName('property-write');
2✔
300

301
            if (!empty($parsedPropertyTags)) {
2✔
302
                foreach ($parsedPropertyTags as $parsedPropertyTag) {
2✔
303
                    if (
304
                        $parsedPropertyTag instanceof \phpDocumentor\Reflection\DocBlock\Tags\PropertyRead
×
305
                        ||
306
                        $parsedPropertyTag instanceof \phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite
×
307
                        ||
308
                        $parsedPropertyTag instanceof \phpDocumentor\Reflection\DocBlock\Tags\Property
×
309
                    ) {
310
                        $propertyPhp = new PHPProperty($this->parserContainer);
×
311

312
                        $nameTmp = $parsedPropertyTag->getVariableName();
×
313
                        if (!$nameTmp) {
×
314
                            continue;
×
315
                        }
316

317
                        $propertyPhp->name = $nameTmp;
×
318

319
                        $propertyPhp->access = 'public';
×
320

321
                        $type = $parsedPropertyTag->getType();
×
322

323
                        $propertyPhp->typeFromPhpDoc = Utils::normalizePhpType($type . '');
×
324

325
                        $typeFromPhpDocMaybeWithCommentTmp = \trim((string) $parsedPropertyTag);
×
326
                        if (
327
                            $typeFromPhpDocMaybeWithCommentTmp
×
328
                            &&
329
                            \strpos($typeFromPhpDocMaybeWithCommentTmp, '$') !== 0
×
330
                        ) {
331
                            $propertyPhp->typeFromPhpDocMaybeWithComment = $typeFromPhpDocMaybeWithCommentTmp;
×
332
                        }
333

334
                        $typeTmp = Utils::parseDocTypeObject($type);
×
335
                        if ($typeTmp !== '') {
×
336
                            $propertyPhp->typeFromPhpDocSimple = $typeTmp;
×
337
                        }
338

339
                        if ($propertyPhp->typeFromPhpDoc) {
×
340
                            $propertyPhp->typeFromPhpDocExtended = Utils::modernPhpdoc($propertyPhp->typeFromPhpDoc);
×
341
                        }
342

343
                        $this->properties[$propertyPhp->name] = $propertyPhp;
×
344
                    }
345
                }
346
            }
347
        } catch (\Exception $e) {
×
348
            $tmpErrorMessage = ($this->name ?: '?') . ':' . ($this->line ?? '?') . ' | ' . \print_r($e->getMessage(), true);
×
349
            $this->parseError[\md5($tmpErrorMessage)] = $tmpErrorMessage;
×
350
        }
351
    }
352
}
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