PageRenderTime 39ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/src/pocketmine/utils/Config.php

https://gitlab.com/kennethgomad01/genisys
PHP | 503 lines | 310 code | 48 blank | 145 comment | 26 complexity | bbcbd46ebe1ab78f5a98d6b44016dae4 MD5 | raw file
  1. <?php
  2. /*
  3. *
  4. * ____ _ _ __ __ _ __ __ ____
  5. * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
  6. * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
  7. * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
  8. * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Lesser General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * @author PocketMine Team
  16. * @link http://www.pocketmine.net/
  17. *
  18. *
  19. */
  20. namespace pocketmine\utils;
  21. use pocketmine\scheduler\FileWriteTask;
  22. use pocketmine\Server;
  23. /**
  24. * Class Config
  25. *
  26. * Config Class for simple config manipulation of multiple formats.
  27. */
  28. class Config{
  29. const DETECT = -1; //Detect by file extension
  30. const PROPERTIES = 0; // .properties
  31. const CNF = Config::PROPERTIES; // .cnf
  32. const JSON = 1; // .js, .json
  33. const YAML = 2; // .yml, .yaml
  34. //const EXPORT = 3; // .export, .xport
  35. const SERIALIZED = 4; // .sl
  36. const ENUM = 5; // .txt, .list, .enum
  37. const ENUMERATION = Config::ENUM;
  38. /** @var array */
  39. private $config = [];
  40. private $nestedCache = [];
  41. /** @var string */
  42. private $file;
  43. /** @var boolean */
  44. private $correct = false;
  45. /** @var integer */
  46. private $type = Config::DETECT;
  47. public static $formats = [
  48. "properties" => Config::PROPERTIES,
  49. "cnf" => Config::CNF,
  50. "conf" => Config::CNF,
  51. "config" => Config::CNF,
  52. "json" => Config::JSON,
  53. "js" => Config::JSON,
  54. "yml" => Config::YAML,
  55. "yaml" => Config::YAML,
  56. //"export" => Config::EXPORT,
  57. //"xport" => Config::EXPORT,
  58. "sl" => Config::SERIALIZED,
  59. "serialize" => Config::SERIALIZED,
  60. "txt" => Config::ENUM,
  61. "list" => Config::ENUM,
  62. "enum" => Config::ENUM,
  63. ];
  64. /**
  65. * @param string $file Path of the file to be loaded
  66. * @param int $type Config type to load, -1 by default (detect)
  67. * @param array $default Array with the default values that will be written to the file if it did not exist
  68. * @param null &$correct Sets correct to true if everything has been loaded correctly
  69. */
  70. public function __construct($file, $type = Config::DETECT, $default = [], &$correct = null){
  71. $this->load($file, $type, $default);
  72. $correct = $this->correct;
  73. }
  74. /**
  75. * Removes all the changes in memory and loads the file again
  76. */
  77. public function reload(){
  78. $this->config = [];
  79. $this->nestedCache = [];
  80. $this->correct = false;
  81. $this->load($this->file);
  82. $this->load($this->file, $this->type);
  83. }
  84. /**
  85. * @param $str
  86. *
  87. * @return mixed
  88. */
  89. public static function fixYAMLIndexes($str){
  90. return preg_replace("#^([ ]*)([a-zA-Z_]{1}[ ]*)\\:$#m", "$1\"$2\":", $str);
  91. }
  92. /**
  93. * @param $file
  94. * @param int $type
  95. * @param array $default
  96. *
  97. * @return bool
  98. */
  99. public function load($file, $type = Config::DETECT, $default = []){
  100. $this->correct = true;
  101. $this->type = (int) $type;
  102. $this->file = $file;
  103. if(!is_array($default)){
  104. $default = [];
  105. }
  106. if(!file_exists($file)){
  107. $this->config = $default;
  108. $this->save();
  109. }else{
  110. if($this->type === Config::DETECT){
  111. $extension = explode(".", basename($this->file));
  112. $extension = strtolower(trim(array_pop($extension)));
  113. if(isset(Config::$formats[$extension])){
  114. $this->type = Config::$formats[$extension];
  115. }else{
  116. $this->correct = false;
  117. }
  118. }
  119. if($this->correct === true){
  120. $content = file_get_contents($this->file);
  121. switch($this->type){
  122. case Config::PROPERTIES:
  123. case Config::CNF:
  124. $this->parseProperties($content);
  125. break;
  126. case Config::JSON:
  127. $this->config = json_decode($content, true);
  128. break;
  129. case Config::YAML:
  130. $content = self::fixYAMLIndexes($content);
  131. $this->config = yaml_parse($content);
  132. break;
  133. case Config::SERIALIZED:
  134. $this->config = unserialize($content);
  135. break;
  136. case Config::ENUM:
  137. $this->parseList($content);
  138. break;
  139. default:
  140. $this->correct = false;
  141. return false;
  142. }
  143. if(!is_array($this->config)){
  144. $this->config = $default;
  145. }
  146. if($this->fillDefaults($default, $this->config) > 0){
  147. $this->save();
  148. }
  149. }else{
  150. return false;
  151. }
  152. }
  153. return true;
  154. }
  155. /**
  156. * @return boolean
  157. */
  158. public function check(){
  159. return $this->correct === true;
  160. }
  161. /**
  162. * @param bool $async
  163. *
  164. * @return boolean
  165. */
  166. public function save($async = false){
  167. if($this->correct === true){
  168. try{
  169. $content = null;
  170. switch($this->type){
  171. case Config::PROPERTIES:
  172. case Config::CNF:
  173. $content = $this->writeProperties();
  174. break;
  175. case Config::JSON:
  176. $content = json_encode($this->config, JSON_PRETTY_PRINT | JSON_BIGINT_AS_STRING);
  177. break;
  178. case Config::YAML:
  179. $content = yaml_emit($this->config, YAML_UTF8_ENCODING);
  180. break;
  181. case Config::SERIALIZED:
  182. $content = serialize($this->config);
  183. break;
  184. case Config::ENUM:
  185. $content = implode("\r\n", array_keys($this->config));
  186. break;
  187. }
  188. if($async){
  189. Server::getInstance()->getScheduler()->scheduleAsyncTask(new FileWriteTask($this->file, $content));
  190. }else{
  191. file_put_contents($this->file, $content);
  192. }
  193. }catch(\Throwable $e){
  194. $logger = Server::getInstance()->getLogger();
  195. $logger->critical("Could not save Config " . $this->file . ": " . $e->getMessage());
  196. if(\pocketmine\DEBUG > 1 and $logger instanceof MainLogger){
  197. $logger->logException($e);
  198. }
  199. }
  200. return true;
  201. }else{
  202. return false;
  203. }
  204. }
  205. /**
  206. * @param $k
  207. *
  208. * @return boolean|mixed
  209. */
  210. public function __get($k){
  211. return $this->get($k);
  212. }
  213. /**
  214. * @param $k
  215. * @param $v
  216. */
  217. public function __set($k, $v){
  218. $this->set($k, $v);
  219. }
  220. /**
  221. * @param $k
  222. *
  223. * @return boolean
  224. */
  225. public function __isset($k){
  226. return $this->exists($k);
  227. }
  228. /**
  229. * @param $k
  230. */
  231. public function __unset($k){
  232. $this->remove($k);
  233. }
  234. /**
  235. * @param $key
  236. * @param $value
  237. */
  238. public function setNested($key, $value){
  239. $vars = explode(".", $key);
  240. $base = array_shift($vars);
  241. if(!isset($this->config[$base])){
  242. $this->config[$base] = [];
  243. }
  244. $base =& $this->config[$base];
  245. while(count($vars) > 0){
  246. $baseKey = array_shift($vars);
  247. if(!isset($base[$baseKey])){
  248. $base[$baseKey] = [];
  249. }
  250. $base =& $base[$baseKey];
  251. }
  252. $base = $value;
  253. $this->nestedCache[$key] = $value;
  254. }
  255. /**
  256. * @param $key
  257. * @param mixed $default
  258. *
  259. * @return mixed
  260. */
  261. public function getNested($key, $default = null){
  262. if(isset($this->nestedCache[$key])){
  263. return $this->nestedCache[$key];
  264. }
  265. $vars = explode(".", $key);
  266. $base = array_shift($vars);
  267. if(isset($this->config[$base])){
  268. $base = $this->config[$base];
  269. }else{
  270. return $default;
  271. }
  272. while(count($vars) > 0){
  273. $baseKey = array_shift($vars);
  274. if(is_array($base) and isset($base[$baseKey])){
  275. $base = $base[$baseKey];
  276. }else{
  277. return $default;
  278. }
  279. }
  280. return $this->nestedCache[$key] = $base;
  281. }
  282. /**
  283. * @param $k
  284. * @param mixed $default
  285. *
  286. * @return boolean|mixed
  287. */
  288. public function get($k, $default = false){
  289. return ($this->correct and isset($this->config[$k])) ? $this->config[$k] : $default;
  290. }
  291. /**
  292. * @param string $path
  293. *
  294. * @deprecated
  295. *
  296. * @return mixed
  297. */
  298. public function getPath($path){
  299. $currPath =& $this->config;
  300. foreach(explode(".", $path) as $component){
  301. if(isset($currPath[$component])){
  302. $currPath =& $currPath[$component];
  303. }else{
  304. $currPath = null;
  305. }
  306. }
  307. return $currPath;
  308. }
  309. /**
  310. *
  311. * @deprecated
  312. *
  313. * @param string $path
  314. * @param mixed $value
  315. */
  316. public function setPath($path, $value){
  317. $currPath =& $this->config;
  318. $components = explode(".", $path);
  319. $final = array_pop($components);
  320. foreach($components as $component){
  321. if(!isset($currPath[$component])){
  322. $currPath[$component] = [];
  323. }
  324. $currPath =& $currPath[$component];
  325. }
  326. $currPath[$final] = $value;
  327. }
  328. /**
  329. * @param string $k key to be set
  330. * @param mixed $v value to set key
  331. */
  332. public function set($k, $v = true){
  333. $this->config[$k] = $v;
  334. foreach($this->nestedCache as $nestedKey => $nvalue){
  335. if(substr($nestedKey, 0, strlen($k) + 1) === ($k . ".")){
  336. unset($this->nestedCache[$nestedKey]);
  337. }
  338. }
  339. }
  340. /**
  341. * @param array $v
  342. */
  343. public function setAll($v){
  344. $this->config = $v;
  345. }
  346. /**
  347. * @param $k
  348. * @param bool $lowercase If set, searches Config in single-case / lowercase.
  349. *
  350. * @return boolean
  351. */
  352. public function exists($k, $lowercase = false){
  353. if($lowercase === true){
  354. $k = strtolower($k); //Convert requested key to lower
  355. $array = array_change_key_case($this->config, CASE_LOWER); //Change all keys in array to lower
  356. return isset($array[$k]); //Find $k in modified array
  357. }else{
  358. return isset($this->config[$k]);
  359. }
  360. }
  361. /**
  362. * @param $k
  363. */
  364. public function remove($k){
  365. unset($this->config[$k]);
  366. }
  367. /**
  368. * @param bool $keys
  369. *
  370. * @return array
  371. */
  372. public function getAll($keys = false){
  373. return ($keys === true ? array_keys($this->config) : $this->config);
  374. }
  375. /**
  376. * @param array $defaults
  377. */
  378. public function setDefaults(array $defaults){
  379. $this->fillDefaults($defaults, $this->config);
  380. }
  381. /**
  382. * @param $default
  383. * @param $data
  384. *
  385. * @return integer
  386. */
  387. private function fillDefaults($default, &$data){
  388. $changed = 0;
  389. foreach($default as $k => $v){
  390. if(is_array($v)){
  391. if(!isset($data[$k]) or !is_array($data[$k])){
  392. $data[$k] = [];
  393. }
  394. $changed += $this->fillDefaults($v, $data[$k]);
  395. }elseif(!isset($data[$k])){
  396. $data[$k] = $v;
  397. ++$changed;
  398. }
  399. }
  400. return $changed;
  401. }
  402. /**
  403. * @param $content
  404. */
  405. private function parseList($content){
  406. foreach(explode("\n", trim(str_replace("\r\n", "\n", $content))) as $v){
  407. $v = trim($v);
  408. if($v == ""){
  409. continue;
  410. }
  411. $this->config[$v] = true;
  412. }
  413. }
  414. /**
  415. * @return string
  416. */
  417. private function writeProperties(){
  418. $content = "#Properties Config file\r\n#" . date("D M j H:i:s T Y") . "\r\n";
  419. foreach($this->config as $k => $v){
  420. if(is_bool($v) === true){
  421. $v = $v === true ? "on" : "off";
  422. }elseif(is_array($v)){
  423. $v = implode(";", $v);
  424. }
  425. $content .= $k . "=" . $v . "\r\n";
  426. }
  427. return $content;
  428. }
  429. /**
  430. * @param $content
  431. */
  432. private function parseProperties($content){
  433. if(preg_match_all('/([a-zA-Z0-9\-_\.]*)=([^\r\n]*)/u', $content, $matches) > 0){ //false or 0 matches
  434. foreach($matches[1] as $i => $k){
  435. $v = trim($matches[2][$i]);
  436. switch(strtolower($v)){
  437. case "on":
  438. case "true":
  439. case "yes":
  440. $v = true;
  441. break;
  442. case "off":
  443. case "false":
  444. case "no":
  445. $v = false;
  446. break;
  447. }
  448. if(isset($this->config[$k])){
  449. MainLogger::getLogger()->debug("[Config] Repeated property " . $k . " on file " . $this->file);
  450. }
  451. $this->config[$k] = $v;
  452. }
  453. }
  454. }
  455. }