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

voku / Simple-PHP-Code-Parser / 11223107730

07 Oct 2024 08:05PM UTC coverage: 79.926% (-2.3%) from 82.259%
11223107730

Pull #49

github

web-flow
Merge 0aed533f6 into 898f2c327
Pull Request #49: Update actions/checkout digest to eef6144

1302 of 1629 relevant lines covered (79.93%)

6.18 hits per line

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

66.92
/src/voku/SimplePhpParser/Model/PHPProperty.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\Property;
9
use ReflectionProperty;
10
use voku\SimplePhpParser\Parsers\Helper\Utils;
11

12
class PHPProperty extends BasePHPElement
13
{
14
    /**
15
     * @var mixed|null
16
     */
17
    public $defaultValue;
18

19
    public ?string $phpDocRaw = null;
20

21
    public ?string $type = null;
22

23
    public ?string $typeFromDefaultValue = null;
24

25
    public ?string $typeFromPhpDoc = null;
26

27
    public ?string $typeFromPhpDocSimple = null;
28

29
    public ?string $typeFromPhpDocExtended = null;
30

31
    public ?string $typeFromPhpDocMaybeWithComment = null;
32

33
    /**
34
     * @phpstan-var ''|'private'|'protected'|'public'
35
     */
36
    public string $access = '';
37

38
    public ?bool $is_static = null;
39

40
    public ?bool $is_readonly = null;
41

42
    public ?bool $is_inheritdoc = null;
43

44
    /**
45
     * @param Property    $node
46
     * @param string|null $classStr
47
     *
48
     * @phpstan-param class-string|null $classStr
49
     *
50
     * @return $this
51
     */
52
    public function readObjectFromPhpNode($node, $classStr = null): self
53
    {
54
        $this->name = $this->getConstantFQN($node, $node->props[0]->name->name);
8✔
55

56
        $this->is_static = $node->isStatic();
8✔
57

58
        if (method_exists($node, 'isReadonly')) {
8✔
59
            $this->is_readonly = $node->isReadonly();
8✔
60
        }
61

62
        $this->prepareNode($node);
8✔
63

64
        $docComment = $node->getDocComment();
8✔
65
        if ($docComment) {
8✔
66
            $docCommentText = $docComment->getText();
8✔
67

68
            if (\stripos($docCommentText, '@inheritdoc') !== false) {
8✔
69
                $this->is_inheritdoc = true;
×
70
            }
71

72
            $this->readPhpDoc($docComment);
8✔
73
        }
74

75
        if ($node->type !== null) {
8✔
76
            if (!$this->type) {
×
77
                if (\method_exists($node->type, 'getParts')) {
×
78
                    $parts = $node->type->getParts();
×
79
                    if (!empty($parts)) {
×
80
                        $this->type = '\\' . \implode('\\', $parts);
×
81
                    }
82
                } elseif (\property_exists($node->type, 'name') && $node->type->name) {
×
83
                    $this->type = $node->type->name;
×
84
                }
85
            }
86

87
            if ($node->type instanceof \PhpParser\Node\NullableType) {
×
88
                if ($this->type && $this->type !== 'null' && \strpos($this->type, 'null|') !== 0) {
×
89
                    $this->type = 'null|' . $this->type;
×
90
                } elseif (!$this->type) {
×
91
                    $this->type = 'null|mixed';
×
92
                }
93
            }
94
        }
95

96
        if ($node->props[0]->default !== null) {
8✔
97
            $defaultValue = Utils::getPhpParserValueFromNode($node->props[0]->default, $classStr);
5✔
98
            if ($defaultValue !== Utils::GET_PHP_PARSER_VALUE_FROM_NODE_HELPER) {
5✔
99
                $this->defaultValue = $defaultValue;
5✔
100

101
                $this->typeFromDefaultValue = Utils::normalizePhpType(\gettype($this->defaultValue));
5✔
102
            }
103
        }
104

105
        if ($node->isPrivate()) {
8✔
106
            $this->access = 'private';
×
107
        } elseif ($node->isProtected()) {
8✔
108
            $this->access = 'protected';
1✔
109
        } else {
110
            $this->access = 'public';
8✔
111
        }
112

113
        return $this;
8✔
114
    }
115

116
    /**
117
     * @param ReflectionProperty $property
118
     *
119
     * @return $this
120
     */
121
    public function readObjectFromReflection($property): self
122
    {
123
        $this->name = $property->getName();
10✔
124

125
        $file = $property->getDeclaringClass()->getFileName();
10✔
126
        if ($file) {
10✔
127
            $this->file = $file;
10✔
128
        }
129

130
        $this->is_static = $property->isStatic();
10✔
131

132
        if ($this->is_static) {
10✔
133
            try {
134
                if (\class_exists($property->getDeclaringClass()->getName(), true)) {
1✔
135
                    $this->defaultValue = $property->getValue();
1✔
136
                }
137
            } catch (\Exception $e) {
1✔
138
                // nothing
139
            }
140

141
            if ($this->defaultValue !== null) {
1✔
142
                $this->typeFromDefaultValue = Utils::normalizePhpType(\gettype($this->defaultValue));
×
143
            }
144
        }
145

146
        if (method_exists($property, 'isReadOnly')) {
10✔
147
            $this->is_readonly = $property->isReadOnly();
×
148
        }
149

150
        $docComment = $property->getDocComment();
10✔
151
        if ($docComment) {
10✔
152
            if (\stripos($docComment, '@inheritdoc') !== false) {
10✔
153
                $this->is_inheritdoc = true;
×
154
            }
155

156
            $this->readPhpDoc($docComment);
10✔
157
        }
158

159
        if (\method_exists($property, 'getType')) {
10✔
160
            $type = $property->getType();
10✔
161
            if ($type !== null) {
10✔
162
                if (\method_exists($type, 'getName')) {
×
163
                    $this->type = Utils::normalizePhpType($type->getName(), true);
×
164
                } else {
165
                    $this->type = Utils::normalizePhpType($type . '', true);
×
166
                }
167
                try {
168
                    if ($this->type && \class_exists($this->type, true)) {
×
169
                        $this->type = '\\' . \ltrim($this->type, '\\');
×
170
                    }
171
                } catch (\Exception $e) {
×
172
                    // nothing
173
                }
174

175
                if ($type->allowsNull()) {
×
176
                    if ($this->type && $this->type !== 'null' && \strpos($this->type, 'null|') !== 0) {
×
177
                        $this->type = 'null|' . $this->type;
×
178
                    } elseif (!$this->type) {
×
179
                        $this->type = 'null|mixed';
×
180
                    }
181
                }
182
            }
183
        }
184

185
        if ($property->isProtected()) {
10✔
186
            $access = 'protected';
3✔
187
        } elseif ($property->isPrivate()) {
9✔
188
            $access = 'private';
1✔
189
        } else {
190
            $access = 'public';
9✔
191
        }
192
        $this->access = $access;
10✔
193

194
        return $this;
10✔
195
    }
196

197
    /**
198
     * @return string|null
199
     */
200
    public function getType(): ?string
201
    {
202
        if ($this->typeFromPhpDocExtended) {
×
203
            return $this->typeFromPhpDocExtended;
×
204
        }
205

206
        if ($this->type) {
×
207
            return $this->type;
×
208
        }
209

210
        if ($this->typeFromPhpDocSimple) {
×
211
            return $this->typeFromPhpDocSimple;
×
212
        }
213

214
        return null;
×
215
    }
216

217
    /**
218
     * @param Doc|string $doc
219
     */
220
    private function readPhpDoc($doc): void
221
    {
222
        if ($doc instanceof Doc) {
10✔
223
            $docComment = $doc->getText();
8✔
224
        } else {
225
            $docComment = $doc;
10✔
226
        }
227
        if ($docComment === '') {
10✔
228
            return;
×
229
        }
230

231
        try {
232
            $phpDoc = Utils::createDocBlockInstance()->create($docComment);
10✔
233

234
            $parsedParamTags = $phpDoc->getTagsByName('var');
10✔
235

236
            if (!empty($parsedParamTags)) {
10✔
237
                foreach ($parsedParamTags as $parsedParamTag) {
10✔
238
                    $parsedParamTagParam = (string) $parsedParamTag;
10✔
239

240
                    if ($parsedParamTag instanceof \phpDocumentor\Reflection\DocBlock\Tags\Var_) {
10✔
241
                        $type = $parsedParamTag->getType();
10✔
242

243
                        $this->typeFromPhpDoc = Utils::normalizePhpType($type . '');
10✔
244

245
                        $typeFromPhpDocMaybeWithCommentTmp = \trim($parsedParamTagParam);
10✔
246
                        if (
247
                            $typeFromPhpDocMaybeWithCommentTmp
10✔
248
                            &&
249
                            \strpos($typeFromPhpDocMaybeWithCommentTmp, '$') !== 0
10✔
250
                        ) {
251
                            $this->typeFromPhpDocMaybeWithComment = $typeFromPhpDocMaybeWithCommentTmp;
10✔
252
                        }
253

254
                        $typeTmp = Utils::parseDocTypeObject($type);
10✔
255
                        if ($typeTmp !== '') {
10✔
256
                            $this->typeFromPhpDocSimple = $typeTmp;
10✔
257
                        }
258
                    }
259

260
                    $this->phpDocRaw = $parsedParamTagParam;
10✔
261
                    $this->typeFromPhpDocExtended = Utils::modernPhpdoc($parsedParamTagParam);
10✔
262
                }
263
            }
264

265
            $parsedParamTags = $phpDoc->getTagsByName('psalm-var')
10✔
266
                               + $phpDoc->getTagsByName('phpstan-var');
10✔
267

268
            if (!empty($parsedParamTags)) {
10✔
269
                foreach ($parsedParamTags as $parsedParamTag) {
10✔
270
                    if (!$parsedParamTag instanceof \phpDocumentor\Reflection\DocBlock\Tags\Generic) {
6✔
271
                        continue;
×
272
                    }
273

274
                    $spitedData = Utils::splitTypeAndVariable($parsedParamTag);
6✔
275
                    $parsedParamTagStr = $spitedData['parsedParamTagStr'];
6✔
276

277
                    $this->typeFromPhpDocExtended = Utils::modernPhpdoc($parsedParamTagStr);
6✔
278
                }
279
            }
280
        } catch (\Exception $e) {
×
281
            $tmpErrorMessage = $this->name . ':' . ($this->line ?? '?') . ' | ' . \print_r($e->getMessage(), true);
×
282
            $this->parseError[\md5($tmpErrorMessage)] = $tmpErrorMessage;
×
283
        }
284

285
        try {
286
            $this->readPhpDocByTokens($docComment);
10✔
287
        } catch (\Exception $e) {
×
288
            $tmpErrorMessage = $this->name . ':' . ($this->line ?? '?') . ' | ' . \print_r($e->getMessage(), true);
×
289
            $this->parseError[\md5($tmpErrorMessage)] = $tmpErrorMessage;
×
290
        }
291
    }
292

293
    /**
294
     * @throws \PHPStan\PhpDocParser\Parser\ParserException
295
     */
296
    private function readPhpDocByTokens(string $docComment): void
297
    {
298
        $tokens = Utils::modernPhpdocTokens($docComment);
10✔
299

300
        $varContent = null;
10✔
301
        foreach ($tokens->getTokens() as $token) {
10✔
302
            $content = $token[0];
10✔
303

304
            if ($content === '@var' || $content === '@psalm-var' || $content === '@phpstan-var') {
10✔
305
                // reset
306
                $varContent = '';
10✔
307

308
                continue;
10✔
309
            }
310

311
            if ($varContent !== null) {
10✔
312
                $varContent .= $content;
10✔
313
            }
314
        }
315

316
        $varContent = $varContent ? \trim($varContent) : null;
10✔
317
        if ($varContent) {
10✔
318
            if (!$this->phpDocRaw) {
10✔
319
                $this->phpDocRaw = $varContent;
×
320
            }
321
            $this->typeFromPhpDocExtended = Utils::modernPhpdoc($varContent);
10✔
322
        }
323
    }
324
}
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