1: | <?php declare(strict_types = 1); |
2: | |
3: | namespace ApiGen; |
4: | |
5: | use ApiGen\Info\ClassLikeReferenceInfo; |
6: | use Composer\Autoload\ClassLoader; |
7: | use JetBrains\PHPStormStub\PhpStormStubsMap; |
8: | use League; |
9: | use Nette\Utils\FileSystem; |
10: | use Nette\Utils\Json; |
11: | use PHPStan\Php8StubsMap; |
12: | use Symfony\Component\Console\Style\OutputStyle; |
13: | |
14: | use function dirname; |
15: | use function implode; |
16: | use function is_dir; |
17: | use function is_file; |
18: | use function strtolower; |
19: | |
20: | use const PHP_VERSION_ID; |
21: | |
22: | |
23: | class Locator |
24: | { |
25: | |
26: | |
27: | |
28: | public function __construct( |
29: | protected array $stubsMap, |
30: | protected ClassLoader $classLoader, |
31: | ) { |
32: | } |
33: | |
34: | |
35: | public static function create(OutputStyle $output, string $projectDir): self |
36: | { |
37: | return new self( |
38: | self::createStubsMap(), |
39: | self::createComposerClassLoader($output, $projectDir), |
40: | ); |
41: | } |
42: | |
43: | |
44: | |
45: | |
46: | |
47: | protected static function createStubsMap(): array |
48: | { |
49: | $stubsMap = []; |
50: | |
51: | $phpStormStubsDir = dirname(Helpers::classLikePath(PhpStormStubsMap::class)); |
52: | foreach (PhpStormStubsMap::CLASSES as $class => $path) { |
53: | $stubsMap[strtolower($class)] = "$phpStormStubsDir/$path"; |
54: | } |
55: | |
56: | $phpStanStubsDir = dirname(Helpers::classLikePath(Php8StubsMap::class)); |
57: | foreach ((new Php8StubsMap(PHP_VERSION_ID))->classes as $class => $path) { |
58: | $stubsMap[$class] = "$phpStanStubsDir/$path"; |
59: | } |
60: | |
61: | return $stubsMap; |
62: | } |
63: | |
64: | |
65: | protected static function createComposerClassLoader(OutputStyle $output, string $projectDir): ClassLoader |
66: | { |
67: | $loader = new ClassLoader(); |
68: | $composerJsonPath = "$projectDir/composer.json"; |
69: | |
70: | if (!is_file($composerJsonPath)) { |
71: | $output->warning(implode("\n", [ |
72: | "Unable to use Composer autoloader for finding dependencies because file", |
73: | "$composerJsonPath does not exist. Use --working-dir to specify directory where composer.json is located", |
74: | ])); |
75: | |
76: | return $loader; |
77: | } |
78: | |
79: | $composerJson = Json::decode(FileSystem::read($composerJsonPath), forceArrays: true); |
80: | $vendorDir = FileSystem::joinPaths($projectDir, $composerJson['config']['vendor-dir'] ?? 'vendor'); |
81: | |
82: | if (!is_dir($vendorDir)) { |
83: | $output->warning(implode("\n", [ |
84: | "Unable to use Composer autoloader for finding dependencies because directory", |
85: | "$vendorDir does not exist. Run composer install to install dependencies.", |
86: | ])); |
87: | |
88: | return $loader; |
89: | } |
90: | |
91: | $output->text("Using Composer autoloader for finding dependencies in $vendorDir.\n"); |
92: | $loader->addClassMap(require "$vendorDir/composer/autoload_classmap.php"); |
93: | |
94: | foreach (require "$vendorDir/composer/autoload_namespaces.php" as $prefix => $paths) { |
95: | $loader->set($prefix, $paths); |
96: | } |
97: | |
98: | foreach (require "$vendorDir/composer/autoload_psr4.php" as $prefix => $paths) { |
99: | $loader->setPsr4($prefix, $paths); |
100: | } |
101: | |
102: | return $loader; |
103: | } |
104: | |
105: | |
106: | public function locate(ClassLikeReferenceInfo $name): ?string |
107: | { |
108: | return $this->classLoader->findFile($name->full) ?: $this->stubsMap[$name->fullLower] ?? null; |
109: | } |
110: | } |
111: | |