Passed
Pull Request — master (#96)
by Dmitriy
18:07 queued 10s
created

Builder::getConfigPluginData()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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