|
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
|
|
|
namespace Exen\Konfig; |
|
12
|
|
|
|
|
13
|
|
|
use ArrayAccess; |
|
14
|
|
|
use Exen\Konfig\Utils; |
|
15
|
|
|
use Iterator; |
|
16
|
|
|
|
|
17
|
|
|
abstract class AbstractKonfig implements ArrayAccess, Iterator, KonfigInterface |
|
18
|
|
|
{ |
|
19
|
|
|
/** |
|
20
|
|
|
* Stores the configuration items |
|
21
|
|
|
* |
|
22
|
|
|
* @var array|null |
|
23
|
|
|
* @since 0.1 |
|
24
|
|
|
*/ |
|
25
|
|
|
protected $data = null; |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* Caches the configuration data |
|
29
|
|
|
* |
|
30
|
|
|
* @var array |
|
31
|
|
|
* @since 0.1 |
|
32
|
|
|
*/ |
|
33
|
|
|
protected $cache = []; |
|
34
|
|
|
|
|
35
|
|
|
/** |
|
36
|
|
|
* @var string $defaultCheckValue Random value used as a not-found check in get() |
|
37
|
|
|
* @since 0.1 |
|
38
|
|
|
*/ |
|
39
|
|
|
static $defaultCheckValue; |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* Constructor method and sets default options, if any |
|
43
|
|
|
* |
|
44
|
|
|
* @param array $input |
|
45
|
|
|
* @since 0.1 |
|
46
|
|
|
*/ |
|
47
|
|
|
public function __construct($input) |
|
48
|
|
|
{ |
|
49
|
|
|
// $this->data = array_merge($this->getDefaults(), $input); |
|
50
|
|
|
$this->data = Utils::arrayMerge($this->getDefaults(), $input); |
|
51
|
|
|
} |
|
52
|
|
|
|
|
53
|
|
|
/** |
|
54
|
|
|
* Override this method in your own subclass to provide an array of default |
|
55
|
|
|
* options and values |
|
56
|
|
|
* |
|
57
|
|
|
* @codeCoverageIgnore |
|
58
|
|
|
* @return array |
|
59
|
|
|
* @since 0.1 |
|
60
|
|
|
*/ |
|
61
|
|
|
protected function getDefaults() |
|
62
|
|
|
{ |
|
63
|
|
|
return []; |
|
64
|
|
|
} |
|
65
|
|
|
|
|
66
|
|
|
#: KonfigInterface Methods |
|
67
|
|
|
|
|
68
|
|
|
public function all() |
|
69
|
|
|
{ |
|
70
|
|
|
return $this->data; |
|
71
|
|
|
} |
|
72
|
|
|
|
|
73
|
|
|
/** |
|
74
|
|
|
* {@inheritDoc} |
|
75
|
|
|
*/ |
|
76
|
|
|
public function has($key) |
|
77
|
|
|
{ |
|
78
|
|
|
// Check if already cached |
|
79
|
|
|
if (isset($this->cache[$key])) { |
|
80
|
|
|
return true; |
|
|
|
|
|
|
81
|
|
|
} |
|
82
|
|
|
|
|
83
|
|
|
$chunks = explode('.', $key); |
|
84
|
|
|
$root = $this->data; |
|
85
|
|
|
|
|
86
|
|
|
// Nested case |
|
87
|
|
|
foreach ($chunks as $chunk) { |
|
88
|
|
|
if (array_key_exists($chunk, $root)) { |
|
89
|
|
|
$root = $root[$chunk]; |
|
90
|
|
|
continue; |
|
91
|
|
|
} else { |
|
92
|
|
|
return false; |
|
|
|
|
|
|
93
|
|
|
} |
|
94
|
|
|
} |
|
95
|
|
|
|
|
96
|
|
|
// Set cache for the given key |
|
97
|
|
|
$this->cache[$key] = $root; |
|
98
|
|
|
|
|
99
|
|
|
return true; |
|
|
|
|
|
|
100
|
|
|
} |
|
101
|
|
|
|
|
102
|
|
|
/** |
|
103
|
|
|
* {@inheritDoc} |
|
104
|
|
|
*/ |
|
105
|
|
|
public function get($key, $default = null) |
|
106
|
|
|
{ |
|
107
|
|
|
if ($this->has($key)) { |
|
108
|
|
|
return $this->cache[$key]; |
|
109
|
|
|
} |
|
110
|
|
|
|
|
111
|
|
|
return $default; |
|
112
|
|
|
} |
|
113
|
|
|
|
|
114
|
|
|
/** |
|
115
|
|
|
* {@inheritDoc} |
|
116
|
|
|
*/ |
|
117
|
|
|
public function set($key, $value) |
|
118
|
|
|
{ |
|
119
|
|
|
$chunks = explode('.', $key); |
|
120
|
|
|
$root = &$this->data; |
|
121
|
|
|
$cacheKey = ''; |
|
122
|
|
|
|
|
123
|
|
|
// Look for the key, creating nested keys if needed |
|
124
|
|
|
while ($part = array_shift($chunks)) { |
|
125
|
|
|
if ($cacheKey != '') { |
|
126
|
|
|
$cacheKey .= '.'; |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
$cacheKey .= $part; |
|
130
|
|
|
|
|
131
|
|
|
if (!isset($root[$part]) && count($chunks)) { |
|
132
|
|
|
$root[$part] = []; |
|
133
|
|
|
} |
|
134
|
|
|
|
|
135
|
|
|
$root = &$root[$part]; |
|
136
|
|
|
|
|
137
|
|
|
// Unset all old nested cache |
|
138
|
|
|
if (isset($this->cache[$cacheKey])) { |
|
139
|
|
|
unset($this->cache[$cacheKey]); |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
// Unset all old nested cache in case of array |
|
143
|
|
|
if (count($chunks) == 0) { |
|
144
|
|
|
foreach ($this->cache as $cacheLocalKey => $cacheValue) { |
|
145
|
|
|
if (substr($cacheLocalKey, 0, strlen($cacheKey)) === $cacheKey) { |
|
146
|
|
|
unset($this->cache[$cacheLocalKey]); |
|
147
|
|
|
} |
|
148
|
|
|
} |
|
149
|
|
|
} |
|
150
|
|
|
} |
|
151
|
|
|
|
|
152
|
|
|
// Assign value at target node |
|
153
|
|
|
$this->cache[$key] = $root = $value; |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
/** |
|
157
|
|
|
* {@inheritDoc} |
|
158
|
|
|
*/ |
|
159
|
|
|
public function delete($key) |
|
160
|
|
|
{ |
|
161
|
|
|
if (isset($this->cache[$key])) { |
|
162
|
|
|
unset($this->cache[$key]); |
|
163
|
|
|
} |
|
164
|
|
|
|
|
165
|
|
|
return Utils::arrDelete($this->data, $key); |
|
|
|
|
|
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
#: ArrayAccess Methods |
|
169
|
|
|
|
|
170
|
|
|
/** |
|
171
|
|
|
* Gets a value using the offset as a key |
|
172
|
|
|
* |
|
173
|
|
|
* @param string $offset |
|
174
|
|
|
* @return mixed |
|
175
|
|
|
* @since 0.1 |
|
176
|
|
|
*/ |
|
177
|
|
|
public function offsetGet($offset) |
|
178
|
|
|
{ |
|
179
|
|
|
return $this->get($offset); |
|
180
|
|
|
} |
|
181
|
|
|
|
|
182
|
|
|
/** |
|
183
|
|
|
* Checks if a key exists |
|
184
|
|
|
* |
|
185
|
|
|
* @param string $offset |
|
186
|
|
|
* @return bool |
|
187
|
|
|
* @since 0.1 |
|
188
|
|
|
*/ |
|
189
|
|
|
public function offsetExists($offset) |
|
190
|
|
|
{ |
|
191
|
|
|
return $this->has($offset); |
|
192
|
|
|
} |
|
193
|
|
|
|
|
194
|
|
|
/** |
|
195
|
|
|
* Sets a value using the offset as a key |
|
196
|
|
|
* |
|
197
|
|
|
* @param string $offset |
|
198
|
|
|
* @param mixed $value |
|
199
|
|
|
* @return void |
|
200
|
|
|
* @since 0.1 |
|
201
|
|
|
*/ |
|
202
|
|
|
public function offsetSet($offset, $value) |
|
203
|
|
|
{ |
|
204
|
|
|
$this->set($offset, $value); |
|
205
|
|
|
} |
|
206
|
|
|
|
|
207
|
|
|
/** |
|
208
|
|
|
* Deletes a key and its value |
|
209
|
|
|
* |
|
210
|
|
|
* @param string $offset |
|
211
|
|
|
* @return void |
|
212
|
|
|
* @since 0.1 |
|
213
|
|
|
*/ |
|
214
|
|
|
public function offsetUnset($offset) |
|
215
|
|
|
{ |
|
216
|
|
|
$this->set($offset, null); |
|
217
|
|
|
} |
|
218
|
|
|
|
|
219
|
|
|
#: Iterator Methods |
|
220
|
|
|
|
|
221
|
|
|
/** |
|
222
|
|
|
* Tests whether the iterator's current index is valid |
|
223
|
|
|
* |
|
224
|
|
|
* @return bool True if the current index is valid; false otherwise |
|
225
|
|
|
* @since 0.1 |
|
226
|
|
|
*/ |
|
227
|
|
|
public function valid() |
|
228
|
|
|
{ |
|
229
|
|
|
return (is_array($this->data) ? key($this->data) !== null : false); |
|
230
|
|
|
} |
|
231
|
|
|
|
|
232
|
|
|
/** |
|
233
|
|
|
* Returns the data array index referenced by its internal cursor |
|
234
|
|
|
* |
|
235
|
|
|
* @return mixed The index referenced by the data array's internal cursor. |
|
236
|
|
|
* If the array is empty or undefined or there is no element at the cursor, |
|
237
|
|
|
* the function returns null |
|
238
|
|
|
* @since 0.1 |
|
239
|
|
|
*/ |
|
240
|
|
|
public function key() |
|
241
|
|
|
{ |
|
242
|
|
|
return (is_array($this->data) ? key($this->data) : null); |
|
243
|
|
|
} |
|
244
|
|
|
|
|
245
|
|
|
/** |
|
246
|
|
|
* Returns the data array element referenced by its internal cursor |
|
247
|
|
|
* |
|
248
|
|
|
* @return mixed The element referenced by the data array's internal cursor. |
|
249
|
|
|
* If the array is empty or there is no element at the cursor, |
|
250
|
|
|
* the function returns false. If the array is undefined, the function |
|
251
|
|
|
* returns null |
|
252
|
|
|
* @since 0.1 |
|
253
|
|
|
*/ |
|
254
|
|
|
public function current() |
|
255
|
|
|
{ |
|
256
|
|
|
return (is_array($this->data) ? current($this->data) : null); |
|
257
|
|
|
} |
|
258
|
|
|
|
|
259
|
|
|
/** |
|
260
|
|
|
* Moves the data array's internal cursor forward one element |
|
261
|
|
|
* |
|
262
|
|
|
* @return mixed The element referenced by the data array's internal cursor |
|
263
|
|
|
* after the move is completed. If there are no more elements in the |
|
264
|
|
|
* array after the move, the function returns false. If the data array |
|
265
|
|
|
* is undefined, the function returns null |
|
266
|
|
|
* @since 0.1 |
|
267
|
|
|
*/ |
|
268
|
|
|
public function next() |
|
269
|
|
|
{ |
|
270
|
|
|
return (is_array($this->data) ? next($this->data) : null); |
|
271
|
|
|
} |
|
272
|
|
|
|
|
273
|
|
|
/** |
|
274
|
|
|
* Moves the data array's internal cursor to the first element |
|
275
|
|
|
* |
|
276
|
|
|
* @return mixed The element referenced by the data array's internal cursor |
|
277
|
|
|
* after the move is completed. If the data array is empty, the function |
|
278
|
|
|
* returns false. If the data array is undefined, the function returns null |
|
279
|
|
|
* @since 0.1 |
|
280
|
|
|
*/ |
|
281
|
|
|
public function rewind() |
|
282
|
|
|
{ |
|
283
|
|
|
return (is_array($this->data) ? reset($this->data) : null); |
|
284
|
|
|
} |
|
285
|
|
|
} |
|
286
|
|
|
|
|
287
|
|
|
#: END OF ./src/AbstractKonfig.php FILE |
|
288
|
|
|
|
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_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.