forked from vergnet/site-accueil-insa
495 lines
15 KiB
PHP
495 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Matomo - free/libre analytics platform
|
|
*
|
|
* @link https://matomo.org
|
|
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*
|
|
*/
|
|
|
|
namespace Piwik;
|
|
|
|
use Exception;
|
|
use Piwik\Application\Kernel\GlobalSettingsProvider;
|
|
use Piwik\Container\StaticContainer;
|
|
use Piwik\Exception\MissingFilePermissionException;
|
|
use Piwik\Plugins\CoreAdminHome\Controller;
|
|
use Piwik\Plugins\CorePluginsAdmin\CorePluginsAdmin;
|
|
use Piwik\ProfessionalServices\Advertising;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
/**
|
|
* Singleton that provides read & write access to Piwik's INI configuration.
|
|
*
|
|
* This class reads and writes to the `config/config.ini.php` file. If config
|
|
* options are missing from that file, this class will look for their default
|
|
* values in `config/global.ini.php`.
|
|
*
|
|
* ### Examples
|
|
*
|
|
* **Getting a value:**
|
|
*
|
|
* // read the minimum_memory_limit option under the [General] section
|
|
* $minValue = Config::getInstance()->General['minimum_memory_limit'];
|
|
*
|
|
* **Setting a value:**
|
|
*
|
|
* // set the minimum_memory_limit option
|
|
* Config::getInstance()->General['minimum_memory_limit'] = 256;
|
|
* Config::getInstance()->forceSave();
|
|
*
|
|
* **Setting an entire section:**
|
|
*
|
|
* Config::getInstance()->MySection = array('myoption' => 1);
|
|
* Config::getInstance()->forceSave();
|
|
*/
|
|
class Config
|
|
{
|
|
const DEFAULT_LOCAL_CONFIG_PATH = '/config/config.ini.php';
|
|
const DEFAULT_COMMON_CONFIG_PATH = '/config/common.config.ini.php';
|
|
const DEFAULT_GLOBAL_CONFIG_PATH = '/config/global.ini.php';
|
|
|
|
/**
|
|
* @var boolean
|
|
*/
|
|
protected $doNotWriteConfigInTests = false;
|
|
|
|
/**
|
|
* @var GlobalSettingsProvider
|
|
*/
|
|
protected $settings;
|
|
|
|
/**
|
|
* @return Config
|
|
*/
|
|
public static function getInstance()
|
|
{
|
|
return StaticContainer::get('Piwik\Config');
|
|
}
|
|
|
|
public function __construct(GlobalSettingsProvider $settings)
|
|
{
|
|
$this->settings = $settings;
|
|
}
|
|
|
|
/**
|
|
* Returns the path to the local config file used by this instance.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getLocalPath()
|
|
{
|
|
return $this->settings->getPathLocal();
|
|
}
|
|
|
|
/**
|
|
* Returns the path to the global config file used by this instance.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getGlobalPath()
|
|
{
|
|
return $this->settings->getPathGlobal();
|
|
}
|
|
|
|
/**
|
|
* Returns the path to the common config file used by this instance.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getCommonPath()
|
|
{
|
|
return $this->settings->getPathCommon();
|
|
}
|
|
|
|
/**
|
|
* Returns absolute path to the global configuration file
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getGlobalConfigPath()
|
|
{
|
|
return PIWIK_DOCUMENT_ROOT . self::DEFAULT_GLOBAL_CONFIG_PATH;
|
|
}
|
|
|
|
/**
|
|
* Returns absolute path to the common configuration file.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getCommonConfigPath()
|
|
{
|
|
return PIWIK_USER_PATH . self::DEFAULT_COMMON_CONFIG_PATH;
|
|
}
|
|
|
|
/**
|
|
* Returns default absolute path to the local configuration file.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getDefaultLocalConfigPath()
|
|
{
|
|
return PIWIK_USER_PATH . self::DEFAULT_LOCAL_CONFIG_PATH;
|
|
}
|
|
|
|
/**
|
|
* Returns absolute path to the local configuration file
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getLocalConfigPath()
|
|
{
|
|
if (!empty($GLOBALS['CONFIG_INI_PATH_RESOLVER']) && is_callable($GLOBALS['CONFIG_INI_PATH_RESOLVER'])) {
|
|
return call_user_func($GLOBALS['CONFIG_INI_PATH_RESOLVER']);
|
|
}
|
|
|
|
$path = self::getByDomainConfigPath();
|
|
if ($path) {
|
|
return $path;
|
|
}
|
|
return self::getDefaultLocalConfigPath();
|
|
}
|
|
|
|
private static function getLocalConfigInfoForHostname($hostname)
|
|
{
|
|
if (!$hostname) {
|
|
return array();
|
|
}
|
|
|
|
// Remove any port number to get actual hostname
|
|
$hostname = Url::getHostSanitized($hostname);
|
|
$standardConfigName = 'config.ini.php';
|
|
$perHostFilename = $hostname . '.' . $standardConfigName;
|
|
$pathDomainConfig = PIWIK_USER_PATH . '/config/' . $perHostFilename;
|
|
$pathDomainMiscUser = PIWIK_USER_PATH . '/misc/user/' . $hostname . '/' . $standardConfigName;
|
|
|
|
$locations = array(
|
|
array('file' => $perHostFilename, 'path' => $pathDomainConfig),
|
|
array('file' => $standardConfigName, 'path' => $pathDomainMiscUser)
|
|
);
|
|
|
|
return $locations;
|
|
}
|
|
|
|
public function getConfigHostnameIfSet()
|
|
{
|
|
if ($this->getByDomainConfigPath() === false) {
|
|
return false;
|
|
}
|
|
return $this->getHostname();
|
|
}
|
|
|
|
public function getClientSideOptions()
|
|
{
|
|
$general = $this->General;
|
|
|
|
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'],
|
|
'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(),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param $general
|
|
* @return mixed
|
|
*/
|
|
private function getDatatableRowLimits()
|
|
{
|
|
$limits = $this->General['datatable_row_limits'];
|
|
$limits = explode(",", $limits);
|
|
$limits = array_map('trim', $limits);
|
|
return $limits;
|
|
}
|
|
|
|
public static function getByDomainConfigPath()
|
|
{
|
|
$host = self::getHostname();
|
|
$hostConfigs = self::getLocalConfigInfoForHostname($host);
|
|
|
|
foreach ($hostConfigs as $hostConfig) {
|
|
if (Filesystem::isValidFilename($hostConfig['file'])
|
|
&& file_exists($hostConfig['path'])
|
|
) {
|
|
return $hostConfig['path'];
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the hostname of the current request (without port number)
|
|
* @param bool $checkIfTrusted Check trusted requires config which is maybe not ready yet,
|
|
* make sure the config is ready when you call with true
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getHostname($checkIfTrusted = false)
|
|
{
|
|
$host = Url::getHost($checkIfTrusted);
|
|
|
|
// Remove any port number to get actual hostname
|
|
$host = Url::getHostSanitized($host);
|
|
|
|
return $host;
|
|
}
|
|
|
|
/**
|
|
* If set, Piwik will use the hostname config no matter if it exists or not. Useful for instance if you want to
|
|
* create a new hostname config:
|
|
*
|
|
* $config = Config::getInstance();
|
|
* $config->forceUsageOfHostnameConfig('piwik.example.com');
|
|
* $config->save();
|
|
*
|
|
* @param string $hostname eg piwik.example.com
|
|
* @param string $preferredPath If there are different paths for the config that can be used, eg /config/* and /misc/user/*,
|
|
* and a preferred path is given, then the config path must contain the preferred path.
|
|
* @return string
|
|
* @throws \Exception In case the domain contains not allowed characters
|
|
* @internal
|
|
*/
|
|
public function forceUsageOfLocalHostnameConfig($hostname, $preferredPath = null)
|
|
{
|
|
$hostConfigs = self::getLocalConfigInfoForHostname($hostname);
|
|
$fileNames = '';
|
|
|
|
foreach ($hostConfigs as $hostConfig) {
|
|
if (count($hostConfigs) > 1
|
|
&& $preferredPath
|
|
&& strpos($hostConfig['path'], $preferredPath) === false) {
|
|
continue;
|
|
}
|
|
|
|
$filename = $hostConfig['file'];
|
|
$fileNames .= $filename . ' ';
|
|
|
|
if (Filesystem::isValidFilename($filename)) {
|
|
$pathLocal = $hostConfig['path'];
|
|
|
|
try {
|
|
$this->reload($pathLocal);
|
|
} catch (Exception $ex) {
|
|
// pass (not required for local file to exist at this point)
|
|
}
|
|
|
|
return $pathLocal;
|
|
}
|
|
}
|
|
|
|
throw new Exception('Matomo domain is not a valid looking hostname (' . trim($fileNames) . ').');
|
|
}
|
|
|
|
/**
|
|
* Returns `true` if the local configuration file is writable.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isFileWritable()
|
|
{
|
|
return is_writable($this->settings->getPathLocal());
|
|
}
|
|
|
|
/**
|
|
* Reloads config data from disk.
|
|
*
|
|
* @throws \Exception if the global config file is not found and this is a tracker request, or
|
|
* if the local config file is not found and this is NOT a tracker request.
|
|
*/
|
|
protected function reload($pathLocal = null, $pathGlobal = null, $pathCommon = null)
|
|
{
|
|
$this->settings->reload($pathGlobal, $pathLocal, $pathCommon);
|
|
}
|
|
|
|
public function existsLocalConfig()
|
|
{
|
|
return is_readable($this->getLocalPath());
|
|
}
|
|
|
|
public function deleteLocalConfig()
|
|
{
|
|
$configLocal = $this->getLocalPath();
|
|
|
|
if(file_exists($configLocal)){
|
|
@unlink($configLocal);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns a configuration value or section by name.
|
|
*
|
|
* @param string $name The value or section name.
|
|
* @return string|array The requested value requested. Returned by reference.
|
|
* @throws Exception If the value requested not found in either `config.ini.php` or
|
|
* `global.ini.php`.
|
|
* @api
|
|
*/
|
|
public function &__get($name)
|
|
{
|
|
$section =& $this->settings->getIniFileChain()->get($name);
|
|
return $section;
|
|
}
|
|
|
|
/**
|
|
* @api
|
|
*/
|
|
public function getFromGlobalConfig($name)
|
|
{
|
|
return $this->settings->getIniFileChain()->getFrom($this->getGlobalPath(), $name);
|
|
}
|
|
|
|
/**
|
|
* @api
|
|
*/
|
|
public function getFromCommonConfig($name)
|
|
{
|
|
return $this->settings->getIniFileChain()->getFrom($this->getCommonPath(), $name);
|
|
}
|
|
|
|
/**
|
|
* @api
|
|
*/
|
|
public function getFromLocalConfig($name)
|
|
{
|
|
return $this->settings->getIniFileChain()->getFrom($this->getLocalPath(), $name);
|
|
}
|
|
|
|
/**
|
|
* Sets a configuration value or section.
|
|
*
|
|
* @param string $name This section name or value name to set.
|
|
* @param mixed $value
|
|
* @api
|
|
*/
|
|
public function __set($name, $value)
|
|
{
|
|
$this->settings->getIniFileChain()->set($name, $value);
|
|
}
|
|
|
|
/**
|
|
* Dump config
|
|
*
|
|
* @return string|null
|
|
* @throws \Exception
|
|
*/
|
|
public function dumpConfig()
|
|
{
|
|
$chain = $this->settings->getIniFileChain();
|
|
|
|
$header = "; <?php exit; ?> DO NOT REMOVE THIS LINE\n";
|
|
$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";
|
|
return $chain->dumpChanges($header);
|
|
}
|
|
|
|
/**
|
|
* Write user configuration file
|
|
*
|
|
* @throws \Exception if config file not writable
|
|
*/
|
|
protected function writeConfig()
|
|
{
|
|
$output = $this->dumpConfig();
|
|
|
|
if ($output !== null && $output !== false) {
|
|
$localPath = $this->getLocalPath();
|
|
|
|
if ($this->doNotWriteConfigInTests) {
|
|
// simulate whether it would be successful
|
|
$success = is_writable($localPath);
|
|
} else {
|
|
$success = @file_put_contents($localPath, $output, LOCK_EX);
|
|
}
|
|
|
|
if ($success === false) {
|
|
throw $this->getConfigNotWritableException();
|
|
}
|
|
|
|
if (!$this->sanityCheck($localPath, $output)) {
|
|
// If sanity check fails, try to write the contents once more before logging the issue.
|
|
if (@file_put_contents($localPath, $output, LOCK_EX) === false || !$this->sanityCheck($localPath, $output, true)) {
|
|
StaticContainer::get(LoggerInterface::class)->info("The configuration file {$localPath} did not write correctly.");
|
|
}
|
|
}
|
|
|
|
$this->settings->getIniFileChain()->deleteConfigCache();
|
|
|
|
/**
|
|
* Triggered when a INI config file is changed on disk.
|
|
*
|
|
* @param string $localPath Absolute path to the changed file on the server.
|
|
*/
|
|
Piwik::postEvent('Core.configFileChanged', [$localPath]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes the current configuration to the **config.ini.php** file. Only writes options whose
|
|
* values are different from the default.
|
|
*
|
|
* @api
|
|
*/
|
|
public function forceSave()
|
|
{
|
|
$this->writeConfig();
|
|
}
|
|
|
|
/**
|
|
* @throws \Exception
|
|
*/
|
|
public function getConfigNotWritableException()
|
|
{
|
|
$path = "config/" . basename($this->getLocalPath());
|
|
return new MissingFilePermissionException(Piwik::translate('General_ConfigFileIsNotWritable', array("(" . $path . ")", "")));
|
|
}
|
|
|
|
/**
|
|
* Convenience method for setting settings in a single section. Will set them in a new array first
|
|
* to be compatible with certain PHP versions.
|
|
*
|
|
* @param string $sectionName Section name.
|
|
* @param string $name The setting name.
|
|
* @param mixed $value The setting value to set.
|
|
*/
|
|
public static function setSetting($sectionName, $name, $value)
|
|
{
|
|
$section = self::getInstance()->$sectionName;
|
|
$section[$name] = $value;
|
|
self::getInstance()->$sectionName = $section;
|
|
}
|
|
|
|
/**
|
|
* Sanity check a config file by checking contents
|
|
*
|
|
* @param string $localPath
|
|
* @param string $expectedContent
|
|
* @param bool $notify
|
|
* @return bool
|
|
*/
|
|
public function sanityCheck(string $localPath, string $expectedContent, bool $notify = false): bool
|
|
{
|
|
clearstatcache(true, $localPath);
|
|
|
|
$content = @file_get_contents($localPath);
|
|
|
|
if (trim($content) !== trim($expectedContent)) {
|
|
if ($notify) {
|
|
/**
|
|
* Triggered when the INI config file was not written correctly with the expected content.
|
|
*
|
|
* @param string $localPath Absolute path to the changed file on the server.
|
|
*/
|
|
Piwik::postEvent('Core.configFileSanityCheckFailed', [$localPath]);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|