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 $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(static function (array $directories): array {
39: return array_map(static function (string $directory) : string {
40: return rtrim($directory, '/');
41: }, $directories);
42: }, $mappings);
43:
44: return $instance;
45: }
46:
47: /** {@inheritDoc} */
48: public function resolvePossibleFilePaths(Identifier $identifier): array
49: {
50: if (! $identifier->isClass()) {
51: return [];
52: }
53:
54: $className = $identifier->getName();
55: $matchingPrefixes = $this->matchingPrefixes($className);
56:
57: return array_merge([], ...array_map(static function (array $paths, string $prefix) use ($className): array {
58: $subPath = ltrim(str_replace('\\', '/', substr($className, strlen($prefix))), '/');
59:
60: if ($subPath === '') {
61: return [];
62: }
63:
64: return array_map(static function (string $path) use ($subPath) : string {
65: return $path . '/' . $subPath . '.php';
66: }, $paths);
67: }, $matchingPrefixes, array_keys($matchingPrefixes)));
68: }
69:
70: /** @return array<string, list<string>> */
71: private function matchingPrefixes(string $className): array
72: {
73: return array_filter($this->mappings, static function (string $prefix) use ($className): bool {
74: if ($prefix === '') {
75: return false;
76: }
77:
78: return strpos($className, $prefix) === 0;
79: }, ARRAY_FILTER_USE_KEY);
80: }
81:
82: /** {@inheritDoc} */
83: public function directories(): array
84: {
85: return array_values(array_unique(array_merge([], ...array_values($this->mappings))));
86: }
87: }
88: