Passed
Pull Request — master (#96)
by Mlax
10:34
created

Builder::getYamlData()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 10
ccs 0
cts 0
cp 0
rs 10
cc 4
nc 4
nop 1
crap 20
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Composer\Config;
6
7
use JsonException;
8
use Symfony\Component\Yaml\Yaml;
0 ignored issues
show
Bug introduced by
The type Symfony\Component\Yaml\Yaml was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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