InstanceConfigTrait.php 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Core;
  16. use Cake\Core\Exception\Exception;
  17. use Cake\Utility\Hash;
  18. /**
  19. * A trait for reading and writing instance config
  20. *
  21. * Implementing objects are expected to declare a `$_defaultConfig` property.
  22. */
  23. trait InstanceConfigTrait
  24. {
  25. /**
  26. * Runtime config
  27. *
  28. * @var array
  29. */
  30. protected $_config = [];
  31. /**
  32. * Whether the config property has already been configured with defaults
  33. *
  34. * @var bool
  35. */
  36. protected $_configInitialized = false;
  37. /**
  38. * Sets the config.
  39. *
  40. * ### Usage
  41. *
  42. * Setting a specific value:
  43. *
  44. * ```
  45. * $this->setConfig('key', $value);
  46. * ```
  47. *
  48. * Setting a nested value:
  49. *
  50. * ```
  51. * $this->setConfig('some.nested.key', $value);
  52. * ```
  53. *
  54. * Updating multiple config settings at the same time:
  55. *
  56. * ```
  57. * $this->setConfig(['one' => 'value', 'another' => 'value']);
  58. * ```
  59. *
  60. * @param string|array $key The key to set, or a complete array of configs.
  61. * @param mixed|null $value The value to set.
  62. * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
  63. * @return $this
  64. * @throws \Cake\Core\Exception\Exception When trying to set a key that is invalid.
  65. */
  66. public function setConfig($key, $value = null, $merge = true)
  67. {
  68. if (!$this->_configInitialized) {
  69. $this->_config = $this->_defaultConfig;
  70. $this->_configInitialized = true;
  71. }
  72. $this->_configWrite($key, $value, $merge);
  73. return $this;
  74. }
  75. /**
  76. * Returns the config.
  77. *
  78. * ### Usage
  79. *
  80. * Reading the whole config:
  81. *
  82. * ```
  83. * $this->getConfig();
  84. * ```
  85. *
  86. * Reading a specific value:
  87. *
  88. * ```
  89. * $this->getConfig('key');
  90. * ```
  91. *
  92. * Reading a nested value:
  93. *
  94. * ```
  95. * $this->getConfig('some.nested.key');
  96. * ```
  97. *
  98. * Reading with default value:
  99. *
  100. * ```
  101. * $this->getConfig('some-key', 'default-value');
  102. * ```
  103. *
  104. * @param string|null $key The key to get or null for the whole config.
  105. * @param mixed $default The return value when the key does not exist.
  106. * @return mixed Config value being read.
  107. */
  108. public function getConfig($key = null, $default = null)
  109. {
  110. if (!$this->_configInitialized) {
  111. $this->_config = $this->_defaultConfig;
  112. $this->_configInitialized = true;
  113. }
  114. $return = $this->_configRead($key);
  115. return $return === null ? $default : $return;
  116. }
  117. /**
  118. * Gets/Sets the config.
  119. *
  120. * ### Usage
  121. *
  122. * Reading the whole config:
  123. *
  124. * ```
  125. * $this->config();
  126. * ```
  127. *
  128. * Reading a specific value:
  129. *
  130. * ```
  131. * $this->config('key');
  132. * ```
  133. *
  134. * Reading a nested value:
  135. *
  136. * ```
  137. * $this->config('some.nested.key');
  138. * ```
  139. *
  140. * Setting a specific value:
  141. *
  142. * ```
  143. * $this->config('key', $value);
  144. * ```
  145. *
  146. * Setting a nested value:
  147. *
  148. * ```
  149. * $this->config('some.nested.key', $value);
  150. * ```
  151. *
  152. * Updating multiple config settings at the same time:
  153. *
  154. * ```
  155. * $this->config(['one' => 'value', 'another' => 'value']);
  156. * ```
  157. *
  158. * @deprecated 3.4.0 use setConfig()/getConfig() instead.
  159. * @param string|array|null $key The key to get/set, or a complete array of configs.
  160. * @param mixed|null $value The value to set.
  161. * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
  162. * @return mixed Config value being read, or the object itself on write operations.
  163. * @throws \Cake\Core\Exception\Exception When trying to set a key that is invalid.
  164. */
  165. public function config($key = null, $value = null, $merge = true)
  166. {
  167. if (is_array($key) || func_num_args() >= 2) {
  168. return $this->setConfig($key, $value, $merge);
  169. }
  170. return $this->getConfig($key);
  171. }
  172. /**
  173. * Merge provided config with existing config. Unlike `config()` which does
  174. * a recursive merge for nested keys, this method does a simple merge.
  175. *
  176. * Setting a specific value:
  177. *
  178. * ```
  179. * $this->configShallow('key', $value);
  180. * ```
  181. *
  182. * Setting a nested value:
  183. *
  184. * ```
  185. * $this->configShallow('some.nested.key', $value);
  186. * ```
  187. *
  188. * Updating multiple config settings at the same time:
  189. *
  190. * ```
  191. * $this->configShallow(['one' => 'value', 'another' => 'value']);
  192. * ```
  193. *
  194. * @param string|array $key The key to set, or a complete array of configs.
  195. * @param mixed|null $value The value to set.
  196. * @return $this
  197. */
  198. public function configShallow($key, $value = null)
  199. {
  200. if (!$this->_configInitialized) {
  201. $this->_config = $this->_defaultConfig;
  202. $this->_configInitialized = true;
  203. }
  204. $this->_configWrite($key, $value, 'shallow');
  205. return $this;
  206. }
  207. /**
  208. * Reads a config key.
  209. *
  210. * @param string|null $key Key to read.
  211. * @return mixed
  212. */
  213. protected function _configRead($key)
  214. {
  215. if ($key === null) {
  216. return $this->_config;
  217. }
  218. if (strpos($key, '.') === false) {
  219. return isset($this->_config[$key]) ? $this->_config[$key] : null;
  220. }
  221. $return = $this->_config;
  222. foreach (explode('.', $key) as $k) {
  223. if (!is_array($return) || !isset($return[$k])) {
  224. $return = null;
  225. break;
  226. }
  227. $return = $return[$k];
  228. }
  229. return $return;
  230. }
  231. /**
  232. * Writes a config key.
  233. *
  234. * @param string|array $key Key to write to.
  235. * @param mixed $value Value to write.
  236. * @param bool|string $merge True to merge recursively, 'shallow' for simple merge,
  237. * false to overwrite, defaults to false.
  238. * @return void
  239. * @throws \Cake\Core\Exception\Exception if attempting to clobber existing config
  240. */
  241. protected function _configWrite($key, $value, $merge = false)
  242. {
  243. if (is_string($key) && $value === null) {
  244. $this->_configDelete($key);
  245. return;
  246. }
  247. if ($merge) {
  248. $update = is_array($key) ? $key : [$key => $value];
  249. if ($merge === 'shallow') {
  250. $this->_config = array_merge($this->_config, Hash::expand($update));
  251. } else {
  252. $this->_config = Hash::merge($this->_config, Hash::expand($update));
  253. }
  254. return;
  255. }
  256. if (is_array($key)) {
  257. foreach ($key as $k => $val) {
  258. $this->_configWrite($k, $val);
  259. }
  260. return;
  261. }
  262. if (strpos($key, '.') === false) {
  263. $this->_config[$key] = $value;
  264. return;
  265. }
  266. $update =& $this->_config;
  267. $stack = explode('.', $key);
  268. foreach ($stack as $k) {
  269. if (!is_array($update)) {
  270. throw new Exception(sprintf('Cannot set %s value', $key));
  271. }
  272. if (!isset($update[$k])) {
  273. $update[$k] = [];
  274. }
  275. $update =& $update[$k];
  276. }
  277. $update = $value;
  278. }
  279. /**
  280. * Deletes a single config key.
  281. *
  282. * @param string $key Key to delete.
  283. * @return void
  284. * @throws \Cake\Core\Exception\Exception if attempting to clobber existing config
  285. */
  286. protected function _configDelete($key)
  287. {
  288. if (strpos($key, '.') === false) {
  289. unset($this->_config[$key]);
  290. return;
  291. }
  292. $update =& $this->_config;
  293. $stack = explode('.', $key);
  294. $length = count($stack);
  295. foreach ($stack as $i => $k) {
  296. if (!is_array($update)) {
  297. throw new Exception(sprintf('Cannot unset %s value', $key));
  298. }
  299. if (!isset($update[$k])) {
  300. break;
  301. }
  302. if ($i === $length - 1) {
  303. unset($update[$k]);
  304. break;
  305. }
  306. $update =& $update[$k];
  307. }
  308. }
  309. }