Completed
Pull Request — master (#82)
by Loren
03:39
created

ReflectionFunctionLikeTrait::getDocComment()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 10
c 0
b 0
f 0
ccs 6
cts 6
cp 1
rs 9.4285
cc 3
eloc 6
nc 3
nop 0
crap 3
1
<?php
2
/**
3
 * Parser Reflection API
4
 *
5
 * @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
6
 *
7
 * This source file is subject to the license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
11
namespace Go\ParserReflection\Traits;
12
13
14
use Go\ParserReflection\NodeVisitor\GeneratorDetector;
15
use Go\ParserReflection\NodeVisitor\StaticVariablesCollector;
16
use Go\ParserReflection\ReflectionParameter;
17
use Go\ParserReflection\ReflectionExtension;
18
use ReflectionParameter as BaseReflectionParameter;
19
use Go\ParserReflection\ReflectionType;
20
use PhpParser\Node\Expr\Closure;
21
use PhpParser\Node\Expr\ClassConstFetch;
22
use PhpParser\Node\Expr\ConstFetch;
23
use PhpParser\Node\FunctionLike;
24
use PhpParser\Node\Name\FullyQualified as FullyQualifiedName;
25
use PhpParser\Node\Name;
26
use PhpParser\Node\NullableType;
27
use PhpParser\Node\Stmt\ClassMethod;
28
use PhpParser\Node\Stmt\Function_;
29
use PhpParser\Builder\Param as ParamNodeBuilder;
30
use PhpParser\NodeTraverser;
31
32
/**
33
 * General trait for all function-like reflections
34
 */
35
trait ReflectionFunctionLikeTrait
36
{
37
    use InitializationTrait;
38
39
    /**
40
     * @var FunctionLike
41
     */
42
    protected $functionLikeNode;
43
44
    /**
45
     * Namespace name
46
     *
47
     * @var string
48
     */
49
    protected $namespaceName = '';
50
51
    /**
52
     * @var array|ReflectionParameter[]
53
     */
54
    protected $parameters;
55
56
    /**
57
     * {@inheritDoc}
58
     */
59 154
    public function getClosureScopeClass()
60
    {
61 154
        $this->initializeInternalReflection();
62
63 154
        return parent::getClosureScopeClass();
64
    }
65
66
    /**
67
     * {@inheritDoc}
68
     */
69 154
    public function getClosureThis()
70
    {
71 154
        $this->initializeInternalReflection();
72
73 154
        return parent::getClosureThis();
74
    }
75
76 399
    public function getDocComment()
77
    {
78 399
        if (!$this->functionLikeNode) {
79 77
            $this->initializeInternalReflection();
80 77
            return parent::getDocComment();
81
        }
82 322
        $docComment = $this->functionLikeNode->getDocComment();
83
84 322
        return $docComment ? $docComment->getText() : false;
85
    }
86
87 395
    public function getEndLine()
88
    {
89 395
        if (!$this->functionLikeNode) {
90 73
            $this->initializeInternalReflection();
91 73
            return parent::getEndLine();
92
        }
93 322
        return $this->functionLikeNode->getAttribute('endLine');
94
    }
95
96 300
    public function getExtension()
97
    {
98 300
        $extName = $this->getExtensionName();
99 300
        if (!$extName) {
100 161
            return null;
101
        }
102
        // The purpose of Go\ParserReflection\ReflectionExtension is
103
        // to behave exactly like \ReflectionExtension, but return
104
        // Go\ParserReflection\ReflectionFunction and
105
        // Go\ParserReflection\ReflectionClass where apropriate.
106 139
        return new ReflectionExtension($extName);
107
    }
108
109 534
    public function getExtensionName()
110
    {
111 534
        if (!$this->functionLikeNode) {
112 212
            $this->initializeInternalReflection();
113 212
            return parent::getExtensionName();
114
        }
115 322
        return false;
116
    }
117
118 177
    public function getFileName()
119
    {
120 177
        if (!$this->functionLikeNode) {
121
            // If we got here, we're probably a built-in method/function, and
122
            // filename is probably false.
123 7
            $this->initializeInternalReflection();
124 7
            return parent::getFileName();
125
        }
126 170
        return $this->functionLikeNode->getAttribute('fileName');
127
    }
128
129
    /**
130
     * {@inheritDoc}
131
     */
132 6353
    public function getName()
133
    {
134 6353
        if (!$this->functionLikeNode) {
135 2301
            $this->initializeInternalReflection();
136 2301
            return parent::getName();
137
        }
138 4052
        if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
139 4052
            $functionName = $this->functionLikeNode->name;
140
141 4052
            return $this->namespaceName ? $this->namespaceName . '\\' . $functionName : $functionName;
142
        }
143
144
        return false;
145
    }
146
147
    /**
148
     * {@inheritDoc}
149
     */
150 245
    public function getNamespaceName()
151
    {
152 245
        return $this->namespaceName;
153
    }
154
155
    /**
156
     * Get the number of parameters that a function defines, both optional and required.
157
     *
158
     * @link http://php.net/manual/en/reflectionfunctionabstract.getnumberofparameters.php
159
     *
160
     * @return int
161
     */
162 371
    public function getNumberOfParameters()
163
    {
164 371
        if (!$this->functionLikeNode) {
165 77
            $this->initializeInternalReflection();
166 77
            return parent::getNumberOfParameters();
167
        }
168 294
        return count($this->functionLikeNode->getParams());
169
    }
170
171
    /**
172
     * Get the number of required parameters that a function defines.
173
     *
174
     * @link http://php.net/manual/en/reflectionfunctionabstract.getnumberofrequiredparameters.php
175
     *
176
     * @return int
177
     */
178 234
    public function getNumberOfRequiredParameters()
179
    {
180 234
        $requiredParameters = 0;
181 234
        foreach ($this->getParameters() as $parameter) {
182 75
            if (!$parameter->isOptional()) {
183 75
                $requiredParameters++;
184
            }
185
        }
186
187 234
        return $requiredParameters;
188
    }
189
190
    /**
191
     * {@inheritDoc}
192
     */
193 630
    public function getParameters()
194
    {
195 630
        if (!isset($this->parameters)) {
196 300
            $parameters = [];
197
198 300
            if ($this->functionLikeNode) {
199 220
                foreach ($this->functionLikeNode->getParams() as $parameterIndex => $parameterNode) {
200 88
                    $reflectionParameter = new ReflectionParameter(
201 88
                        $this->getName(),
202 88
                        $parameterNode->name,
203 88
                        $parameterNode,
204 88
                        $parameterIndex,
205 88
                        $this
206
                    );
207 220
                    $parameters[] = $reflectionParameter;
208
                }
209
            } else {
210 80
                $this->initializeInternalReflection();
211 80
                $nativeParamRefs = parent::getParameters();
212 80
                foreach ($nativeParamRefs as $parameterIndex => $parameterNode) {
213 34
                    $parameters[$parameterIndex] = $this->getRefParam($parameterNode);
214
                }
215
            }
216
217 300
            $this->parameters = $parameters;
218
        }
219
220 630
        return $this->parameters;
221
    }
222
223
    /**
224
     * Gets the specified return type of a function
225
     *
226
     * @return \ReflectionType
227
     *
228
     * @link http://php.net/manual/en/reflectionfunctionabstract.getreturntype.php
229
     */
230 166
    public function getReturnType()
231
    {
232 166
        $isBuiltin  = false;
233 166
        if ($this->functionLikeNode) {
234 162
            $returnType = $this->functionLikeNode->getReturnType();
235 162
            $isNullable = $returnType instanceof NullableType;
236
237 162
            if ($isNullable) {
238 12
                $returnType = $returnType->type;
239
            }
240 162
            if (is_object($returnType)) {
241 17
                $returnType = $returnType->toString();
242 146
            } elseif (is_string($returnType)) {
243 27
                $isBuiltin = true;
244
            } else {
245 162
                return null;
246
            }
247
        } else {
248 4
            if (PHP_VERSION_ID < 70000) {
249
                return null;
250
            }
251 4
            $this->initializeInternalReflection();
252 4
            $nativeType = parent::getReturnType();
253 4
            if (!$nativeType) {
254 4
                return null;
255
            }
256
            $isNullable = $nativeType->allowsNull();
257
            $isBuiltin = $nativeType->isBuiltin();
258
            $returnType = (string)$nativeType;
259
        }
260
261 43
        return new ReflectionType($returnType, $isNullable, $isBuiltin);
262
    }
263
264
    /**
265
     * {@inheritDoc}
266
     */
267 234
    public function getShortName()
268
    {
269 234
        if (!$this->functionLikeNode) {
270 73
            $this->initializeInternalReflection();
271 73
            return parent::getShortName();
272
        }
273 161
        if ($this->functionLikeNode instanceof Function_ || $this->functionLikeNode instanceof ClassMethod) {
274 161
            return $this->functionLikeNode->name;
275
        }
276
277
        return false;
278
    }
279
280 395
    public function getStartLine()
281
    {
282 395
        if (!$this->functionLikeNode) {
283 73
            $this->initializeInternalReflection();
284 73
            return parent::getStartLine();
285
        }
286 322
        return $this->functionLikeNode->getAttribute('startLine');
287
    }
288
289
    /**
290
     * {@inheritDoc}
291
     */
292 234
    public function getStaticVariables()
293
    {
294 234
        if (!$this->functionLikeNode) {
295 73
            $this->initializeInternalReflection();
296 73
            return parent::getStaticVariables();
297
        }
298
        // In nikic/PHP-Parser < 2.0.0 the default behavior is cloning
299
        //     nodes when traversing them. Passing FALSE to the constructor
300
        //     prevents this.
301
        // In nikic/PHP-Parser >= 2.0.0 and < 3.0.0 the default behavior was
302
        //     changed to not clone nodes, but the parameter was retained as
303
        //     an option.
304
        // In nikic/PHP-Parser >= 3.0.0 the option to clone nodes was removed
305
        //     as a constructor parameter, so Scrutinizer will pick this up as
306
        //     an issue. It is retained for legacy compatibility.
307 161
        $nodeTraverser      = new NodeTraverser(false);
0 ignored issues
show
Unused Code introduced by Lisachenko Alexander
The call to NodeTraverser::__construct() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
308 161
        $variablesCollector = new StaticVariablesCollector($this);
309 161
        $nodeTraverser->addVisitor($variablesCollector);
310
311
        /* @see https://github.com/nikic/PHP-Parser/issues/235 */
312 161
        $nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
313
314 161
        return $variablesCollector->getStaticVariables();
315
    }
316
317
    /**
318
     * Checks if the function has a specified return type
319
     *
320
     * @return bool
321
     *
322
     * @link http://php.net/manual/en/reflectionfunctionabstract.hasreturntype.php
323
     */
324 235
    public function hasReturnType()
325
    {
326 235
        if (!$this->functionLikeNode) {
327 73
            $this->initializeInternalReflection();
328 73
            return parent::hasReturnType();
329
        }
330 162
        $returnType = $this->functionLikeNode->getReturnType();
331
332 162
        return isset($returnType);
333
    }
334
335
    /**
336
     * {@inheritDoc}
337
     */
338 234
    public function inNamespace()
339
    {
340 234
        return !empty($this->namespaceName);
341
    }
342
343
    /**
344
     * {@inheritDoc}
345
     */
346 200
    public function isClosure()
347
    {
348 200
        if (!$this->functionLikeNode) {
349 70
            $this->initializeInternalReflection();
350 70
            return parent::isClosure();
351
        }
352 130
        return $this->functionLikeNode instanceof Closure;
353
    }
354
355
    /**
356
     * {@inheritDoc}
357
     */
358 204
    public function isDeprecated()
359
    {
360 204
        if (!$this->functionLikeNode) {
361 74
            $this->initializeInternalReflection();
362 74
            return parent::isDeprecated();
363
        }
364 130
        return false;
365
    }
366
367
    /**
368
     * {@inheritDoc}
369
     */
370 200
    public function isGenerator()
371
    {
372 200
        if (!$this->functionLikeNode) {
373 70
            $this->initializeInternalReflection();
374 70
            return parent::isGenerator();
375
        }
376
        // In nikic/PHP-Parser < 2.0.0 the default behavior is cloning
377
        //     nodes when traversing them. Passing FALSE to the constructor
378
        //     prevents this.
379
        // In nikic/PHP-Parser >= 2.0.0 and < 3.0.0 the default behavior was
380
        //     changed to not clone nodes, but the parameter was retained as
381
        //     an option.
382
        // In nikic/PHP-Parser >= 3.0.0 the option to clone nodes was removed
383
        //     as a constructor parameter, so Scrutinizer will pick this up as
384
        //     an issue. It is retained for legacy compatibility.
385 130
        $nodeTraverser = new NodeTraverser(false);
0 ignored issues
show
Unused Code introduced by Lisachenko Alexander
The call to NodeTraverser::__construct() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
386 130
        $nodeDetector  = new GeneratorDetector();
387 130
        $nodeTraverser->addVisitor($nodeDetector);
388
389
        /* @see https://github.com/nikic/PHP-Parser/issues/235 */
390 130
        $nodeTraverser->traverse($this->functionLikeNode->getStmts() ?: array());
391
392 130
        return $nodeDetector->isGenerator();
393
    }
394
395
    /**
396
     * {@inheritDoc}
397
     */
398 200
    public function isInternal()
399
    {
400 200
        if (!$this->functionLikeNode) {
401 70
            $this->initializeInternalReflection();
402 70
            return parent::isInternal();
403
        }
404 130
        return false;
405
    }
406
407
    /**
408
     * {@inheritDoc}
409
     */
410 235
    public function isUserDefined()
411
    {
412 235
        if (!$this->functionLikeNode) {
413 74
            $this->initializeInternalReflection();
414 74
            return parent::isUserDefined();
415
        }
416
        // always defined by user, because we parse the source code
417 161
        return true;
418
    }
419
420
    /**
421
     * {@inheritDoc}
422
     */
423 200
    public function isVariadic()
424
    {
425 200
        foreach ($this->getParameters() as $parameter) {
426 58
            if ($parameter->isVariadic()) {
427 58
                return true;
428
            }
429
        }
430
431 195
        return false;
432
    }
433
434
    /**
435
     * {@inheritDoc}
436
     */
437 234
    public function returnsReference()
438
    {
439 234
        if (!$this->functionLikeNode) {
440 73
            $this->initializeInternalReflection();
441 73
            return parent::returnsReference();
442
        }
443 161
        return $this->functionLikeNode->returnsByRef();
444
    }
445
446 34
    private function getRefParam(BaseReflectionParameter $orig)
447
    {
448 34
        $nullableImplied = false;
449 34
        $builder = new ParamNodeBuilder($orig->name);
450 34
        if ($orig->isDefaultValueAvailable() || $orig->isOptional()) {
451 21
            if ($orig->isDefaultValueAvailable()) {
452
                if (method_exists($orig, 'isDefaultValueConstant') && $orig->isDefaultValueConstant()) {
0 ignored issues
show
Bug introduced by Loren Osborn
It seems like you code against a specific sub-type and not the parent class ReflectionParameter as the method isDefaultValueConstant() does only exist in the following sub-classes of ReflectionParameter: Go\ParserReflection\ReflectionParameter. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
453
                    $constNameParts = explode('::', $orig->getDefaultValueConstantName(), 2);
454
                    if (count($constNameParts) > 1) {
455
                        $classNameNode = new FullyQualifiedName($constNameParts[0]);
456
                        $default = new ClassConstFetch($classNameNode, $constNameParts[1]);
457
                    } else {
458
                        $constNameNode = new FullyQualifiedName($constNameParts[0]);
459
                        $default = new ConstFetch($constNameNode);
460
                    }
461
                } else {
462
                    $default = $orig->getDefaultValue();
463
                    if (is_null($default) || is_bool($default)) {
464
                        $constantName = var_export($default, true);
465
                        $default = new ConstFetch(new Name($constantName));
466
                    }
467
                }
468
                if (is_null($orig->getDefaultValue())) {
469
                    $nullableImplied = true;
470
                }
471
            } else {
472 21
                $default = new ConstFetch(new Name('null'), ['implied' => true]);
473 21
                $nullableImplied = true;
474
            }
475 21
            $builder->setDefault($default);
476
        }
477 34
        if ($orig->isPassedByReference()) {
478 3
            $builder->makeByRef();
479
        }
480 34
        if (method_exists($orig, 'isVariadic') && $orig->isVariadic()) {
0 ignored issues
show
Bug introduced by Loren Osborn
It seems like you code against a specific sub-type and not the parent class ReflectionParameter as the method isVariadic() does only exist in the following sub-classes of ReflectionParameter: Go\ParserReflection\ReflectionParameter. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
481 1
            $builder->makeVariadic();
482
        }
483 34
        if (method_exists($orig, 'hasType') && $orig->hasType()) {
0 ignored issues
show
Bug introduced by Loren Osborn
It seems like you code against a specific sub-type and not the parent class ReflectionParameter as the method hasType() does only exist in the following sub-classes of ReflectionParameter: Go\ParserReflection\ReflectionParameter. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
484 1
            $typeRef = $orig->getType();
0 ignored issues
show
Bug introduced by Loren Osborn
It seems like you code against a specific sub-type and not the parent class ReflectionParameter as the method getType() does only exist in the following sub-classes of ReflectionParameter: Go\ParserReflection\ReflectionParameter. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
485 1
            $stringType = ltrim((string)$typeRef, '?'); // ltrim() is precautionary.
486 1
            if (PHP_VERSION_ID >= 70100 && $typeRef->allowsNull()) {
487
                $stringType = '?' . $stringType;
488
                $nullableImplied = true;
489
            }
490 1
            $builder->setTypeHint($stringType);
491
        } else {
492 33
            $hintedClass = $orig->getClass();
493 33
            if ($hintedClass) {
494
                $builder->setTypeHint($hintedClass->name);
495 33
            } else if ($orig->isArray()) {
496
                $builder->setTypeHint('array');
497 33
            } else if ($orig->isCallable()) {
498
                $builder->setTypeHint('callable');
499
            } else {
500 33
                $nullableImplied = true;
501
            }
502
        }
503 34
        $fakeParamNode = $builder->getNode();
504 34
        if (!$orig->allowsNull() && $nullableImplied) {
505 33
            $fakeParamNode->setAttribute('prohibit_null', true);
506
        }
507 34
        return new ReflectionParameter(
508 34
            $this->getName(), // Calling function name:   Unused.
509 34
            $orig->name, // Parameter variable name: Unused.
510 34
            $fakeParamNode, // Synthetic parse node.
511 34
            $orig->getPosition(), // Parameter index.
512 34
            $this // Function or method being described.
513
        );
514
    }
515
}
516