vendor/contao/core-bundle/src/Resources/contao/library/Contao/Config.php line 158

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of Contao.
  4. *
  5. * (c) Leo Feyer
  6. *
  7. * @license LGPL-3.0-or-later
  8. */
  9. namespace Contao;
  10. use Symfony\Component\Filesystem\Filesystem;
  11. use Symfony\Component\Filesystem\Path;
  12. /**
  13. * Loads and writes the local configuration file
  14. *
  15. * Custom settings above or below the `### INSTALL SCRIPT ###` markers will be
  16. * preserved.
  17. */
  18. class Config
  19. {
  20. /**
  21. * Object instance (Singleton)
  22. * @var Config
  23. */
  24. protected static $objInstance;
  25. /**
  26. * Files object
  27. * @var Files
  28. */
  29. protected $Files;
  30. /**
  31. * Top content
  32. * @var string
  33. */
  34. protected $strTop = '';
  35. /**
  36. * Bottom content
  37. * @var string
  38. */
  39. protected $strBottom = '';
  40. /**
  41. * Modification indicator
  42. * @var boolean
  43. */
  44. protected $blnIsModified = false;
  45. /**
  46. * Local file existence
  47. * @var boolean
  48. */
  49. protected static $blnHasLcf;
  50. /**
  51. * Data
  52. * @var array
  53. */
  54. protected $arrData = array();
  55. /**
  56. * Cache
  57. * @var array
  58. */
  59. protected $arrCache = array();
  60. /**
  61. * Root dir
  62. * @var string
  63. */
  64. protected $strRootDir;
  65. private static $arrDeprecatedMap = array
  66. (
  67. 'dbHost' => 'database_host',
  68. 'dbPort' => 'database_port',
  69. 'dbUser' => 'database_user',
  70. 'dbPass' => 'database_password',
  71. 'dbDatabase' => 'database_name',
  72. 'smtpHost' => 'mailer_host',
  73. 'smtpUser' => 'mailer_user',
  74. 'smtpPass' => 'mailer_password',
  75. 'smtpPort' => 'mailer_port',
  76. 'smtpEnc' => 'mailer_encryption',
  77. 'addLanguageToUrl' => 'contao.prepend_locale',
  78. 'urlSuffix' => 'contao.url_suffix',
  79. 'uploadPath' => 'contao.upload_path',
  80. 'editableFiles' => 'contao.editable_files',
  81. 'debugMode' => 'kernel.debug',
  82. 'characterSet' => 'kernel.charset',
  83. 'enableSearch' => 'contao.search.default_indexer.enable',
  84. 'indexProtected' => 'contao.search.index_protected',
  85. );
  86. private static $arrDeprecated = array
  87. (
  88. 'validImageTypes' => 'contao.image.valid_extensions',
  89. 'jpgQuality' => 'contao.image.imagine_options[jpeg_quality]',
  90. );
  91. private static $arrToBeRemoved = array
  92. (
  93. 'os' => true,
  94. 'browser' => true,
  95. 'dbCharset' => true,
  96. 'dbCollation' => true,
  97. 'disableRefererCheck' => true,
  98. 'requestTokenWhitelist' => true,
  99. 'encryptionMode' => true,
  100. 'encryptionCipher' => true,
  101. 'sessionTimeout' => true,
  102. 'disableInsertTags' => true,
  103. 'rootFiles' => true,
  104. 'exampleWebsite' => true,
  105. 'coreOnlyMode' => true,
  106. 'privacyAnonymizeIp' => true,
  107. 'privacyAnonymizeGA' => true,
  108. 'bypassCache' => true,
  109. 'sslProxyDomain' => true,
  110. );
  111. /**
  112. * Prevent direct instantiation (Singleton)
  113. */
  114. protected function __construct()
  115. {
  116. $this->strRootDir = System::getContainer()->getParameter('kernel.project_dir');
  117. }
  118. /**
  119. * Automatically save the local configuration
  120. */
  121. public function __destruct()
  122. {
  123. if ($this->blnIsModified)
  124. {
  125. $this->save();
  126. }
  127. }
  128. /**
  129. * Prevent cloning of the object (Singleton)
  130. */
  131. final public function __clone()
  132. {
  133. }
  134. /**
  135. * Return the current object instance (Singleton)
  136. *
  137. * @return static The object instance
  138. */
  139. public static function getInstance()
  140. {
  141. if (static::$objInstance === null)
  142. {
  143. static::$objInstance = new static();
  144. static::$objInstance->initialize();
  145. }
  146. return static::$objInstance;
  147. }
  148. /**
  149. * Load all configuration files
  150. */
  151. protected function initialize()
  152. {
  153. if (static::$blnHasLcf === null)
  154. {
  155. static::preload();
  156. }
  157. $strCacheDir = System::getContainer()->getParameter('kernel.cache_dir');
  158. if (file_exists($strCacheDir . '/contao/config/config.php'))
  159. {
  160. include $strCacheDir . '/contao/config/config.php';
  161. }
  162. else
  163. {
  164. try
  165. {
  166. $files = System::getContainer()->get('contao.resource_locator')->locate('config/config.php', null, false);
  167. }
  168. catch (\InvalidArgumentException $e)
  169. {
  170. $files = array();
  171. }
  172. foreach ($files as $file)
  173. {
  174. include $file;
  175. }
  176. }
  177. // Include the local configuration file again
  178. if (static::$blnHasLcf)
  179. {
  180. include $this->strRootDir . '/system/config/localconfig.php';
  181. }
  182. static::loadParameters();
  183. }
  184. /**
  185. * Mark the object as modified
  186. */
  187. protected function markModified()
  188. {
  189. // Return if marked as modified already
  190. if ($this->blnIsModified === true)
  191. {
  192. return;
  193. }
  194. $this->blnIsModified = true;
  195. // Reset the top and bottom content (see #344)
  196. $this->strTop = '';
  197. $this->strBottom = '';
  198. // Import the Files object (required in the destructor)
  199. $this->Files = Files::getInstance();
  200. // Parse the local configuration file
  201. if (static::$blnHasLcf)
  202. {
  203. $strMode = 'top';
  204. $resFile = fopen($this->strRootDir . '/system/config/localconfig.php', 'r');
  205. while (!feof($resFile))
  206. {
  207. $strLine = fgets($resFile);
  208. $strTrim = trim($strLine);
  209. if ($strTrim == '?>')
  210. {
  211. continue;
  212. }
  213. if ($strTrim == '### INSTALL SCRIPT START ###')
  214. {
  215. $strMode = 'data';
  216. continue;
  217. }
  218. if ($strTrim == '### INSTALL SCRIPT STOP ###')
  219. {
  220. $strMode = 'bottom';
  221. continue;
  222. }
  223. if ($strMode == 'top')
  224. {
  225. $this->strTop .= $strLine;
  226. }
  227. elseif ($strMode == 'bottom')
  228. {
  229. $this->strBottom .= $strLine;
  230. }
  231. elseif ($strTrim)
  232. {
  233. $arrChunks = array_map('trim', explode('=', $strLine, 2));
  234. $this->arrData[$arrChunks[0]] = $arrChunks[1];
  235. }
  236. }
  237. fclose($resFile);
  238. }
  239. }
  240. /**
  241. * Save the local configuration file
  242. */
  243. public function save()
  244. {
  245. if (!$this->strTop)
  246. {
  247. $this->strTop = '<?php';
  248. }
  249. $strFile = trim($this->strTop) . "\n\n";
  250. $strFile .= "### INSTALL SCRIPT START ###\n";
  251. foreach ($this->arrData as $k=>$v)
  252. {
  253. $strFile .= "$k = $v\n";
  254. }
  255. $strFile .= "### INSTALL SCRIPT STOP ###\n";
  256. $this->strBottom = trim($this->strBottom);
  257. if ($this->strBottom)
  258. {
  259. $strFile .= "\n" . $this->strBottom . "\n";
  260. }
  261. $strTemp = Path::join($this->strRootDir, 'system/tmp', md5(uniqid(mt_rand(), true)));
  262. // Write to a temp file first
  263. $objFile = fopen($strTemp, 'w');
  264. fwrite($objFile, $strFile);
  265. fclose($objFile);
  266. // Make sure the file has been written (see #4483)
  267. if (!filesize($strTemp))
  268. {
  269. System::getContainer()->get('monolog.logger.contao.error')->error('The local configuration file could not be written. Have you reached your quota limit?');
  270. return;
  271. }
  272. $fs = new Filesystem();
  273. // Adjust the file permissions (see #8178)
  274. $fs->chmod($strTemp, 0666 & ~umask());
  275. $strDestination = Path::join($this->strRootDir, 'system/config/localconfig.php');
  276. // Get the realpath in case it is a symlink (see #2209)
  277. if ($realpath = realpath($strDestination))
  278. {
  279. $strDestination = $realpath;
  280. }
  281. // Then move the file to its final destination
  282. $fs->rename($strTemp, $strDestination, true);
  283. // Reset the Zend OPcache
  284. if (\function_exists('opcache_invalidate'))
  285. {
  286. opcache_invalidate($strDestination, true);
  287. }
  288. // Recompile the APC file (thanks to Trenker)
  289. if (\function_exists('apc_compile_file') && !\ini_get('apc.stat'))
  290. {
  291. apc_compile_file($strDestination);
  292. }
  293. $this->blnIsModified = false;
  294. }
  295. /**
  296. * Return true if the installation is complete
  297. *
  298. * @return boolean True if the installation is complete
  299. */
  300. public static function isComplete()
  301. {
  302. return static::$blnHasLcf !== null && static::has('licenseAccepted');
  303. }
  304. /**
  305. * Return all active modules as array
  306. *
  307. * @return array An array of active modules
  308. *
  309. * @deprecated Deprecated since Contao 4.0, to be removed in Contao 5.0.
  310. * Use the container parameter "kernel.bundles" instead.
  311. */
  312. public function getActiveModules()
  313. {
  314. trigger_deprecation('contao/core-bundle', '4.0', 'Using "Contao\Config::getActiveModules()" has been deprecated and will no longer work in Contao 5.0. Use "kernel.bundles" instead.');
  315. return ModuleLoader::getActive();
  316. }
  317. /**
  318. * Add a configuration variable to the local configuration file
  319. *
  320. * @param string $strKey The full variable name
  321. * @param mixed $varValue The configuration value
  322. */
  323. public function add($strKey, $varValue)
  324. {
  325. $this->markModified();
  326. $this->arrData[$strKey] = $this->escape($varValue) . ';';
  327. }
  328. /**
  329. * Alias for Config::add()
  330. *
  331. * @param string $strKey The full variable name
  332. * @param mixed $varValue The configuration value
  333. */
  334. public function update($strKey, $varValue)
  335. {
  336. $this->add($strKey, $varValue);
  337. }
  338. /**
  339. * Remove a configuration variable
  340. *
  341. * @param string $strKey The full variable name
  342. */
  343. public function delete($strKey)
  344. {
  345. $this->markModified();
  346. unset($this->arrData[$strKey]);
  347. }
  348. /**
  349. * Check whether a configuration value exists
  350. *
  351. * @param string $strKey The short key
  352. *
  353. * @return boolean True if the configuration value exists
  354. */
  355. public static function has($strKey)
  356. {
  357. return \array_key_exists($strKey, $GLOBALS['TL_CONFIG']);
  358. }
  359. /**
  360. * Return a configuration value
  361. *
  362. * @param string $strKey The short key
  363. *
  364. * @return mixed The configuration value
  365. */
  366. public static function get($strKey)
  367. {
  368. if ($newKey = self::getNewKey($strKey))
  369. {
  370. trigger_deprecation('contao/core-bundle', '4.12', 'Using "%s(\'%s\')" has been deprecated. Use the "%s" parameter instead.', __METHOD__, $strKey, $newKey);
  371. }
  372. if (isset(self::$arrToBeRemoved[$strKey]))
  373. {
  374. trigger_deprecation('contao/core-bundle', '4.13', 'Using "%s(\'%s\')" has been deprecated.', __METHOD__, $strKey, self::$arrToBeRemoved[$strKey]);
  375. }
  376. return $GLOBALS['TL_CONFIG'][$strKey] ?? null;
  377. }
  378. /**
  379. * Temporarily set a configuration value
  380. *
  381. * @param string $strKey The short key
  382. * @param mixed $varValue The configuration value
  383. */
  384. public static function set($strKey, $varValue)
  385. {
  386. if ($newKey = self::getNewKey($strKey))
  387. {
  388. trigger_deprecation('contao/core-bundle', '4.12', 'Using "%s(\'%s\', …)" has been deprecated. Use the "%s" parameter instead.', __METHOD__, $strKey, $newKey);
  389. }
  390. if (isset(self::$arrToBeRemoved[$strKey]))
  391. {
  392. trigger_deprecation('contao/core-bundle', '4.13', 'Using "%s(\'%s\')" has been deprecated.', __METHOD__, $strKey, self::$arrToBeRemoved[$strKey]);
  393. }
  394. $GLOBALS['TL_CONFIG'][$strKey] = $varValue;
  395. }
  396. /**
  397. * Return the new key if the existing one is deprecated
  398. *
  399. * @internal
  400. *
  401. * @param string $strKey The short key
  402. *
  403. * @return string|null
  404. */
  405. public static function getNewKey($strKey)
  406. {
  407. return self::$arrDeprecated[$strKey] ?? self::$arrDeprecatedMap[$strKey] ?? null;
  408. }
  409. /**
  410. * Permanently set a configuration value
  411. *
  412. * @param string $strKey The short key or full variable name
  413. * @param mixed $varValue The configuration value
  414. */
  415. public static function persist($strKey, $varValue)
  416. {
  417. $objConfig = static::getInstance();
  418. if (strncmp($strKey, '$GLOBALS', 8) !== 0)
  419. {
  420. $strKey = "\$GLOBALS['TL_CONFIG']['$strKey']";
  421. }
  422. $objConfig->add($strKey, $varValue);
  423. }
  424. /**
  425. * Permanently remove a configuration value
  426. *
  427. * @param string $strKey The short key or full variable name
  428. */
  429. public static function remove($strKey)
  430. {
  431. $objConfig = static::getInstance();
  432. if (strncmp($strKey, '$GLOBALS', 8) !== 0)
  433. {
  434. $strKey = "\$GLOBALS['TL_CONFIG']['$strKey']";
  435. }
  436. $objConfig->delete($strKey);
  437. }
  438. /**
  439. * Preload the default and local configuration
  440. */
  441. public static function preload()
  442. {
  443. // Load the default files
  444. include __DIR__ . '/../../config/default.php';
  445. include __DIR__ . '/../../config/agents.php';
  446. include __DIR__ . '/../../config/mimetypes.php';
  447. $projectDir = System::getContainer()->getParameter('kernel.project_dir');
  448. // Include the local configuration file
  449. if (($blnHasLcf = file_exists($projectDir . '/system/config/localconfig.php')) === true)
  450. {
  451. include $projectDir . '/system/config/localconfig.php';
  452. }
  453. static::loadParameters();
  454. static::$blnHasLcf = $blnHasLcf;
  455. }
  456. /**
  457. * Override the database and SMTP parameters
  458. */
  459. protected static function loadParameters()
  460. {
  461. $container = System::getContainer();
  462. if ($container === null)
  463. {
  464. return;
  465. }
  466. if ($container->hasParameter('contao.localconfig') && \is_array($params = $container->getParameter('contao.localconfig')))
  467. {
  468. foreach ($params as $key=>$value)
  469. {
  470. $GLOBALS['TL_CONFIG'][$key] = $value;
  471. }
  472. }
  473. foreach (self::$arrDeprecatedMap as $strKey=>$strParam)
  474. {
  475. if ($container->hasParameter($strParam))
  476. {
  477. $GLOBALS['TL_CONFIG'][$strKey] = $container->getParameter($strParam);
  478. }
  479. }
  480. $objRequest = $container->get('request_stack')->getCurrentRequest();
  481. /** @var PageModel $objPage */
  482. if (null !== $objRequest && ($objPage = $objRequest->attributes->get('pageModel')) instanceof PageModel)
  483. {
  484. $GLOBALS['TL_CONFIG']['addLanguageToUrl'] = $objPage->urlPrefix !== '';
  485. $GLOBALS['TL_CONFIG']['urlSuffix'] = $objPage->urlSuffix;
  486. }
  487. if ($container->hasParameter('contao.image.valid_extensions'))
  488. {
  489. $GLOBALS['TL_CONFIG']['validImageTypes'] = implode(',', $container->getParameter('contao.image.valid_extensions'));
  490. }
  491. if ($container->hasParameter('contao.image.imagine_options'))
  492. {
  493. $GLOBALS['TL_CONFIG']['jpgQuality'] = $container->getParameter('contao.image.imagine_options')['jpeg_quality'];
  494. }
  495. }
  496. /**
  497. * Escape a value depending on its type
  498. *
  499. * @param mixed $varValue The value
  500. *
  501. * @return mixed The escaped value
  502. */
  503. protected function escape($varValue)
  504. {
  505. if (is_numeric($varValue) && $varValue < PHP_INT_MAX && !preg_match('/e|^[+-]?0[^.]/', $varValue))
  506. {
  507. return $varValue;
  508. }
  509. if (\is_bool($varValue))
  510. {
  511. return $varValue ? 'true' : 'false';
  512. }
  513. if ($varValue == 'true')
  514. {
  515. return 'true';
  516. }
  517. if ($varValue == 'false')
  518. {
  519. return 'false';
  520. }
  521. return "'" . str_replace('\\"', '"', preg_replace('/[\n\r\t ]+/', ' ', addslashes($varValue))) . "'";
  522. }
  523. }
  524. class_alias(Config::class, 'Config');