Completed
Pull Request — master (#111)
by Filip
02:56
created

Config::loadFromFile()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 18
cts 18
cp 1
rs 9.424
c 0
b 0
f 0
cc 4
nc 4
nop 2
crap 4
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\FileParser\FileParserInterface;
10
use Noodlehaus\StringParser\StringParserInterface;
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
 * @link       https://github.com/noodlehaus/config
19
 * @license    MIT
20
 */
21
class Config extends AbstractConfig
22
{
23
    /**
24
     * All file formats supported by Config.
25
     *
26
     * @var array
27
     */
28
    protected $supportedFileParsers = [
29
        'Noodlehaus\FileParser\Php',
30
        'Noodlehaus\FileParser\Ini',
31
        'Noodlehaus\FileParser\Json',
32
        'Noodlehaus\FileParser\Xml',
33
        'Noodlehaus\FileParser\Yaml'
34
    ];
35
36
    /**
37
     * All string formats supported by Config.
38
     *
39
     * @var array
40
     */
41
    protected $supportedStringParsers = [
42
        'Noodlehaus\StringParser\Php',
43
        'Noodlehaus\StringParser\Ini',
44
        'Noodlehaus\StringParser\Json',
45
        'Noodlehaus\StringParser\Xml',
46
        'Noodlehaus\StringParser\Yaml'
47
    ];
48
49
    /**
50
     * Static method for loading a Config instance.
51
     *
52
     * @param  string|array                              $values Filenames or string with configuration
53
     * @param  FileParserInterface|StringParserInterface $parser Configuration parser
54
     *
55
     * @return Config
56
     */
57 3
    public static function load($values, $parser = null)
58
    {
59 3
        return new static($values, $parser);
60
    }
61
62
    /**
63
     * Loads a Config instance.
64
     *
65
     * @param  string|array                              $values Filenames or string with configuration
66
     * @param  FileParserInterface|StringParserInterface $parser Configuration parser
67
     *
68
     * @throws InvalidArgumentException If `$parser` is not implementing correct interface
69
     */
70 42
    public function __construct($values, $parser = null)
71
    {
72 42
        if ($parser instanceof FileParserInterface || $parser === null) {
73 36
            $this->loadFromFile($values, $parser);
74 22
        } elseif ($parser instanceof StringParserInterface) {
75 3
            $this->loadFromString($values, $parser);
0 ignored issues
show
Bug introduced by
It seems like $values defined by parameter $values on line 70 can also be of type array; however, Noodlehaus\Config::loadFromString() does only seem to accept string, maybe add an additional type check?

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.

Loading history...
76 2
        } else {
77 3
            throw new InvalidArgumentException('Parser not implementing correct interface');
78
        }
79
80 27
        parent::__construct($this->data);
0 ignored issues
show
Bug introduced by
It seems like $this->data can also be of type null; however, Noodlehaus\AbstractConfig::__construct() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
81 27
    }
82
83
    /**
84
     * Loads configuration from file.
85
     *
86
     * @param  string|array         $path   Filenames or directories with configuration
87
     * @param  FileParserInterface  $parser Configuration parser
88
     *
89
     * @throws EmptyDirectoryException If `$path` is an empty directory
90
     */
91 39
    protected function loadFromFile($path, FileParserInterface $parser = null)
92
    {
93 39
        $paths      = $this->getValidPath($path);
94 30
        $this->data = [];
95
96 30
        foreach ($paths as $path) {
97 30
            if ($parser === null) {
98
                // Get file information
99 27
                $info      = pathinfo($path);
100 27
                $parts     = explode('.', $info['basename']);
101 27
                $extension = array_pop($parts);
102
103
                // Skip the `dist` extension
104 27
                if ($extension === 'dist') {
105 3
                    $extension = array_pop($parts);
106 2
                }
107
108
                // Get file parser
109 27
                $parser = $this->getParser($extension);
110
111
                // Try to load file
112 21
                $this->data = array_replace_recursive($this->data, (array) $parser->parse($path));
113
114
                // Clean parser
115 21
                $parser = null;
116 14
            } else {
117
                // Try to load file using specified parser
118 10
                $this->data = array_replace_recursive($this->data, (array) $parser->parse($path));
119
            }
120 16
        }
121 24
    }
122
123
    /**
124
     * Loads configuration from string.
125
     *
126
     * @param  string                $configuration String with configuration
127
     * @param  StringParserInterface $parser        Configuration parser
128
     */
129 3
    protected function loadFromString($configuration, $parser)
130
    {
131 3
        $this->data = [];
132
133
        // Try to parse string
134 3
        $this->data = array_replace_recursive($this->data, (array) $parser->parse($configuration));
135 3
    }
136
137
    /**
138
     * Gets a parser for a given file extension.
139
     *
140
     * @param  string $extension
141
     *
142
     * @return Noodlehaus\FileParser\FileParserInterface
143
     *
144
     * @throws UnsupportedFormatException If `$extension` is an unsupported file format
145
     */
146 27
    protected function getParser($extension)
147
    {
148 27
        foreach ($this->supportedFileParsers as $fileParser) {
149 27
            if (in_array($extension, $fileParser::getSupportedExtensions())) {
150 23
                return new $fileParser();
151
            }
152 18
        }
153
154
        // If none exist, then throw an exception
155 6
        throw new UnsupportedFormatException('Unsupported configuration format');
156
    }
157
158
    /**
159
     * Gets an array of paths
160
     *
161
     * @param  array $path
162
     *
163
     * @return array
164
     *
165
     * @throws FileNotFoundException   If a file is not found at `$path`
166
     */
167 12
    protected function getPathFromArray($path)
168
    {
169 12
        $paths = [];
170
171 12
        foreach ($path as $unverifiedPath) {
172
            try {
173
                // Check if `$unverifiedPath` is optional
174
                // If it exists, then it's added to the list
175
                // If it doesn't, it throws an exception which we catch
176 12
                if ($unverifiedPath[0] !== '?') {
177 12
                    $paths = array_merge($paths, $this->getValidPath($unverifiedPath));
178 12
                    continue;
179
                }
180
181 6
                $optionalPath = ltrim($unverifiedPath, '?');
182 6
                $paths = array_merge($paths, $this->getValidPath($optionalPath));
183 8
            } catch (FileNotFoundException $e) {
184
                // If `$unverifiedPath` is optional, then skip it
185 6
                if ($unverifiedPath[0] === '?') {
186 3
                    continue;
187
                }
188
189
                // Otherwise rethrow the exception
190 4
                throw $e;
191
            }
192 6
        }
193
194 9
        return $paths;
195
    }
196
197
    /**
198
     * Checks `$path` to see if it is either an array, a directory, or a file.
199
     *
200
     * @param  string|array $path
201
     *
202
     * @return array
203
     *
204
     * @throws EmptyDirectoryException If `$path` is an empty directory
205
     *
206
     * @throws FileNotFoundException   If a file is not found at `$path`
207
     */
208 33
    protected function getValidPath($path)
209
    {
210
        // If `$path` is array
211 33
        if (is_array($path)) {
212 12
            return $this->getPathFromArray($path);
213
        }
214
215
        // If `$path` is a directory
216 33
        if (is_dir($path)) {
217 6
            $paths = glob($path . '/*.*');
218 6
            if (empty($paths)) {
219 3
                throw new EmptyDirectoryException("Configuration directory: [$path] is empty");
220
            }
221
222 3
            return $paths;
223
        }
224
225
        // If `$path` is not a file, throw an exception
226 27
        if (!file_exists($path)) {
227 9
            throw new FileNotFoundException("Configuration file: [$path] cannot be found");
228
        }
229
230 24
        return [$path];
231
    }
232
}
233