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