Config::putFile()   A
last analyzed

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\exceptions\FailedWriteException;
15
use hiqdev\composer\config\Helper;
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
 * @since php5.5
24
 */
25
class Config
26
{
27
    const UNIX_DS = '/';
28
    const BASE_DIR_MARKER = '<<<base-dir>>>';
29
30
    /**
31
     * @var string config name
32
     */
33
    protected $name;
34
35
    /**
36
     * @var array sources - paths to config source files
37
     */
38
    protected $sources = [];
39
40
    /**
41
     * @var array config value
42
     */
43
    protected $values = [];
44
45
    /**
46
     * @var Builder
47
     */
48
    protected $builder;
49
50
    /**
51
     * Config constructor.
52
     * @param Builder $builder
53
     * @param string  $name
54
     */
55 1
    public function __construct(Builder $builder, $name)
56
    {
57 1
        $this->builder = $builder;
58 1
        $this->name = $name;
59 1
    }
60
61 1
    public function getBuilder()
62
    {
63 1
        return $this->builder;
64
    }
65
66 1
    public function getName()
67
    {
68 1
        return $this->name;
69
    }
70
71
    public function getValues()
72
    {
73
        return $this->values;
74
    }
75
76
    public function load(array $paths = [])
77
    {
78
        $configs = [];
79
        foreach ($paths as $path) {
80
            $config = $this->loadFile($path);
81
            if (!empty($config)) {
82
                $configs[] = $config;
83
            }
84
        }
85
86
        $this->sources = $configs;
87
88
        return $this;
89
    }
90
91
    /**
92
     * Reads config file.
93
     * @param string $path
94
     * @return array configuration read from file
95
     */
96
    protected function loadFile($path)
97
    {
98
        $reader = ReaderFactory::get($this->builder, $path);
99
100
        return $reader->read($path);
101
    }
102
103
    /**
104
     * Merges given configs and writes at given name.
105
     * @return Config
106
     */
107
    public function build()
108
    {
109
        $this->values = $this->calcValues($this->sources);
110
111
        return $this;
112
    }
113
114
    public function write()
115
    {
116
        $this->writeFile($this->getOutputPath(), $this->values);
117
118
        return $this;
119
    }
120
121
    protected function calcValues(array $sources)
122
    {
123
        $values = call_user_func_array([Helper::class, 'mergeConfig'], $sources);
124
125
        return $this->substituteOutputDirs($values);
126
    }
127
128
    /**
129
     * @param string $path
130
     * @param array  $data
131
     * @throws FailedWriteException
132
     * @throws \ReflectionException
133
     */
134
    protected function writeFile($path, array $data)
135
    {
136
        $this->writePhpFile($path, $data, true, true);
137
    }
138
139
    /**
140
     * Writes complete PHP config file by full path.
141
     * @param string       $path
142
     * @param string|array $data
143
     * @param bool         $withEnv
144
     * @param bool         $withDefines
145
     * @throws FailedWriteException
146
     * @throws \ReflectionException
147
     */
148
    protected function writePhpFile($path, $data, $withEnv, $withDefines)
149
    {
150
        static::putFile($path, $this->replaceMarkers(implode("\n\n", array_filter([
151
                'header'  => '<?php',
152
                'baseDir' => '$baseDir = dirname(dirname(dirname(__DIR__)));',
153
                'BASEDIR' => "defined('COMPOSER_CONFIG_PLUGIN_BASEDIR') or define('COMPOSER_CONFIG_PLUGIN_BASEDIR', \$baseDir);",
154
                'dotenv'  => $withEnv ? "\$_ENV = array_merge((array) require __DIR__ . '/dotenv.php', (array) \$_ENV);" : '',
155
                'defines' => $withDefines ? $this->builder->getConfig('defines')->buildRequires() : '',
156
                'content' => is_array($data) ? $this->renderVars($data) : $data,
157
            ]))) . "\n");
158
    }
159
160
    /**
161
     * @param array $vars array to be exported
162
     * @return string
163
     * @throws \ReflectionException
164
     */
165
    protected function renderVars(array $vars)
166
    {
167
        return 'return ' . Helper::exportVar($vars) . ';';
168
    }
169
170
    /**
171
     * @param string $content
172
     * @return string
173
     */
174
    protected function replaceMarkers($content)
175
    {
176
        $content = str_replace("'" . static::BASE_DIR_MARKER, "\$baseDir . '", $content);
177
178
        return str_replace("'?" . static::BASE_DIR_MARKER, "'?' . \$baseDir . '", $content);
179
    }
180
181
    /**
182
     * Writes file if content changed.
183
     * @param string $path
184
     * @param string $content
185
     * @throws FailedWriteException
186
     */
187
    protected static function putFile($path, $content)
188
    {
189
        if (file_exists($path) && $content === file_get_contents($path)) {
190
            return;
191
        }
192
        if (!file_exists(dirname($path))) {
193
            mkdir(dirname($path), 0777, true);
194
        }
195
        if (false === file_put_contents($path, $content)) {
196
            throw new FailedWriteException("Failed write file $path");
197
        }
198
    }
199
200
    /**
201
     * Substitute output paths in given data array recursively with marker.
202
     * @param array $data
203
     * @return array
204
     */
205
    public function substituteOutputDirs(array $data)
206
    {
207
        $dir = static::normalizePath(dirname(dirname(dirname($this->getOutputDir()))));
208
209
        return static::substitutePaths($data, $dir, static::BASE_DIR_MARKER);
210
    }
211
212
    /**
213
     * Normalizes given path with given directory separator.
214
     * Default forced to Unix directory separator for substitutePaths to work properly in Windows.
215
     * @param string $path path to be normalized
216
     * @param string $ds directory separator
217
     * @return string
218
     */
219
    public static function normalizePath($path, $ds = self::UNIX_DS)
220
    {
221
        return rtrim(strtr($path, '/\\', $ds . $ds), $ds);
222
    }
223
224
    /**
225
     * Substitute all paths in given array recursively with alias if applicable.
226
     * @param array $data
227
     * @param string $dir
228
     * @param string $alias
229
     * @return array
230
     */
231
    public static function substitutePaths($data, $dir, $alias)
232
    {
233
        foreach ($data as &$value) {
234
            if (is_string($value)) {
235
                $value = static::substitutePath($value, $dir, $alias);
236
            } elseif (is_array($value)) {
237
                $value = static::substitutePaths($value, $dir, $alias);
238
            }
239
        }
240
241
        return $data;
242
    }
243
244
    /**
245
     * Substitute path with alias if applicable.
246
     * @param string $path
247
     * @param string $dir
248
     * @param string $alias
249
     * @return string
250
     */
251
    protected static function substitutePath($path, $dir, $alias)
252
    {
253
        $end = $dir . self::UNIX_DS;
254
        $skippable = 0 === strncmp($path, '?', 1) ? '?' : '';
255
        if ($skippable) {
256
            $path = substr($path, 1);
257
        }
258
        $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...
259
        if ($path === $dir) {
260
            $result = $alias;
261
        } elseif (substr($path, 0, strlen($end)) === $end) {
262
            $result = $alias . substr($path, strlen($end) - 1);
263
        } else {
264
            $result = $path;
265
        }
266
267
        return $skippable . $result;
268
    }
269
270
    public function getOutputDir()
271
    {
272
        return $this->builder->getOutputDir();
273
    }
274
275
    /**
276
     * @param string|null $name
277
     * @return string
278
     */
279
    public function getOutputPath($name = null)
280
    {
281
        return $this->builder->getOutputPath($name ?: $this->name);
282
    }
283
}
284