PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/core/Config.php

https://github.com/quarkness/piwik
PHP | 343 lines | 227 code | 27 blank | 89 comment | 30 complexity | 26ca096d9ca3f0b74b1b6f7703e697ad MD5 | raw file
  1. <?php
  2. /**
  3. * Piwik - Open source web analytics
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. * @version $Id$
  8. *
  9. * @category Piwik
  10. * @package Piwik
  11. */
  12. /**
  13. * This class is used to access configuration files values.
  14. * You can also set these values, the updated configuration files will be written at the end of the script execution.
  15. *
  16. * Example reading a value from the configuration file:
  17. * $minValue = Zend_Registry::get('config')->General->minimum_memory_limit;
  18. *
  19. * will read the value minimumMemoryLimit under the [General] section of the config file
  20. *
  21. * @package Piwik
  22. * @subpackage Piwik_Config
  23. */
  24. class Piwik_Config
  25. {
  26. /**
  27. * When the user modifies the configuration file and there is one value missing, we suggest the default config file
  28. *
  29. * @var string
  30. */
  31. protected $urlToPiwikHelpMissingValueInConfigurationFile =
  32. 'http://dev.piwik.org/trac/browser/trunk/config/global.ini.php?format=raw';
  33. protected $defaultConfig = null;
  34. protected $userConfig = null;
  35. protected $pathIniFileUserConfig = null;
  36. protected $pathIniFileDefaultConfig = null;
  37. protected $configFileUpdated = false;
  38. protected $doWriteFileWhenUpdated = true;
  39. protected $cachedConfigArray = array();
  40. protected $isTestEnvironment = false;
  41. /**
  42. * Returns default relative path for user configuration file
  43. *
  44. * @return string
  45. */
  46. static public function getDefaultUserConfigPath()
  47. {
  48. return PIWIK_USER_PATH .'/config/config.ini.php';
  49. }
  50. /**
  51. * Returns default relative path for global configuration file
  52. *
  53. * @return string
  54. */
  55. static public function getDefaultDefaultConfigPath()
  56. {
  57. return PIWIK_USER_PATH .'/config/global.ini.php';
  58. }
  59. /**
  60. * Builds the Config object, given the optional path for the user INI file
  61. * If not specified, it will use the default path
  62. *
  63. * @param string $pathIniFileUserConfig
  64. */
  65. function __construct($pathIniFileUserConfig = null, $pathIniFileDefaultConfig = null)
  66. {
  67. if(is_null($pathIniFileUserConfig))
  68. {
  69. $pathIniFileUserConfig = self::getDefaultUserConfigPath();
  70. }
  71. $this->pathIniFileUserConfig = $pathIniFileUserConfig;
  72. if(is_null($pathIniFileDefaultConfig))
  73. {
  74. $pathIniFileDefaultConfig = self::getDefaultDefaultConfigPath();
  75. }
  76. $this->pathIniFileDefaultConfig = $pathIniFileDefaultConfig;
  77. }
  78. /**
  79. * By default, when calling setting configuration values using
  80. * $config->database = array(...)
  81. * Piwik will automatically save the updated config file in __destruct()
  82. * This can be disabled (when setting partial configuration values during the installation process for example)
  83. */
  84. public function disableSavingConfigurationFileUpdates()
  85. {
  86. $this->doWriteFileWhenUpdated = false;
  87. }
  88. public function init()
  89. {
  90. if(!is_readable($this->pathIniFileDefaultConfig))
  91. {
  92. Piwik_ExitWithMessage(Piwik_TranslateException('General_ExceptionConfigurationFileNotFound', array($this->pathIniFileDefaultConfig)));
  93. }
  94. $this->defaultConfig = new Piwik_Config_Ini($this->pathIniFileDefaultConfig, null, true);
  95. if(is_null($this->defaultConfig) || count($this->defaultConfig->toArray()) == 0)
  96. {
  97. Piwik_ExitWithMessage(Piwik_TranslateException('General_ExceptionUnreadableFileDisabledMethod', array($this->pathIniFileDefaultConfig, "parse_ini_file()")));
  98. }
  99. if(!is_readable($this->pathIniFileUserConfig))
  100. {
  101. throw new Exception(Piwik_TranslateException('General_ExceptionConfigurationFileNotFound', array($this->pathIniFileUserConfig)));
  102. }
  103. $this->userConfig = new Piwik_Config_Ini($this->pathIniFileUserConfig, null, true);
  104. if(is_null($this->userConfig) || count($this->userConfig->toArray()) == 0)
  105. {
  106. Piwik_ExitWithMessage(Piwik_TranslateException('General_ExceptionUnreadableFileDisabledMethod', array($this->pathIniFileUserConfig, "parse_ini_file()")));
  107. }
  108. }
  109. /**
  110. * Write user configuration file
  111. *
  112. * @param array $globalConfig
  113. * @param array $userConfig
  114. * @param string $filename
  115. */
  116. function writeConfig($globalConfig, $userConfig, $filename)
  117. {
  118. $configFile = "; <?php exit; ?> DO NOT REMOVE THIS LINE\n";
  119. $configFile .= "; file automatically generated or modified by Piwik; you can manually override the default values in global.ini.php by redefining them in this file.\n";
  120. foreach($userConfig as $section => $arraySection)
  121. {
  122. $arraySection = $arraySection->toArray();
  123. $configFile .= "[$section]\n";
  124. foreach($arraySection as $name => $value)
  125. {
  126. if(is_numeric($name))
  127. {
  128. $name = $section;
  129. $value = array($value);
  130. }
  131. if(is_array($value))
  132. {
  133. foreach($value as $currentValue)
  134. {
  135. $configFile .= $name."[] = \"$currentValue\"\n";
  136. }
  137. }
  138. else
  139. {
  140. if(!is_numeric($value))
  141. {
  142. $value = "\"$value\"";
  143. }
  144. $configFile .= $name.' = '.$value."\n";
  145. }
  146. }
  147. $configFile .= "\n";
  148. }
  149. @file_put_contents($filename, $configFile );
  150. }
  151. /**
  152. * At the script shutdown, we save the new configuration file, if the user has set some values
  153. */
  154. function __destruct()
  155. {
  156. if($this->configFileUpdated === true
  157. && $this->doWriteFileWhenUpdated === true)
  158. {
  159. $this->writeConfig($this->defaultConfig, $this->userConfig, $this->pathIniFileUserConfig);
  160. }
  161. }
  162. public function isFileWritable()
  163. {
  164. return is_writable($this->pathIniFileUserConfig);
  165. }
  166. /**
  167. * If called, we use the database_tests credentials
  168. */
  169. public function setTestEnvironment()
  170. {
  171. $this->isTestEnvironment = true;
  172. foreach(Piwik_Tracker_Config::$toRestoreFromGlobalConfig as $section)
  173. {
  174. $this->$section = $this->defaultConfig->$section->toArray();
  175. }
  176. $this->database = $this->database_tests->toArray();
  177. // for unit tests, we set that no plugin is installed. This will force
  178. // the test initialization to create the plugins tables, execute ALTER queries, etc.
  179. $this->PluginsInstalled = array();
  180. $this->disableSavingConfigurationFileUpdates();
  181. }
  182. /**
  183. * Is the config file set to use the test values?
  184. * @return bool
  185. */
  186. public function isTestEnvironment()
  187. {
  188. return $this->isTestEnvironment;
  189. }
  190. /**
  191. * Called when setting configuration values eg.
  192. * Zend_Registry::get('config')->MyConfigSection = 'foobar';
  193. *
  194. * The values will be saved in the configuration file at the end of the script @see __destruct()
  195. *
  196. * @param string $name
  197. * @param mixed $values
  198. */
  199. public function __set($name, $values)
  200. {
  201. $this->cachedConfigArray = array();
  202. $this->checkWritePermissionOnFile();
  203. if(is_null($this->userConfig))
  204. {
  205. $this->userConfig = new Zend_Config(array(), true);
  206. }
  207. $values = self::encodeValues($values);
  208. if(is_array($values)
  209. || $this->userConfig->$name != $values)
  210. {
  211. $this->configFileUpdated = true;
  212. }
  213. $this->userConfig->$name = $values;
  214. }
  215. private function encodeValues($values)
  216. {
  217. if(is_array($values))
  218. {
  219. foreach($values as &$value)
  220. {
  221. $value = self::encodeValues($value);
  222. }
  223. }
  224. else
  225. {
  226. $values = htmlentities($values, ENT_COMPAT);
  227. }
  228. return $values;
  229. }
  230. private function decodeValues($values)
  231. {
  232. if(is_array($values))
  233. {
  234. foreach($values as &$value)
  235. {
  236. $value = self::decodeValues($value);
  237. }
  238. }
  239. else
  240. {
  241. $values = html_entity_decode($values, ENT_COMPAT);
  242. }
  243. return $values;
  244. }
  245. protected function checkWritePermissionOnFile()
  246. {
  247. static $enoughPermission = null;
  248. if(is_null($enoughPermission))
  249. {
  250. if($this->doWriteFileWhenUpdated)
  251. {
  252. Piwik::checkDirectoriesWritableOrDie( array('/config/') );
  253. }
  254. $enoughPermission = true;
  255. }
  256. return $enoughPermission;
  257. }
  258. /**
  259. * Loop through the Default and the User configuration objects and cache them in arrays.
  260. * This slightly helps reducing the Zend overhead when accessing config entries hundreds of times.
  261. */
  262. protected function cacheConfigArray()
  263. {
  264. $allSections = array();
  265. foreach($this->defaultConfig as $sectionName => $valueInDefaultConfig)
  266. {
  267. $allSections[] = $sectionName;
  268. }
  269. if(!is_null($this->userConfig))
  270. {
  271. foreach($this->userConfig as $sectionName => $valueInUserConfig)
  272. {
  273. $allSections[] = $sectionName;
  274. }
  275. }
  276. $allSections = array_unique($allSections);
  277. foreach($allSections as $sectionName)
  278. {
  279. $section = array();
  280. if(($valueInDefaultConfig = $this->defaultConfig->$sectionName) !== null)
  281. {
  282. $valueInDefaultConfig = $valueInDefaultConfig->toArray();
  283. $section = array_merge($section, $valueInDefaultConfig);
  284. }
  285. if( !is_null($this->userConfig)
  286. && null !== ($valueInUserConfig = $this->userConfig->$sectionName))
  287. {
  288. $valueInUserConfig = $valueInUserConfig->toArray();
  289. $valueInUserConfig = self::decodeValues($valueInUserConfig);
  290. $section = array_merge($section, $valueInUserConfig);
  291. }
  292. $this->cachedConfigArray[$sectionName] = new Zend_Config($section, true);
  293. }
  294. }
  295. /**
  296. * Called when getting a configuration value, eg. Zend_Registry::get('config')->superuser->login
  297. *
  298. * @param string $name
  299. * @return mixed value
  300. *
  301. * @throws exception if the value was not found in the configuration file
  302. */
  303. public function __get($name)
  304. {
  305. if(empty($this->cachedConfigArray))
  306. {
  307. $this->cacheConfigArray();
  308. }
  309. if(!isset($this->cachedConfigArray[$name]))
  310. {
  311. throw new Exception("Error while trying to read a specific config file entry <b>'$name'</b> in your configuration file <b>config/global.ini.php</b>
  312. This problem would usually appear after a Piwik upgrade. If so, please check that the file config/global.ini.php was overwritten with the equivalent file from the latest Piwik version.");
  313. }
  314. return $this->cachedConfigArray[$name];
  315. }
  316. }