PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.12.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.12.1
5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Config.php
matomo / app / core Last commit date
API 2 weeks ago Access 2 weeks ago Application 2 weeks ago Archive 2 weeks ago ArchiveProcessor 2 weeks ago Archiver 2 years ago AssetManager 2 weeks ago Auth 2 weeks ago Category 2 weeks ago Changes 2 months ago CliMulti 2 weeks ago Columns 2 weeks ago Concurrency 2 weeks ago Config 2 weeks ago Container 2 months ago CronArchive 2 weeks ago DataAccess 2 weeks ago DataFiles 2 years ago DataTable 2 weeks ago Db 2 weeks ago DeviceDetector 1 year ago Email 2 years ago Exception 5 months ago Http 2 weeks ago Intl 4 months ago Log 2 years ago Mail 1 year ago Measurable 8 months ago Menu 2 weeks ago Metrics 2 weeks ago Notification 8 months ago Period 2 weeks ago Plugin 2 weeks ago Policy 2 months ago ProfessionalServices 1 year ago Report 1 year ago ReportRenderer 2 weeks ago Request 2 weeks ago Scheduler 2 weeks ago Segment 2 weeks ago Session 1 month ago Settings 2 weeks ago Tracker 2 weeks ago Translation 2 months ago Twig 1 year ago UpdateCheck 4 months ago Updater 2 weeks ago Updates 1 month ago Validators 1 year ago View 2 months ago ViewDataTable 2 weeks ago Visualization 2 weeks ago Widget 2 weeks ago .htaccess 2 years ago Access.php 2 weeks ago Archive.php 2 months ago ArchiveProcessor.php 2 weeks ago AssetManager.php 2 weeks ago Auth.php 8 months ago AuthResult.php 8 months ago BaseFactory.php 2 years ago Cache.php 2 weeks ago CacheId.php 5 months ago CliMulti.php 2 weeks ago Common.php 2 weeks ago Config.php 2 weeks ago Console.php 4 months ago Context.php 2 years ago Cookie.php 2 weeks ago CronArchive.php 2 weeks ago DI.php 4 months ago DataArray.php 2 weeks ago DataTable.php 2 weeks ago Date.php 2 months ago Db.php 2 months ago DbHelper.php 2 weeks ago Development.php 2 weeks ago ErrorHandler.php 8 months ago EventDispatcher.php 2 months ago ExceptionHandler.php 5 months ago FileIntegrity.php 2 weeks ago Filechecks.php 1 year ago Filesystem.php 2 weeks ago FrontController.php 2 weeks ago Http.php 2 weeks ago IP.php 1 year ago Log.php 4 months ago LogDeleter.php 2 weeks ago Mail.php 1 year ago Metrics.php 2 months ago NoAccessException.php 2 years ago Nonce.php 8 months ago Notification.php 2 months ago NumberFormatter.php 2 weeks ago Option.php 2 weeks ago Period.php 2 weeks ago Piwik.php 2 weeks ago Plugin.php 2 months ago Process.php 2 months ago Profiler.php 2 weeks ago ProxyHeaders.php 5 months ago ProxyHttp.php 2 weeks ago QuickForm2.php 4 months ago RankingQuery.php 2 months ago ReportRenderer.php 2 weeks ago Request.php 2 months ago Segment.php 2 weeks ago Sequence.php 2 weeks ago Session.php 1 month ago SettingsPiwik.php 2 weeks ago SettingsServer.php 2 weeks ago Singleton.php 2 years ago Site.php 2 months ago SiteContentDetector.php 2 months ago SupportedBrowser.php 2 years ago TCPDF.php 1 year ago Theme.php 2 weeks ago Timer.php 2 weeks ago Tracker.php 2 weeks ago Twig.php 2 months ago Unzip.php 1 year ago UpdateCheck.php 2 months ago Updater.php 2 weeks ago UpdaterErrorException.php 2 years ago Updates.php 2 weeks ago Url.php 2 weeks ago UrlHelper.php 2 months ago Version.php 2 weeks ago View.php 2 weeks ago bootstrap.php 1 year ago dispatch.php 2 years ago testMinimumPhpVersion.php 8 months ago
Config.php
424 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 */
9 namespace Piwik;
10
11 use Exception;
12 use Piwik\Application\Kernel\GlobalSettingsProvider;
13 use Piwik\Container\StaticContainer;
14 use Piwik\Exception\MissingFilePermissionException;
15 use Piwik\Plugins\CoreAdminHome\Controller;
16 use Piwik\Plugins\CorePluginsAdmin\CorePluginsAdmin;
17 use Piwik\ProfessionalServices\Advertising;
18 use Piwik\Log\LoggerInterface;
19 /**
20 * Singleton that provides read & write access to Piwik's INI configuration.
21 *
22 * This class reads and writes to the `config/config.ini.php` file. If config
23 * options are missing from that file, this class will look for their default
24 * values in `config/global.ini.php`.
25 *
26 * ### Examples
27 *
28 * **Getting a value:**
29 *
30 * // read the minimum_memory_limit option under the [General] section
31 * $minValue = Config::getInstance()->General['minimum_memory_limit'];
32 *
33 * **Setting a value:**
34 *
35 * // set the minimum_memory_limit option
36 * Config::getInstance()->General['minimum_memory_limit'] = 256;
37 * Config::getInstance()->forceSave();
38 *
39 * **Setting an entire section:**
40 *
41 * Config::getInstance()->MySection = array('myoption' => 1);
42 * Config::getInstance()->forceSave();
43 */
44 class Config
45 {
46 public const DEFAULT_LOCAL_CONFIG_PATH = '/config/config.ini.php';
47 public const DEFAULT_COMMON_CONFIG_PATH = '/config/common.config.ini.php';
48 public const DEFAULT_GLOBAL_CONFIG_PATH = '/config/global.ini.php';
49 /**
50 * @var boolean
51 */
52 protected $doNotWriteConfigInTests = \false;
53 /**
54 * @var GlobalSettingsProvider
55 */
56 protected $settings;
57 /**
58 * @return Config
59 */
60 public static function getInstance()
61 {
62 return StaticContainer::get('Piwik\\Config');
63 }
64 public function __construct(GlobalSettingsProvider $settings)
65 {
66 $this->settings = $settings;
67 }
68 /**
69 * Returns the path to the local config file used by this instance.
70 *
71 * @return string
72 */
73 public function getLocalPath()
74 {
75 return $this->settings->getPathLocal();
76 }
77 /**
78 * Returns the path to the global config file used by this instance.
79 *
80 * @return string
81 */
82 public function getGlobalPath()
83 {
84 return $this->settings->getPathGlobal();
85 }
86 /**
87 * Returns the path to the common config file used by this instance.
88 *
89 * @return string
90 */
91 public function getCommonPath()
92 {
93 return $this->settings->getPathCommon();
94 }
95 /**
96 * Returns absolute path to the global configuration file
97 *
98 * @return string
99 */
100 public static function getGlobalConfigPath()
101 {
102 return PIWIK_DOCUMENT_ROOT . self::DEFAULT_GLOBAL_CONFIG_PATH;
103 }
104 /**
105 * Returns absolute path to the common configuration file.
106 *
107 * @return string
108 */
109 public static function getCommonConfigPath()
110 {
111 return PIWIK_USER_PATH . self::DEFAULT_COMMON_CONFIG_PATH;
112 }
113 /**
114 * Returns default absolute path to the local configuration file.
115 *
116 * @return string
117 */
118 public static function getDefaultLocalConfigPath()
119 {
120 return PIWIK_USER_PATH . self::DEFAULT_LOCAL_CONFIG_PATH;
121 }
122 /**
123 * Returns absolute path to the local configuration file
124 *
125 * @return string
126 */
127 public static function getLocalConfigPath()
128 {
129 if (!empty($GLOBALS['CONFIG_INI_PATH_RESOLVER']) && is_callable($GLOBALS['CONFIG_INI_PATH_RESOLVER'])) {
130 return call_user_func($GLOBALS['CONFIG_INI_PATH_RESOLVER']);
131 }
132 $path = self::getByDomainConfigPath();
133 if ($path) {
134 return $path;
135 }
136 return self::getDefaultLocalConfigPath();
137 }
138 private static function getLocalConfigInfoForHostname($hostname)
139 {
140 if (!$hostname) {
141 return array();
142 }
143 // Remove any port number to get actual hostname
144 $hostname = \Piwik\Url::getHostSanitized($hostname);
145 $standardConfigName = 'config.ini.php';
146 $perHostFilename = $hostname . '.' . $standardConfigName;
147 $pathDomainConfig = PIWIK_USER_PATH . '/config/' . $perHostFilename;
148 $pathDomainMiscUser = PIWIK_USER_PATH . '/misc/user/' . $hostname . '/' . $standardConfigName;
149 $locations = array(array('file' => $perHostFilename, 'path' => $pathDomainConfig), array('file' => $standardConfigName, 'path' => $pathDomainMiscUser));
150 return $locations;
151 }
152 public function getConfigHostnameIfSet()
153 {
154 if ($this->getByDomainConfigPath() === \false) {
155 return \false;
156 }
157 return $this->getHostname();
158 }
159 public function getClientSideOptions()
160 {
161 $general = $this->General;
162 return array('action_url_category_delimiter' => $general['action_url_category_delimiter'], 'action_title_category_delimiter' => $general['action_title_category_delimiter'], 'are_ads_enabled' => Advertising::isAdsEnabledInConfig($general), 'autocomplete_min_sites' => $general['autocomplete_min_sites'], 'data_comparison_segment_limit' => $general['data_comparison_segment_limit'], 'datatable_export_range_as_day' => $general['datatable_export_range_as_day'], 'datatable_row_limits' => $this->getDatatableRowLimits(), 'enable_general_settings_admin' => Controller::isGeneralSettingsAdminEnabled(), 'enable_plugins_admin' => CorePluginsAdmin::isPluginsAdminEnabled());
163 }
164 /**
165 * @return string[]
166 */
167 private function getDatatableRowLimits()
168 {
169 $limits = $this->General['datatable_row_limits'];
170 $limits = explode(",", $limits);
171 $limits = array_map('trim', $limits);
172 return $limits;
173 }
174 public static function getByDomainConfigPath()
175 {
176 $host = self::getHostname();
177 $hostConfigs = self::getLocalConfigInfoForHostname($host);
178 foreach ($hostConfigs as $hostConfig) {
179 if (\Piwik\Filesystem::isValidFilename($hostConfig['file']) && file_exists($hostConfig['path'])) {
180 return $hostConfig['path'];
181 }
182 }
183 return \false;
184 }
185 /**
186 * Returns the hostname of the current request (without port number)
187 * @param bool $checkIfTrusted Check trusted requires config which is maybe not ready yet,
188 * make sure the config is ready when you call with true
189 *
190 * @return string
191 */
192 public static function getHostname($checkIfTrusted = \false)
193 {
194 $host = \Piwik\Url::getHost($checkIfTrusted);
195 // Remove any port number to get actual hostname
196 $host = \Piwik\Url::getHostSanitized($host);
197 return $host;
198 }
199 /**
200 * If set, Piwik will use the hostname config no matter if it exists or not. Useful for instance if you want to
201 * create a new hostname config:
202 *
203 * $config = Config::getInstance();
204 * $config->forceUsageOfHostnameConfig('piwik.example.com');
205 * $config->save();
206 *
207 * @param string $hostname eg piwik.example.com
208 * @param string $preferredPath If there are different paths for the config that can be used, eg /config/* and /misc/user/*,
209 * and a preferred path is given, then the config path must contain the preferred path.
210 * @return string
211 * @throws \Exception In case the domain contains not allowed characters
212 * @internal
213 */
214 public function forceUsageOfLocalHostnameConfig($hostname, $preferredPath = null)
215 {
216 $hostConfigs = self::getLocalConfigInfoForHostname($hostname);
217 $fileNames = '';
218 foreach ($hostConfigs as $hostConfig) {
219 if (count($hostConfigs) > 1 && $preferredPath && strpos($hostConfig['path'], $preferredPath) === \false) {
220 continue;
221 }
222 $filename = $hostConfig['file'];
223 $fileNames .= $filename . ' ';
224 if (\Piwik\Filesystem::isValidFilename($filename)) {
225 $pathLocal = $hostConfig['path'];
226 try {
227 $this->reload($pathLocal);
228 } catch (Exception $ex) {
229 // pass (not required for local file to exist at this point)
230 }
231 return $pathLocal;
232 }
233 }
234 throw new Exception('Matomo domain is not a valid looking hostname (' . trim($fileNames) . ').');
235 }
236 /**
237 * Returns `true` if the local configuration file is writable.
238 *
239 * @return bool
240 */
241 public function isFileWritable()
242 {
243 return is_writable($this->settings->getPathLocal());
244 }
245 /**
246 * Reloads config data from disk.
247 *
248 * @throws \Exception if the global config file is not found and this is a tracker request, or
249 * if the local config file is not found and this is NOT a tracker request.
250 */
251 protected function reload($pathLocal = null, $pathGlobal = null, $pathCommon = null)
252 {
253 $this->settings->reload($pathGlobal, $pathLocal, $pathCommon);
254 }
255 public function existsLocalConfig()
256 {
257 return is_readable($this->getLocalPath());
258 }
259 public function deleteLocalConfig()
260 {
261 $configLocal = $this->getLocalPath();
262 if (file_exists($configLocal)) {
263 @unlink($configLocal);
264 }
265 }
266 /**
267 * Returns a configuration value or section by name.
268 *
269 * @param string $name The value or section name.
270 * @return string|array The requested value requested. Returned by reference.
271 * @throws Exception If the value requested not found in either `config.ini.php` or
272 * `global.ini.php`.
273 * @api
274 */
275 public function &__get($name)
276 {
277 $section =& $this->settings->getIniFileChain()->get($name);
278 return $section;
279 }
280 /**
281 * @api
282 */
283 public function getFromGlobalConfig($name)
284 {
285 return $this->settings->getIniFileChain()->getFrom($this->getGlobalPath(), $name);
286 }
287 /**
288 * @api
289 */
290 public function getFromCommonConfig($name)
291 {
292 return $this->settings->getIniFileChain()->getFrom($this->getCommonPath(), $name);
293 }
294 /**
295 * @api
296 */
297 public function getFromLocalConfig($name)
298 {
299 return $this->settings->getIniFileChain()->getFrom($this->getLocalPath(), $name);
300 }
301 /**
302 * Sets a configuration value or section.
303 *
304 * @param string $name This section name or value name to set.
305 * @param mixed $value
306 * @api
307 */
308 public function __set($name, $value)
309 {
310 $this->settings->getIniFileChain()->set($name, $value);
311 }
312 /**
313 * Dump config
314 *
315 * @return string|null
316 * @throws \Exception
317 */
318 public function dumpConfig()
319 {
320 $chain = $this->settings->getIniFileChain();
321 $header = "; <?php exit; ?> DO NOT REMOVE THIS LINE\n";
322 $header .= "; file automatically generated or modified by Matomo; you can manually override the default values in global.ini.php by redefining them in this file.\n";
323 return $chain->dumpChanges($header);
324 }
325 /**
326 * Write user configuration file
327 *
328 * @throws \Exception if config file not writable
329 */
330 protected function writeConfig()
331 {
332 $output = $this->dumpConfig();
333 if ($output !== null && $output !== \false) {
334 $localPath = $this->getLocalPath();
335 if ($this->doNotWriteConfigInTests) {
336 // simulate whether it would be successful
337 $success = is_writable($localPath);
338 } else {
339 $success = @file_put_contents($localPath, $output, \LOCK_EX);
340 }
341 if ($success === \false) {
342 throw $this->getConfigNotWritableException();
343 }
344 if (!$this->sanityCheck($localPath, $output)) {
345 // If sanity check fails, try to write the contents once more before logging the issue.
346 if (@file_put_contents($localPath, $output, \LOCK_EX) === \false || !$this->sanityCheck($localPath, $output, \true)) {
347 StaticContainer::get(LoggerInterface::class)->info("The configuration file {$localPath} did not write correctly.");
348 }
349 }
350 $this->settings->getIniFileChain()->deleteConfigCache();
351 /**
352 * Triggered when a INI config file is changed on disk.
353 *
354 * @param string $localPath Absolute path to the changed file on the server.
355 */
356 \Piwik\Piwik::postEvent('Core.configFileChanged', [$localPath]);
357 }
358 }
359 /**
360 * Writes the current configuration to the **config.ini.php** file. Only writes options whose
361 * values are different from the default.
362 *
363 * @api
364 */
365 public function forceSave()
366 {
367 $this->writeConfig();
368 }
369 /**
370 * @return MissingFilePermissionException
371 */
372 public function getConfigNotWritableException()
373 {
374 $path = "config/" . basename($this->getLocalPath());
375 return new MissingFilePermissionException(\Piwik\Piwik::translate('General_ConfigFileIsNotWritable', array("(" . $path . ")", "")));
376 }
377 /**
378 * @throws MissingFilePermissionException If config file is not writable.
379 */
380 public function checkConfigIsWritable()
381 {
382 if (!$this->isFileWritable()) {
383 throw $this->getConfigNotWritableException();
384 }
385 }
386 /**
387 * Convenience method for setting settings in a single section. Will set them in a new array first
388 * to be compatible with certain PHP versions.
389 *
390 * @param string $sectionName Section name.
391 * @param string $name The setting name.
392 * @param mixed $value The setting value to set.
393 */
394 public static function setSetting($sectionName, $name, $value)
395 {
396 $section = self::getInstance()->{$sectionName};
397 $section[$name] = $value;
398 self::getInstance()->{$sectionName} = $section;
399 }
400 /**
401 * Sanity check a config file by checking contents
402 */
403 public function sanityCheck(string $localPath, string $expectedContent, bool $notify = \false) : bool
404 {
405 clearstatcache(\true, $localPath);
406 if (function_exists('opcache_invalidate')) {
407 @opcache_invalidate($localPath, $force = \true);
408 }
409 $content = @file_get_contents($localPath);
410 if (trim($content) !== trim($expectedContent)) {
411 if ($notify) {
412 /**
413 * Triggered when the INI config file was not written correctly with the expected content.
414 *
415 * @param string $localPath Absolute path to the changed file on the server.
416 */
417 \Piwik\Piwik::postEvent('Core.configFileSanityCheckFailed', [$localPath]);
418 }
419 return \false;
420 }
421 return \true;
422 }
423 }
424