Completed
Pull Request — develop (#122)
by
unknown
12:36
created

Config::writeToFile()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 7
cts 7
cp 1
rs 9.504
c 0
b 0
f 0
cc 3
nc 3
nop 2
crap 3
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);
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...
Bug introduced by
It seems like $parser defined by parameter $parser on line 70 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...
74
        } else {
75
            $this->loadFromFile($values, $parser);
76 39
        }
77
78 39
        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...
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;
0 ignored issues
show
Unused Code introduced by
$writer is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
150
        } else {
151
            // Try to load file using specified writer
152 12
            $writer->toFile($this->all(), $filename);
0 ignored issues
show
Bug introduced by
It seems like $this->all() targeting Noodlehaus\AbstractConfig::all() can also be of type null; however, Noodlehaus\Writer\WriterInterface::toFile() does only seem to accept array, maybe add an additional type check?

This check looks at variables that 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...
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);
0 ignored issues
show
Bug introduced by
It seems like $this->all() targeting Noodlehaus\AbstractConfig::all() can also be of type null; however, Noodlehaus\Writer\WriterInterface::toString() does only seem to accept array, maybe add an additional type check?

This check looks at variables that 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...
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