SetConfig.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace OC\Core\Command\Config\System;
  8. use OC\SystemConfig;
  9. use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
  10. use Symfony\Component\Console\Input\InputArgument;
  11. use Symfony\Component\Console\Input\InputInterface;
  12. use Symfony\Component\Console\Input\InputOption;
  13. use Symfony\Component\Console\Output\OutputInterface;
  14. class SetConfig extends Base {
  15. public function __construct(
  16. SystemConfig $systemConfig,
  17. ) {
  18. parent::__construct($systemConfig);
  19. }
  20. protected function configure() {
  21. parent::configure();
  22. $this
  23. ->setName('config:system:set')
  24. ->setDescription('Set a system config value')
  25. ->addArgument(
  26. 'name',
  27. InputArgument::REQUIRED | InputArgument::IS_ARRAY,
  28. 'Name of the config parameter, specify multiple for array parameter'
  29. )
  30. ->addOption(
  31. 'type',
  32. null,
  33. InputOption::VALUE_REQUIRED,
  34. 'Value type [string, integer, double, boolean]',
  35. 'string'
  36. )
  37. ->addOption(
  38. 'value',
  39. null,
  40. InputOption::VALUE_REQUIRED,
  41. 'The new value of the config'
  42. )
  43. ->addOption(
  44. 'update-only',
  45. null,
  46. InputOption::VALUE_NONE,
  47. 'Only updates the value, if it is not set before, it is not being added'
  48. )
  49. ;
  50. }
  51. protected function execute(InputInterface $input, OutputInterface $output): int {
  52. $configNames = $input->getArgument('name');
  53. $configName = $configNames[0];
  54. $configValue = $this->castValue($input->getOption('value'), $input->getOption('type'));
  55. $updateOnly = $input->getOption('update-only');
  56. if (count($configNames) > 1) {
  57. $existingValue = $this->systemConfig->getValue($configName);
  58. $newValue = $this->mergeArrayValue(
  59. array_slice($configNames, 1), $existingValue, $configValue['value'], $updateOnly
  60. );
  61. $this->systemConfig->setValue($configName, $newValue);
  62. } else {
  63. if ($updateOnly && !in_array($configName, $this->systemConfig->getKeys(), true)) {
  64. throw new \UnexpectedValueException('Config parameter does not exist');
  65. }
  66. $this->systemConfig->setValue($configName, $configValue['value']);
  67. }
  68. $output->writeln('<info>System config value ' . implode(' => ', $configNames) . ' set to ' . $configValue['readable-value'] . '</info>');
  69. return 0;
  70. }
  71. /**
  72. * @param string $value
  73. * @param string $type
  74. * @return mixed
  75. * @throws \InvalidArgumentException
  76. */
  77. protected function castValue($value, $type) {
  78. switch ($type) {
  79. case 'integer':
  80. case 'int':
  81. if (!is_numeric($value)) {
  82. throw new \InvalidArgumentException('Non-numeric value specified');
  83. }
  84. return [
  85. 'value' => (int)$value,
  86. 'readable-value' => 'integer ' . (int)$value,
  87. ];
  88. case 'double':
  89. case 'float':
  90. if (!is_numeric($value)) {
  91. throw new \InvalidArgumentException('Non-numeric value specified');
  92. }
  93. return [
  94. 'value' => (float)$value,
  95. 'readable-value' => 'double ' . (float)$value,
  96. ];
  97. case 'boolean':
  98. case 'bool':
  99. $value = strtolower($value);
  100. switch ($value) {
  101. case 'true':
  102. return [
  103. 'value' => true,
  104. 'readable-value' => 'boolean ' . $value,
  105. ];
  106. case 'false':
  107. return [
  108. 'value' => false,
  109. 'readable-value' => 'boolean ' . $value,
  110. ];
  111. default:
  112. throw new \InvalidArgumentException('Unable to parse value as boolean');
  113. }
  114. // no break
  115. case 'null':
  116. return [
  117. 'value' => null,
  118. 'readable-value' => 'null',
  119. ];
  120. case 'string':
  121. $value = (string)$value;
  122. return [
  123. 'value' => $value,
  124. 'readable-value' => ($value === '') ? 'empty string' : 'string ' . $value,
  125. ];
  126. default:
  127. throw new \InvalidArgumentException('Invalid type');
  128. }
  129. }
  130. /**
  131. * @param array $configNames
  132. * @param mixed $existingValues
  133. * @param mixed $value
  134. * @param bool $updateOnly
  135. * @return array merged value
  136. * @throws \UnexpectedValueException
  137. */
  138. protected function mergeArrayValue(array $configNames, $existingValues, $value, $updateOnly) {
  139. $configName = array_shift($configNames);
  140. if (!is_array($existingValues)) {
  141. $existingValues = [];
  142. }
  143. if (!empty($configNames)) {
  144. if (isset($existingValues[$configName])) {
  145. $existingValue = $existingValues[$configName];
  146. } else {
  147. $existingValue = [];
  148. }
  149. $existingValues[$configName] = $this->mergeArrayValue($configNames, $existingValue, $value, $updateOnly);
  150. } else {
  151. if (!isset($existingValues[$configName]) && $updateOnly) {
  152. throw new \UnexpectedValueException('Config parameter does not exist');
  153. }
  154. $existingValues[$configName] = $value;
  155. }
  156. return $existingValues;
  157. }
  158. /**
  159. * @param string $optionName
  160. * @param CompletionContext $context
  161. * @return string[]
  162. */
  163. public function completeOptionValues($optionName, CompletionContext $context) {
  164. if ($optionName === 'type') {
  165. return ['string', 'integer', 'double', 'boolean'];
  166. }
  167. return parent::completeOptionValues($optionName, $context);
  168. }
  169. }