Completed
Push — master ( 18bc5f...856801 )
by Xeriab
03:27
created

Konfig::getParser()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5.0073

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 26
ccs 14
cts 15
cp 0.9333
rs 8.439
cc 5
eloc 13
nc 7
nop 1
crap 5.0073
1
<?php
2
3
/**
4
 * Konfig.
5
 *
6
 * Yet another simple configuration loader library.
7
 *
8
 * PHP version 5
9
 *
10
 * @category Library
11
 * @package  Konfig
12
 * @author   Xeriab Nabil (aka KodeBurner) <[email protected]>
13
 * @license  https://raw.github.com/xeriab/konfig/master/LICENSE MIT
14
 * @link     https://xeriab.github.io/projects/konfig
15
 */
16
17
namespace Exen\Konfig;
18
19
use Exen\Konfig\Exception\Exception;
20
use Exen\Konfig\Exception\EmptyDirectoryException;
21
use Exen\Konfig\Exception\FileNotFoundException;
22
use Exen\Konfig\Exception\UnsupportedFileFormatException;
23
24
/**
25
 * Main Konfig class.
26
 *
27
 * @category Main
28
 * @package  Konfig
29
 * @author   Xeriab Nabil (aka KodeBurner) <[email protected]>
30
 * @license  https://raw.github.com/xeriab/konfig/master/LICENSE MIT
31
 * @link     https://xeriab.github.io/projects/konfig
32
 *
33
 * @extends AbstractKonfig
34
 */
35
final class Konfig extends AbstractKonfig
36
{
37
    /**
38
     * Array of file parsers objects.
39
     *
40
     * @var array|null
41
     *
42
     * @since 0.1.0
43
     */
44
    protected $fileParsers;
45
46
    /**
47
     * Stores loaded configuration files.
48
     *
49
     * @var array Array of loaded configuration files
50
     *
51
     * @since 0.1.0
52
     */
53
    protected static $loadedFiles = [];
54
55
    /**
56
     * Array of loaded data.
57
     *
58
     * @var array|null
59
     *
60
     * @since 0.1.0
61
     */
62
    protected static $loadedData = null;
63
64
    /**
65
     * Loads a supported configuration file format.
66
     *
67
     * @param string|array|mixed $path      String file | configuration array
68
     * | Konfig instance | configuration array | Konfig instance
69
     * @param array              $parsers   Parsers
70
     * @param bool               $overwrite Whether to overwrite existing values
71
     * @param bool               $cache     Allow caching
72
     *
73
     * @throws EmptyDirectoryException If `$path` is an empty directory
74
     */
75 33
    public function __construct(
76
        $path = null,
77
        array $parsers = [],
78
        $overwrite = false,
0 ignored issues
show
Unused Code introduced by
The parameter $overwrite is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
79
        $cache = true
0 ignored issues
show
Unused Code introduced by
The parameter $cache is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
80
    ) {
81 33
        $this->setFileParsers($parsers);
82
83 33
        $paths = $this->getValidPath($path);
84
85 24
        $this->data = [];
86
87 24
        foreach ($paths as $path) {
88
            // Get file information
89 24
            $info = pathinfo($path);
90
            // $info  = pathinfo($path, PATHINFO_EXTENSION);
91 24
            $parts = explode('.', $info['basename']);
92 24
            $ext = array_pop($parts);
93
94 24
            if ($ext === 'dist') {
95 3
                $ext = array_pop($parts);
96 1
            }
97
98 24
            $parser = $this->getParser($ext);
99
100
            // Try and load file
101 21
            $this->data = array_replace_recursive(
102 21
                $this->data,
103 21
                (array) $parser->parse($path)
104 7
            );
105
106 21
            self::$loadedFiles[$path] = true;
107 7
        }
108
109 21
        self::$loadedData = $this->data;
110
111 21
        parent::__construct($this->data);
112 21
    }
113
114
    /**
115
     * Static method for loading a Konfig instance.
116
     *
117
     * @param string|array|mixed $path      string file | configuration array
118
     *                                      | Konfig instance
119
     * @param array              $parsers   Parsers to use with Konfig
120
     * @param bool               $overwrite Whether to overwrite existing values
121
     * @param bool               $cache     Allow caching
122
     *
123
     * @return Konfig
124
     */
125 3
    public static function load(
126
        $path = null,
127
        array $parsers = [],
128
        $overwrite = false,
0 ignored issues
show
Unused Code introduced by
The parameter $overwrite is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
129
        $cache = true
0 ignored issues
show
Unused Code introduced by
The parameter $cache is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
130
    ) {
131 3
        return new static($path, $parsers);
132
    }
133
134
    /**
135
     * Static method for getting loaded Konfig files.
136
     *
137
     * @return array
138
     */
139
    public static function loaded()
140
    {
141
        return self::$loadedFiles;
142
    }
143
144
    /**
145
     * Get file parsers.
146
     *
147
     * @return FileParser[]
148
     *
149
     * @since              0.1.0
150
     * @codeCoverageIgnore
151
     */
152
    public function getFileParsers()
153
    {
154
        return $this->fileParsers;
155
    }
156
157
    /**
158
     * Add file parsers.
159
     *
160
     * @param FileParser $fileParser Parser
161
     *
162
     * @return             void Void
163
     * @since              0.1.0
164
     * @codeCoverageIgnore
165
     */
166
    protected function addFileParser(FileParser $fileParser)
167
    {
168
        $this->fileParsers[] = $fileParser;
169
    }
170
171
    /**
172
     * Set file parsers.
173
     *
174
     * @param array $fileParsers Parsers array
175
     *
176
     * @return             void Void
177
     * @since              0.1.0
178
     * @codeCoverageIgnore
179
     */
180
    protected function setFileParsers(array $fileParsers = [])
181
    {
182
        if (empty($fileParsers)) {
183
            $fileParsers = [
184
                // Default parsers
185
                new FileParser\Xml(),
186
                new FileParser\Ini(),
187
                new FileParser\Json(),
188
                new FileParser\Php(),
189
190
                // Additional parsers
191
                new FileParser\Yaml(),
192
                new FileParser\Neon(),
193
                new FileParser\Toml(),
194
                new FileParser\Properties(),
195
            ];
196
        }
197
198
        $this->fileParsers = [];
199
200
        foreach ($fileParsers as $fileParser) {
201
            $this->addFileParser($fileParser);
202
        }
203
    }
204
205
    /**
206
     * Gets a parser for a given file extension.
207
     *
208
     * @param string|null $ext File extension
209
     *
210
     * @return FileParser
211
     *
212
     * @throws Exception                      If `$ext` is empty
213
     * @throws UnsupportedFileFormatException If `$path`
214
     * is an unsupported file format
215
     */
216 27
    private function getParser($ext = null)
217
    {
218 27
        $parser = null;
219
220 27
        if (empty($ext)) {
221
            throw new Exception('Files with empty extensions are not allowed');
222
        }
223
224 27
        $fileParsers = $this->getFileParsers();
225
226 27
        foreach ($fileParsers as $fileParser) {
0 ignored issues
show
Bug introduced by
The expression $fileParsers of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
227 27
            if (in_array($ext, $fileParser->getSupportedFileExtensions(), true)) {
228 21
                $parser = $fileParser;
229 25
                break;
230
            }
231 9
        }
232
233
        // If none exist, then throw an exception
234 27
        if (is_null($parser)) {
235 6
            throw new UnsupportedFileFormatException(
236 4
                'Unsupported configuration format'
237 2
            );
238
        }
239
240 21
        return $parser;
241
    }
242
243
    /**
244
     * Gets an array of paths.
245
     *
246
     * @param array $path Path to analyze and handle
247
     *
248
     * @return array
249
     *
250
     * @throws             FileNotFoundException If a file is not found in `$path`
251
     * @codeCoverageIgnore
252
     */
253
    private function pathFromArray($path)
254
    {
255
        $paths = [];
256
257
        foreach ($path as $unverifiedPath) {
258
            try {
259
                // Check if `$unverifiedPath` is optional
260
                // If it exists, then it's added to the list
261
                // If it doesn't, it throws an exception which we catch
262
                if ($unverifiedPath[0] !== '?') {
263
                    $paths = array_merge(
264
                        $paths,
265
                        $this->getValidPath($unverifiedPath)
266
                    );
267
268
                    continue;
269
                }
270
271
                $optionalPath = ltrim($unverifiedPath, '?');
272
273
                $paths = array_merge($paths, $this->getValidPath($optionalPath));
274
            } catch (FileNotFoundException $e) {
275
                // If `$unverifiedPath` is optional, then skip it
276
                if ($unverifiedPath[0] === '?') {
277
                    continue;
278
                }
279
280
                // Otherwise rethrow the exception
281
                throw $e;
282
            }
283
        }
284
285
        return $paths;
286
    }
287
288
    /**
289
     * Checks `$path` to see if it is either an array, a directory, or a file.
290
     *
291
     * @param string|array $path Path to analyze and handle
292
     *
293
     * @return array
294
     *
295
     * @throws EmptyDirectoryException If `$path` is an empty directory
296
     * @throws FileNotFoundException   If a file is not found at `$path`
297
     */
298 30
    private function getValidPath($path)
299
    {
300
        // If `$path` is array
301 30
        if (is_array($path)) {
302 12
            return $this->pathFromArray($path);
303
        }
304
305
        // If `$path` is a directory
306 30
        if (is_dir($path)) {
307 6
            $paths = glob($path.'/*.*');
308
309 6
            if (empty($paths)) {
310 3
                throw new EmptyDirectoryException(
311 3
                    "Configuration directory: [$path] is empty"
312 1
                );
313
            }
314
315 3
            return $paths;
316
        }
317
318
        // If `$path` is not a file, throw an exception
319 24
        if (!file_exists($path)) {
320 9
            throw new FileNotFoundException(
321 9
                "Configuration file: [$path] cannot be found"
322 3
            );
323
        }
324
325 21
        return [$path];
326
    }
327
328
    /**
329
     * __toString.
330
     *
331
     * @return             string
332
     * @since              0.1.2
333
     * @codeCoverageIgnore
334
     */
335
    public function __toString()
336
    {
337
        return 'Exen\Konfig\Konfig' . PHP_EOL;
338
    }
339
}
340
341
// END OF ./src/Konfig.php FILE
342