Passed
Push — master ( 14a42b...4e4f6b )
by Andrii
02:16
created

Config::putFile()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 10
ccs 0
cts 7
cp 0
rs 9.6111
c 0
b 0
f 0
cc 5
nc 5
nop 2
crap 30
1
<?php
2
/**
3
 * Composer plugin for config assembling
4
 *
5
 * @link      https://github.com/hiqdev/composer-config-plugin
6
 * @package   composer-config-plugin
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2016-2018, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\composer\config\configs;
12
13
use hiqdev\composer\config\Builder;
14
use hiqdev\composer\config\Helper;
15
use hiqdev\composer\config\exceptions\FailedWriteException;
16
use hiqdev\composer\config\readers\ReaderFactory;
17
18
/**
19
 * Config class represents output configuration file.
20
 *
21
 * @author Andrii Vasyliev <[email protected]>
22
 */
23
class Config
24
{
25
    const UNIX_DS = '/';
26
    const BASE_DIR_MARKER = '<<<base-dir>>>';
27
28
    protected $builder;
29
30
    protected $name;
31
32
    protected $sources = [];
33
34
    protected $values = [];
35
36 1
    public function __construct(Builder $builder, string $name)
37
    {
38 1
        $this->builder = $builder;
39 1
        $this->name = $name;
40 1
    }
41
42 1
    public function getBuilder(): Builder
43
    {
44 1
        return $this->builder;
45
    }
46
47 1
    public function getName(): string
48
    {
49 1
        return $this->name;
50
    }
51
52
    public function getValues(): array
53
    {
54
        return $this->values;
55
    }
56
57
    public function load(array $paths)
58
    {
59
        $configs = [];
60
        foreach ($paths as $path) {
61
            $config = $this->loadFile($path);
62
            if (!empty($config)) {
63
                $configs[] = $config;
64
            }
65
        }
66
67
        $this->sources = $configs;
68
69
        return $this;
70
    }
71
72
    /**
73
     * Reads config file.
74
     * @param string $path
75
     * @return array configuration read from file
76
     */
77
    protected function loadFile($path)
78
    {
79
        $reader = ReaderFactory::get($this->builder, $path);
80
81
        return $reader->read($path);
82
    }
83
84
    /**
85
     * Merges given configs and writes at given name.
86
     * @param mixed $name
87
     * @param array $configs
88
     */
89
    public function build()
90
    {
91
        $this->values = $this->calcValues($this->sources);
92
93
        return $this;
94
    }
95
96
    public function write()
97
    {
98
        $this->writeFile($this->getOutputPath(), $this->values);
99
100
        return $this;
101
    }
102
103
    protected function calcValues(array $sources)
104
    {
105
        $values = call_user_func_array([Helper::class, 'mergeConfig'], $sources);
106
107
        return $this->substituteOutputDirs($values);
108
    }
109
110
    protected function writeFile(string $path, array $data)
111
    {
112
        $this->writePhpFile($path, $data, true);
113
    }
114
115
    /**
116
     * Writes complete PHP config file by full path.
117
     * @param string $path
118
     * @param array $data
119
     * @param bool $requireDefines
120
     */
121
    protected function writePhpFile(string $path, array $data, bool $requireDefines)
122
    {
123
        static::putFile($path, implode("\n\n", array_filter([
124
            'header'  => '<?php',
125
            'defines' => $requireDefines ? "require_once __DIR__ . '/defines.php';" : '',
126
            'baseDir' => "\$baseDir = dirname(dirname(dirname(__DIR__)));",
127
            'content' => $this->renderVars($data),
128
        ])));
129
    }
130
131
    protected function renderVars(array $vars)
132
    {
133
        $content = 'return ' . Helper::exportVar($vars) . ";\n";
134
        $content = str_replace("'" . static::BASE_DIR_MARKER, "\$baseDir . '", $content);
135
136
        return str_replace("'?" . static::BASE_DIR_MARKER, "'?' . \$baseDir . '", $content);
137
    }
138
139
    /**
140
     * Writes file if content changed.
141
     * @param string $path
142
     * @param string $content
143
     */
144
    protected static function putFile($path, $content)
145
    {
146
        if (file_exists($path) && $content === file_get_contents($path)) {
147
            return;
148
        }
149
        if (!file_exists(dirname($path))) {
150
            mkdir(dirname($path), 0777, true);
151
        }
152
        if (false === file_put_contents($path, $content)) {
153
            throw new FailedWriteException("Failed write file $path");
154
        }
155
    }
156
157
    /**
158
     * Substitute output paths in given data array recursively with marker.
159
     * @param array $data
160
     * @return array
161
     */
162
    public function substituteOutputDirs(array $data)
163
    {
164
        $dir = static::normalizePath(dirname(dirname(dirname($this->getOutputDir()))));
165
166
        return static::substitutePaths($data, $dir, static::BASE_DIR_MARKER);
167
    }
168
169
    /**
170
     * Normalizes given path with given directory separator.
171
     * Default forced to Unix directory separator for substitutePaths to work properly in Windows.
172
     * @param string $path path to be normalized
173
     * @param string $ds directory separator
174
     * @return string
175
     */
176
    public static function normalizePath($path, $ds = self::UNIX_DS)
177
    {
178
        return rtrim(strtr($path, '/\\', $ds . $ds), $ds);
179
    }
180
181
    /**
182
     * Substitute all paths in given array recursively with alias if applicable.
183
     * @param array $data
184
     * @param string $dir
185
     * @param string $alias
186
     * @return array
187
     */
188
    public static function substitutePaths($data, $dir, $alias)
189
    {
190
        foreach ($data as &$value) {
191
            if (is_string($value)) {
192
                $value = static::substitutePath($value, $dir, $alias);
193
            } elseif (is_array($value)) {
194
                $value = static::substitutePaths($value, $dir, $alias);
195
            }
196
        }
197
198
        return $data;
199
    }
200
201
    /**
202
     * Substitute path with alias if applicable.
203
     * @param string $path
204
     * @param string $dir
205
     * @param string $alias
206
     * @return string
207
     */
208
    protected static function substitutePath($path, $dir, $alias)
209
    {
210
        $end = $dir . self::UNIX_DS;
211
        $skippable = 0 === strncmp($path, '?', 1) ? '?' : '';
212
        if ($skippable) {
213
            $path = substr($path, 1);
214
        }
215
        $result = (substr($path, 0, strlen($dir)) === $dir) ? $alias . substr($path, strlen($dir) - 1) : $path;
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
216
        if ($path === $dir) {
217
            $result = $alias;
218
        } elseif (substr($path, 0, strlen($end)) === $end) {
219
            $result = $alias . substr($path, strlen($end) - 1);
220
        } else {
221
            $result = $path;
222
        }
223
224
        return $skippable . $result;
225
    }
226
227
    public function getOutputDir(): string
228
    {
229
        return $this->builder->getOutputDir();
230
    }
231
232
    public function getOutputPath(string $name = null): string
233
    {
234
        return $this->builder->getOutputPath($name ?: $this->name);
235
    }
236
}
237