Passed
Pull Request — master (#83)
by Dmitriy
11:40
created

Builder::getOutputPath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 0
cts 2
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
        $alt->configs['packages'] = $this->getConfig('packages')->clone($alt);
56
57
        return $alt;
58
    }
59
60
    public function setOutputDir(?string $outputDir): void
61
    {
62
        $this->outputDir = $outputDir
63
            ? static::buildAbsPath($this->getBaseDir(), $outputDir)
64
            : static::findOutputDir($this->getBaseDir());
65
    }
66
67
    public static function rebuild(?string $baseDir = null): void
68
    {
69
        $builder = new self(new ConfigFactory(), $baseDir ?? self::findBaseDir());
70
        $files = $builder->getConfig('__files')->load();
71
72
        $builder->buildUserConfigs($files->getValues());
73
    }
74
75
    /**
76
     * Returns default output dir.
77
     *
78
     * @param string|null $baseDir path to the root Composer package. When `null`,
79
     * @return string
80
     * @throws JsonException
81
     */
82
    private static function findOutputDir(string $baseDir = null): string
83
    {
84 5
        if ($baseDir === null) {
85
            $baseDir = static::findBaseDir();
86 5
        }
87
        $path = $baseDir . DIRECTORY_SEPARATOR . 'composer.json';
88
        $data = @json_decode(file_get_contents($path), true);
89 5
        $dir = $data['extra'][Package::EXTRA_OUTPUT_DIR_OPTION_NAME] ?? null;
90 5
91 5
        return $dir ? static::buildAbsPath($baseDir, $dir) : static::defaultOutputDir($baseDir);
92
    }
93 5
94
    private static function findBaseDir(): string
95
    {
96
        $candidates = [
97
            // normal relative path
98
            dirname(__DIR__, 4),
99
            // console
100
            getcwd(),
101
            // symlinked web
102
            dirname(getcwd())
103
        ];
104
105
        foreach ($candidates as $baseDir) {
106
            if (file_exists($baseDir . DIRECTORY_SEPARATOR . 'composer.json')) {
107
                return $baseDir;
108
            }
109
        }
110
111
        throw new \RuntimeException('Cannot find directory that contains composer.json');
112
    }
113
114
    /**
115
     * Returns default output dir.
116
     *
117
     * @param string $baseDir path to base directory
118
     * @return string
119
     */
120
    private static function defaultOutputDir(string $baseDir = null): string
121
    {
122 5
        if ($baseDir) {
123
            $dir = $baseDir . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'yiisoft' . DIRECTORY_SEPARATOR . basename(dirname(__DIR__));
124 5
        } else {
125 5
            $dir = dirname(__DIR__);
126
        }
127
128
        return $dir . static::OUTPUT_DIR_SUFFIX;
129
    }
130 5
131
    /**
132
     * Returns full path to assembled config file.
133
     *
134
     * @param string $filename name of config
135
     * @param string $baseDir path to base dir
136
     * @return string absolute path
137
     * @throws JsonException
138
     */
139
    public static function path(string $filename, string $baseDir = null): string
140
    {
141 5
        return static::buildAbsPath(static::findOutputDir($baseDir), $filename . '.php');
142
    }
143 5
144
    private static function buildAbsPath(string $dir, string $file): string
145
    {
146 5
        return self::isAbsolutePath($file) ? $file : $dir . DIRECTORY_SEPARATOR . $file;
147
    }
148 5
149
    private static function isAbsolutePath(string $path): bool
150
    {
151 5
        return strpos($path, '/') === 0 || strpos($path, ':') === 1 || strpos($path, '\\\\') === 0;
152
    }
153 5
154
    /**
155
     * Builds all (user and system) configs by given files list.
156
     *
157
     * @param null|array $files files to process: config name => list of files
158
     */
159
    public function buildAllConfigs(array $files): void
160
    {
161
        $this->buildUserConfigs($files);
162
        $this->buildSystemConfigs($files);
163
    }
164
165
    /**
166
     * Builds configs by given files list.
167
     *
168
     * @param null|array $files files to process: config name => list of files
169
     * @return array
170
     */
171
    private function buildUserConfigs(array $files): array
172
    {
173
        $resolver = new Resolver($files);
174
        $files = $resolver->get();
175
        foreach ($files as $name => $paths) {
176
            $this->getConfig($name)->load($paths)->build()->write();
177
        }
178
179
        return $files;
180
    }
181
182
    private function buildSystemConfigs(array $files): void
183
    {
184
        $this->getConfig('__files')->setValues($files);
185
        foreach (['__files', 'packages'] as $name) {
186
            $this->getConfig($name)->build()->write();
187
        }
188
    }
189
190
    public function getOutputPath(string $name): string
191
    {
192
        return $this->outputDir . DIRECTORY_SEPARATOR . $name . '.php';
193
    }
194
195
    public function getConfig(string $name)
196
    {
197
        if (!array_key_exists($name, $this->configs)) {
198
            $this->configs[$name] = $this->configFactory->create($this, $name);
199
        }
200
201
        return $this->configs[$name];
202
    }
203
204
    public function getVars(): array
205
    {
206
        $vars = [];
207
        foreach ($this->configs as $name => $config) {
208
            $vars[$name] = $config->getValues();
209
        }
210
211
        return $vars;
212
    }
213
214
    public function setPackage(string $name, array $data): void
215
    {
216
        $this->getConfig('packages')->setValue($name, $data);
217
    }
218
219
    /**
220
     * @return string a full path to the project root
221
     */
222
    public function getBaseDir(): string
223
    {
224
        return $this->baseDir;
225
    }
226
}
227