InstanceConfigTrait.php 9.6 KB

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