|
Server : Apache System : Linux s1230 5.15.0-139-generic #149~20.04.1 SMP Tue Jul 14 11:21:49 UTC 2026 x86_64 User : p141464 ( 418825) PHP Version : 7.4.33.12 Disable Function : NONE Directory : /html/KMU-CSR-PLANER/wp-content/plugins/matomo/app/plugins/CoreAdminHome/Commands/ |
Upload File : |
<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\CoreAdminHome\Commands;
use Piwik\Config;
use Piwik\Plugin\ConsoleCommand;
use Piwik\Settings\FieldConfig;
use Piwik\Settings\Plugin\SystemConfigSetting;
class ConfigDelete extends ConsoleCommand
{
// Message output if no matching setting is found.
private const MSG_NOTHING_FOUND = 'Nothing found';
// Message output on success.
private const MSG_SUCCESS = 'Success: The setting has been deleted';
protected function configure()
{
$this->setName('config:delete');
$this->setDescription('Delete a config setting');
$this->addOptionalArgument('argument', "A config setting in the format Section.key or Section.array_key[], e.g. 'Database.username' or 'PluginsInstalled.PluginsInstalled.CustomDimensions'");
$this->addRequiredValueOption('section', 's', 'The section the INI config setting belongs to.');
$this->addRequiredValueOption('key', 'k', 'The name of the INI config setting.');
$this->addRequiredValueOption('value', 'i', 'For arrays, specify the array value to be deleted.');
$this->setHelp("This command can be used to delete a INI config setting.\n\nYou can delete config values per the two sections below, where:\n- [Section] is the name of the section, e.g. database or General.\n- [config_setting_name] the name of the setting, e.g. username.\n- [array_value] For arrays, the specific array value to delete.\n\n(1) By settings options --section=[Section] and --key=[config_setting_name], and optionally --value=[array_value]. Examples:\n#Delete this setting.\n\$ ./console %command.name% --section=database --key=username\n#Delete one value in an array:\n\$ ./console %command.name% --section=PluginsInstalled --key=PluginsInstalled --value=DevicesDetection\n\nOR\n\n(2) By using a command argument in the format [Section].[config_setting_name].[array_value]. Examples:\n#Delete this setting.\n\$ ./console %command.name% 'database.username'\n#Delete one value in an array:\n\$ ./console %command.name% 'PluginsInstalled.PluginsInstalled.DevicesDetection'\n\nNOTES:\n- Settings may still appear to exist if they are set in global.ini.php or elsewhere.\n- Section names, key names, and array values are all case-sensitive; so e.g. --section=Database fails but --section=database works. Look in config.ini.php and global.ini.php for the proper case.\n- If no matching section/setting is found, the string \"" . self::MSG_NOTHING_FOUND . "\" shows.\n- For safety, this tool cannot be used to delete a whole section of settings or an array of values in a single command.\n");
}
protected function doExecute() : int
{
$input = $this->getInput();
$output = $this->getOutput();
// Gather options, then discard ones that are empty so we do not need to check for empty later.
$options = array_filter(['section' => $input->getOption('section'), 'key' => $input->getOption('key'), 'value' => $input->getOption('value')]);
$argument = trim($input->getArgument('argument') ?? '');
// Sanity check inputs.
switch (\true) {
case empty($argument) && empty($options):
throw new \InvalidArgumentException('You must set either an argument or set options --section, --key and optional --value');
case !empty($argument) && !empty($options):
throw new \InvalidArgumentException('You cannot set both an argument (' . serialize($argument) . ') and options (' . serialize($argument) . ')');
case empty($argument) && (!isset($options['section']) || empty($options['section']) || !isset($options['key']) || empty($options['key'])):
throw new \InvalidArgumentException('When using options, --section and --key must be set');
case !empty($argument):
$settingStr = $argument;
break;
case !empty($options):
$settingStr = implode('.', $options);
break;
default:
// We should not get here, but just in case.
throw new \Exception('Some unexpected error occurred parsing input values');
}
// Convenience wrapper used to augment SystemConfigSetting without extending SystemConfigSetting or adding random properties to the instance.
$settingWrapped = (object) ['setting' => null, 'isArray' => \false, 'arrayVal' => ''];
// Parse the $settingStr into a $settingWrapped object.
$settingWrapped = self::parseSettingStr($settingStr, $settingWrapped);
// Check the setting exists and user has permissions, then populates the $settingWrapped properties.
$settingWrapped = $this->checkAndPopulate($settingWrapped);
if (!isset($settingWrapped->setting) || empty($settingWrapped->setting)) {
$output->writeln(self::wrapInTag('comment', self::MSG_NOTHING_FOUND));
} else {
// Pass both static and array config items out to the delete logic.
$result = $this->deleteConfigSetting($settingWrapped);
if ($result) {
$output->writeln($this->wrapInTag('info', self::MSG_SUCCESS));
}
}
return self::SUCCESS;
}
/**
* Check the setting exists and user has permissions, then return a new, populated SystemConfigSetting wrapper.
*
* @param object $settingWrapped A wrapped SystemConfigSetting object e.g. what is returned from parseSettingStr().
* @return object A new wrapped SystemConfigSetting object.
*/
private function checkAndPopulate(object $settingWrapped) : object
{
// Sanity check inputs.
if (!$settingWrapped->setting instanceof SystemConfigSetting) {
throw new \InvalidArgumentException('This function expects $settingWrapped->setting to be a SystemConfigSetting instance');
}
$config = Config::getInstance();
// Check the setting exists and user has permissions. If so, put it in the wrapper.
switch (\true) {
case empty($sectionName = $settingWrapped->setting->getConfigSectionName()):
throw new \InvalidArgumentException('A section name must be specified');
case empty($settingName = $settingWrapped->setting->getName()):
throw new \InvalidArgumentException('A setting name must be specified');
case empty($section = $config->__get($sectionName)):
return new \stdClass();
case empty($section = (object) $section) || !isset($section->{$settingName}):
return new \stdClass();
default:
// We have a valid scalar or array setting in a valid section, so just fall out of the switch statement.
break;
}
$settingWrappedNew = clone $settingWrapped;
$settingWrappedNew->isArray = is_array($section->{$settingName});
if (!$settingWrappedNew->isArray && !empty($settingWrappedNew->arrayVal)) {
throw new \InvalidArgumentException('This config setting is not an array');
}
if ($settingWrappedNew->isArray) {
if (empty($settingWrappedNew->arrayVal)) {
throw new \InvalidArgumentException('This config setting is an array, but no array value was specified for deletion');
}
if (\false === array_search($settingWrappedNew->arrayVal, $section->{$settingName})) {
return new \stdClass();
}
}
return $settingWrappedNew;
}
/**
* Delete a single config section.setting or section.setting[array_key].
*
* @param object $settingWrapped Wrapper around a setting object describing what to get e.g. from self::make().
* @return bool True on success. If the delete fails, throws an exception.
*/
private function deleteConfigSetting(object $settingWrapped) : bool
{
// Sanity check inputs.
if (!$settingWrapped->setting instanceof SystemConfigSetting) {
throw new \InvalidArgumentException('This function expects $settingWrapped->setting to be a SystemConfigSetting instance');
}
// Make easy shortcuts to some info.
$sectionName = $settingWrapped->setting->getConfigSectionName();
$settingName = $settingWrapped->setting->getName();
// Get the actual config section.
$config = Config::getInstance();
$section = $config->{$sectionName};
$setting = $section[$settingName];
// Do the delete.
// This does not do the job with value=null or value='': $config->setSetting($sectionName, $settingName, $value).
switch (\true) {
case $settingWrapped->isArray === \true && empty($settingWrapped->arrayVal):
throw new \InvalidArgumentException('This function refuses to delete config arrays. See usage for how to delete config array values.');
case $settingWrapped->isArray === \true:
// Array config values.
$key = array_search($settingWrapped->arrayVal, $setting);
if ($key !== \false) {
unset($setting[$key]);
}
// Save the setting into the section.
$section[$settingName] = $setting;
break;
default:
// Scalar config values.
// Remove the setting from the section.
unset($section[$settingName]);
break;
}
// Save the section into the config.
$config->{$sectionName} = $section;
// Save the config.
$config->forceSave();
return \true;
}
/**
* Build a SystemConfigSetting object from a string.
*
* @param string $settingStr Config setting string to parse.
* @return object A new wrapped SystemConfigSetting object.
*/
public static function parseSettingStr(string $settingStr, object $settingWrapped) : object
{
$matches = [];
if (!preg_match('/^([a-zA-Z0-9_]+)(?:\\.([a-zA-Z0-9_]+))?(?:\\[\\])?(?:\\.(.+))?/', $settingStr, $matches) || empty($matches[1])) {
throw new \InvalidArgumentException("Invalid input string='{$settingStr}': expected section.name or section.name[]");
}
$settingName = $matches[2] ?? null;
$arrayVal = $matches[3] ?? null;
$systemConfigSetting = new SystemConfigSetting(
// Setting name.
$settingName,
// Default value.
'',
// Type.
FieldConfig::TYPE_STRING,
// Plugin name.
'core',
// Section name.
$matches[1]
);
$settingWrappedNew = clone $settingWrapped;
$settingWrappedNew->setting = $systemConfigSetting;
if ($settingWrappedNew->isArray = !empty($arrayVal)) {
$settingWrappedNew->arrayVal = $arrayVal;
}
return $settingWrappedNew;
}
}