Passed
Pull Request — master (#102)
by Marco
02:42
created

LocateSourcesViaComposerJson::classMapFiles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 2
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\BackwardCompatibility\LocateSources;
6
7
use Assert\Assert;
8
use Roave\BetterReflection\SourceLocator\Ast\Locator;
9
use Roave\BetterReflection\SourceLocator\Type\AggregateSourceLocator;
10
use Roave\BetterReflection\SourceLocator\Type\DirectoriesSourceLocator;
11
use Roave\BetterReflection\SourceLocator\Type\SingleFileSourceLocator;
12
use Roave\BetterReflection\SourceLocator\Type\SourceLocator;
13
use function array_filter;
14
use function array_map;
15
use function array_merge;
16
use function array_values;
17
use function file_get_contents;
18
use function is_array;
19
use function is_dir;
20
use function is_file;
21
use function json_decode;
22
use function ltrim;
23
use function realpath;
24
use function strpos;
25
use function substr;
26
27
final class LocateSourcesViaComposerJson implements LocateSources
28
{
29
    /** @var Locator */
30
    private $astLocator;
31
32
    public function __construct(Locator $astLocator)
33
    {
34
        $this->astLocator = $astLocator;
35
    }
36
37
    public function __invoke(string $installationPath) : SourceLocator
38
    {
39
        $realInstallationPath = realpath($installationPath);
40
41
        Assert::that($realInstallationPath)->string();
42
43
        $composerJsonPath = $realInstallationPath . '/composer.json';
44
45
        Assert::that($composerJsonPath)
46
              ->file()
47
              ->readable();
48
49
        $composerDefinitionString = file_get_contents($composerJsonPath);
50
51
        Assert::that($composerDefinitionString)->string();
52
53
        $composerDefinition = json_decode($composerDefinitionString, true);
54
55
        Assert::that($composerDefinition)->isArray();
56
57
        $autoloadDefinition = $composerDefinition['autoload'] ?? [];
58
59
        Assert::that($autoloadDefinition)->isArray();
60
61
        $prependInstallationPath = $this->prependInstallationPath($realInstallationPath);
62
63
        return new AggregateSourceLocator(array_merge(
64
            [
65
                new DirectoriesSourceLocator(
66
                    array_map(
67
                        $prependInstallationPath,
68
                        array_merge(
69
                            $this->psr0Directories($autoloadDefinition),
70
                            $this->psr4Directories($autoloadDefinition),
71
                            $this->classMapDirectories($autoloadDefinition, $installationPath)
72
                        )
73
                    ),
74
                    $this->astLocator
75
                ),
76
            ],
77
            array_map(
78
                function (string $path) : SourceLocator {
79
                    return new SingleFileSourceLocator($path, $this->astLocator);
80
                },
81
                array_map(
82
                    $prependInstallationPath,
83
                    $this->classMapFiles($autoloadDefinition, $installationPath)
84
                )
85
            ),
86
            array_map(
87
                function (string $path) : SourceLocator {
88
                    return new SingleFileSourceLocator($path, $this->astLocator);
89
                },
90
                array_map(
91
                    $prependInstallationPath,
92
                    $this->files($autoloadDefinition)
93
                )
94
            )
95
        ));
96
    }
97
98
    /**
99
     * @param mixed[] $autoloadDefinition
100
     *
101
     * @return string[]
102
     */
103
    private function classMapDirectories(array $autoloadDefinition, string $installationPath) : array
104
    {
105
        return array_values(array_filter(
106
            $autoloadDefinition['classmap'] ?? [],
107
            function (string $path) use ($installationPath) : bool {
108
                $filePath = ($this->prependInstallationPath($installationPath))($path);
109
110
                return is_dir($filePath);
111
            }
112
        ));
113
    }
114
115
    /**
116
     * @param mixed[] $autoloadDefinition
117
     *
118
     * @return string[]
119
     */
120
    private function classMapFiles(array $autoloadDefinition, string $installationPath) : array
121
    {
122
        return array_values(array_filter(
123
            $autoloadDefinition['classmap'] ?? [],
124
            function (string $path) use ($installationPath) : bool {
125
                $filePath = ($this->prependInstallationPath($installationPath))($path);
126
127
                return is_file($filePath);
128
            }
129
        ));
130
    }
131
132
    /**
133
     * @param mixed[] $autoloadDefinition
134
     *
135
     * @return string[]
136
     */
137
    private function files(array $autoloadDefinition) : array
138
    {
139
        return array_values($autoloadDefinition['files'] ?? []);
140
    }
141
142
    /**
143
     * @param mixed[] $autoloadDefinition
144
     *
145
     * @return string[]
146
     */
147
    private function psr0Directories(array $autoloadDefinition) : array
148
    {
149
        return array_merge(
150
            [],
151
            ...array_values(array_map([$this, 'stringToArray'], $autoloadDefinition['psr-0'] ?? []))
152
        );
153
    }
154
155
    /**
156
     * @param mixed[] $autoloadDefinition
157
     *
158
     * @return string[]
159
     */
160
    private function psr4Directories(array $autoloadDefinition) : array
161
    {
162
        return array_merge(
163
            [],
164
            ...array_values(array_map([$this, 'stringToArray'], $autoloadDefinition['psr-4'] ?? []))
165
        );
166
    }
167
168
    /** @return callable (string $path) : string */
169
    private function prependInstallationPath(string $installationPath) : callable
170
    {
171
        return function (string $path) use ($installationPath) : string {
172
            if (strpos($path, './') === 0) {
173
                return $installationPath . '/' . substr($path, 2);
174
            }
175
176
            return $installationPath . '/' . ltrim($path, '/');
177
        };
178
    }
179
180
    /**
181
     * @param string|string[] $entry
182
     *
183
     * @return string[]
184
     */
185
    private function stringToArray($entry) : array
186
    {
187
        if (is_array($entry)) {
188
            return array_values($entry);
189
        }
190
191
        return [$entry];
192
    }
193
}
194