Passed
Pull Request — master (#96)
by Mlax
14:21
created

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