Completed
Push — develop ( 930556...77256d )
by Hassan
9s
created

Config   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 197
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 4
dl 0
loc 197
ccs 62
cts 62
cp 1
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A load() 0 4 1
A __construct() 0 10 2
A loadFromFile() 0 31 4
A loadFromString() 0 7 1
A getParser() 0 11 3
A getPathFromArray() 0 29 5
A getValidPath() 0 24 5
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
11
/**
12
 * Configuration reader and writer for PHP.
13
 *
14
 * @package    Config
15
 * @author     Jesus A. Domingo <[email protected]>
16
 * @author     Hassan Khan <[email protected]>
17
 * @author     Filip Š <[email protected]>
18
 * @link       https://github.com/noodlehaus/config
19
 * @license    MIT
20
 */
21
class Config extends AbstractConfig
22
{
23
    /**
24
     * All formats supported by Config.
25
     *
26
     * @var array
27
     */
28
    protected $supportedParsers = [
29
        'Noodlehaus\Parser\Php',
30
        'Noodlehaus\Parser\Ini',
31
        'Noodlehaus\Parser\Json',
32
        'Noodlehaus\Parser\Xml',
33
        'Noodlehaus\Parser\Yaml'
34
    ];
35
36
    /**
37
     * Static method for loading a Config instance.
38
     *
39
     * @param  string|array    $values Filenames or string with configuration
40
     * @param  ParserInterface $parser Configuration parser
41
     * @param  bool            $string Enable loading from string
42
     *
43
     * @return Config
44
     */
45 3
    public static function load($values, $parser = null, $string = false)
46
    {
47 3
        return new static($values, $parser, $string);
48
    }
49
50
    /**
51
     * Loads a Config instance.
52
     *
53
     * @param  string|array    $values Filenames or string with configuration
54
     * @param  ParserInterface $parser Configuration parser
55
     * @param  bool            $string Enable loading from string
56
     */
57 39
    public function __construct($values, ParserInterface $parser = null, $string = false)
58
    {
59 39
        if ($string === true) {
60 3
            $this->loadFromString($values, $parser);
0 ignored issues
show
Bug introduced by
It seems like $values defined by parameter $values on line 57 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...
Bug introduced by
It seems like $parser defined by parameter $parser on line 57 can be null; however, Noodlehaus\Config::loadFromString() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

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