Passed
Push — master ( e85357...d9046f )
by Alexander
21:03 queued 14:54
created

Builder::buildAllConfigs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
cc 1
nc 1
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Composer\Config;
6
7
use JsonException;
8
use Yiisoft\Composer\Config\Config\Config;
9
use Yiisoft\Composer\Config\Config\ConfigFactory;
10
use Yiisoft\Composer\Config\Util\Resolver;
11
12
use function dirname;
13
14
/**
15
 * Builder assembles config files.
16
 */
17
class Builder
18
{
19
    private const OUTPUT_DIR_SUFFIX = '-output';
20
21
    /**
22
     * @var string path to the Composer project root
23
     */
24
    private string $baseDir;
25
26
    /**
27
     * @var string path to output assembled configs
28
     */
29
    private string $outputDir;
30
31
    /**
32
     * @var Config[] configurations
33
     */
34
    private array $configs = [];
35
36
    private ConfigFactory $configFactory;
37
38
    /**
39
     * Builder constructor.
40
     *
41
     * @param ConfigFactory $configFactory
42
     * @param string $baseDir path to the Composer project root
43
     */
44
    public function __construct(ConfigFactory $configFactory, string $baseDir)
45
    {
46
        $this->configFactory = $configFactory;
47
        $this->baseDir = $baseDir;
48
        $this->outputDir = self::findOutputDir($baseDir);
49
    }
50
51
    public function createAlternative($name): Builder
52
    {
53
        $alt = new static($this->configFactory, $this->baseDir);
54
        $alt->setOutputDir($this->outputDir . DIRECTORY_SEPARATOR . $name);
55
        foreach (['aliases', 'packages'] as $key) {
56
            $alt->configs[$key] = $this->getConfig($key)->clone($alt);
57
        }
58
59
        return $alt;
60
    }
61
62
    public function setOutputDir(?string $outputDir): void
63
    {
64
        $this->outputDir = $outputDir
65
            ? static::buildAbsPath($this->getBaseDir(), $outputDir)
66
            : static::findOutputDir($this->getBaseDir());
67
    }
68
69
    public static function rebuild(?string $baseDir = null): void
70
    {
71
        Plugin::buildAllConfigs($baseDir ?? self::findBaseDir());
72
    }
73
74
    /**
75
     * Returns default output dir.
76
     *
77
     * @param string|null $baseDir path to the root Composer package. When `null`,
78
     * @return string
79
     * @throws JsonException
80
     */
81 5
    private static function findOutputDir(string $baseDir = null): string
82
    {
83 5
        if ($baseDir === null) {
84
            $baseDir = static::findBaseDir();
85
        }
86 5
        $path = $baseDir . DIRECTORY_SEPARATOR . 'composer.json';
87 5
        $data = @json_decode(file_get_contents($path), true);
88 5
        $dir = $data['extra'][Package::EXTRA_OUTPUT_DIR_OPTION_NAME] ?? null;
89
90 5
        return $dir ? static::buildAbsPath($baseDir, $dir) : static::defaultOutputDir($baseDir);
91
    }
92
93
    private static function findBaseDir(): string
94
    {
95
        $candidates = [
96
            // normal relative path
97
            dirname(__DIR__, 4),
98
            // console
99
            getcwd(),
100
            // symlinked web
101
            dirname(getcwd())
102
        ];
103
104
        foreach ($candidates as $baseDir) {
105
            if (file_exists($baseDir . DIRECTORY_SEPARATOR . 'composer.json')) {
106
                return $baseDir;
107
            }
108
        }
109
110
        throw new \RuntimeException('Cannot find directory that contains composer.json');
111
    }
112
113
    /**
114
     * Returns default output dir.
115
     *
116
     * @param string $baseDir path to base directory
117
     * @return string
118
     */
119 5
    private static function defaultOutputDir(string $baseDir = null): string
120
    {
121 5
        if ($baseDir) {
122 5
            $dir = $baseDir . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'yiisoft' . DIRECTORY_SEPARATOR . basename(dirname(__DIR__));
123
        } else {
124
            $dir = dirname(__DIR__);
125
        }
126
127 5
        return $dir . static::OUTPUT_DIR_SUFFIX;
128
    }
129
130
    /**
131
     * Returns full path to assembled config file.
132
     *
133
     * @param string $filename name of config
134
     * @param string $baseDir path to base dir
135
     * @return string absolute path
136
     * @throws JsonException
137
     */
138 5
    public static function path(string $filename, string $baseDir = null): string
139
    {
140 5
        return static::buildAbsPath(static::findOutputDir($baseDir), $filename . '.php');
141
    }
142
143 5
    private static function buildAbsPath(string $dir, string $file): string
144
    {
145 5
        return self::isAbsolutePath($file) ? $file : $dir . DIRECTORY_SEPARATOR . $file;
146
    }
147
148 5
    private static function isAbsolutePath(string $path): bool
149
    {
150 5
        return strpos($path, '/') === 0 || strpos($path, ':') === 1 || strpos($path, '\\\\') === 0;
151
    }
152
153
    /**
154
     * Builds all (user and system) configs by given files list.
155
     *
156
     * @param null|array $files files to process: config name => list of files
157
     */
158
    public function buildAllConfigs(array $files): void
159
    {
160
        $this->buildUserConfigs($files);
161
        $this->buildSystemConfigs();
162
    }
163
164
    /**
165
     * Builds configs by given files list.
166
     *
167
     * @param null|array $files files to process: config name => list of files
168
     * @return array
169
     */
170
    private function buildUserConfigs(array $files): array
171
    {
172
        $resolver = new Resolver($files);
173
        $files = $resolver->get();
174
        foreach ($files as $name => $paths) {
175
            $this->getConfig($name)->load($paths)->build()->write();
176
        }
177
178
        return $files;
179
    }
180
181
    private function buildSystemConfigs(): void
182
    {
183
        foreach (['aliases', 'packages'] as $name) {
184
            $this->getConfig($name)->build()->write();
185
        }
186
    }
187
188
    public function getOutputPath(string $name): string
189
    {
190
        return $this->outputDir . DIRECTORY_SEPARATOR . $name . '.php';
191
    }
192
193
    public function getConfig(string $name)
194
    {
195
        if (!array_key_exists($name, $this->configs)) {
196
            $this->configs[$name] = $this->configFactory->create($this, $name);
197
        }
198
199
        return $this->configs[$name];
200
    }
201
202
    public function getVars(): array
203
    {
204
        $vars = [];
205
        foreach ($this->configs as $name => $config) {
206
            $vars[$name] = $config->getValues();
207
        }
208
209
        return $vars;
210
    }
211
212
    public function mergeAliases(array $aliases): void
213
    {
214
        $this->getConfig('aliases')->mergeValues($aliases);
215
    }
216
217
    public function setPackage(string $name, array $data): void
218
    {
219
        $this->getConfig('packages')->setValue($name, $data);
220
    }
221
222
    /**
223
     * @return string a full path to the project root
224
     */
225
    public function getBaseDir(): string
226
    {
227
        return $this->baseDir;
228
    }
229
}
230