1: | <?php |
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | namespace Aurora\System; |
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | abstract class AbstractSettings |
16: | { |
17: | public const JSON_FILE_NAME = 'config.json'; |
18: | |
19: | |
20: | |
21: | |
22: | protected $aContainer; |
23: | |
24: | |
25: | |
26: | |
27: | protected $sPath; |
28: | |
29: | |
30: | |
31: | |
32: | protected $bIsLoaded; |
33: | |
34: | |
35: | |
36: | |
37: | |
38: | |
39: | public function __construct($sSettingsPath) |
40: | { |
41: | $this->aContainer = []; |
42: | $this->sPath = $sSettingsPath; |
43: | $this->bIsLoaded = false; |
44: | } |
45: | |
46: | |
47: | |
48: | |
49: | |
50: | |
51: | |
52: | public function __isset($sName) |
53: | { |
54: | if (!$this->bIsLoaded) { |
55: | $this->Load(); |
56: | } |
57: | |
58: | return isset($this->aContainer[$sName]); |
59: | } |
60: | |
61: | |
62: | |
63: | |
64: | |
65: | |
66: | public function __set($sName, $mValue) |
67: | { |
68: | $this->SetValue($sName, $mValue); |
69: | } |
70: | |
71: | |
72: | |
73: | |
74: | |
75: | |
76: | |
77: | public function __get($sName) |
78: | { |
79: | return $this->GetValue($sName); |
80: | } |
81: | |
82: | |
83: | |
84: | |
85: | public function GetValues() |
86: | { |
87: | return $this->aContainer; |
88: | } |
89: | |
90: | |
91: | |
92: | |
93: | |
94: | public function GetPath() |
95: | { |
96: | return $this->sPath; |
97: | } |
98: | |
99: | |
100: | |
101: | |
102: | public function SetValues($aValues) |
103: | { |
104: | $this->aContainer = $aValues; |
105: | } |
106: | |
107: | |
108: | |
109: | |
110: | |
111: | |
112: | public function GetValue($sKey, $mDefault = null) |
113: | { |
114: | if (!$this->bIsLoaded) { |
115: | $this->Load(); |
116: | } |
117: | |
118: | $value = $mDefault; |
119: | if (isset($this->aContainer[$sKey])) { |
120: | $prop = $this->aContainer[$sKey]; |
121: | if ($prop->Type === 'encrypted') { |
122: | $value = \Aurora\System\Utils::DecryptValue($prop->Value); |
123: | if (!$value) { |
124: | $value = $prop->Value; |
125: | $this->SetValue($sKey, $value); |
126: | $this->Save(); |
127: | } |
128: | } else { |
129: | $value = $prop->Value; |
130: | } |
131: | } |
132: | |
133: | return $value; |
134: | } |
135: | |
136: | |
137: | |
138: | |
139: | |
140: | |
141: | public function GetSettingsProperty($sKey) |
142: | { |
143: | if (!$this->bIsLoaded) { |
144: | $this->Load(); |
145: | } |
146: | |
147: | return (isset($this->aContainer[$sKey])) ? $this->aContainer[$sKey] : null; |
148: | } |
149: | |
150: | |
151: | |
152: | |
153: | |
154: | |
155: | |
156: | |
157: | |
158: | public function GetConf($sKey, $mDefault = null) |
159: | { |
160: | return $this->GetValue($sKey, $mDefault); |
161: | } |
162: | |
163: | |
164: | |
165: | |
166: | |
167: | |
168: | |
169: | public function SetValue($sKey, $mValue) |
170: | { |
171: | $bResult = false; |
172: | |
173: | $sType = (isset($this->aContainer[$sKey])) ? $this->aContainer[$sKey]->Type : \gettype($mValue); |
174: | if (!isset($this->aContainer[$sKey])) { |
175: | $this->aContainer[$sKey] = new SettingsProperty($mValue, $sType); |
176: | } |
177: | |
178: | switch ($sType) { |
179: | case 'string': |
180: | $mValue = (string) $mValue; |
181: | break; |
182: | case 'int': |
183: | case 'integer': |
184: | $mValue = (int) $mValue; |
185: | break; |
186: | case 'bool': |
187: | case 'boolean': |
188: | $mValue = (bool) $mValue; |
189: | break; |
190: | case 'spec': |
191: | $mValue = $this->specValidate($mValue, $this->aContainer[$sKey]->SpecType); |
192: | break; |
193: | case 'array': |
194: | if (!Utils::IsAssocArray($mValue)) { |
195: | |
196: | $mValue = array_values($mValue); |
197: | } |
198: | break; |
199: | case 'encrypted': |
200: | $mValue = \Aurora\System\Utils::EncryptValue($mValue); |
201: | break; |
202: | default: |
203: | $mValue = null; |
204: | break; |
205: | } |
206: | $this->aContainer[$sKey]->Value = $mValue; |
207: | $this->aContainer[$sKey]->Changed = true; |
208: | |
209: | return $bResult; |
210: | } |
211: | |
212: | |
213: | |
214: | |
215: | |
216: | |
217: | |
218: | |
219: | |
220: | public function SetConf($sKey, $mValue) |
221: | { |
222: | return $this->SetValue($sKey, $mValue); |
223: | } |
224: | |
225: | public function IsExists() |
226: | { |
227: | return \file_exists($this->sPath); |
228: | } |
229: | |
230: | public function BackupConfigFile() |
231: | { |
232: | $sJsonFile = $this->sPath; |
233: | if (\file_exists($sJsonFile)) { |
234: | \copy($sJsonFile, $sJsonFile . '.bak'); |
235: | } |
236: | } |
237: | |
238: | public function LoadDataFromBackup() |
239: | { |
240: | return $this->LoadDataFromFile($this->sPath . '.bak'); |
241: | } |
242: | |
243: | public function CheckConfigFile() |
244: | { |
245: | $bResult = true; |
246: | |
247: | |
248: | $sJsonFile = $this->sPath; |
249: | if (!\file_exists(\dirname($sJsonFile))) { |
250: | \set_error_handler(function () {}); |
251: | \mkdir(\dirname($sJsonFile), 0777); |
252: | \restore_error_handler(); |
253: | $bResult = \file_exists(\dirname($sJsonFile)); |
254: | } |
255: | |
256: | return $bResult; |
257: | } |
258: | |
259: | public function SaveDataToConfigFile($aData) |
260: | { |
261: | $sJsonFile = $this->sPath; |
262: | return (bool) \file_put_contents( |
263: | $sJsonFile, |
264: | \json_encode( |
265: | $aData, |
266: | JSON_PRETTY_PRINT | JSON_OBJECT_AS_ARRAY |
267: | ) |
268: | ); |
269: | } |
270: | |
271: | public function ParseData($aData) |
272: | { |
273: | $aContainer = []; |
274: | |
275: | if (\is_array($aData)) { |
276: | foreach ($aData as $sKey => $mValue) { |
277: | if (isset($aData[$sKey])) { |
278: | $sSpecType = null; |
279: | $sDescription = ''; |
280: | if (\is_array($mValue)) { |
281: | $sType = isset($mValue[1]) ? $mValue[1] : (isset($mValue[0]) ? \gettype($mValue[0]) : "string"); |
282: | $sSpecType = isset($mValue[2]) ? $mValue[2] : null; |
283: | $sDescription = isset($mValue[3]) ? $mValue[3] : ''; |
284: | $mValue = isset($mValue[0]) ? $mValue[0] : ''; |
285: | if (isset($aData[$sKey . '_Description'])) { |
286: | $sDescription = isset($aData[$sKey . '_Description'][0]) ? $aData[$sKey . '_Description'][0] : ''; |
287: | unset($aData[$sKey . '_Description']); |
288: | } |
289: | } else { |
290: | $sType = \gettype($mValue); |
291: | } |
292: | |
293: | switch ($sType) { |
294: | case 'string': |
295: | $mValue = (string) $mValue; |
296: | break; |
297: | case 'int': |
298: | case 'integer': |
299: | $sType = 'int'; |
300: | $mValue = (int) $mValue; |
301: | break; |
302: | case 'bool': |
303: | case 'boolean': |
304: | $sType = 'bool'; |
305: | $mValue = (bool) $mValue; |
306: | break; |
307: | case 'spec': |
308: | $mValue = $this->specConver($mValue, $sSpecType); |
309: | break; |
310: | case 'array': |
311: | break; |
312: | default: |
313: | $mValue = null; |
314: | break; |
315: | } |
316: | if (null !== $mValue) { |
317: | $aContainer[$sKey] = new SettingsProperty($mValue, $sType, $sSpecType, $sDescription); |
318: | } |
319: | } |
320: | } |
321: | } |
322: | |
323: | return $aContainer; |
324: | } |
325: | |
326: | |
327: | |
328: | |
329: | |
330: | |
331: | public function LoadDataFromFile($sJsonFile) |
332: | { |
333: | $mResult = false; |
334: | |
335: | if (\file_exists($sJsonFile)) { |
336: | $sJsonData = \file_get_contents($sJsonFile); |
337: | $mResult = \json_decode($sJsonData, true); |
338: | } |
339: | |
340: | return $mResult; |
341: | } |
342: | |
343: | |
344: | |
345: | |
346: | |
347: | |
348: | public function Load($bForceLoad = false) |
349: | { |
350: | $bResult = false; |
351: | if ($this->bIsLoaded && !$bForceLoad) { |
352: | $bResult = true; |
353: | } else { |
354: | $mData = false; |
355: | |
356: | if (\file_exists($this->sPath)) { |
357: | $mData = $this->LoadDataFromFile($this->sPath); |
358: | } |
359: | |
360: | if (!$mData) { |
361: | $mData = $this->LoadDataFromBackup(); |
362: | } |
363: | |
364: | if ($mData) { |
365: | $aParsedData = $this->ParseData($mData); |
366: | foreach ($aParsedData as $key => $val) { |
367: | $val->IsDefault = false; |
368: | if (isset($this->aContainer[$key])) { |
369: | $val->Description = $this->aContainer[$key]->Description; |
370: | } |
371: | $this->aContainer[$key] = $val; |
372: | } |
373: | $bResult = true; |
374: | } else { |
375: | $bResult = $this->Save(); |
376: | } |
377: | |
378: | $this->bIsLoaded = true; |
379: | } |
380: | |
381: | return $bResult; |
382: | } |
383: | |
384: | |
385: | |
386: | |
387: | public function GetData() |
388: | { |
389: | $aResult = []; |
390: | foreach ($this->aContainer as $sKey => $mValue) { |
391: | $aValue = []; |
392: | $value = $mValue->Value; |
393: | if ($mValue->Type === 'spec') { |
394: | $value = $this->specBackConver($mValue->Value, $mValue->SpecType); |
395: | $aValue[] = $mValue->SpecType; |
396: | } else { |
397: | $aValue[] = null; |
398: | } |
399: | \array_unshift( |
400: | $aValue, |
401: | $value, |
402: | $mValue->Type |
403: | ); |
404: | $aValue[] = $mValue->Description; |
405: | |
406: | $aResult[$sKey] = $aValue; |
407: | } |
408: | |
409: | return $aResult; |
410: | } |
411: | |
412: | |
413: | |
414: | |
415: | |
416: | |
417: | public function Save($bBackupConfigFile = true) |
418: | { |
419: | $bResult = false; |
420: | $aData = $this->GetData(); |
421: | if (count($aData) > 0) { |
422: | if ($this->CheckConfigFile()) { |
423: | if ($bBackupConfigFile) { |
424: | $this->BackupConfigFile(); |
425: | } |
426: | if ($this->SaveDataToConfigFile($aData)) { |
427: | $bResult = true; |
428: | } else { |
429: | throw new \Aurora\System\Exceptions\SettingsException('Can\'t write settings to the configuration file'); |
430: | } |
431: | } |
432: | } |
433: | |
434: | return $bResult; |
435: | } |
436: | |
437: | |
438: | |
439: | |
440: | |
441: | |
442: | |
443: | protected function specBackConver($sValue, $sEnumName) |
444: | { |
445: | $mResult = $sValue; |
446: | if (null !== $sEnumName) { |
447: | $mResult = Enums\EnumConvert::ToXml($sValue, $sEnumName); |
448: | } |
449: | |
450: | return $mResult; |
451: | } |
452: | |
453: | |
454: | |
455: | |
456: | |
457: | |
458: | |
459: | protected function specValidate($sValue, $sEnumName) |
460: | { |
461: | $mResult = null; |
462: | if (null !== $sEnumName) { |
463: | $mResult = Enums\EnumConvert::validate($sValue, $sEnumName); |
464: | } |
465: | return $mResult; |
466: | } |
467: | |
468: | |
469: | |
470: | |
471: | |
472: | |
473: | |
474: | protected function specConver($sValue, $sEnumName) |
475: | { |
476: | $mResult = null; |
477: | if (null !== $sEnumName) { |
478: | $mResult = Enums\EnumConvert::FromXml($sValue, $sEnumName); |
479: | } |
480: | |
481: | return $this->specValidate($mResult, $sEnumName); |
482: | } |
483: | |
484: | |
485: | |
486: | |
487: | protected function init() {} |
488: | } |
489: | |