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

voku / Simple-PHP-Code-Parser / 12186673668

05 Dec 2024 07:26PM UTC coverage: 79.926% (-2.3%) from 82.259%
12186673668

Pull #54

github

web-flow
Merge 30436eb42 into 898f2c327
Pull Request #54: Update actions/cache action to v4

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

81.82
/src/voku/SimplePhpParser/Model/PHPParameter.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\FunctionLike;
9
use PhpParser\Node\Param;
10
use ReflectionParameter;
11
use voku\SimplePhpParser\Parsers\Helper\Utils;
12

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

20
    public ?string $phpDocRaw = null;
21

22
    public ?string $type = null;
23

24
    public ?string $typeFromDefaultValue = null;
25

26
    public ?string $typeFromPhpDoc = null;
27

28
    public ?string $typeFromPhpDocSimple = null;
29

30
    public ?string $typeFromPhpDocExtended = null;
31

32
    public ?string $typeFromPhpDocMaybeWithComment = null;
33

34
    public ?bool $is_vararg = null;
35

36
    public ?bool $is_passed_by_ref = null;
37

38
    public ?bool $is_inheritdoc = null;
39

40
    /**
41
     * @param Param        $parameter
42
     * @param FunctionLike $node
43
     * @param mixed|null   $classStr
44
     *
45
     * @return $this
46
     */
47
    public function readObjectFromPhpNode($parameter, $node = null, $classStr = null): self
48
    {
49
        $parameterVar = $parameter->var;
11✔
50
        if ($parameterVar instanceof \PhpParser\Node\Expr\Error) {
11✔
51
            $this->parseError[] = ($this->line ?? '?') . ':' . ($this->pos ?? '') . ' | may be at this position an expression is required';
×
52

53
            $this->name = \md5(\uniqid('error', true));
×
54

55
            return $this;
×
56
        }
57

58
        $this->name = \is_string($parameterVar->name) ? $parameterVar->name : '';
11✔
59

60
        if ($node) {
11✔
61
            $this->prepareNode($node);
11✔
62

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

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

71
                $this->readPhpDoc($docComment, $this->name);
11✔
72
            }
73
        }
74

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

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

96
        if ($parameter->default) {
11✔
97
            $defaultValue = Utils::getPhpParserValueFromNode($parameter->default, $classStr, $this->parserContainer);
7✔
98
            if ($defaultValue !== Utils::GET_PHP_PARSER_VALUE_FROM_NODE_HELPER) {
7✔
99
                $this->defaultValue = $defaultValue;
7✔
100

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

105
        $this->is_vararg = $parameter->variadic;
11✔
106

107
        $this->is_passed_by_ref = $parameter->byRef;
11✔
108

109
        return $this;
11✔
110
    }
111

112
    /**
113
     * @param ReflectionParameter $parameter
114
     *
115
     * @return $this
116
     */
117
    public function readObjectFromReflection($parameter): self
118
    {
119
        $this->name = $parameter->getName();
11✔
120

121
        if ($parameter->isDefaultValueAvailable()) {
11✔
122
            try {
123
                $this->defaultValue = $parameter->getDefaultValue();
7✔
124
            } catch (\ReflectionException $e) {
×
125
                // nothing
126
            }
127
            if ($this->defaultValue !== null) {
7✔
128
                $this->typeFromDefaultValue = Utils::normalizePhpType(\gettype($this->defaultValue));
7✔
129
            }
130
        }
131

132
        $method = $parameter->getDeclaringFunction();
11✔
133

134
        $docComment = $method->getDocComment();
11✔
135
        if ($docComment) {
11✔
136
            if (\stripos($docComment, '@inheritdoc') !== false) {
10✔
137
                $this->is_inheritdoc = true;
5✔
138
            }
139

140
            $this->readPhpDoc($docComment, $this->name);
10✔
141
        }
142

143
        try {
144
            $type = $parameter->getType();
11✔
145
        } catch (\ReflectionException $e) {
×
146
            $type = null;
×
147
        }
148
        if ($type !== null) {
11✔
149
            if (\method_exists($type, 'getName')) {
10✔
150
                $this->type = Utils::normalizePhpType($type->getName(), true);
10✔
151
            } else {
152
                $this->type = Utils::normalizePhpType($type . '', true);
×
153
            }
154
            if ($this->type && \class_exists($this->type, true)) {
10✔
155
                $this->type = '\\' . \ltrim($this->type, '\\');
10✔
156
            }
157

158
            try {
159
                $constNameTmp = $parameter->getDefaultValueConstantName();
10✔
160
                if ($constNameTmp && \defined($constNameTmp)) {
7✔
161
                    $defaultTmp = \constant($constNameTmp);
2✔
162
                    if ($defaultTmp === null) {
2✔
163
                        if ($this->type && $this->type !== 'null' && \strpos($this->type, 'null|') !== 0) {
×
164
                            $this->type = 'null|' . $this->type;
×
165
                        } elseif (!$this->type) {
×
166
                            $this->type = 'null|mixed';
7✔
167
                        }
168
                    }
169
                }
170
            } catch (\ReflectionException $e) {
10✔
171
                if ($type->allowsNull()) {
10✔
172
                    if ($this->type && $this->type !== 'null' && \strpos($this->type, 'null|') !== 0) {
2✔
173
                        $this->type = 'null|' . $this->type;
2✔
174
                    } elseif (!$this->type) {
×
175
                        $this->type = 'null|mixed';
×
176
                    }
177
                }
178
            }
179
        }
180

181
        $this->is_vararg = $parameter->isVariadic();
11✔
182

183
        $this->is_passed_by_ref = $parameter->isPassedByReference();
11✔
184

185
        return $this;
11✔
186
    }
187

188
    /**
189
     * @return string|null
190
     */
191
    public function getType(): ?string
192
    {
193
        if ($this->typeFromPhpDocExtended) {
×
194
            return $this->typeFromPhpDocExtended;
×
195
        }
196

197
        if ($this->type) {
×
198
            return $this->type;
×
199
        }
200

201
        if ($this->typeFromPhpDocSimple) {
×
202
            return $this->typeFromPhpDocSimple;
×
203
        }
204

205
        return null;
×
206
    }
207

208
    /**
209
     * @param Doc|string $doc
210
     */
211
    private function readPhpDoc($doc, string $parameterName): void
212
    {
213
        if ($doc instanceof Doc) {
12✔
214
            $docComment = $doc->getText();
11✔
215
        } else {
216
            $docComment = $doc;
10✔
217
        }
218
        if ($docComment === '') {
12✔
219
            return;
×
220
        }
221

222
        try {
223
            $phpDoc = Utils::createDocBlockInstance()->create($docComment);
12✔
224

225
            $parsedParamTags = $phpDoc->getTagsByName('param');
12✔
226

227
            if (!empty($parsedParamTags)) {
12✔
228
                foreach ($parsedParamTags as $parsedParamTag) {
11✔
229
                    if ($parsedParamTag instanceof \phpDocumentor\Reflection\DocBlock\Tags\Param) {
11✔
230
                        // check only the current "param"-tag
231
                        if (\strtoupper($parameterName) !== \strtoupper((string) $parsedParamTag->getVariableName())) {
11✔
232
                            continue;
10✔
233
                        }
234

235
                        $type = $parsedParamTag->getType();
11✔
236

237
                        $this->typeFromPhpDoc = Utils::normalizePhpType($type . '');
11✔
238

239
                        $typeFromPhpDocMaybeWithCommentTmp = \trim((string) $parsedParamTag);
11✔
240
                        if (
241
                            $typeFromPhpDocMaybeWithCommentTmp
11✔
242
                            &&
243
                            \strpos($typeFromPhpDocMaybeWithCommentTmp, '$') !== 0
11✔
244
                        ) {
245
                            $this->typeFromPhpDocMaybeWithComment = $typeFromPhpDocMaybeWithCommentTmp;
11✔
246
                        }
247

248
                        $typeTmp = Utils::parseDocTypeObject($type);
11✔
249
                        if ($typeTmp !== '') {
11✔
250
                            $this->typeFromPhpDocSimple = $typeTmp;
11✔
251
                        }
252
                    }
253

254
                    $parsedParamTagParam = (string) $parsedParamTag;
11✔
255
                    $spitedData = Utils::splitTypeAndVariable($parsedParamTag);
11✔
256
                    $variableName = $spitedData['variableName'];
11✔
257

258
                    // check only the current "param"-tag
259
                    if ($variableName && \strtoupper($parameterName) === \strtoupper($variableName)) {
11✔
260
                        $this->phpDocRaw = $parsedParamTagParam;
11✔
261
                        $this->typeFromPhpDocExtended = Utils::modernPhpdoc($parsedParamTagParam);
11✔
262
                    }
263

264
                    break;
11✔
265
                }
266
            }
267

268
            $parsedParamTags = $phpDoc->getTagsByName('psalm-param')
12✔
269
                               + $phpDoc->getTagsByName('phpstan-param');
12✔
270

271
            if (!empty($parsedParamTags)) {
12✔
272
                foreach ($parsedParamTags as $parsedParamTag) {
12✔
273
                    if (!$parsedParamTag instanceof \phpDocumentor\Reflection\DocBlock\Tags\Generic) {
6✔
274
                        continue;
×
275
                    }
276

277
                    $spitedData = Utils::splitTypeAndVariable($parsedParamTag);
6✔
278
                    $parsedParamTagStr = $spitedData['parsedParamTagStr'];
6✔
279
                    $variableName = $spitedData['variableName'];
6✔
280

281
                    // check only the current "param"-tag
282
                    if (!$variableName || \strtoupper($parameterName) !== \strtoupper($variableName)) {
6✔
283
                        continue;
6✔
284
                    }
285

286
                    $this->typeFromPhpDocExtended = Utils::modernPhpdoc($parsedParamTagStr);
6✔
287
                }
288
            }
289
        } catch (\Exception $e) {
7✔
290
            $tmpErrorMessage = $this->name . ':' . ($this->line ?? '?') . ' | ' . \print_r($e->getMessage(), true);
7✔
291
            $this->parseError[\md5($tmpErrorMessage)] = $tmpErrorMessage;
7✔
292
        }
293

294
        try {
295
            $this->readPhpDocByTokens($docComment, $parameterName);
12✔
296
        } catch (\Exception $e) {
1✔
297
            $tmpErrorMessage = $this->name . ':' . ($this->line ?? '?') . ' | ' . \print_r($e->getMessage(), true);
1✔
298
            $this->parseError[\md5($tmpErrorMessage)] = $tmpErrorMessage;
1✔
299
        }
300
    }
301

302
    /**
303
     * @throws \PHPStan\PhpDocParser\Parser\ParserException
304
     */
305
    private function readPhpDocByTokens(string $docComment, string $parameterName): void
306
    {
307
        $tokens = Utils::modernPhpdocTokens($docComment);
12✔
308

309
        $paramContent = null;
12✔
310
        foreach ($tokens->getTokens() as $token) {
12✔
311
            $content = $token[0];
12✔
312

313
            if ($content === '@param' || $content === '@psalm-param' || $content === '@phpstan-param') {
12✔
314
                // reset
315
                $paramContent = '';
11✔
316

317
                continue;
11✔
318
            }
319

320
            // We can stop if we found the param variable e.g. `@param array{foo:int} $param`.
321
            if ($content === '$' . $parameterName) {
12✔
322
                break;
11✔
323
            }
324

325
            if ($paramContent !== null) {
12✔
326
                $paramContent .= $content;
11✔
327
            }
328
        }
329

330
        $paramContent = $paramContent ? \trim($paramContent) : null;
12✔
331
        if ($paramContent) {
12✔
332
            if (!$this->phpDocRaw) {
11✔
333
                $this->phpDocRaw = $paramContent . ' ' . '$' . $parameterName;
6✔
334
            }
335
            $this->typeFromPhpDocExtended = Utils::modernPhpdoc($paramContent);
11✔
336
        }
337
    }
338
}
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