InstanceConfigTrait.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  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. deprecationWarning(
  168. get_called_class() . '::config() is deprecated. ' .
  169. 'Use setConfig()/getConfig() instead.'
  170. );
  171. if (is_array($key) || func_num_args() >= 2) {
  172. return $this->setConfig($key, $value, $merge);
  173. }
  174. return $this->getConfig($key);
  175. }
  176. /**
  177. * Merge provided config with existing config. Unlike `config()` which does
  178. * a recursive merge for nested keys, this method does a simple merge.
  179. *
  180. * Setting a specific value:
  181. *
  182. * ```
  183. * $this->configShallow('key', $value);
  184. * ```
  185. *
  186. * Setting a nested value:
  187. *
  188. * ```
  189. * $this->configShallow('some.nested.key', $value);
  190. * ```
  191. *
  192. * Updating multiple config settings at the same time:
  193. *
  194. * ```
  195. * $this->configShallow(['one' => 'value', 'another' => 'value']);
  196. * ```
  197. *
  198. * @param string|array $key The key to set, or a complete array of configs.
  199. * @param mixed|null $value The value to set.
  200. * @return $this
  201. */
  202. public function configShallow($key, $value = null)
  203. {
  204. if (!$this->_configInitialized) {
  205. $this->_config = $this->_defaultConfig;
  206. $this->_configInitialized = true;
  207. }
  208. $this->_configWrite($key, $value, 'shallow');
  209. return $this;
  210. }
  211. /**
  212. * Reads a config key.
  213. *
  214. * @param string|null $key Key to read.
  215. * @return mixed
  216. */
  217. protected function _configRead($key)
  218. {
  219. if ($key === null) {
  220. return $this->_config;
  221. }
  222. if (strpos($key, '.') === false) {
  223. return isset($this->_config[$key]) ? $this->_config[$key] : null;
  224. }
  225. $return = $this->_config;
  226. foreach (explode('.', $key) as $k) {
  227. if (!is_array($return) || !isset($return[$k])) {
  228. $return = null;
  229. break;
  230. }
  231. $return = $return[$k];
  232. }
  233. return $return;
  234. }
  235. /**
  236. * Writes a config key.
  237. *
  238. * @param string|array $key Key to write to.
  239. * @param mixed $value Value to write.
  240. * @param bool|string $merge True to merge recursively, 'shallow' for simple merge,
  241. * false to overwrite, defaults to false.
  242. * @return void
  243. * @throws \Cake\Core\Exception\Exception if attempting to clobber existing config
  244. */
  245. protected function _configWrite($key, $value, $merge = false)
  246. {
  247. if (is_string($key) && $value === null) {
  248. $this->_configDelete($key);
  249. return;
  250. }
  251. if ($merge) {
  252. $update = is_array($key) ? $key : [$key => $value];
  253. if ($merge === 'shallow') {
  254. $this->_config = array_merge($this->_config, Hash::expand($update));
  255. } else {
  256. $this->_config = Hash::merge($this->_config, Hash::expand($update));
  257. }
  258. return;
  259. }
  260. if (is_array($key)) {
  261. foreach ($key as $k => $val) {
  262. $this->_configWrite($k, $val);
  263. }
  264. return;
  265. }
  266. if (strpos($key, '.') === false) {
  267. $this->_config[$key] = $value;
  268. return;
  269. }
  270. $update =& $this->_config;
  271. $stack = explode('.', $key);
  272. foreach ($stack as $k) {
  273. if (!is_array($update)) {
  274. throw new Exception(sprintf('Cannot set %s value', $key));
  275. }
  276. if (!isset($update[$k])) {
  277. $update[$k] = [];
  278. }
  279. $update =& $update[$k];
  280. }
  281. $update = $value;
  282. }
  283. /**
  284. * Deletes a single config key.
  285. *
  286. * @param string $key Key to delete.
  287. * @return void
  288. * @throws \Cake\Core\Exception\Exception if attempting to clobber existing config
  289. */
  290. protected function _configDelete($key)
  291. {
  292. if (strpos($key, '.') === false) {
  293. unset($this->_config[$key]);
  294. return;
  295. }
  296. $update =& $this->_config;
  297. $stack = explode('.', $key);
  298. $length = count($stack);
  299. foreach ($stack as $i => $k) {
  300. if (!is_array($update)) {
  301. throw new Exception(sprintf('Cannot unset %s value', $key));
  302. }
  303. if (!isset($update[$k])) {
  304. break;
  305. }
  306. if ($i === $length - 1) {
  307. unset($update[$k]);
  308. break;
  309. }
  310. $update =& $update[$k];
  311. }
  312. }
  313. }