1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\Util;
6:
7: use InvalidArgumentException;
8: use PHPStan\BetterReflection\Identifier\IdentifierType;
9: use PHPStan\BetterReflection\Reflection\Reflection;
10: use PHPStan\BetterReflection\Reflection\ReflectionClass;
11: use PHPStan\BetterReflection\Reflection\ReflectionConstant;
12: use PHPStan\BetterReflection\Reflection\ReflectionFunction;
13: use PHPStan\BetterReflection\Reflection\ReflectionMethod;
14: use PHPStan\BetterReflection\Reflector\DefaultReflector;
15: use PHPStan\BetterReflection\SourceLocator\Ast\Exception\ParseToAstFailure;
16: use PHPStan\BetterReflection\SourceLocator\Ast\Locator;
17: use PHPStan\BetterReflection\SourceLocator\Exception\InvalidFileLocation;
18: use PHPStan\BetterReflection\SourceLocator\Type\AggregateSourceLocator;
19: use PHPStan\BetterReflection\SourceLocator\Type\SingleFileSourceLocator;
20: use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator;
21:
22: use function array_merge;
23:
24: final class FindReflectionOnLine
25: {
26: /**
27: * @var \PHPStan\BetterReflection\SourceLocator\Type\SourceLocator
28: */
29: private $sourceLocator;
30: /**
31: * @var \PHPStan\BetterReflection\SourceLocator\Ast\Locator
32: */
33: private $astLocator;
34: public function __construct(SourceLocator $sourceLocator, Locator $astLocator)
35: {
36: $this->sourceLocator = $sourceLocator;
37: $this->astLocator = $astLocator;
38: }
39:
40: /**
41: * Find a reflection on the specified line number.
42: *
43: * Returns null if no reflections found on the line.
44: *
45: * @throws InvalidFileLocation
46: * @throws ParseToAstFailure
47: * @throws InvalidArgumentException
48: * @return \PHPStan\BetterReflection\Reflection\ReflectionMethod|\PHPStan\BetterReflection\Reflection\ReflectionClass|\PHPStan\BetterReflection\Reflection\ReflectionFunction|\PHPStan\BetterReflection\Reflection\ReflectionConstant|\PHPStan\BetterReflection\Reflection\Reflection|null
49: */
50: public function __invoke(string $filename, int $lineNumber)
51: {
52: $reflections = $this->computeReflections($filename);
53:
54: foreach ($reflections as $reflection) {
55: if ($reflection instanceof ReflectionClass && $this->containsLine($reflection, $lineNumber)) {
56: foreach ($reflection->getMethods() as $method) {
57: if ($this->containsLine($method, $lineNumber)) {
58: return $method;
59: }
60: }
61:
62: return $reflection;
63: }
64:
65: if ($reflection instanceof ReflectionFunction && $this->containsLine($reflection, $lineNumber)) {
66: return $reflection;
67: }
68:
69: if ($reflection instanceof ReflectionConstant && $this->containsLine($reflection, $lineNumber)) {
70: return $reflection;
71: }
72: }
73:
74: return null;
75: }
76:
77: /**
78: * Find all class and function reflections in the specified file
79: *
80: * @return list<Reflection>
81: *
82: * @throws ParseToAstFailure
83: * @throws InvalidFileLocation
84: */
85: private function computeReflections(string $filename): array
86: {
87: $singleFileSourceLocator = new SingleFileSourceLocator($filename, $this->astLocator);
88: $reflector = new DefaultReflector(new AggregateSourceLocator([$singleFileSourceLocator, $this->sourceLocator]));
89:
90: return array_merge($singleFileSourceLocator->locateIdentifiersByType($reflector, new IdentifierType(IdentifierType::IDENTIFIER_CLASS)), $singleFileSourceLocator->locateIdentifiersByType($reflector, new IdentifierType(IdentifierType::IDENTIFIER_FUNCTION)), $singleFileSourceLocator->locateIdentifiersByType($reflector, new IdentifierType(IdentifierType::IDENTIFIER_CONSTANT)));
91: }
92:
93: /**
94: * Check to see if the line is within the boundaries of the reflection specified.
95: * @param \PHPStan\BetterReflection\Reflection\ReflectionClass|\PHPStan\BetterReflection\Reflection\ReflectionMethod|\PHPStan\BetterReflection\Reflection\ReflectionFunction|\PHPStan\BetterReflection\Reflection\ReflectionConstant $reflection
96: */
97: private function containsLine($reflection, int $lineNumber): bool
98: {
99: return $lineNumber >= $reflection->getStartLine() && $lineNumber <= $reflection->getEndLine();
100: }
101: }
102: