Completed
Push — master ( 6f9845...68f10d )
by Marco
12:19 queued 14s
created

prefixPaths()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\BetterReflection\SourceLocator\Type\Composer\Factory;
6
7
use Roave\BetterReflection\SourceLocator\Ast\Locator;
8
use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator;
9
use Roave\BetterReflection\SourceLocator\Type\Composer\Factory\Exception\FailedToParseJson;
10
use Roave\BetterReflection\SourceLocator\Type\Composer\Factory\Exception\InvalidProjectDirectory;
11
use Roave\BetterReflection\SourceLocator\Type\Composer\Factory\Exception\MissingComposerJson;
12
use Roave\BetterReflection\SourceLocator\Type\Composer\Factory\Exception\MissingInstalledJson;
13
use Roave\BetterReflection\SourceLocator\Type\Composer\Psr\Psr0Mapping;
14
use Roave\BetterReflection\SourceLocator\Type\Composer\Psr\Psr4Mapping;
15
use Roave\BetterReflection\SourceLocator\Type\Composer\PsrAutoloaderLocator;
16
use Roave\BetterReflection\SourceLocator\Type\DirectoriesSourceLocator;
17
use Roave\BetterReflection\SourceLocator\Type\SingleFileSourceLocator;
18
use Roave\BetterReflection\SourceLocator\Type\SourceLocator;
19
use function array_filter;
20
use function array_map;
21
use function array_merge;
22
use function array_merge_recursive;
23
use function file_exists;
24
use function file_get_contents;
25
use function is_array;
26
use function is_dir;
27
use function json_decode;
28
use function realpath;
29
30
final class MakeLocatorForComposerJsonAndInstalledJson
31
{
32
    public function __invoke(string $installationPath, Locator $astLocator) : SourceLocator
33
    {
34
        $realInstallationPath = (string) realpath($installationPath);
35
36
        if (! is_dir($realInstallationPath)) {
37
            throw InvalidProjectDirectory::atPath($installationPath);
38
        }
39
40
        $composerJsonPath  = $realInstallationPath . '/composer.json';
41
        $installedJsonPath = $realInstallationPath . '/vendor/composer/installed.json';
42
43
        if (! file_exists($composerJsonPath)) {
44
            throw MissingComposerJson::inProjectPath($installationPath);
45
        }
46
47
        if (! file_exists($installedJsonPath)) {
48
            throw MissingInstalledJson::inProjectPath($installationPath);
49
        }
50
51
        $composer  = json_decode((string) file_get_contents($composerJsonPath), true);
52
        $installed = json_decode((string) file_get_contents($installedJsonPath), true);
53
54
        if (! is_array($composer)) {
55
            throw FailedToParseJson::inFile($composerJsonPath);
56
        }
57
58
        if (! is_array($installed)) {
59
            throw FailedToParseJson::inFile($installedJsonPath);
60
        }
61
62
        $classMapPaths       = array_merge(
63
            $this->prefixPaths($this->packageToClassMapPaths($composer), $realInstallationPath . '/'),
64
            ...array_map(function (array $package) use ($realInstallationPath) : array {
65
                return $this->prefixPaths(
66
                    $this->packageToClassMapPaths($package),
67
                    $this->packagePrefixPath($realInstallationPath, $package)
68
                );
69
            }, $installed)
70
        );
71
        $classMapFiles       = array_filter($classMapPaths, 'is_file');
72
        $classMapDirectories = array_filter($classMapPaths, 'is_dir');
73
        $filePaths           = array_merge(
74
            $this->prefixPaths($this->packageToFilePaths($composer), $realInstallationPath . '/'),
75
            ...array_map(function (array $package) use ($realInstallationPath) : array {
76
                return $this->prefixPaths(
77
                    $this->packageToFilePaths($package),
78
                    $this->packagePrefixPath($realInstallationPath, $package)
79
                );
80
            }, $installed)
81
        );
82
83
        return new AggregateSourceLocator(array_merge(
84
            [
85
                new PsrAutoloaderLocator(
86
                    Psr4Mapping::fromArrayMappings(array_merge_recursive(
87
                        $this->prefixWithInstallationPath($this->packageToPsr4AutoloadNamespaces($composer), $realInstallationPath),
88
                        ...array_map(function (array $package) use ($realInstallationPath) : array {
89
                            return $this->prefixWithPackagePath(
90
                                $this->packageToPsr4AutoloadNamespaces($package),
91
                                $realInstallationPath,
92
                                $package
93
                            );
94
                        }, $installed)
95
                    )),
96
                    $astLocator
97
                ),
98
                new PsrAutoloaderLocator(
99
                    Psr0Mapping::fromArrayMappings(array_merge_recursive(
100
                        $this->prefixWithInstallationPath($this->packageToPsr0AutoloadNamespaces($composer), $realInstallationPath),
101
                        ...array_map(function (array $package) use ($realInstallationPath) : array {
102
                            return $this->prefixWithPackagePath(
103
                                $this->packageToPsr0AutoloadNamespaces($package),
104
                                $realInstallationPath,
105
                                $package
106
                            );
107
                        }, $installed)
108
                    )),
109
                    $astLocator
110
                ),
111
                new DirectoriesSourceLocator($classMapDirectories, $astLocator),
112
            ],
113
            ...array_map(static function (string $file) use ($astLocator) : array {
114
                return [new SingleFileSourceLocator($file, $astLocator)];
115
            }, array_merge($classMapFiles, $filePaths))
116
        ));
117
    }
118
119
    /**
120
     * @param mixed[] $package
121
     *
122
     * @return array<string, array<int, string>>
0 ignored issues
show
Documentation introduced by Marco Pivetta
The doc-type array<string, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
123
     */
124
    private function packageToPsr4AutoloadNamespaces(array $package) : array
125
    {
126
        return array_map(static function ($namespacePaths) : array {
127
            return (array) $namespacePaths;
128
        }, $package['autoload']['psr-4'] ?? []);
129
    }
130
131
    /**
132
     * @param mixed[] $package
133
     *
134
     * @return array<string, array<int, string>>
0 ignored issues
show
Documentation introduced by Marco Pivetta
The doc-type array<string, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
135
     */
136
    private function packageToPsr0AutoloadNamespaces(array $package) : array
137
    {
138
        return array_map(static function ($namespacePaths) : array {
139
            return (array) $namespacePaths;
140
        }, $package['autoload']['psr-0'] ?? []);
141
    }
142
143
    /**
144
     * @param mixed[] $package
145
     *
146
     * @return array<int, string>
0 ignored issues
show
Documentation introduced by Marco Pivetta
The doc-type array<int, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
147
     */
148
    private function packageToClassMapPaths(array $package) : array
149
    {
150
        return $package['autoload']['classmap'] ?? [];
151
    }
152
153
    /**
154
     * @param mixed[] $package
155
     *
156
     * @return array<int, string>
0 ignored issues
show
Documentation introduced by Marco Pivetta
The doc-type array<int, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
157
     */
158
    private function packageToFilePaths(array $package) : array
159
    {
160
        return $package['autoload']['files'] ?? [];
161
    }
162
163
    /**
164
     * @param mixed[] $package
165
     *
166
     * @psalm-param array{name: string} $package
167
     */
168
    private function packagePrefixPath(string $trimmedInstallationPath, array $package) : string
169
    {
170
        return $trimmedInstallationPath . '/vendor/' . $package['name'] . '/';
171
    }
172
173
    /**
174
     * @param array<string, array<int, string>> $paths
175
     * @param array<string, array<int, string>> $package
176
     *
177
     * @return array<string, array<int, string>>
0 ignored issues
show
Documentation introduced by Marco Pivetta
The doc-type array<string, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
178
     *
179
     * @psalm-param array{name: string} $package
180
     */
181 View Code Duplication
    private function prefixWithPackagePath(array $paths, string $trimmedInstallationPath, array $package) : array
0 ignored issues
show
Duplication introduced by Marco Pivetta
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
    {
183
        $prefix = $this->packagePrefixPath($trimmedInstallationPath, $package);
184
185
        return array_map(function (array $paths) use ($prefix) : array {
186
            return $this->prefixPaths($paths, $prefix);
187
        }, $paths);
188
    }
189
190
    /**
191
     * @param array<int|string, array<string>> $paths
192
     *
193
     * @return array<int|string, array<string>>
0 ignored issues
show
Documentation introduced by Marco Pivetta
The doc-type array<int|string, could not be parsed: Expected ">" at position 7, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
194
     */
195
    private function prefixWithInstallationPath(array $paths, string $trimmedInstallationPath) : array
196
    {
197
        return array_map(function (array $paths) use ($trimmedInstallationPath) : array {
198
            return $this->prefixPaths($paths, $trimmedInstallationPath . '/');
199
        }, $paths);
200
    }
201
202
    /**
203
     * @param array<int, string> $paths
204
     *
205
     * @return array<int, string>
0 ignored issues
show
Documentation introduced by Marco Pivetta
The doc-type array<int, could not be parsed: Expected ">" at position 5, but found "end of type". (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
206
     */
207
    private function prefixPaths(array $paths, string $prefix) : array
208
    {
209
        return array_map(static function (string $path) use ($prefix) {
210
            return $prefix . $path;
211
        }, $paths);
212
    }
213
}
214