1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Noodlehaus; |
4
|
|
|
|
5
|
|
|
use Noodlehaus\Exception\FileNotFoundException; |
6
|
|
|
use Noodlehaus\Exception\UnsupportedFormatException; |
7
|
|
|
use Noodlehaus\Exception\EmptyDirectoryException; |
8
|
|
|
use InvalidArgumentException; |
9
|
|
|
use Noodlehaus\Parser\ParserInterface; |
10
|
|
|
use Noodlehaus\Writer\WriterInterface; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Configuration reader and writer for PHP. |
14
|
|
|
* |
15
|
|
|
* @package Config |
16
|
|
|
* @author Jesus A. Domingo <[email protected]> |
17
|
|
|
* @author Hassan Khan <[email protected]> |
18
|
|
|
* @author Filip Š <[email protected]> |
19
|
|
|
* @link https://github.com/noodlehaus/config |
20
|
|
|
* @license MIT |
21
|
|
|
*/ |
22
|
|
|
class Config extends AbstractConfig |
23
|
|
|
{ |
24
|
|
|
/** |
25
|
|
|
* All formats supported by Config. |
26
|
|
|
* |
27
|
|
|
* @var array |
28
|
|
|
*/ |
29
|
|
|
protected $supportedParsers = [ |
30
|
|
|
'Noodlehaus\Parser\Php', |
31
|
|
|
'Noodlehaus\Parser\Ini', |
32
|
|
|
'Noodlehaus\Parser\Json', |
33
|
|
|
'Noodlehaus\Parser\Xml', |
34
|
|
|
'Noodlehaus\Parser\Yaml' |
35
|
|
|
]; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* All formats supported by Config. |
39
|
|
|
* |
40
|
|
|
* @var array |
41
|
|
|
*/ |
42
|
|
|
protected $supportedWriters = [ |
43
|
|
|
'Noodlehaus\Writer\Ini', |
44
|
|
|
'Noodlehaus\Writer\Json', |
45
|
3 |
|
'Noodlehaus\Writer\Xml', |
46
|
|
|
'Noodlehaus\Writer\Yaml' |
47
|
3 |
|
]; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Static method for loading a Config instance. |
51
|
|
|
* |
52
|
|
|
* @param string|array $values Filenames or string with configuration |
53
|
|
|
* @param ParserInterface $parser Configuration parser |
54
|
|
|
* @param bool $string Enable loading from string |
55
|
|
|
* |
56
|
|
|
* @return Config |
57
|
39 |
|
*/ |
58
|
|
|
public static function load($values, $parser = null, $string = false) |
59
|
39 |
|
{ |
60
|
3 |
|
return new static($values, $parser, $string); |
61
|
|
|
} |
62
|
36 |
|
|
63
|
|
|
/** |
64
|
|
|
* Loads a Config instance. |
65
|
27 |
|
* |
66
|
27 |
|
* @param string|array $values Filenames or string with configuration |
67
|
|
|
* @param ParserInterface $parser Configuration parser |
68
|
|
|
* @param bool $string Enable loading from string |
69
|
|
|
*/ |
70
|
|
|
public function __construct($values, ParserInterface $parser = null, $string = false) |
71
|
|
|
{ |
72
|
|
|
if ($string === true) { |
73
|
|
|
$this->loadFromString($values, $parser); |
|
|
|
|
74
|
|
|
} else { |
75
|
|
|
$this->loadFromFile($values, $parser); |
76
|
39 |
|
} |
77
|
|
|
|
78
|
39 |
|
parent::__construct($this->data); |
|
|
|
|
79
|
30 |
|
} |
80
|
|
|
|
81
|
30 |
|
/** |
82
|
30 |
|
* Loads configuration from file. |
83
|
|
|
* |
84
|
27 |
|
* @param string|array $path Filenames or directories with configuration |
85
|
27 |
|
* @param ParserInterface $parser Configuration parser |
86
|
27 |
|
* |
87
|
|
|
* @throws EmptyDirectoryException If `$path` is an empty directory |
88
|
|
|
*/ |
89
|
27 |
|
protected function loadFromFile($path, ParserInterface $parser = null) |
90
|
3 |
|
{ |
91
|
|
|
$paths = $this->getValidPath($path); |
92
|
|
|
$this->data = []; |
93
|
|
|
|
94
|
27 |
|
foreach ($paths as $path) { |
95
|
|
|
if ($parser === null) { |
96
|
|
|
// Get file information |
97
|
21 |
|
$info = pathinfo($path); |
98
|
|
|
$parts = explode('.', $info['basename']); |
99
|
|
|
$extension = array_pop($parts); |
100
|
21 |
|
|
101
|
|
|
// Skip the `dist` extension |
102
|
|
|
if ($extension === 'dist') { |
103
|
3 |
|
$extension = array_pop($parts); |
104
|
|
|
} |
105
|
|
|
|
106
|
24 |
|
// Get file parser |
107
|
|
|
$parser = $this->getParser($extension); |
108
|
|
|
|
109
|
|
|
// Try to load file |
110
|
|
|
$this->data = array_replace_recursive($this->data, $parser->parseFile($path)); |
111
|
|
|
|
112
|
|
|
// Clean parser |
113
|
|
|
$parser = null; |
114
|
3 |
|
} else { |
115
|
|
|
// Try to load file using specified parser |
116
|
3 |
|
$this->data = array_replace_recursive($this->data, $parser->parseFile($path)); |
117
|
|
|
} |
118
|
|
|
} |
119
|
3 |
|
} |
120
|
3 |
|
|
121
|
|
|
/** |
122
|
|
|
* Writes configuration to file. |
123
|
|
|
* |
124
|
|
|
* @param string $filename Filename to save configuration to |
125
|
|
|
* @param WriterInterface $writer Configuration writer |
126
|
|
|
* |
127
|
|
|
* @throws WriteException if the data could not be written to the file |
128
|
|
|
*/ |
129
|
|
|
public function writeToFile($filename, WriterInterface $writer = null) |
130
|
|
|
{ |
131
|
27 |
|
if ($writer === null) { |
132
|
|
|
// Get file information |
133
|
27 |
|
$info = pathinfo($filename); |
134
|
27 |
|
$parts = explode('.', $info['basename']); |
135
|
21 |
|
$extension = array_pop($parts); |
136
|
|
|
|
137
|
|
|
// Skip the `dist` extension |
138
|
|
|
if ($extension === 'dist') { |
139
|
|
|
$extension = array_pop($parts); |
140
|
6 |
|
} |
141
|
|
|
|
142
|
|
|
// Get file writer |
143
|
|
|
$writer = $this->getWriter($extension); |
144
|
|
|
|
145
|
|
|
// Try to save file |
146
|
|
|
$writer->toFile($this->all(), $filename); |
147
|
|
|
|
148
|
|
|
// Clean writer |
149
|
|
|
$writer = null; |
|
|
|
|
150
|
|
|
} else { |
151
|
|
|
// Try to load file using specified writer |
152
|
12 |
|
$writer->toFile($this->all(), $filename); |
|
|
|
|
153
|
|
|
} |
154
|
12 |
|
} |
155
|
|
|
|
156
|
12 |
|
/** |
157
|
|
|
* Loads configuration from string. |
158
|
|
|
* |
159
|
|
|
* @param string $configuration String with configuration |
160
|
|
|
* @param ParserInterface $parser Configuration parser |
161
|
12 |
|
*/ |
162
|
12 |
|
protected function loadFromString($configuration, ParserInterface $parser) |
163
|
12 |
|
{ |
164
|
|
|
$this->data = []; |
165
|
|
|
|
166
|
6 |
|
// Try to parse string |
167
|
6 |
|
$this->data = array_replace_recursive($this->data, $parser->parseString($configuration)); |
168
|
6 |
|
} |
169
|
|
|
|
170
|
6 |
|
/** |
171
|
3 |
|
* Writes configuration to string. |
172
|
|
|
* |
173
|
|
|
* @param WriterInterface $writer Configuration writer |
174
|
|
|
* @param boolean $pretty Encode pretty |
175
|
3 |
|
*/ |
176
|
|
|
public function writeToString(WriterInterface $writer, $pretty = true) |
177
|
|
|
{ |
178
|
|
|
return $writer->toString($this->all(), $pretty); |
|
|
|
|
179
|
9 |
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Gets a parser for a given file extension. |
183
|
|
|
* |
184
|
|
|
* @param string $extension |
185
|
|
|
* |
186
|
|
|
* @return Noodlehaus\Parser\ParserInterface |
187
|
|
|
* |
188
|
|
|
* @throws UnsupportedFormatException If `$extension` is an unsupported file format |
189
|
|
|
*/ |
190
|
|
|
protected function getParser($extension) |
191
|
|
|
{ |
192
|
|
|
foreach ($this->supportedParsers as $parser) { |
193
|
33 |
|
if (in_array($extension, $parser::getSupportedExtensions())) { |
194
|
|
|
return new $parser(); |
195
|
|
|
} |
196
|
33 |
|
} |
197
|
12 |
|
|
198
|
|
|
// If none exist, then throw an exception |
199
|
|
|
throw new UnsupportedFormatException('Unsupported configuration format'); |
200
|
|
|
} |
201
|
33 |
|
|
202
|
6 |
|
/** |
203
|
6 |
|
* Gets a writer for a given file extension. |
204
|
3 |
|
* |
205
|
|
|
* @param string $extension |
206
|
|
|
* |
207
|
3 |
|
* @return Noodlehaus\Writer\WriterInterface |
208
|
|
|
* |
209
|
|
|
* @throws UnsupportedFormatException If `$extension` is an unsupported file format |
210
|
|
|
*/ |
211
|
27 |
|
protected function getWriter($extension) |
212
|
9 |
|
{ |
213
|
|
|
foreach ($this->supportedWriters as $writer) { |
214
|
|
|
if (in_array($extension, $writer::getSupportedExtensions())) { |
215
|
24 |
|
return new $writer(); |
216
|
|
|
} |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
// If none exist, then throw an exception |
220
|
|
|
throw new UnsupportedFormatException('Unsupported configuration format'.$extension); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Gets an array of paths |
225
|
|
|
* |
226
|
|
|
* @param array $path |
227
|
|
|
* |
228
|
|
|
* @return array |
229
|
|
|
* |
230
|
|
|
* @throws FileNotFoundException If a file is not found at `$path` |
231
|
|
|
*/ |
232
|
|
|
protected function getPathFromArray($path) |
233
|
|
|
{ |
234
|
|
|
$paths = []; |
235
|
|
|
|
236
|
|
|
foreach ($path as $unverifiedPath) { |
237
|
|
|
try { |
238
|
|
|
// Check if `$unverifiedPath` is optional |
239
|
|
|
// If it exists, then it's added to the list |
240
|
|
|
// If it doesn't, it throws an exception which we catch |
241
|
|
|
if ($unverifiedPath[0] !== '?') { |
242
|
|
|
$paths = array_merge($paths, $this->getValidPath($unverifiedPath)); |
243
|
|
|
continue; |
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
$optionalPath = ltrim($unverifiedPath, '?'); |
247
|
|
|
$paths = array_merge($paths, $this->getValidPath($optionalPath)); |
248
|
|
|
} catch (FileNotFoundException $e) { |
249
|
|
|
// If `$unverifiedPath` is optional, then skip it |
250
|
|
|
if ($unverifiedPath[0] === '?') { |
251
|
|
|
continue; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
// Otherwise rethrow the exception |
255
|
|
|
throw $e; |
256
|
|
|
} |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
return $paths; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* Checks `$path` to see if it is either an array, a directory, or a file. |
264
|
|
|
* |
265
|
|
|
* @param string|array $path |
266
|
|
|
* |
267
|
|
|
* @return array |
268
|
|
|
* |
269
|
|
|
* @throws EmptyDirectoryException If `$path` is an empty directory |
270
|
|
|
* |
271
|
|
|
* @throws FileNotFoundException If a file is not found at `$path` |
272
|
|
|
*/ |
273
|
|
|
protected function getValidPath($path) |
274
|
|
|
{ |
275
|
|
|
// If `$path` is array |
276
|
|
|
if (is_array($path)) { |
277
|
|
|
return $this->getPathFromArray($path); |
278
|
|
|
} |
279
|
|
|
|
280
|
|
|
// If `$path` is a directory |
281
|
|
|
if (is_dir($path)) { |
282
|
|
|
$paths = glob($path . '/*.*'); |
283
|
|
|
if (empty($paths)) { |
284
|
|
|
throw new EmptyDirectoryException("Configuration directory: [$path] is empty"); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
return $paths; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
// If `$path` is not a file, throw an exception |
291
|
|
|
if (!file_exists($path)) { |
292
|
|
|
throw new FileNotFoundException("Configuration file: [$path] cannot be found"); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
return [$path]; |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.