Passed
Pull Request — master (#111)
by
unknown
14:42
created

Builder::createAlternative()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 9
ccs 0
cts 4
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
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
    /**
37
     * @var array
38
     */
39
    private array $overrideParams = [];
40
41
    private ConfigFactory $configFactory;
42
43
    /**
44
     * Builder constructor.
45
     *
46
     * @param ConfigFactory $configFactory
47
     * @param string $baseDir path to the Composer project root
48
     */
49
    public function __construct(ConfigFactory $configFactory, string $baseDir)
50
    {
51
        $this->configFactory = $configFactory;
52
        $this->baseDir = $baseDir;
53
        $this->outputDir = self::findOutputDir($baseDir);
54
    }
55
56
    public function createAlternative($name): Builder
57
    {
58
        $alt = new static($this->configFactory, $this->baseDir);
59
        $alt->setOutputDir($this->outputDir . DIRECTORY_SEPARATOR . $name);
60
        foreach (['aliases', 'packages'] as $key) {
61
            $alt->configs[$key] = $this->getConfig($key)->clone($alt);
62
        }
63
64
        return $alt;
65
    }
66
67
    public function setOutputDir(?string $outputDir): void
68
    {
69
        $this->outputDir = $outputDir
70
            ? static::buildAbsPath($this->getBaseDir(), $outputDir)
71
            : static::findOutputDir($this->getBaseDir());
72
    }
73
74
    public static function rebuild(?string $baseDir = null): void
75
    {
76
        // Ensure COMPOSER_HOME is set in case web server does not give PHP OS environment variables
77
        if (!(getenv('APPDATA') || getenv('HOME') || getenv('COMPOSER_HOME'))) {
78
            $path = sys_get_temp_dir() . '/.composer';
79
            if (!is_dir($path) && !mkdir($path)) {
80
                throw new \RuntimeException(sprintf('Directory "%s" was not created', $path));
81
            }
82
            putenv('COMPOSER_HOME=' . $path);
83
        }
84
85
        Plugin::buildAllConfigs($baseDir ?? self::findBaseDir());
86
    }
87
88
    /**
89
     * Returns default output dir.
90 5
     *
91
     * @param string|null $baseDir path to the root Composer package. When `null`,
92 5
     * @return string
93
     * @throws JsonException
94
     */
95 5
    private static function findOutputDir(string $baseDir = null): string
96 5
    {
97 5
        if ($baseDir === null) {
98
            $baseDir = static::findBaseDir();
99 5
        }
100
        $path = $baseDir . DIRECTORY_SEPARATOR . 'composer.json';
101
        $data = @json_decode(file_get_contents($path), true);
102
        $dir = $data['extra'][Package::EXTRA_OUTPUT_DIR_OPTION_NAME] ?? null;
103
104
        return $dir ? static::buildAbsPath($baseDir, $dir) : static::defaultOutputDir($baseDir);
105
    }
106
107
    private static function findBaseDir(): string
108
    {
109
        $candidates = [
110
            // normal relative path
111
            dirname(__DIR__, 4),
112
            // console
113
            getcwd(),
114
            // symlinked web
115
            dirname(getcwd())
116
        ];
117
118
        foreach ($candidates as $baseDir) {
119
            if (file_exists($baseDir . DIRECTORY_SEPARATOR . 'composer.json')) {
120
                return $baseDir;
121
            }
122
        }
123
124
        throw new \RuntimeException('Cannot find directory that contains composer.json');
125
    }
126
127
    /**
128 5
     * Returns default output dir.
129
     *
130 5
     * @param string $baseDir path to base directory
131 5
     * @return string
132
     */
133
    private static function defaultOutputDir(string $baseDir = null): string
134
    {
135
        if ($baseDir) {
136 5
            $dir = $baseDir . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'yiisoft' . DIRECTORY_SEPARATOR . basename(dirname(__DIR__));
137
        } else {
138
            $dir = dirname(__DIR__);
139
        }
140
141
        return $dir . static::OUTPUT_DIR_SUFFIX;
142
    }
143
144
    /**
145
     * Returns full path to assembled config file.
146
     *
147 5
     * @param string $filename name of config
148
     * @param string $baseDir path to base dir
149 5
     * @return string absolute path
150
     * @throws JsonException
151
     */
152 5
    public static function path(string $filename, string $baseDir = null): string
153
    {
154 5
        return static::buildAbsPath(static::findOutputDir($baseDir), $filename . '.php');
155
    }
156
157 5
    private static function buildAbsPath(string $dir, string $file): string
158
    {
159 5
        return self::isAbsolutePath($file) ? $file : $dir . DIRECTORY_SEPARATOR . $file;
160
    }
161
162
    private static function isAbsolutePath(string $path): bool
163
    {
164
        return strpos($path, '/') === 0 || strpos($path, ':') === 1 || strpos($path, '\\\\') === 0;
165
    }
166
167
    /**
168
     * @param array $overrideParams
169
     * @return void
170
     */
171
    public function mergeOverrideParams(array $overrideParams): void
172
    {
173
        if ($overrideParams) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $overrideParams of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
174
            $this->overrideParams = array_merge($this->overrideParams, $overrideParams);
175
        }
176
    }
177
178
    /**
179
     * @param string $name
180
     * @return array
181
     */
182
    public function getConfigParams(string $name): array
183
    {
184
        $paramsConfigName = $this->overrideParams[$name] ?? 'params';
185
        return isset($this->configs[$paramsConfigName]) ? $this->configs[$paramsConfigName]->getValues() : [];
186
    }
187
188
    /**
189
     * Builds all (user and system) configs by given files list.
190
     *
191
     * @param null|array $files files to process: config name => list of files
192
     */
193
    public function buildAllConfigs(array $files): void
194
    {
195
        $this->buildUserConfigs($files);
196
        $this->buildSystemConfigs();
197
    }
198
199
    /**
200
     * Builds configs by given files list.
201
     *
202
     * @param null|array $files files to process: config name => list of files
203
     * @return array
204
     */
205
    private function buildUserConfigs(array $files): array
206
    {
207
        $resolver = new Resolver($files);
208
        $files = $resolver->get();
209
        foreach ($files as $name => $paths) {
210
            $this->getConfig($name)->load($paths)->build()->write();
211
        }
212
213
        return $files;
214
    }
215
216
    private function buildSystemConfigs(): void
217
    {
218
        foreach (['aliases', 'packages'] as $name) {
219
            $this->getConfig($name)->build()->write();
220
        }
221
    }
222
223
    public function getOutputPath(string $name): string
224
    {
225
        return $this->outputDir . DIRECTORY_SEPARATOR . $name . '.php';
226
    }
227
228
    public function getConfig(string $name)
229
    {
230
        if (!array_key_exists($name, $this->configs)) {
231
            $this->configs[$name] = $this->configFactory->create($this, $name);
232
        }
233
234
        return $this->configs[$name];
235
    }
236
237
    public function getVars(): array
238
    {
239
        $vars = [];
240
        foreach ($this->configs as $name => $config) {
241
            $vars[$name] = $config->getValues();
242
        }
243
244
        return $vars;
245
    }
246
247
    public function mergeAliases(array $aliases): void
248
    {
249
        $this->getConfig('aliases')->mergeValues($aliases);
250
    }
251
252
    public function setPackage(string $name, array $data): void
253
    {
254
        $this->getConfig('packages')->setValue($name, $data);
255
    }
256
257
    /**
258
     * @return string a full path to the project root
259
     */
260
    public function getBaseDir(): string
261
    {
262
        return $this->baseDir;
263
    }
264
}
265