1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Konfig. |
5
|
|
|
* |
6
|
|
|
* Yet another simple configuration loader library. |
7
|
|
|
* |
8
|
|
|
* PHP version 5 |
9
|
|
|
* |
10
|
|
|
* @category Library |
11
|
|
|
* @package Konfig |
12
|
|
|
* @author Xeriab Nabil (aka KodeBurner) <[email protected]> |
13
|
|
|
* @license https://raw.github.com/xeriab/konfig/master/LICENSE MIT |
14
|
|
|
* @link https://xeriab.github.io/projects/konfig |
15
|
|
|
*/ |
16
|
|
|
|
17
|
|
|
namespace Exen\Konfig\FileParser; |
18
|
|
|
|
19
|
|
|
use Exen\Konfig\Arr; |
20
|
|
|
use Exen\Konfig\Utils; |
21
|
|
|
use Exen\Konfig\Exception\KonfigException as Exception; |
22
|
|
|
use Exen\Konfig\Exception\ParseException; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Properties |
26
|
|
|
* Konfig's Java-Properties parser class. |
27
|
|
|
* |
28
|
|
|
* @category FileParser |
29
|
|
|
* @package Konfig |
30
|
|
|
* @author Xeriab Nabil (aka KodeBurner) <[email protected]> |
31
|
|
|
* @license https://raw.github.com/xeriab/konfig/master/LICENSE MIT |
32
|
|
|
* @link https://xeriab.github.io/projects/konfig |
33
|
|
|
* |
34
|
|
|
* @implements Exen\Konfig\FileParser\AbstractFileParser |
35
|
|
|
*/ |
36
|
|
|
class Properties extends AbstractFileParser |
37
|
|
|
{ |
38
|
|
|
protected $parsedFile; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Loads a PROPERTIES file as an array. |
42
|
|
|
* |
43
|
|
|
* @param string $path File path |
44
|
|
|
* |
45
|
|
|
* @throws ParseException If there is an error parsing PROPERTIES file |
46
|
|
|
* |
47
|
|
|
* @return array The parsed data |
48
|
|
|
* |
49
|
|
|
* @since 0.2.4 |
50
|
|
|
*/ |
51
|
6 |
|
public function parse($path) |
52
|
|
|
{ |
53
|
6 |
|
$this->loadFile($path); |
54
|
|
|
|
55
|
6 |
|
$data = $this->getProperties(); |
56
|
|
|
|
57
|
6 |
|
if (!$data || empty($data) || !is_array($data)) { |
|
|
|
|
58
|
3 |
|
throw new ParseException( |
59
|
|
|
[ |
60
|
3 |
|
'message' => 'Error parsing PROPERTIES file', |
61
|
3 |
|
'file' => $path, |
62
|
|
|
] |
63
|
2 |
|
); |
64
|
|
|
} |
65
|
|
|
|
66
|
3 |
|
return $data; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* {@inheritdoc} |
71
|
|
|
* |
72
|
|
|
* @return array Supported extensions |
73
|
|
|
* |
74
|
|
|
* @since 0.1.0 |
75
|
|
|
*/ |
76
|
3 |
|
public function getSupportedFileExtensions() |
77
|
|
|
{ |
78
|
3 |
|
return ['properties']; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* {@inheritdoc} |
83
|
|
|
* |
84
|
|
|
* @return array The exteacted data |
85
|
|
|
* |
86
|
|
|
* @since 0.2.4 |
87
|
|
|
* @codeCoverageIgnore |
88
|
|
|
*/ |
89
|
|
|
public function extractData() |
90
|
|
|
{ |
91
|
|
|
$analysis = []; |
92
|
|
|
|
93
|
|
|
// First pass, we categorize each line |
94
|
|
|
foreach ($this->parsedFile as $lineNb => $line) { |
|
|
|
|
95
|
|
|
if (Utils::stringStart('#', $line)) { |
96
|
|
|
$analysis[$lineNb] = ['comment', trim(substr($line, 1))]; |
97
|
|
|
|
98
|
|
|
continue; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
// Property name, check for escaped equal sign |
102
|
|
|
if (substr_count($line, '=') > substr_count($line, '\=')) { |
103
|
|
|
$temp = explode('=', $line, 2); |
104
|
|
|
$temp = Utils::trimArrayElements($temp); |
105
|
|
|
|
106
|
|
|
if (count($temp) === 2) { |
107
|
|
|
$temp[1] = Utils::removeQuotes($temp[1]); |
108
|
|
|
|
109
|
|
|
$analysis[$lineNb] = ['property', $temp[0], $temp[1]]; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
unset($temp); |
113
|
|
|
|
114
|
|
|
continue; |
115
|
|
|
} else { |
116
|
|
|
break; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
// Multiline data |
120
|
|
|
if (substr_count($line, '=') === 0) { |
|
|
|
|
121
|
|
|
$analysis[$lineNb] = ['multiline', '', $line]; |
122
|
|
|
continue; |
123
|
|
|
} |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
// Second pass, we associate comments to entities |
127
|
|
|
$counter = Utils::getNumberLinesMatching('comment', $analysis); |
128
|
|
|
|
129
|
|
|
while ($counter > 0) { |
130
|
|
|
foreach ($analysis as $lineNb => $line) { |
131
|
|
|
if ($line[0] === 'comment' |
132
|
|
|
&& isset($analysis[$lineNb + 1][0]) |
133
|
|
|
&& $analysis[$lineNb + 1][0] === 'comment' |
134
|
|
|
) { |
135
|
|
|
$analysis[$lineNb][1] .= ' '.$analysis[$lineNb + 1][1]; |
136
|
|
|
$analysis[$lineNb + 1][0] = 'erase'; |
137
|
|
|
|
138
|
|
|
break; |
139
|
|
|
} elseif ($line[0] === 'comment' |
140
|
|
|
&& isset($analysis[$lineNb + 1][0]) |
141
|
|
|
&& $analysis[$lineNb + 1][0] === 'property' |
142
|
|
|
) { |
143
|
|
|
$analysis[$lineNb + 1][3] = $line[1]; |
144
|
|
|
$analysis[$lineNb][0] = 'erase'; |
145
|
|
|
} |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
$counter = Utils::getNumberLinesMatching('comment', $analysis); |
149
|
|
|
$analysis = $this->_deleteFields('erase', $analysis); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
// Third pass, we merge multiline strings |
153
|
|
|
|
154
|
|
|
// We remove the backslashes at end of strings if they exist |
155
|
|
|
$analysis = Utils::stripBackslashes($analysis); |
156
|
|
|
|
157
|
|
|
// Count # of multilines |
158
|
|
|
$counter = Utils::getNumberLinesMatching('multiline', $analysis); |
|
|
|
|
159
|
|
|
|
160
|
|
|
while ($counter > 0) { |
161
|
|
|
foreach ($analysis as $lineNb => $line) { |
|
|
|
|
162
|
|
|
if ($line[0] === 'multiline' |
163
|
|
|
&& isset($analysis[$lineNb - 1][0]) |
164
|
|
|
&& $analysis[$lineNb - 1][0] === 'property' |
165
|
|
|
) { |
166
|
|
|
$analysis[$lineNb - 1][2] .= ' '.trim($line[2]); |
167
|
|
|
$analysis[$lineNb][0] = 'erase'; |
168
|
|
|
break; |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
$counter = Utils::getNumberLinesMatching('multiline', $analysis); |
|
|
|
|
173
|
|
|
$analysis = $this->_deleteFields('erase', $analysis); |
|
|
|
|
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
// Step 4, we clean up strings from escaped characters in properties |
177
|
|
|
$analysis = $this->_unescapeProperties($analysis); |
|
|
|
|
178
|
|
|
|
179
|
|
|
// Step 5, we only have properties now, remove redondant field 0 |
180
|
|
|
foreach ($analysis as $key => $value) { |
181
|
|
|
array_splice($analysis[$key], 0, 1); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
return $analysis; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* {@inheritdoc} |
189
|
|
|
* |
190
|
|
|
* @param array $analysis Configuration items |
191
|
|
|
* |
192
|
|
|
* @return array The configuration items |
193
|
|
|
* |
194
|
|
|
* @since 0.2.4 |
195
|
|
|
* @codeCoverageIgnore |
196
|
|
|
*/ |
197
|
|
|
private function _unescapeProperties($analysis) |
198
|
|
|
{ |
199
|
|
|
foreach ($analysis as $key => $value) { |
200
|
|
|
$analysis[$key][2] = str_replace('\=', '=', $value[2]); |
201
|
|
|
} |
202
|
|
|
|
203
|
|
|
return $analysis; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* {@inheritdoc} |
208
|
|
|
* |
209
|
|
|
* @param string $field Field name |
210
|
|
|
* @param array $analysis Configuration items |
211
|
|
|
* |
212
|
|
|
* @return array Configuration items after deletion |
213
|
|
|
* |
214
|
|
|
* @since 0.2.4 |
215
|
|
|
* @codeCoverageIgnore |
216
|
|
|
*/ |
217
|
|
|
private function _deleteFields($field, $analysis) |
218
|
|
|
{ |
219
|
|
|
foreach ($analysis as $key => $value) { |
220
|
|
|
if ($value[0] === $field) { |
221
|
|
|
unset($analysis[$key]); |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
return array_values($analysis); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* {@inheritdoc} |
230
|
|
|
* |
231
|
|
|
* @param string|null $file File path |
232
|
|
|
* |
233
|
|
|
* @return array Configuration items |
234
|
|
|
* |
235
|
|
|
* @since 0.2.4 |
236
|
|
|
* @codeCoverageIgnore |
237
|
|
|
*/ |
238
|
|
|
public function getProperties($file = null) |
239
|
|
|
{ |
240
|
|
|
if ($file && !is_null($file)) { |
|
|
|
|
241
|
|
|
$this->loadFile($file); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
$source = $this->extractData(); |
245
|
|
|
$data = []; |
246
|
|
|
|
247
|
|
|
foreach ($source as $value) { |
248
|
|
|
Arr::set($data, $value[0], $value[1]); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
unset($this->parsedFile); |
252
|
|
|
|
253
|
|
|
return $data; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* Loads in the given file and parses it. |
258
|
|
|
* |
259
|
|
|
* @param string|bool|null $file File to load |
260
|
|
|
* |
261
|
|
|
* @return array The parsed file data |
262
|
|
|
* |
263
|
|
|
* @since 0.2.4 |
264
|
|
|
* @codeCoverageIgnore |
265
|
|
|
*/ |
266
|
|
|
protected function loadFile($file = null) |
267
|
|
|
{ |
268
|
|
|
$this->file = is_file($file) ? $file : false; |
|
|
|
|
269
|
|
|
|
270
|
|
|
$contents = $this->parseVars(Utils::getContent($this->file)); |
|
|
|
|
271
|
|
|
|
272
|
|
|
if ($this->file && !is_null($file)) { |
273
|
|
|
$this->parsedFile = Utils::fileContentToArray($contents); |
274
|
|
|
} else { |
275
|
|
|
$this->parsedFile = false; |
276
|
|
|
} |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* Returns the formatted configuration file contents. |
281
|
|
|
* |
282
|
|
|
* @param array $contents configuration array |
283
|
|
|
* |
284
|
|
|
* @return string formatted configuration file contents |
285
|
|
|
* |
286
|
|
|
* @since 0.2.4 |
287
|
|
|
* @codeCoverageIgnore |
288
|
|
|
*/ |
289
|
|
|
protected function exportFormat($contents = null) |
290
|
|
|
{ |
291
|
|
|
throw new Exception( |
292
|
|
|
'Saving configuration to `Properties` is not supported at this time' |
293
|
|
|
); |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
// END OF ./src/FileParser/Properties.php FILE |
298
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.