FileAwareTrait   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 1
dl 0
loc 157
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
B loadFile() 0 37 8
A loadIniFile() 0 11 2
A loadJsonFile() 0 20 5
A loadPhpFile() 0 18 5
A loadYamlFile() 0 20 4
1
<?php
2
3
namespace Charcoal\Config;
4
5
use Traversable;
6
use Throwable;
7
use Exception;
8
use LogicException;
9
use InvalidArgumentException;
10
use UnexpectedValueException;
11
12
// From 'symfony/yaml'
13
use Symfony\Component\Yaml\Parser as YamlParser;
14
15
/**
16
 * Provides an object with the ability to read file contents.
17
 *
18
 * Supported file formats: INI, JSON, PHP, YAML*.
19
 *
20
 * Note: YAML requires the {@link https://packagist.org/packages/symfony/yaml Symfony YAML component}.
21
 *
22
 * This is a full implementation of {@see FileAwareInterface}.
23
 */
24
trait FileAwareTrait
25
{
26
    /**
27
     * Loads a configuration file.
28
     *
29
     * @param  string $path A path to a supported file.
30
     * @throws InvalidArgumentException If the path is invalid.
31
     * @return array An array on success.
32
     */
33
    public function loadFile($path)
34
    {
35
        if (!is_string($path)) {
36
            throw new InvalidArgumentException(
37
                'File must be a string'
38
            );
39
        }
40
41
        if (!file_exists($path)) {
42
            throw new InvalidArgumentException(
43
                sprintf('File "%s" does not exist', $path)
44
            );
45
        }
46
47
        $ext = pathinfo($path, PATHINFO_EXTENSION);
48
        switch ($ext) {
49
            case 'php':
50
                return $this->loadPhpFile($path);
51
52
            case 'json':
53
                return $this->loadJsonFile($path);
54
55
            case 'ini':
56
                return $this->loadIniFile($path);
57
58
            case 'yml':
59
            case 'yaml':
60
                return $this->loadYamlFile($path);
61
        }
62
63
        $validConfigExts = [ 'ini', 'json', 'php', 'yml' ];
64
        throw new InvalidArgumentException(sprintf(
65
            'Unsupported file format for "%s"; must be one of "%s"',
66
            $path,
67
            implode('", "', $validConfigExts)
68
        ));
69
    }
70
71
    /**
72
     * Load an INI file as an array.
73
     *
74
     * @param  string $path A path to an INI file.
75
     * @throws UnexpectedValueException If the file can not correctly be parsed into an array.
76
     * @return array An array on success.
77
     */
78
    private function loadIniFile($path)
79
    {
80
        $data = parse_ini_file($path, true);
81
        if ($data === false) {
82
            throw new UnexpectedValueException(
83
                sprintf('INI file "%s" is empty or invalid', $path)
84
            );
85
        }
86
87
        return $data;
88
    }
89
90
    /**
91
     * Load a JSON file as an array.
92
     *
93
     * @param  string $path A path to a JSON file.
94
     * @throws UnexpectedValueException If the file can not correctly be parsed into an array.
95
     * @return array An array on success.
96
     *     If the file is parsed as any other type, an empty array is returned.
97
     */
98
    private function loadJsonFile($path)
99
    {
100
        $data = null;
101
        $json = file_get_contents($path);
102
        if ($json) {
103
            $data = json_decode($json, true);
104
            if (json_last_error() !== JSON_ERROR_NONE) {
105
                $error = json_last_error_msg() ?: 'Unknown error';
106
                throw new UnexpectedValueException(
107
                    sprintf('JSON file "%s" could not be parsed: %s', $path, $error)
108
                );
109
            }
110
        }
111
112
        if (!is_array($data)) {
113
            return [];
114
        }
115
116
        return $data;
117
    }
118
119
    /**
120
     * Load a PHP file, maybe as an array.
121
     *
122
     * Note:
123
     * - The context of $this is bound to the current object.
124
     * - Data may be any value; the {@see self::addFile()} method will ignore
125
     *   anything that isn't an (associative) array.
126
     *
127
     * @param  string $path A path to a PHP file.
128
     * @throws UnexpectedValueException If the file can not correctly be parsed.
129
     * @return array|Traversable An array or iterable object on success.
130
     *     If the file is parsed as any other type, an empty array is returned.
131
     */
132
    private function loadPhpFile($path)
133
    {
134
        try {
135
            $data = include $path;
136
        } catch (Exception $e) {
137
            $message = sprintf('PHP file "%s" could not be parsed: %s', $path, $e->getMessage());
138
            throw new UnexpectedValueException($message, 0, $e);
139
        } catch (Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
140
            $message = sprintf('PHP file "%s" could not be parsed: %s', $path, $e->getMessage());
141
            throw new UnexpectedValueException($message, 0, $e);
142
        }
143
144
        if (is_array($data) || ($data instanceof Traversable)) {
145
            return $data;
146
        }
147
148
        return [];
149
    }
150
151
    /**
152
     * Load a YAML file as an array.
153
     *
154
     * @param  string $path A path to a YAML/YML file.
155
     * @throws LogicException If a YAML parser is unavailable.
156
     * @throws UnexpectedValueException If the file can not correctly be parsed into an array.
157
     * @return array An array on success.
158
     *     If the file is parsed as any other type, an empty array is returned.
159
     */
160
    private function loadYamlFile($path)
161
    {
162
        if (!class_exists('Symfony\Component\Yaml\Parser')) {
163
            throw new LogicException('YAML format requires the Symfony YAML component');
164
        }
165
166
        try {
167
            $yaml = new YamlParser();
168
            $data = $yaml->parseFile($path);
169
        } catch (Exception $e) {
170
            $message = sprintf('YAML file "%s" could not be parsed: %s', $path, $e->getMessage());
171
            throw new UnexpectedValueException($message, 0, $e);
172
        }
173
174
        if (!is_array($data)) {
175
            return [];
176
        }
177
178
        return $data;
179
    }
180
}
181