1: <?php
2:
3: declare(strict_types=1);
4:
5: namespace PHPStan\BetterReflection\SourceLocator\Type\Composer\Psr;
6:
7: use PHPStan\BetterReflection\Identifier\Identifier;
8:
9: use function array_filter;
10: use function array_keys;
11: use function array_map;
12: use function array_merge;
13: use function array_unique;
14: use function array_values;
15: use function ltrim;
16: use function rtrim;
17: use function str_replace;
18: use function str_starts_with;
19: use function strlen;
20: use function substr;
21:
22: use const ARRAY_FILTER_USE_KEY;
23:
24: final class Psr4Mapping implements PsrAutoloaderMapping
25: {
26: /** @var array<string, list<string>> */
27: private array $mappings = [];
28:
29: private function __construct()
30: {
31: }
32:
33: /** @param array<string, list<string>> $mappings */
34: public static function fromArrayMappings(array $mappings): self
35: {
36: $instance = new self();
37:
38: $instance->mappings = array_map(
39: static function (array $directories): array {
40: return array_map(static fn (string $directory): string => rtrim($directory, '/'), $directories);
41: },
42: $mappings,
43: );
44:
45: return $instance;
46: }
47:
48: /** {@inheritDoc} */
49: public function resolvePossibleFilePaths(Identifier $identifier): array
50: {
51: if (! $identifier->isClass()) {
52: return [];
53: }
54:
55: $className = $identifier->getName();
56: $matchingPrefixes = $this->matchingPrefixes($className);
57:
58: return array_merge(
59: [],
60: ...array_map(static function (array $paths, string $prefix) use ($className): array {
61: $subPath = ltrim(str_replace('\\', '/', substr($className, strlen($prefix))), '/');
62:
63: if ($subPath === '') {
64: return [];
65: }
66:
67: return array_map(static fn (string $path): string => $path . '/' . $subPath . '.php', $paths);
68: }, $matchingPrefixes, array_keys($matchingPrefixes)),
69: );
70: }
71:
72: /** @return array<string, list<string>> */
73: private function matchingPrefixes(string $className): array
74: {
75: return array_filter(
76: $this->mappings,
77: static function (string $prefix) use ($className): bool {
78: if ($prefix === '') {
79: return false;
80: }
81:
82: return strpos($className, $prefix) === 0;
83: },
84: ARRAY_FILTER_USE_KEY,
85: );
86: }
87:
88: /** {@inheritDoc} */
89: public function directories(): array
90: {
91: return array_values(array_unique(array_merge([], ...array_values($this->mappings))));
92: }
93: }
94: