1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Konfig |
4
|
|
|
* |
5
|
|
|
* Yet another simple configuration file loader library. |
6
|
|
|
* |
7
|
|
|
* @author Xeriab Nabil (aka KodeBurner) <[email protected]> |
8
|
|
|
* @license https://raw.github.com/xeriab/konfig/master/LICENSE MIT |
9
|
|
|
* @link https://xeriab.github.io/projects/konfig |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Exen\Konfig; |
13
|
|
|
|
14
|
|
|
use SplStack; |
15
|
|
|
use Exen\Konfig\Exception\EmptyDirectoryException; |
16
|
|
|
use Exen\Konfig\Exception\FileNotFoundException; |
17
|
|
|
use Exen\Konfig\Exception\UnsupportedFileFormatException; |
18
|
|
|
|
19
|
|
|
final class Konfig extends AbstractKonfig |
20
|
|
|
{ |
21
|
|
|
/** |
22
|
|
|
* @var SplStack |
23
|
|
|
* @since 0.1 |
24
|
|
|
*/ |
25
|
|
|
protected $fileParsers; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Stores loaded configuration files |
29
|
|
|
* |
30
|
|
|
* @var array $loadedFiles Array of loaded configuration files |
31
|
|
|
*/ |
32
|
|
|
static $loadedFiles = []; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Loads a supported configuration file format. |
36
|
|
|
* |
37
|
|
|
* @param string|array|mixed $path String file | configuration array | Konfig instance |
38
|
|
|
* @throws EmptyDirectoryException If `$path` is an empty directory |
39
|
|
|
*/ |
40
|
|
|
public function __construct($path, array $parsers = []) |
41
|
|
|
{ |
42
|
|
|
$this->setFileParsers($parsers); |
43
|
|
|
// if (!isset($path)) { |
44
|
|
|
// return; |
45
|
|
|
// } |
46
|
|
|
$paths = $this->getValidPath($path); |
47
|
|
|
|
48
|
|
|
$this->configData = []; |
49
|
|
|
|
50
|
|
|
foreach ($paths as $path) { |
51
|
|
|
// Get file information |
52
|
|
|
$ext = pathinfo($path, PATHINFO_EXTENSION); |
53
|
|
|
$parser = $this->getParser($ext); |
54
|
|
|
|
55
|
|
|
// Try and load file |
56
|
|
|
$this->configData = array_replace_recursive($this->configData, $parser->parse($path)); |
57
|
|
|
|
58
|
|
|
self::$loadedFiles[$path] = true; |
59
|
|
|
} // END foreach |
60
|
|
|
|
61
|
|
|
parent::__construct($this->configData); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Static method for loading a Konfig instance. |
66
|
|
|
* |
67
|
|
|
* @param string|array|mixed $path string file | configuration array | Konfig instance |
68
|
|
|
* @return Konfig |
69
|
|
|
*/ |
70
|
|
|
public static function load($path = null) |
71
|
|
|
{ |
72
|
|
|
return new static($path); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Static method for getting loaded Konfig files. |
77
|
|
|
* |
78
|
|
|
* @return array |
79
|
|
|
*/ |
80
|
|
|
public static function loaded() |
81
|
|
|
{ |
82
|
|
|
return self::$loadedFiles; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* @return FileParser[] |
87
|
|
|
* @since 0.1 |
88
|
|
|
*/ |
89
|
|
|
public function getFileParsers() |
90
|
|
|
{ |
91
|
|
|
return $this->fileParsers; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* @return void |
96
|
|
|
* @since 0.1 |
97
|
|
|
*/ |
98
|
|
|
protected function addFileParser(FileParser $fileParser) |
99
|
|
|
{ |
100
|
|
|
$this->fileParsers[] = $fileParser; |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
/** |
104
|
|
|
* @return void |
105
|
|
|
* @since 0.1 |
106
|
|
|
*/ |
107
|
|
|
protected function setFileParsers(array $fileParsers = []) |
108
|
|
|
{ |
109
|
|
|
if (empty($fileParsers)) { |
110
|
|
|
$fileParsers = [ |
111
|
|
|
new FileParser\Json(), |
112
|
|
|
new FileParser\Yaml(), |
113
|
|
|
]; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
$this->fileParsers = new SplStack(); |
117
|
|
|
|
118
|
|
|
foreach ($fileParsers as $fileParser) { |
119
|
|
|
$this->addFileParser($fileParser); |
120
|
|
|
} |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Gets a parser for a given file extension |
125
|
|
|
* |
126
|
|
|
* @param string $ext |
127
|
|
|
* @return Konfig\FileParser |
128
|
|
|
* @throws UnsupportedFileFormatException If `$path` is an unsupported file format |
129
|
|
|
*/ |
130
|
|
|
private function getParser($ext) |
131
|
|
|
{ |
132
|
|
|
$parser = null; |
133
|
|
|
|
134
|
|
|
if (empty($ext)) { |
135
|
|
|
// @TODO: Throw an exception. |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
$fileParsers = $this->getFileParsers(); |
139
|
|
|
|
140
|
|
|
foreach ($fileParsers as $fileParser) { |
141
|
|
|
if (in_array($ext, $fileParser->getSupportedFileExtensions(), true)) { |
142
|
|
|
$parser = $fileParser; |
143
|
|
|
break; |
144
|
|
|
} |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
// If none exist, then throw an exception |
148
|
|
|
if (is_null($parser)) { |
149
|
|
|
throw new UnsupportedFileFormatException('Unsupported configuration format'); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
return $parser; |
|
|
|
|
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Checks `$path` to see if it is either an array, a directory, or a file |
157
|
|
|
* |
158
|
|
|
* @param string | array $path |
159
|
|
|
* @return array |
160
|
|
|
* @throws EmptyDirectoryException If `$path` is an empty directory |
161
|
|
|
* @throws FileNotFoundException If a file is not found at `$path` |
162
|
|
|
*/ |
163
|
|
|
private function getValidPath($path = null) |
164
|
|
|
{ |
165
|
|
|
#: Get path from Array |
166
|
|
|
|
167
|
|
|
// If `$path` is an array |
168
|
|
|
// The below code is to get the path from a given $path array |
169
|
|
|
if (is_array($path)) { |
170
|
|
|
$paths = []; |
171
|
|
|
|
172
|
|
|
foreach ($path as $unverifiedPath) { |
173
|
|
|
try { |
174
|
|
|
// Check if `$unverifiedPath` is optional |
175
|
|
|
// If it exists, then it's added to the list |
176
|
|
|
// If it doesn't, it throws an exception which we catch |
177
|
|
|
if ($unverifiedPath[0] !== '?') { |
178
|
|
|
$paths = array_merge($paths, $this->getValidPath($unverifiedPath)); |
179
|
|
|
continue; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
$optionalPath = ltrim($unverifiedPath, '?'); |
183
|
|
|
|
184
|
|
|
$paths = array_merge($paths, $this->getValidPath($optionalPath)); |
185
|
|
|
} catch (FileNotFoundException $e) { |
186
|
|
|
// If `$unverifiedPath` is optional, then skip it |
187
|
|
|
if ($unverifiedPath[0] === '?') { |
188
|
|
|
continue; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
// Otherwise rethrow the exception |
192
|
|
|
throw $e; |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
return $paths; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
// If `$path` is a directory |
200
|
|
|
if (is_dir($path)) { |
201
|
|
|
#: TODO: Hmmm, I need to end up with something more efficient |
202
|
|
|
// $paths = @glob($path . '/*.{yaml,json,ini,xml,toml,yml,php,inc,php5,conf,cfg}', GLOB_BRACE); |
203
|
|
|
$paths = @glob($path . '/*.*'); |
204
|
|
|
|
205
|
|
|
if (empty($paths)) { |
206
|
|
|
throw new EmptyDirectoryException("Configuration directory: [$path] is empty"); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
return $paths; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
// If `$path` is not a file, throw an exception |
213
|
|
|
if (!file_exists($path) and isset($path)) { |
214
|
|
|
throw new FileNotFoundException("Configuration file: [$path] cannot be found"); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
// If `$path` is not set |
218
|
|
|
if (!isset($path)) { |
219
|
|
|
return; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
return [$path]; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
public function __toString() |
226
|
|
|
{ |
227
|
|
|
return 'Konfig'; |
228
|
|
|
} |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
#: END OF ./src/Konfig.php FILE |
232
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.