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