Merge pull request #21644 from sunilpawar/processed_token
[civicrm-core.git] / CRM / Core / Config / MagicMerge.php
CommitLineData
c0a1f187
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
c0a1f187 5 | |
bc77d7c0
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
c0a1f187
TO
9 +--------------------------------------------------------------------+
10 */
11
12/**
13 * Class CRM_Core_Config_MagicMerge
14 *
15 * Originally, the $config object was based on a single, serialized
16 * data object stored in the database. As the needs for settings
9e1e6020
TO
17 * grew (with robust metadata, system overrides, extension support,
18 * and multi-tenancy), the $config started to store a mix of:
c0a1f187
TO
19 * (a) canonical config options,
20 * (b) dynamically generated runtime data,
21 * (c) cached data derived from other sources (esp civicrm_setting)
22 * (d) instances of service objects
23 *
24 * The config object is now deprecated. Settings and service objects
25 * should generally be accessed via Civi::settings() and Civi::service().
26 *
27 * MagicMerge provides backward compatibility. You may still access
28 * old properties via $config, but they will be loaded from their
29 * new services.
30 */
31class CRM_Core_Config_MagicMerge {
32
33 /**
34 * Map old config properties to their contemporary counterparts.
35 *
36 * @var array
37 * Array(string $configAlias => Array(string $realType, string $realName)).
38 */
39 private $map;
40
518fa0ee
SL
41 private $locals;
42 private $settings;
c0a1f187 43
be2fb01f 44 private $cache = [];
7dba62da 45
8246bca4 46 /**
47 * CRM_Core_Config_MagicMerge constructor.
48 */
c0a1f187
TO
49 public function __construct() {
50 $this->map = self::getPropertyMap();
51 }
52
8246bca4 53 /**
54 * Set the map to the property map.
55 */
c0a1f187
TO
56 public function __wakeup() {
57 $this->map = self::getPropertyMap();
58 }
59
60 /**
f806379b
TO
61 * Get a list of $config properties and the entities to which they map.
62 *
63 * This is used for two purposes:
64 *
65 * 1. Runtime: Provide backward-compatible interface for reading these
66 * properties.
67 * 2. Upgrade: Migrate old properties of config_backend into settings.
68 *
c0a1f187
TO
69 * @return array
70 */
71 public static function getPropertyMap() {
e3d28c74
TO
72 // Each mapping: $propertyName => Array(0 => $type, 1 => $foreignName|NULL, ...).
73 // If $foreignName is omitted/null, then it's assumed to match the $propertyName.
74 // Other parameters may be specified, depending on the type.
be2fb01f 75 return [
9e1e6020 76 // "local" properties are unique to each instance of CRM_Core_Config (each request).
be2fb01f
CW
77 'doNotResetCache' => ['local'],
78 'inCiviCRM' => ['local'],
79 'keyDisable' => ['local'],
80 'userFrameworkFrontend' => ['local'],
81 'userPermissionTemp' => ['local'],
9e1e6020
TO
82
83 // "runtime" properties are computed from define()s, $_ENV, etc.
84 // See also: CRM_Core_Config_Runtime.
be2fb01f
CW
85 'dsn' => ['runtime'],
86 'initialized' => ['runtime'],
87 'userFramework' => ['runtime'],
88 'userFrameworkClass' => ['runtime'],
89 'userFrameworkDSN' => ['runtime'],
90 'userFrameworkURLVar' => ['runtime'],
91 'userHookClass' => ['runtime'],
92 'cleanURL' => ['runtime'],
be2fb01f 93 'templateDir' => ['runtime'],
9e1e6020 94
d4330c62 95 // "boot-svc" properties are critical services needed during init.
7f835399 96 // See also: Civi\Core\Container::getBootService().
be2fb01f
CW
97 'userSystem' => ['boot-svc'],
98 'userPermissionClass' => ['boot-svc'],
d4330c62 99
be2fb01f
CW
100 'userFrameworkBaseURL' => ['user-system', 'getAbsoluteBaseURL'],
101 'userFrameworkVersion' => ['user-system', 'getVersion'],
518fa0ee
SL
102 // ugh typo.
103 'useFrameworkRelativeBase' => ['user-system', 'getRelativeBaseURL'],
d4330c62 104
9e1e6020
TO
105 // "setting" properties are loaded through the setting layer, esp
106 // table "civicrm_setting" and global $civicrm_setting.
107 // See also: Civi::settings().
be2fb01f
CW
108 'backtrace' => ['setting'],
109 'contact_default_language' => ['setting'],
110 'countryLimit' => ['setting'],
111 'customTranslateFunction' => ['setting'],
112 'dateInputFormat' => ['setting'],
113 'dateformatDatetime' => ['setting'],
114 'dateformatFull' => ['setting'],
115 'dateformatPartial' => ['setting'],
116 'dateformatTime' => ['setting'],
117 'dateformatYear' => ['setting'],
118 'dateformatFinancialBatch' => ['setting'],
119 'dateformatshortdate' => ['setting'],
518fa0ee
SL
120 // renamed.
121 'debug' => ['setting', 'debug_enabled'],
be2fb01f 122 'defaultContactCountry' => ['setting'],
7b11bd27 123 'pinnedContactCountries' => ['setting'],
be2fb01f
CW
124 'defaultContactStateProvince' => ['setting'],
125 'defaultCurrency' => ['setting'],
126 'defaultSearchProfileID' => ['setting'],
127 'doNotAttachPDFReceipt' => ['setting'],
128 'empoweredBy' => ['setting'],
518fa0ee
SL
129 // renamed.
130 'enableComponents' => ['setting', 'enable_components'],
be2fb01f
CW
131 'enableSSL' => ['setting'],
132 'fatalErrorHandler' => ['setting'],
133 'fieldSeparator' => ['setting'],
134 'fiscalYearStart' => ['setting'],
135 'geoAPIKey' => ['setting'],
136 'geoProvider' => ['setting'],
137 'includeAlphabeticalPager' => ['setting'],
138 'includeEmailInName' => ['setting'],
139 'includeNickNameInName' => ['setting'],
140 'includeOrderByClause' => ['setting'],
141 'includeWildCardInName' => ['setting'],
142 'inheritLocale' => ['setting'],
143 'languageLimit' => ['setting'],
144 'lcMessages' => ['setting'],
145 'legacyEncoding' => ['setting'],
146 'logging' => ['setting'],
147 'mailThrottleTime' => ['setting'],
148 'mailerBatchLimit' => ['setting'],
149 'mailerJobSize' => ['setting'],
150 'mailerJobsMax' => ['setting'],
151 'mapAPIKey' => ['setting'],
152 'mapProvider' => ['setting'],
153 'maxFileSize' => ['setting'],
518fa0ee
SL
154 // renamed.
155 'maxAttachments' => ['setting', 'max_attachments'],
8913e915 156 'maxAttachmentsBackend' => ['setting', 'max_attachments_backend'],
be2fb01f
CW
157 'monetaryDecimalPoint' => ['setting'],
158 'monetaryThousandSeparator' => ['setting'],
159 'moneyformat' => ['setting'],
160 'moneyvalueformat' => ['setting'],
161 'provinceLimit' => ['setting'],
162 'recaptchaOptions' => ['setting'],
163 'recaptchaPublicKey' => ['setting'],
164 'recaptchaPrivateKey' => ['setting'],
165 'forceRecaptcha' => ['setting'],
166 'replyTo' => ['setting'],
167 'secondDegRelPermissions' => ['setting'],
168 'smartGroupCacheTimeout' => ['setting'],
169 'timeInputFormat' => ['setting'],
170 'userFrameworkLogging' => ['setting'],
171 'userFrameworkUsersTableName' => ['setting'],
172 'verpSeparator' => ['setting'],
173 'wkhtmltopdfPath' => ['setting'],
be2fb01f 174 'wpLoadPhp' => ['setting'],
e3d28c74 175
0bd3f65c
TO
176 // "path" properties are managed via Civi::paths and $civicrm_paths
177 // Option: `mkdir` - auto-create dir
178 // Option: `restrict` - auto-restrict remote access
179 'configAndLogDir' => ['path', 'civicrm.log', ['mkdir', 'restrict']],
180 'templateCompileDir' => ['path', 'civicrm.compile', ['mkdir', 'restrict']],
181
9e1e6020
TO
182 // "setting-path" properties are settings with special filtering
183 // to return normalized file paths.
cdbea577
C
184 // Option: `mkdir` - auto-create dir
185 // Option: `restrict` - auto-restrict remote access
be2fb01f
CW
186 'customFileUploadDir' => ['setting-path', NULL, ['mkdir', 'restrict']],
187 'customPHPPathDir' => ['setting-path'],
188 'customTemplateDir' => ['setting-path'],
189 'extensionsDir' => ['setting-path', NULL, ['mkdir']],
190 'imageUploadDir' => ['setting-path', NULL, ['mkdir']],
191 'uploadDir' => ['setting-path', NULL, ['mkdir', 'restrict']],
e3d28c74 192
cdbea577
C
193 // "setting-url" properties are settings with special filtering
194 // to return normalized URLs.
195 // Option: `noslash` - don't append trailing slash
196 // Option: `rel` - convert to relative URL (if possible)
be2fb01f
CW
197 'customCSSURL' => ['setting-url', NULL, ['noslash']],
198 'extensionsURL' => ['setting-url'],
199 'imageUploadURL' => ['setting-url'],
200 'resourceBase' => ['setting-url', 'userFrameworkResourceURL', ['rel']],
201 'userFrameworkResourceURL' => ['setting-url'],
c0a1f187 202
9e1e6020 203 // "callback" properties are generated on-demand by calling a function.
0d1823d7 204 // @todo remove geocodeMethod. As of Feb 2018, $config->geocodeMethod works but gives a deprecation warning.
be2fb01f
CW
205 'geocodeMethod' => ['callback', 'CRM_Utils_Geocode', 'getProviderClass'],
206 'defaultCurrencySymbol' => ['callback', 'CRM_Core_BAO_Country', 'getDefaultCurrencySymbol'],
11eed110 207 'wpBasePage' => ['callback', 'CRM_Utils_System_WordPress', 'getBasePage'],
be2fb01f 208 ];
c0a1f187
TO
209 }
210
8246bca4 211 /**
212 * Get value.
213 *
214 * @param string $k
215 *
216 * @return mixed
217 * @throws \CRM_Core_Exception
218 */
c0a1f187
TO
219 public function __get($k) {
220 if (!isset($this->map[$k])) {
221 throw new \CRM_Core_Exception("Cannot read unrecognized property CRM_Core_Config::\${$k}.");
222 }
fd78a9e5 223 if (isset($this->cache[$k])) {
7dba62da
TO
224 return $this->cache[$k];
225 }
e3d28c74
TO
226
227 $type = $this->map[$k][0];
2e1f50d6 228 $name = $this->map[$k][1] ?? $k;
c0a1f187
TO
229
230 switch ($type) {
231 case 'setting':
23bb9c85 232 return $this->getSettings()->get($name);
c0a1f187 233
00f6fb2a
TO
234 // The interpretation of 'path' and 'setting-path' is similar, except
235 // that the latter originates in a stored setting.
236 case 'path':
c0a1f187 237 case 'setting-path':
ac47f7ca 238 // Array(0 => $type, 1 => $setting, 2 => $actions).
00f6fb2a
TO
239 $value = ($type === 'path')
240 ? Civi::paths()->getVariable($name, 'path')
241 : Civi::paths()->getPath($this->getSettings()->get($name));
e3d28c74
TO
242 if ($value) {
243 $value = CRM_Utils_File::addTrailingSlash($value);
ac47f7ca 244 if (isset($this->map[$k][2]) && in_array('mkdir', $this->map[$k][2])) {
f7cc6e55 245 if (!is_dir($value) && !CRM_Utils_File::createDir($value, FALSE)) {
be2fb01f 246 CRM_Core_Session::setStatus(ts('Failed to make directory (%1) at "%2". Please update the settings or file permissions.', [
895c19e7
TO
247 1 => $k,
248 2 => $value,
be2fb01f 249 ]));
895c19e7 250 }
e3d28c74 251 }
ac47f7ca 252 if (isset($this->map[$k][2]) && in_array('restrict', $this->map[$k][2])) {
e3d28c74
TO
253 CRM_Utils_File::restrictAccess($value);
254 }
255 }
7dba62da 256 $this->cache[$k] = $value;
e3d28c74
TO
257 return $value;
258
cdbea577 259 case 'setting-url':
be2fb01f 260 $options = !empty($this->map[$k][2]) ? $this->map[$k][2] : [];
6ec7e639 261 $value = $this->getSettings()->get($name);
cdbea577
C
262 if ($value && !(in_array('noslash', $options))) {
263 $value = CRM_Utils_File::addTrailingSlash($value, '/');
6ec7e639 264 }
cdbea577
C
265 $this->cache[$k] = Civi::paths()->getUrl($value,
266 in_array('rel', $options) ? 'relative' : 'absolute');
7dba62da 267 return $this->cache[$k];
c0a1f187
TO
268
269 case 'runtime':
7f835399 270 return \Civi\Core\Container::getBootService('runtime')->{$name};
c0a1f187 271
d4330c62
TO
272 case 'boot-svc':
273 $this->cache[$k] = \Civi\Core\Container::getBootService($name);
274 return $this->cache[$k];
275
c0a1f187
TO
276 case 'local':
277 $this->initLocals();
278 return $this->locals[$name];
279
d4330c62
TO
280 case 'user-system':
281 $userSystem = \Civi\Core\Container::getBootService('userSystem');
be2fb01f 282 $this->cache[$k] = call_user_func([$userSystem, $name]);
d4330c62
TO
283 return $this->cache[$k];
284
c0a1f187
TO
285 case 'service':
286 return \Civi::service($name);
287
288 case 'callback':
289 // Array(0 => $type, 1 => $obj, 2 => $getter, 3 => $setter, 4 => $unsetter).
290 if (!isset($this->map[$k][1], $this->map[$k][2])) {
291 throw new \CRM_Core_Exception("Cannot find getter for property CRM_Core_Config::\${$k}");
292 }
be2fb01f 293 return \Civi\Core\Resolver::singleton()->call([$this->map[$k][1], $this->map[$k][2]], [$k]);
c0a1f187
TO
294
295 default:
296 throw new \CRM_Core_Exception("Cannot read property CRM_Core_Config::\${$k} ($type)");
297 }
298 }
299
8246bca4 300 /**
301 * Set value.
302 *
303 * @param string $k
304 * @param mixed $v
305 *
306 * @throws \CRM_Core_Exception
307 */
c0a1f187
TO
308 public function __set($k, $v) {
309 if (!isset($this->map[$k])) {
310 throw new \CRM_Core_Exception("Cannot set unrecognized property CRM_Core_Config::\${$k}");
311 }
7dba62da 312 unset($this->cache[$k]);
dc640254 313 $type = $this->map[$k][0];
82eff07b 314
c0a1f187
TO
315 switch ($type) {
316 case 'setting':
dc640254 317 case 'setting-path':
cdbea577 318 case 'setting-url':
00f6fb2a 319 case 'path':
d4330c62 320 case 'user-system':
c0a1f187 321 case 'runtime':
dc640254 322 case 'callback':
6504e96d 323 case 'boot-svc':
dc640254 324 // In the past, changes to $config were not persisted automatically.
cdf3884e 325 $this->cache[$k] = $v;
c0a1f187
TO
326 return;
327
328 case 'local':
329 $this->initLocals();
cdf3884e 330 $this->locals[$k] = $v;
c0a1f187
TO
331 return;
332
c0a1f187
TO
333 default:
334 throw new \CRM_Core_Exception("Cannot set property CRM_Core_Config::\${$k} ($type)");
335 }
336 }
337
8246bca4 338 /**
339 * Is value set.
340 *
341 * @param string $k
342 *
343 * @return bool
344 */
c0a1f187
TO
345 public function __isset($k) {
346 return isset($this->map[$k]);
347 }
348
8246bca4 349 /**
350 * Unset value.
351 *
352 * @param string $k
353 *
354 * @throws \CRM_Core_Exception
355 */
c0a1f187
TO
356 public function __unset($k) {
357 if (!isset($this->map[$k])) {
358 throw new \CRM_Core_Exception("Cannot unset unrecognized property CRM_Core_Config::\${$k}");
359 }
7dba62da 360 unset($this->cache[$k]);
dc640254 361 $type = $this->map[$k][0];
2e1f50d6 362 $name = $this->map[$k][1] ?? $k;
c0a1f187
TO
363
364 switch ($type) {
365 case 'setting':
366 case 'setting-path':
cdbea577 367 case 'setting-url':
23bb9c85 368 $this->getSettings()->revert($k);
c0a1f187
TO
369 return;
370
371 case 'local':
372 $this->initLocals();
373 $this->locals[$name] = NULL;
374 return;
375
376 case 'callback':
377 // Array(0 => $type, 1 => $obj, 2 => $getter, 3 => $setter, 4 => $unsetter).
378 if (!isset($this->map[$k][1], $this->map[$k][4])) {
379 throw new \CRM_Core_Exception("Cannot find unsetter for property CRM_Core_Config::\${$k}");
380 }
be2fb01f 381 \Civi\Core\Resolver::singleton()->call([$this->map[$k][1], $this->map[$k][4]], [$k]);
c0a1f187
TO
382 return;
383
384 default:
385 throw new \CRM_Core_Exception("Cannot unset property CRM_Core_Config::\${$k} ($type)");
386 }
387 }
388
23bb9c85
TO
389 /**
390 * @return \Civi\Core\SettingsBag
391 */
392 protected function getSettings() {
393 if ($this->settings === NULL) {
394 $this->settings = Civi::settings();
395 }
396 return $this->settings;
397 }
398
8246bca4 399 /**
400 * Initialise local settings.
401 */
c0a1f187
TO
402 private function initLocals() {
403 if ($this->locals === NULL) {
be2fb01f 404 $this->locals = [
c0a1f187
TO
405 'inCiviCRM' => FALSE,
406 'doNotResetCache' => 0,
5f900b59 407 'keyDisable' => FALSE,
c0a1f187
TO
408 'initialized' => FALSE,
409 'userFrameworkFrontend' => FALSE,
d4330c62 410 'userPermissionTemp' => NULL,
be2fb01f 411 ];
c0a1f187
TO
412 }
413 }
414
415}