1 | <?php |
||
22 | final class Konfig extends AbstractKonfig |
||
23 | { |
||
24 | /** |
||
25 | * @var FileParser[] $fileParsers Array of file parsers objects |
||
26 | * @since 0.1.0 |
||
27 | */ |
||
28 | protected $fileParsers; |
||
29 | |||
30 | /** |
||
31 | * Stores loaded configuration files |
||
32 | * |
||
33 | * @var array $loadedFiles Array of loaded configuration files |
||
34 | * @since 0.1.0 |
||
35 | */ |
||
36 | protected static $loadedFiles = []; |
||
37 | |||
38 | static protected $loadedData = null; |
||
39 | |||
40 | /** |
||
41 | * Loads a supported configuration file format. |
||
42 | * |
||
43 | * @param string|array|mixed $path String file | configuration array | Konfig instance |
||
44 | * @throws EmptyDirectoryException If `$path` is an empty directory |
||
45 | */ |
||
46 | 33 | public function __construct($path = null, array $parsers = []) |
|
47 | { |
||
48 | 33 | $this->setFileParsers($parsers); |
|
49 | |||
50 | 33 | $paths = $this->getValidPath($path); |
|
51 | |||
52 | 24 | $this->data = []; |
|
53 | |||
54 | 24 | foreach ($paths as $path) { |
|
55 | // Get file information |
||
56 | 24 | $info = pathinfo($path); |
|
57 | // $info = pathinfo($path, PATHINFO_EXTENSION); |
||
58 | 24 | $parts = explode('.', $info['basename']); |
|
59 | 24 | $ext = array_pop($parts); |
|
60 | |||
61 | 24 | if ($ext === 'dist') { |
|
62 | 3 | $ext = array_pop($parts); |
|
63 | 2 | } |
|
64 | |||
65 | 24 | $parser = $this->getParser($ext); |
|
66 | |||
67 | // Try and load file |
||
68 | 21 | $this->data = array_replace_recursive($this->data, (array) $parser->parse($path)); |
|
69 | |||
70 | 21 | self::$loadedFiles[$path] = true; |
|
71 | 14 | } |
|
72 | |||
73 | 21 | self::$loadedData = $this->data; |
|
74 | |||
75 | 21 | parent::__construct($this->data); |
|
76 | 21 | } |
|
77 | |||
78 | /** |
||
79 | * Static method for loading a Konfig instance. |
||
80 | * |
||
81 | * @param string|array|mixed $path string file | configuration array | Konfig instance |
||
82 | * @param array $parsers Parsers to use with Konfig |
||
83 | * @return Konfig |
||
84 | */ |
||
85 | 3 | public static function load($path = null, array $parsers = []) |
|
86 | { |
||
87 | 3 | return new static($path, $parsers); |
|
88 | } |
||
89 | |||
90 | /** |
||
91 | * Static method for getting loaded Konfig files. |
||
92 | * |
||
93 | * @return array |
||
94 | */ |
||
95 | public static function loaded() |
||
96 | { |
||
97 | return self::$loadedFiles; |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * Static method for getting all Konfig keys. |
||
102 | * |
||
103 | * @codeCoverageIgnore |
||
104 | * @return array |
||
105 | */ |
||
106 | public static function keys() |
||
107 | { |
||
108 | return Arr::recursiveKeys(self::$loadedData); |
||
109 | } |
||
110 | |||
111 | /** |
||
112 | * @return FileParser[] |
||
113 | * @since 0.1.0 |
||
114 | */ |
||
115 | public function getFileParsers() |
||
116 | { |
||
117 | return $this->fileParsers; |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * @return void |
||
122 | * @since 0.1.0 |
||
123 | */ |
||
124 | protected function addFileParser(FileParser $fileParser) |
||
125 | { |
||
126 | $this->fileParsers[] = $fileParser; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * @return void |
||
131 | * @since 0.1.0 |
||
132 | */ |
||
133 | protected function setFileParsers(array $fileParsers = []) |
||
134 | { |
||
135 | if (empty($fileParsers)) { |
||
136 | $fileParsers = [ |
||
137 | new FileParser\Xml(), |
||
138 | new FileParser\Ini(), |
||
139 | new FileParser\Json(), |
||
140 | new FileParser\Yaml(), |
||
141 | new FileParser\Neon(), |
||
142 | new FileParser\Toml(), |
||
143 | new FileParser\Php(), |
||
144 | ]; |
||
145 | } |
||
146 | |||
147 | $this->fileParsers = []; |
||
148 | |||
149 | foreach ($fileParsers as $fileParser) { |
||
150 | $this->addFileParser($fileParser); |
||
151 | } |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Gets a parser for a given file extension |
||
156 | * |
||
157 | * @param string $ext File extension |
||
158 | * @return Konfig\FileParser |
||
159 | * @throws Exception If `$ext` is empty |
||
160 | * @throws UnsupportedFileFormatException If `$path` is an unsupported file format |
||
161 | */ |
||
162 | 27 | private function getParser($ext = null) |
|
163 | { |
||
164 | 27 | $parser = null; |
|
165 | |||
166 | 27 | if (empty($ext)) { |
|
167 | throw new Exception('Files with empty extensions are not allowed'); |
||
168 | } |
||
169 | |||
170 | 27 | $fileParsers = $this->getFileParsers(); |
|
171 | |||
172 | 27 | foreach ($fileParsers as $fileParser) { |
|
173 | 27 | if (in_array($ext, $fileParser->getSupportedFileExtensions(), true)) { |
|
174 | 21 | $parser = $fileParser; |
|
175 | 23 | break; |
|
176 | } |
||
177 | 18 | } |
|
178 | |||
179 | // If none exist, then throw an exception |
||
180 | 27 | if (is_null($parser)) { |
|
181 | 6 | throw new UnsupportedFileFormatException('Unsupported configuration format'); |
|
182 | } |
||
183 | |||
184 | 21 | return $parser; |
|
185 | } |
||
186 | |||
187 | /** |
||
188 | * Gets an array of paths |
||
189 | * |
||
190 | * @param array $path Path to analyze and handle |
||
191 | * @return array |
||
192 | * @throws FileNotFoundException If a file is not found in `$path` |
||
193 | */ |
||
194 | private function pathFromArray($path) |
||
195 | { |
||
196 | $paths = []; |
||
197 | |||
198 | foreach ($path as $unverifiedPath) { |
||
199 | try { |
||
200 | // Check if `$unverifiedPath` is optional |
||
201 | // If it exists, then it's added to the list |
||
202 | // If it doesn't, it throws an exception which we catch |
||
203 | if ($unverifiedPath[0] !== '?') { |
||
204 | $paths = array_merge($paths, $this->getValidPath($unverifiedPath)); |
||
205 | continue; |
||
206 | } |
||
207 | |||
208 | $optionalPath = ltrim($unverifiedPath, '?'); |
||
209 | |||
210 | $paths = array_merge($paths, $this->getValidPath($optionalPath)); |
||
211 | } catch (FileNotFoundException $e) { |
||
212 | // If `$unverifiedPath` is optional, then skip it |
||
213 | if ($unverifiedPath[0] === '?') { |
||
214 | continue; |
||
215 | } |
||
216 | |||
217 | // Otherwise rethrow the exception |
||
218 | throw $e; |
||
219 | } |
||
220 | } |
||
221 | |||
222 | return $paths; |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Checks `$path` to see if it is either an array, a directory, or a file |
||
227 | * |
||
228 | * @param string|array $path Path to analyze and handle |
||
229 | * @return array |
||
230 | * @throws EmptyDirectoryException If `$path` is an empty directory |
||
231 | * @throws FileNotFoundException If a file is not found at `$path` |
||
232 | */ |
||
233 | 30 | private function getValidPath($path) |
|
234 | { |
||
235 | // If `$path` is array |
||
236 | 30 | if (is_array($path)) { |
|
237 | 12 | return $this->pathFromArray($path); |
|
238 | } |
||
239 | |||
240 | // If `$path` is a directory |
||
241 | 30 | if (is_dir($path)) { |
|
242 | 6 | $paths = glob($path . '/*.*'); |
|
243 | |||
244 | 6 | if (empty($paths)) { |
|
245 | 3 | throw new EmptyDirectoryException("Configuration directory: [$path] is empty"); |
|
246 | } |
||
247 | |||
248 | 3 | return $paths; |
|
249 | } |
||
250 | |||
251 | // If `$path` is not a file, throw an exception |
||
252 | 24 | if (!file_exists($path)) { |
|
253 | 9 | throw new FileNotFoundException("Configuration file: [$path] cannot be found"); |
|
254 | } |
||
255 | |||
256 | 21 | return [$path]; |
|
257 | } |
||
258 | |||
259 | /** |
||
260 | * @return string |
||
261 | * @codeCoverageIgnore |
||
262 | * @since 0.1.2 |
||
263 | */ |
||
264 | public function __toString() |
||
268 | } |
||
269 | |||
270 | // END OF ./src/Konfig.php FILE |
||
271 |