ConfigFile.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Config file management
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin\Config;
  9. use PhpMyAdmin\Config;
  10. use PhpMyAdmin\Core;
  11. /**
  12. * Config file management class.
  13. * Stores its data in $_SESSION
  14. *
  15. * @package PhpMyAdmin
  16. */
  17. class ConfigFile
  18. {
  19. /**
  20. * Stores default PMA config from config.default.php
  21. * @var array
  22. */
  23. private $_defaultCfg;
  24. /**
  25. * Stores allowed values for non-standard fields
  26. * @var array
  27. */
  28. private $_cfgDb;
  29. /**
  30. * Stores original PMA config, not modified by user preferences
  31. * @var Config
  32. */
  33. private $_baseCfg;
  34. /**
  35. * Whether we are currently working in PMA Setup context
  36. * @var bool
  37. */
  38. private $_isInSetup;
  39. /**
  40. * Keys which will be always written to config file
  41. * @var array
  42. */
  43. private $_persistKeys = array();
  44. /**
  45. * Changes keys while updating config in {@link updateWithGlobalConfig()}
  46. * or reading by {@link getConfig()} or {@link getConfigArray()}
  47. * @var array
  48. */
  49. private $_cfgUpdateReadMapping = array();
  50. /**
  51. * Key filter for {@link set()}
  52. * @var array|null
  53. */
  54. private $_setFilter;
  55. /**
  56. * Instance id (key in $_SESSION array, separate for each server -
  57. * ConfigFile{server id})
  58. * @var string
  59. */
  60. private $_id;
  61. /**
  62. * Result for {@link _flattenArray()}
  63. * @var array|null
  64. */
  65. private $_flattenArrayResult;
  66. /**
  67. * Constructor
  68. *
  69. * @param array|null $base_config base configuration read from
  70. * {@link PhpMyAdmin\Config::$base_config},
  71. * use only when not in PMA Setup
  72. */
  73. public function __construct($base_config = null)
  74. {
  75. // load default config values
  76. $cfg = &$this->_defaultCfg;
  77. include './libraries/config.default.php';
  78. $cfg['fontsize'] = '82%';
  79. // load additional config information
  80. $cfg_db = &$this->_cfgDb;
  81. include './libraries/config.values.php';
  82. // apply default values overrides
  83. if (count($cfg_db['_overrides'])) {
  84. foreach ($cfg_db['_overrides'] as $path => $value) {
  85. Core::arrayWrite($path, $cfg, $value);
  86. }
  87. }
  88. $this->_baseCfg = $base_config;
  89. $this->_isInSetup = is_null($base_config);
  90. $this->_id = 'ConfigFile' . $GLOBALS['server'];
  91. if (!isset($_SESSION[$this->_id])) {
  92. $_SESSION[$this->_id] = array();
  93. }
  94. }
  95. /**
  96. * Sets names of config options which will be placed in config file even if
  97. * they are set to their default values (use only full paths)
  98. *
  99. * @param array $keys the names of the config options
  100. *
  101. * @return void
  102. */
  103. public function setPersistKeys(array $keys)
  104. {
  105. // checking key presence is much faster than searching so move values
  106. // to keys
  107. $this->_persistKeys = array_flip($keys);
  108. }
  109. /**
  110. * Returns flipped array set by {@link setPersistKeys()}
  111. *
  112. * @return array
  113. */
  114. public function getPersistKeysMap()
  115. {
  116. return $this->_persistKeys;
  117. }
  118. /**
  119. * By default ConfigFile allows setting of all configuration keys, use
  120. * this method to set up a filter on {@link set()} method
  121. *
  122. * @param array|null $keys array of allowed keys or null to remove filter
  123. *
  124. * @return void
  125. */
  126. public function setAllowedKeys($keys)
  127. {
  128. if ($keys === null) {
  129. $this->_setFilter = null;
  130. return;
  131. }
  132. // checking key presence is much faster than searching so move values
  133. // to keys
  134. $this->_setFilter = array_flip($keys);
  135. }
  136. /**
  137. * Sets path mapping for updating config in
  138. * {@link updateWithGlobalConfig()} or reading
  139. * by {@link getConfig()} or {@link getConfigArray()}
  140. *
  141. * @param array $mapping Contains the mapping of "Server/config options"
  142. * to "Server/1/config options"
  143. *
  144. * @return void
  145. */
  146. public function setCfgUpdateReadMapping(array $mapping)
  147. {
  148. $this->_cfgUpdateReadMapping = $mapping;
  149. }
  150. /**
  151. * Resets configuration data
  152. *
  153. * @return void
  154. */
  155. public function resetConfigData()
  156. {
  157. $_SESSION[$this->_id] = array();
  158. }
  159. /**
  160. * Sets configuration data (overrides old data)
  161. *
  162. * @param array $cfg Configuration options
  163. *
  164. * @return void
  165. */
  166. public function setConfigData(array $cfg)
  167. {
  168. $_SESSION[$this->_id] = $cfg;
  169. }
  170. /**
  171. * Sets config value
  172. *
  173. * @param string $path Path
  174. * @param mixed $value Value
  175. * @param string $canonical_path Canonical path
  176. *
  177. * @return void
  178. */
  179. public function set($path, $value, $canonical_path = null)
  180. {
  181. if ($canonical_path === null) {
  182. $canonical_path = $this->getCanonicalPath($path);
  183. }
  184. // apply key whitelist
  185. if ($this->_setFilter !== null
  186. && ! isset($this->_setFilter[$canonical_path])
  187. ) {
  188. return;
  189. }
  190. // if the path isn't protected it may be removed
  191. if (isset($this->_persistKeys[$canonical_path])) {
  192. Core::arrayWrite($path, $_SESSION[$this->_id], $value);
  193. return;
  194. }
  195. $default_value = $this->getDefault($canonical_path);
  196. $remove_path = $value === $default_value;
  197. if ($this->_isInSetup) {
  198. // remove if it has a default value or is empty
  199. $remove_path = $remove_path
  200. || (empty($value) && empty($default_value));
  201. } else {
  202. // get original config values not overwritten by user
  203. // preferences to allow for overwriting options set in
  204. // config.inc.php with default values
  205. $instance_default_value = Core::arrayRead(
  206. $canonical_path,
  207. $this->_baseCfg
  208. );
  209. // remove if it has a default value and base config (config.inc.php)
  210. // uses default value
  211. $remove_path = $remove_path
  212. && ($instance_default_value === $default_value);
  213. }
  214. if ($remove_path) {
  215. Core::arrayRemove($path, $_SESSION[$this->_id]);
  216. return;
  217. }
  218. Core::arrayWrite($path, $_SESSION[$this->_id], $value);
  219. }
  220. /**
  221. * Flattens multidimensional array, changes indices to paths
  222. * (eg. 'key/subkey').
  223. * Used as array_walk() callback.
  224. *
  225. * @param mixed $value Value
  226. * @param mixed $key Key
  227. * @param mixed $prefix Prefix
  228. *
  229. * @return void
  230. */
  231. private function _flattenArray($value, $key, $prefix)
  232. {
  233. // no recursion for numeric arrays
  234. if (is_array($value) && !isset($value[0])) {
  235. $prefix .= $key . '/';
  236. array_walk($value, array($this, '_flattenArray'), $prefix);
  237. } else {
  238. $this->_flattenArrayResult[$prefix . $key] = $value;
  239. }
  240. }
  241. /**
  242. * Returns default config in a flattened array
  243. *
  244. * @return array
  245. */
  246. public function getFlatDefaultConfig()
  247. {
  248. $this->_flattenArrayResult = array();
  249. array_walk($this->_defaultCfg, array($this, '_flattenArray'), '');
  250. $flat_cfg = $this->_flattenArrayResult;
  251. $this->_flattenArrayResult = null;
  252. return $flat_cfg;
  253. }
  254. /**
  255. * Updates config with values read from given array
  256. * (config will contain differences to defaults from config.defaults.php).
  257. *
  258. * @param array $cfg Configuration
  259. *
  260. * @return void
  261. */
  262. public function updateWithGlobalConfig(array $cfg)
  263. {
  264. // load config array and flatten it
  265. $this->_flattenArrayResult = array();
  266. array_walk($cfg, array($this, '_flattenArray'), '');
  267. $flat_cfg = $this->_flattenArrayResult;
  268. $this->_flattenArrayResult = null;
  269. // save values map for translating a few user preferences paths,
  270. // should be complemented by code reading from generated config
  271. // to perform inverse mapping
  272. foreach ($flat_cfg as $path => $value) {
  273. if (isset($this->_cfgUpdateReadMapping[$path])) {
  274. $path = $this->_cfgUpdateReadMapping[$path];
  275. }
  276. $this->set($path, $value, $path);
  277. }
  278. }
  279. /**
  280. * Returns config value or $default if it's not set
  281. *
  282. * @param string $path Path of config file
  283. * @param mixed $default Default values
  284. *
  285. * @return mixed
  286. */
  287. public function get($path, $default = null)
  288. {
  289. return Core::arrayRead($path, $_SESSION[$this->_id], $default);
  290. }
  291. /**
  292. * Returns default config value or $default it it's not set ie. it doesn't
  293. * exist in config.default.php ($cfg) and config.values.php
  294. * ($_cfg_db['_overrides'])
  295. *
  296. * @param string $canonical_path Canonical path
  297. * @param mixed $default Default value
  298. *
  299. * @return mixed
  300. */
  301. public function getDefault($canonical_path, $default = null)
  302. {
  303. return Core::arrayRead($canonical_path, $this->_defaultCfg, $default);
  304. }
  305. /**
  306. * Returns config value, if it's not set uses the default one; returns
  307. * $default if the path isn't set and doesn't contain a default value
  308. *
  309. * @param string $path Path
  310. * @param mixed $default Default value
  311. *
  312. * @return mixed
  313. */
  314. public function getValue($path, $default = null)
  315. {
  316. $v = Core::arrayRead($path, $_SESSION[$this->_id], null);
  317. if ($v !== null) {
  318. return $v;
  319. }
  320. $path = $this->getCanonicalPath($path);
  321. return $this->getDefault($path, $default);
  322. }
  323. /**
  324. * Returns canonical path
  325. *
  326. * @param string $path Path
  327. *
  328. * @return string
  329. */
  330. public function getCanonicalPath($path)
  331. {
  332. return preg_replace('#^Servers/([\d]+)/#', 'Servers/1/', $path);
  333. }
  334. /**
  335. * Returns config database entry for $path ($cfg_db in config_info.php)
  336. *
  337. * @param string $path path of the variable in config db
  338. * @param mixed $default default value
  339. *
  340. * @return mixed
  341. */
  342. public function getDbEntry($path, $default = null)
  343. {
  344. return Core::arrayRead($path, $this->_cfgDb, $default);
  345. }
  346. /**
  347. * Returns server count
  348. *
  349. * @return int
  350. */
  351. public function getServerCount()
  352. {
  353. return isset($_SESSION[$this->_id]['Servers'])
  354. ? count($_SESSION[$this->_id]['Servers'])
  355. : 0;
  356. }
  357. /**
  358. * Returns server list
  359. *
  360. * @return array|null
  361. */
  362. public function getServers()
  363. {
  364. return isset($_SESSION[$this->_id]['Servers'])
  365. ? $_SESSION[$this->_id]['Servers']
  366. : null;
  367. }
  368. /**
  369. * Returns DSN of given server
  370. *
  371. * @param integer $server server index
  372. *
  373. * @return string
  374. */
  375. public function getServerDSN($server)
  376. {
  377. if (!isset($_SESSION[$this->_id]['Servers'][$server])) {
  378. return '';
  379. }
  380. $path = 'Servers/' . $server;
  381. $dsn = 'mysqli://';
  382. if ($this->getValue("$path/auth_type") == 'config') {
  383. $dsn .= $this->getValue("$path/user");
  384. if (! empty($this->getValue("$path/password"))) {
  385. $dsn .= ':***';
  386. }
  387. $dsn .= '@';
  388. }
  389. if ($this->getValue("$path/host") != 'localhost') {
  390. $dsn .= $this->getValue("$path/host");
  391. $port = $this->getValue("$path/port");
  392. if ($port) {
  393. $dsn .= ':' . $port;
  394. }
  395. } else {
  396. $dsn .= $this->getValue("$path/socket");
  397. }
  398. return $dsn;
  399. }
  400. /**
  401. * Returns server name
  402. *
  403. * @param int $id server index
  404. *
  405. * @return string
  406. */
  407. public function getServerName($id)
  408. {
  409. if (!isset($_SESSION[$this->_id]['Servers'][$id])) {
  410. return '';
  411. }
  412. $verbose = $this->get("Servers/$id/verbose");
  413. if (!empty($verbose)) {
  414. return $verbose;
  415. }
  416. $host = $this->get("Servers/$id/host");
  417. return empty($host) ? 'localhost' : $host;
  418. }
  419. /**
  420. * Removes server
  421. *
  422. * @param int $server server index
  423. *
  424. * @return void
  425. */
  426. public function removeServer($server)
  427. {
  428. if (!isset($_SESSION[$this->_id]['Servers'][$server])) {
  429. return;
  430. }
  431. $last_server = $this->getServerCount();
  432. for ($i = $server; $i < $last_server; $i++) {
  433. $_SESSION[$this->_id]['Servers'][$i]
  434. = $_SESSION[$this->_id]['Servers'][$i + 1];
  435. }
  436. unset($_SESSION[$this->_id]['Servers'][$last_server]);
  437. if (isset($_SESSION[$this->_id]['ServerDefault'])
  438. && $_SESSION[$this->_id]['ServerDefault'] == $last_server
  439. ) {
  440. unset($_SESSION[$this->_id]['ServerDefault']);
  441. }
  442. }
  443. /**
  444. * Returns configuration array (full, multidimensional format)
  445. *
  446. * @return array
  447. */
  448. public function getConfig()
  449. {
  450. $c = $_SESSION[$this->_id];
  451. foreach ($this->_cfgUpdateReadMapping as $map_to => $map_from) {
  452. // if the key $c exists in $map_to
  453. if (Core::arrayRead($map_to, $c) !== null) {
  454. Core::arrayWrite($map_to, $c, Core::arrayRead($map_from, $c));
  455. Core::arrayRemove($map_from, $c);
  456. }
  457. }
  458. return $c;
  459. }
  460. /**
  461. * Returns configuration array (flat format)
  462. *
  463. * @return array
  464. */
  465. public function getConfigArray()
  466. {
  467. $this->_flattenArrayResult = array();
  468. array_walk($_SESSION[$this->_id], array($this, '_flattenArray'), '');
  469. $c = $this->_flattenArrayResult;
  470. $this->_flattenArrayResult = null;
  471. $persistKeys = array_diff(
  472. array_keys($this->_persistKeys),
  473. array_keys($c)
  474. );
  475. foreach ($persistKeys as $k) {
  476. $c[$k] = $this->getDefault($this->getCanonicalPath($k));
  477. }
  478. foreach ($this->_cfgUpdateReadMapping as $map_to => $map_from) {
  479. if (!isset($c[$map_from])) {
  480. continue;
  481. }
  482. $c[$map_to] = $c[$map_from];
  483. unset($c[$map_from]);
  484. }
  485. return $c;
  486. }
  487. }