| 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: |  | 
| 23: | protected $aContainer; | 
| 24: |  | 
| 25: |  | 
| 26: |  | 
| 27: |  | 
| 28: | protected $sPath; | 
| 29: |  | 
| 30: |  | 
| 31: |  | 
| 32: |  | 
| 33: | protected $bIsLoaded; | 
| 34: |  | 
| 35: |  | 
| 36: |  | 
| 37: |  | 
| 38: |  | 
| 39: |  | 
| 40: |  | 
| 41: | public function __construct($sSettingsPath) | 
| 42: | { | 
| 43: | $this->aContainer = []; | 
| 44: | $this->sPath = $sSettingsPath; | 
| 45: | $this->bIsLoaded = false; | 
| 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: | public function __get($sName) | 
| 76: | { | 
| 77: | return $this->GetValue($sName); | 
| 78: | } | 
| 79: |  | 
| 80: |  | 
| 81: |  | 
| 82: |  | 
| 83: | public function GetValues() | 
| 84: | { | 
| 85: | return $this->aContainer; | 
| 86: | } | 
| 87: |  | 
| 88: |  | 
| 89: |  | 
| 90: |  | 
| 91: |  | 
| 92: | public function GetPath() | 
| 93: | { | 
| 94: | return $this->sPath; | 
| 95: | } | 
| 96: |  | 
| 97: |  | 
| 98: |  | 
| 99: |  | 
| 100: | public function SetValues($aValues) | 
| 101: | { | 
| 102: | $this->aContainer = $aValues; | 
| 103: | } | 
| 104: |  | 
| 105: |  | 
| 106: |  | 
| 107: |  | 
| 108: |  | 
| 109: |  | 
| 110: | public function GetValue($sKey, $mDefault = null) | 
| 111: | { | 
| 112: | if (!$this->bIsLoaded) { | 
| 113: | $this->Load(); | 
| 114: | } | 
| 115: |  | 
| 116: | return (isset($this->aContainer[$sKey])) ? $this->aContainer[$sKey]->Value : $mDefault; | 
| 117: | } | 
| 118: |  | 
| 119: |  | 
| 120: |  | 
| 121: |  | 
| 122: |  | 
| 123: |  | 
| 124: |  | 
| 125: |  | 
| 126: | public function GetConf($sKey, $mDefault = null) | 
| 127: | { | 
| 128: | return $this->GetValue($sKey, $mDefault); | 
| 129: | } | 
| 130: |  | 
| 131: |  | 
| 132: |  | 
| 133: |  | 
| 134: |  | 
| 135: |  | 
| 136: |  | 
| 137: | public function SetValue($sKey, $mValue) | 
| 138: | { | 
| 139: | $bResult = false; | 
| 140: |  | 
| 141: | $sType = (isset($this->aContainer[$sKey])) ? $this->aContainer[$sKey]->Type : \gettype($mValue); | 
| 142: | if (!isset($this->aContainer[$sKey])) { | 
| 143: | $this->aContainer[$sKey] = new SettingsProperty($sKey, $mValue, $sType); | 
| 144: | } | 
| 145: |  | 
| 146: | switch ($sType) { | 
| 147: | case 'string': | 
| 148: | $mValue = (string) $mValue; | 
| 149: | break; | 
| 150: | case 'int': | 
| 151: | case 'integer': | 
| 152: | $mValue = (int) $mValue; | 
| 153: | break; | 
| 154: | case 'bool': | 
| 155: | case 'boolean': | 
| 156: | $mValue = (bool) $mValue; | 
| 157: | break; | 
| 158: | case 'spec': | 
| 159: | $mValue = $this->specValidate($mValue, $this->aContainer[$sKey]->SpecType); | 
| 160: | break; | 
| 161: | case 'array': | 
| 162: | if (!Utils::IsAssocArray($mValue)) { | 
| 163: |  | 
| 164: | $mValue = array_values($mValue); | 
| 165: | } | 
| 166: | break; | 
| 167: | default: | 
| 168: | $mValue = null; | 
| 169: | break; | 
| 170: | } | 
| 171: | $this->aContainer[$sKey]->Value = $mValue; | 
| 172: | $this->aContainer[$sKey]->Changed = true; | 
| 173: |  | 
| 174: | return $bResult; | 
| 175: | } | 
| 176: |  | 
| 177: |  | 
| 178: |  | 
| 179: |  | 
| 180: |  | 
| 181: |  | 
| 182: |  | 
| 183: |  | 
| 184: |  | 
| 185: | public function SetConf($sKey, $mValue) | 
| 186: | { | 
| 187: | return $this->SetValue($sKey, $mValue); | 
| 188: | } | 
| 189: |  | 
| 190: | public function IsExists() | 
| 191: | { | 
| 192: | return \file_exists($this->sPath); | 
| 193: | } | 
| 194: |  | 
| 195: | public function BackupConfigFile() | 
| 196: | { | 
| 197: | $sJsonFile = $this->sPath; | 
| 198: | if (\file_exists($sJsonFile)) { | 
| 199: | \copy($sJsonFile, $sJsonFile.'.bak'); | 
| 200: | } | 
| 201: | } | 
| 202: |  | 
| 203: | public function CheckConfigFile() | 
| 204: | { | 
| 205: | $bResult = true; | 
| 206: |  | 
| 207: |  | 
| 208: | $sJsonFile = $this->sPath; | 
| 209: | if (!\file_exists(\dirname($sJsonFile))) { | 
| 210: | \set_error_handler(function () { | 
| 211: | }); | 
| 212: | \mkdir(\dirname($sJsonFile), 0777); | 
| 213: | \restore_error_handler(); | 
| 214: | $bResult = \file_exists(\dirname($sJsonFile)); | 
| 215: | } | 
| 216: |  | 
| 217: | return $bResult; | 
| 218: | } | 
| 219: |  | 
| 220: | public function SaveDataToConfigFile($aData) | 
| 221: | { | 
| 222: | $sJsonFile = $this->sPath; | 
| 223: | return (bool) \file_put_contents( | 
| 224: | $sJsonFile, | 
| 225: | \json_encode( | 
| 226: | $aData, | 
| 227: | JSON_PRETTY_PRINT | JSON_OBJECT_AS_ARRAY | 
| 228: | ) | 
| 229: | ); | 
| 230: | } | 
| 231: |  | 
| 232: | public function ParseData($aData) | 
| 233: | { | 
| 234: | $aContainer = []; | 
| 235: |  | 
| 236: | if (\is_array($aData)) { | 
| 237: | foreach ($aData as $sKey => $mValue) { | 
| 238: | $sSpecType = null; | 
| 239: | if (\is_array($mValue)) { | 
| 240: | $sType = isset($mValue[1]) ? $mValue[1] : (isset($mValue[0]) ? \gettype($mValue[0]) : "string"); | 
| 241: | $sSpecType = isset($mValue[2]) ? $mValue[2] : null; | 
| 242: | $mValue = isset($mValue[0]) ? $mValue[0] : ""; | 
| 243: | } else { | 
| 244: | $sType = \gettype($mValue); | 
| 245: | } | 
| 246: |  | 
| 247: | switch ($sType) { | 
| 248: | case 'string': | 
| 249: | $mValue =(string) $mValue; | 
| 250: | break; | 
| 251: | case 'int': | 
| 252: | case 'integer': | 
| 253: | $sType = 'int'; | 
| 254: | $mValue = (int) $mValue; | 
| 255: | break; | 
| 256: | case 'bool': | 
| 257: | case 'boolean': | 
| 258: | $sType = 'bool'; | 
| 259: | $mValue = (bool) $mValue; | 
| 260: | break; | 
| 261: | case 'spec': | 
| 262: | $mValue = $this->specConver($mValue, $sSpecType); | 
| 263: | break; | 
| 264: | case 'array': | 
| 265: | break; | 
| 266: | default: | 
| 267: | $mValue = null; | 
| 268: | break; | 
| 269: | } | 
| 270: | if (null !== $mValue) { | 
| 271: | $aContainer[$sKey] = new SettingsProperty($sKey, $mValue, $sType, $sSpecType); | 
| 272: | } | 
| 273: | } | 
| 274: | } | 
| 275: |  | 
| 276: | return $aContainer; | 
| 277: | } | 
| 278: |  | 
| 279: |  | 
| 280: |  | 
| 281: |  | 
| 282: |  | 
| 283: |  | 
| 284: | public function LoadDataFromFile($sJsonFile) | 
| 285: | { | 
| 286: | $mResult = false; | 
| 287: |  | 
| 288: | if (\file_exists($sJsonFile)) { | 
| 289: | $sJsonData = \file_get_contents($sJsonFile); | 
| 290: | $mResult = \json_decode($sJsonData, true); | 
| 291: | } | 
| 292: |  | 
| 293: | return $mResult; | 
| 294: | } | 
| 295: |  | 
| 296: |  | 
| 297: |  | 
| 298: |  | 
| 299: |  | 
| 300: |  | 
| 301: | public function Load($bForceLoad = false) | 
| 302: | { | 
| 303: | $bResult = false; | 
| 304: | if ($this->bIsLoaded && !$bForceLoad) { | 
| 305: | $bResult = true; | 
| 306: | } else { | 
| 307: | $mData = $this->LoadDataFromFile($this->sPath); | 
| 308: |  | 
| 309: | if (!$mData) { | 
| 310: | $mData = $this->LoadDataFromFile($this->sPath.'.bak'); | 
| 311: | if ($mData) { | 
| 312: | \copy($this->sPath.'.bak', $this->sPath); | 
| 313: | } | 
| 314: | } | 
| 315: |  | 
| 316: | if ($mData !== false) { | 
| 317: | $this->aContainer = $this->ParseData($mData); | 
| 318: | $this->bIsLoaded = true; | 
| 319: | $bResult = true; | 
| 320: | } | 
| 321: | } | 
| 322: | return $bResult; | 
| 323: | } | 
| 324: |  | 
| 325: |  | 
| 326: |  | 
| 327: |  | 
| 328: | public function GetData() | 
| 329: | { | 
| 330: | $aResult = []; | 
| 331: | foreach ($this->aContainer as $sKey => $mValue) { | 
| 332: | $aValue = []; | 
| 333: | if ($mValue->Type === 'spec') { | 
| 334: | $mValue->Value = $this->specBackConver($mValue->Value, $mValue->SpecType); | 
| 335: | $aValue[] = $mValue->SpecType; | 
| 336: | } | 
| 337: | \array_unshift( | 
| 338: | $aValue, | 
| 339: | $mValue->Value, | 
| 340: | $mValue->Type | 
| 341: | ); | 
| 342: |  | 
| 343: | $aResult[$sKey] = $aValue; | 
| 344: | } | 
| 345: |  | 
| 346: | return $aResult; | 
| 347: | } | 
| 348: |  | 
| 349: |  | 
| 350: |  | 
| 351: |  | 
| 352: | public function Save() | 
| 353: | { | 
| 354: | $bResult = false; | 
| 355: | $aData = $this->GetData(); | 
| 356: | if (count($aData) > 0) { | 
| 357: | if ($this->CheckConfigFile()) { | 
| 358: | $this->BackupConfigFile(); | 
| 359: | if ($this->SaveDataToConfigFile($aData)) { | 
| 360: | $bResult = true; | 
| 361: | } else { | 
| 362: | throw new \Aurora\System\Exceptions\SettingsException('Can\'t write settings to the configuration file'); | 
| 363: | } | 
| 364: | } | 
| 365: | } | 
| 366: |  | 
| 367: | return $bResult; | 
| 368: | } | 
| 369: |  | 
| 370: |  | 
| 371: |  | 
| 372: |  | 
| 373: |  | 
| 374: |  | 
| 375: |  | 
| 376: | protected function specBackConver($sValue, $sEnumName) | 
| 377: | { | 
| 378: | $mResult = $sValue; | 
| 379: | if (null !== $sEnumName) { | 
| 380: | $mResult = Enums\EnumConvert::ToXml($sValue, $sEnumName); | 
| 381: | } | 
| 382: |  | 
| 383: | return $mResult; | 
| 384: | } | 
| 385: |  | 
| 386: |  | 
| 387: |  | 
| 388: |  | 
| 389: |  | 
| 390: |  | 
| 391: |  | 
| 392: | protected function specValidate($sValue, $sEnumName) | 
| 393: | { | 
| 394: | $mResult = null; | 
| 395: | if (null !== $sEnumName) { | 
| 396: | $mResult = Enums\EnumConvert::validate($sValue, $sEnumName); | 
| 397: | } | 
| 398: | return $mResult; | 
| 399: | } | 
| 400: |  | 
| 401: |  | 
| 402: |  | 
| 403: |  | 
| 404: |  | 
| 405: |  | 
| 406: |  | 
| 407: | protected function specConver($sValue, $sEnumName) | 
| 408: | { | 
| 409: | if (null !== $sEnumName) { | 
| 410: | $mResult = Enums\EnumConvert::FromXml($sValue, $sEnumName); | 
| 411: | } | 
| 412: |  | 
| 413: | return $this->specValidate($mResult, $sEnumName); | 
| 414: | } | 
| 415: |  | 
| 416: |  | 
| 417: |  | 
| 418: |  | 
| 419: | protected function init() | 
| 420: | { | 
| 421: | foreach ($this->aMap as $sKey => $aField) { | 
| 422: | $this->aLowerMap[strtolower($sKey)] = $aField; | 
| 423: | $this->SetValue($sKey, $aField[0]); | 
| 424: | } | 
| 425: | } | 
| 426: | } | 
| 427: |  |