1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace TheCodingMachine\PHPStan\BetterReflection; |
4
|
|
|
|
5
|
|
|
use BetterReflection\Identifier\IdentifierType; |
6
|
|
|
use BetterReflection\Reflection\Reflection; |
7
|
|
|
use BetterReflection\Reflection\ReflectionClass; |
8
|
|
|
use BetterReflection\Reflection\ReflectionFunction; |
9
|
|
|
use BetterReflection\Reflection\ReflectionMethod; |
10
|
|
|
use BetterReflection\Reflector\ClassReflector; |
11
|
|
|
use BetterReflection\SourceLocator\Type\AggregateSourceLocator; |
12
|
|
|
use BetterReflection\SourceLocator\Type\AutoloadSourceLocator; |
13
|
|
|
use BetterReflection\SourceLocator\Type\EvaledCodeSourceLocator; |
14
|
|
|
use BetterReflection\SourceLocator\Type\PhpInternalSourceLocator; |
15
|
|
|
use BetterReflection\SourceLocator\Type\SingleFileSourceLocator; |
16
|
|
|
use BetterReflection\SourceLocator\Type\SourceLocator; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* TODO: remove this when https://github.com/Roave/BetterReflection/pull/286 is merged |
20
|
|
|
*/ |
21
|
|
|
final class FindReflectionOnLine |
22
|
|
|
{ |
23
|
|
|
/** |
24
|
|
|
* @var SourceLocator |
25
|
|
|
*/ |
26
|
|
|
private $sourceLocator; |
27
|
|
|
|
28
|
|
|
public function __construct(SourceLocator $sourceLocator = null) |
29
|
|
|
{ |
30
|
|
|
$this->sourceLocator = $sourceLocator; |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @return self |
35
|
|
|
*/ |
36
|
|
|
public static function buildDefaultFinder() : self |
37
|
|
|
{ |
38
|
|
|
return new self(new AggregateSourceLocator([ |
39
|
|
|
new PhpInternalSourceLocator(), |
40
|
|
|
new EvaledCodeSourceLocator(), |
41
|
|
|
new AutoloadSourceLocator(), |
42
|
|
|
])); |
43
|
|
|
} |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Find a reflection on the specified line number. |
47
|
|
|
* |
48
|
|
|
* Returns null if no reflections found on the line. |
49
|
|
|
* |
50
|
|
|
* @param string $filename |
51
|
|
|
* @param int $lineNumber |
52
|
|
|
* @return ReflectionMethod|ReflectionClass|ReflectionFunction|null |
53
|
|
|
* @throws \InvalidArgumentException |
54
|
|
|
*/ |
55
|
|
|
public function __invoke($filename, $lineNumber) |
56
|
|
|
{ |
57
|
|
|
$lineNumber = (int)$lineNumber; |
58
|
|
|
$reflections = $this->computeReflections($filename); |
59
|
|
|
|
60
|
|
|
foreach ($reflections as $reflection) { |
61
|
|
|
if ($reflection instanceof ReflectionClass && $this->containsLine($reflection, $lineNumber)) { |
|
|
|
|
62
|
|
|
foreach ($reflection->getMethods() as $method) { |
63
|
|
|
if ($this->containsLine($method, $lineNumber)) { |
64
|
|
|
return $method; |
65
|
|
|
} |
66
|
|
|
} |
67
|
|
|
return $reflection; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
if ($reflection instanceof ReflectionFunction && $this->containsLine($reflection, $lineNumber)) { |
|
|
|
|
71
|
|
|
return $reflection; |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
return null; |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* Find all class and function reflections in the specified file |
80
|
|
|
* |
81
|
|
|
* @param string $filename |
82
|
|
|
* @return Reflection[] |
83
|
|
|
*/ |
84
|
|
|
private function computeReflections($filename) |
85
|
|
|
{ |
86
|
|
|
$sourceLocator = new SingleFileSourceLocator($filename); |
87
|
|
|
if ($this->sourceLocator !== null) { |
88
|
|
|
$reflector = new ClassReflector(new AggregateSourceLocator([$this->sourceLocator, $sourceLocator])); |
89
|
|
|
} else { |
90
|
|
|
$reflector = new ClassReflector($sourceLocator); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
return array_merge( |
94
|
|
|
$sourceLocator->locateIdentifiersByType($reflector, new IdentifierType(IdentifierType::IDENTIFIER_CLASS)), |
95
|
|
|
$sourceLocator->locateIdentifiersByType($reflector, new IdentifierType(IdentifierType::IDENTIFIER_FUNCTION)) |
96
|
|
|
); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Check to see if the line is within the boundaries of the reflection specified. |
101
|
|
|
* |
102
|
|
|
* @param mixed $reflection |
103
|
|
|
* @param int $lineNumber |
104
|
|
|
* @return bool |
105
|
|
|
* @throws \InvalidArgumentException |
106
|
|
|
*/ |
107
|
|
|
private function containsLine($reflection, $lineNumber) |
108
|
|
|
{ |
109
|
|
|
if (!method_exists($reflection, 'getStartLine')) { |
110
|
|
|
throw new \InvalidArgumentException('Reflection does not have getStartLine method'); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
if (!method_exists($reflection, 'getEndLine')) { |
114
|
|
|
throw new \InvalidArgumentException('Reflection does not have getEndLine method'); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
return $lineNumber >= $reflection->getStartLine() && $lineNumber <= $reflection->getEndLine(); |
118
|
|
|
} |
119
|
|
|
} |
120
|
|
|
|
This error could be the result of:
1. Missing dependencies
PHP Analyzer uses your
composer.json
file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects thecomposer.json
to be in the root folder of your repository.Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the
require
orrequire-dev
section?2. Missing use statement
PHP does not complain about undefined classes in
ìnstanceof
checks. For example, the following PHP code will work perfectly fine:If you have not tested against this specific condition, such errors might go unnoticed.