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

voku / Simple-PHP-Code-Parser / 6075558303

04 Sep 2023 03:54PM UTC coverage: 82.259%. Remained the same
6075558303

push

github

web-flow
Merge pull request #45 from voku/analysis-dj2339

Apply fixes from StyleCI

1340 of 1629 relevant lines covered (82.26%)

28.27 hits per line

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

79.7
/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);
39✔
55

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

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

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

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

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

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

75
        if ($node->type !== null) {
39✔
76
            if (!$this->type) {
4✔
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) {
4✔
88
                if ($this->type && $this->type !== 'null' && \strpos($this->type, 'null|') !== 0) {
3✔
89
                    $this->type = 'null|' . $this->type;
×
90
                } elseif (!$this->type) {
3✔
91
                    $this->type = 'null|mixed';
×
92
                }
93
            }
94
        }
95

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

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

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

113
        return $this;
39✔
114
    }
115

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

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

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

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

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

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

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

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

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

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

185
        if ($property->isProtected()) {
49✔
186
            $access = 'protected';
15✔
187
        } elseif ($property->isPrivate()) {
45✔
188
            $access = 'private';
4✔
189
        } else {
190
            $access = 'public';
45✔
191
        }
192
        $this->access = $access;
49✔
193

194
        return $this;
49✔
195
    }
196

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

206
        if ($this->type) {
3✔
207
            return $this->type;
3✔
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) {
47✔
223
            $docComment = $doc->getText();
39✔
224
        } else {
225
            $docComment = $doc;
47✔
226
        }
227
        if ($docComment === '') {
47✔
228
            return;
×
229
        }
230

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

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

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

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

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

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

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

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

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

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

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

277
                    $this->typeFromPhpDocExtended = Utils::modernPhpdoc($parsedParamTagStr);
24✔
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);
47✔
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);
47✔
299

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

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

308
                continue;
47✔
309
            }
310

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

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