Skip to content

Commit d61ec62

Browse files
committed
refactor: split off value casting out of config:system:set command
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent d161e07 commit d61ec62

6 files changed

Lines changed: 151 additions & 125 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
6+
* SPDX-License-Identifier: AGPL-3.0-or-later
7+
*/
8+
9+
namespace OC\Core\Command\Config\System;
10+
11+
class CastHelper {
12+
/**
13+
* @return array{value: mixed, readable-value: string}
14+
*/
15+
public function castValue(?string $value, string $type): array {
16+
switch ($type) {
17+
case 'integer':
18+
case 'int':
19+
if (!is_numeric($value)) {
20+
throw new \InvalidArgumentException('Non-numeric value specified');
21+
}
22+
return [
23+
'value' => (int)$value,
24+
'readable-value' => 'integer ' . (int)$value,
25+
];
26+
27+
case 'double':
28+
case 'float':
29+
if (!is_numeric($value)) {
30+
throw new \InvalidArgumentException('Non-numeric value specified');
31+
}
32+
return [
33+
'value' => (float)$value,
34+
'readable-value' => 'double ' . (float)$value,
35+
];
36+
37+
case 'boolean':
38+
case 'bool':
39+
$value = strtolower($value);
40+
return match ($value) {
41+
'true' => [
42+
'value' => true,
43+
'readable-value' => 'boolean ' . $value,
44+
],
45+
'false' => [
46+
'value' => false,
47+
'readable-value' => 'boolean ' . $value,
48+
],
49+
default => throw new \InvalidArgumentException('Unable to parse value as boolean'),
50+
};
51+
52+
case 'null':
53+
return [
54+
'value' => null,
55+
'readable-value' => 'null',
56+
];
57+
58+
case 'string':
59+
$value = (string)$value;
60+
return [
61+
'value' => $value,
62+
'readable-value' => ($value === '') ? 'empty string' : 'string ' . $value,
63+
];
64+
65+
case 'json':
66+
$value = json_decode($value, true);
67+
return [
68+
'value' => $value,
69+
'readable-value' => 'json ' . json_encode($value),
70+
];
71+
72+
default:
73+
throw new \InvalidArgumentException('Invalid type');
74+
}
75+
}
76+
}

core/Command/Config/System/SetConfig.php

Lines changed: 2 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
class SetConfig extends Base {
1818
public function __construct(
1919
SystemConfig $systemConfig,
20+
private CastHelper $castHelper,
2021
) {
2122
parent::__construct($systemConfig);
2223
}
@@ -57,7 +58,7 @@ protected function configure() {
5758
protected function execute(InputInterface $input, OutputInterface $output): int {
5859
$configNames = $input->getArgument('name');
5960
$configName = $configNames[0];
60-
$configValue = $this->castValue($input->getOption('value'), $input->getOption('type'));
61+
$configValue = $this->castHelper->castValue($input->getOption('value'), $input->getOption('type'));
6162
$updateOnly = $input->getOption('update-only');
6263

6364
if (count($configNames) > 1) {
@@ -80,80 +81,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
8081
return 0;
8182
}
8283

83-
/**
84-
* @param string $value
85-
* @param string $type
86-
* @return mixed
87-
* @throws \InvalidArgumentException
88-
*/
89-
protected function castValue($value, $type) {
90-
switch ($type) {
91-
case 'integer':
92-
case 'int':
93-
if (!is_numeric($value)) {
94-
throw new \InvalidArgumentException('Non-numeric value specified');
95-
}
96-
return [
97-
'value' => (int)$value,
98-
'readable-value' => 'integer ' . (int)$value,
99-
];
100-
101-
case 'double':
102-
case 'float':
103-
if (!is_numeric($value)) {
104-
throw new \InvalidArgumentException('Non-numeric value specified');
105-
}
106-
return [
107-
'value' => (float)$value,
108-
'readable-value' => 'double ' . (float)$value,
109-
];
110-
111-
case 'boolean':
112-
case 'bool':
113-
$value = strtolower($value);
114-
switch ($value) {
115-
case 'true':
116-
return [
117-
'value' => true,
118-
'readable-value' => 'boolean ' . $value,
119-
];
120-
121-
case 'false':
122-
return [
123-
'value' => false,
124-
'readable-value' => 'boolean ' . $value,
125-
];
126-
127-
default:
128-
throw new \InvalidArgumentException('Unable to parse value as boolean');
129-
}
130-
131-
// no break
132-
case 'null':
133-
return [
134-
'value' => null,
135-
'readable-value' => 'null',
136-
];
137-
138-
case 'string':
139-
$value = (string)$value;
140-
return [
141-
'value' => $value,
142-
'readable-value' => ($value === '') ? 'empty string' : 'string ' . $value,
143-
];
144-
145-
case 'json':
146-
$value = json_decode($value, true);
147-
return [
148-
'value' => $value,
149-
'readable-value' => 'json ' . json_encode($value),
150-
];
151-
152-
default:
153-
throw new \InvalidArgumentException('Invalid type');
154-
}
155-
}
156-
15784
/**
15885
* @param array $configNames
15986
* @param mixed $existingValues

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,6 +1234,7 @@
12341234
'OC\\Core\\Command\\Config\\Import' => $baseDir . '/core/Command/Config/Import.php',
12351235
'OC\\Core\\Command\\Config\\ListConfigs' => $baseDir . '/core/Command/Config/ListConfigs.php',
12361236
'OC\\Core\\Command\\Config\\System\\Base' => $baseDir . '/core/Command/Config/System/Base.php',
1237+
'OC\\Core\\Command\\Config\\System\\CastHelper' => $baseDir . '/core/Command/Config/System/CastHelper.php',
12371238
'OC\\Core\\Command\\Config\\System\\DeleteConfig' => $baseDir . '/core/Command/Config/System/DeleteConfig.php',
12381239
'OC\\Core\\Command\\Config\\System\\GetConfig' => $baseDir . '/core/Command/Config/System/GetConfig.php',
12391240
'OC\\Core\\Command\\Config\\System\\SetConfig' => $baseDir . '/core/Command/Config/System/SetConfig.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,6 +1275,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
12751275
'OC\\Core\\Command\\Config\\Import' => __DIR__ . '/../../..' . '/core/Command/Config/Import.php',
12761276
'OC\\Core\\Command\\Config\\ListConfigs' => __DIR__ . '/../../..' . '/core/Command/Config/ListConfigs.php',
12771277
'OC\\Core\\Command\\Config\\System\\Base' => __DIR__ . '/../../..' . '/core/Command/Config/System/Base.php',
1278+
'OC\\Core\\Command\\Config\\System\\CastHelper' => __DIR__ . '/../../..' . '/core/Command/Config/System/CastHelper.php',
12781279
'OC\\Core\\Command\\Config\\System\\DeleteConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/DeleteConfig.php',
12791280
'OC\\Core\\Command\\Config\\System\\GetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/GetConfig.php',
12801281
'OC\\Core\\Command\\Config\\System\\SetConfig' => __DIR__ . '/../../..' . '/core/Command/Config/System/SetConfig.php',
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
/**
3+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
4+
* SPDX-License-Identifier: AGPL-3.0-only
5+
*/
6+
7+
namespace Core\Command\Config\System;
8+
9+
use OC\Core\Command\Config\System\CastHelper;
10+
use Test\TestCase;
11+
12+
class CastHelperTest extends TestCase {
13+
private CastHelper $castHelper;
14+
15+
protected function setUp(): void {
16+
parent::setUp();
17+
$this->castHelper = new CastHelper();
18+
}
19+
20+
public static function castValueProvider(): array {
21+
return [
22+
[null, 'string', ['value' => '', 'readable-value' => 'empty string']],
23+
24+
['abc', 'string', ['value' => 'abc', 'readable-value' => 'string abc']],
25+
26+
['123', 'integer', ['value' => 123, 'readable-value' => 'integer 123']],
27+
['456', 'int', ['value' => 456, 'readable-value' => 'integer 456']],
28+
29+
['2.25', 'double', ['value' => 2.25, 'readable-value' => 'double 2.25']],
30+
['0.5', 'float', ['value' => 0.5, 'readable-value' => 'double 0.5']],
31+
32+
['', 'null', ['value' => null, 'readable-value' => 'null']],
33+
34+
['true', 'boolean', ['value' => true, 'readable-value' => 'boolean true']],
35+
['false', 'bool', ['value' => false, 'readable-value' => 'boolean false']],
36+
];
37+
}
38+
39+
/**
40+
* @dataProvider castValueProvider
41+
*/
42+
public function testCastValue($value, $type, $expectedValue): void {
43+
$this->assertSame(
44+
$expectedValue,
45+
$this->castHelper->castValue($value, $type)
46+
);
47+
}
48+
49+
public static function castValueInvalidProvider(): array {
50+
return [
51+
['123', 'foobar'],
52+
53+
[null, 'integer'],
54+
['abc', 'integer'],
55+
['76ggg', 'double'],
56+
['true', 'float'],
57+
['foobar', 'boolean'],
58+
];
59+
}
60+
61+
/**
62+
* @dataProvider castValueInvalidProvider
63+
*/
64+
public function testCastValueInvalid($value, $type): void {
65+
$this->expectException(\InvalidArgumentException::class);
66+
67+
$this->castHelper->castValue($value, $type);
68+
}
69+
}

tests/Core/Command/Config/System/SetConfigTest.php

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace Tests\Core\Command\Config\System;
99

10+
use OC\Core\Command\Config\System\CastHelper;
1011
use OC\Core\Command\Config\System\SetConfig;
1112
use OC\SystemConfig;
1213
use Symfony\Component\Console\Input\InputInterface;
@@ -35,7 +36,7 @@ protected function setUp(): void {
3536
$this->consoleOutput = $this->getMockBuilder(OutputInterface::class)->getMock();
3637

3738
/** @var \OC\SystemConfig $systemConfig */
38-
$this->command = new SetConfig($systemConfig);
39+
$this->command = new SetConfig($systemConfig, new CastHelper());
3940
}
4041

4142

@@ -112,53 +113,4 @@ public function testSetUpdateOnly($configNames, $existingData): void {
112113

113114
$this->invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]);
114115
}
115-
116-
public static function castValueProvider(): array {
117-
return [
118-
[null, 'string', ['value' => '', 'readable-value' => 'empty string']],
119-
120-
['abc', 'string', ['value' => 'abc', 'readable-value' => 'string abc']],
121-
122-
['123', 'integer', ['value' => 123, 'readable-value' => 'integer 123']],
123-
['456', 'int', ['value' => 456, 'readable-value' => 'integer 456']],
124-
125-
['2.25', 'double', ['value' => 2.25, 'readable-value' => 'double 2.25']],
126-
['0.5', 'float', ['value' => 0.5, 'readable-value' => 'double 0.5']],
127-
128-
['', 'null', ['value' => null, 'readable-value' => 'null']],
129-
130-
['true', 'boolean', ['value' => true, 'readable-value' => 'boolean true']],
131-
['false', 'bool', ['value' => false, 'readable-value' => 'boolean false']],
132-
];
133-
}
134-
135-
/**
136-
* @dataProvider castValueProvider
137-
*/
138-
public function testCastValue($value, $type, $expectedValue): void {
139-
$this->assertSame($expectedValue,
140-
$this->invokePrivate($this->command, 'castValue', [$value, $type])
141-
);
142-
}
143-
144-
public static function castValueInvalidProvider(): array {
145-
return [
146-
['123', 'foobar'],
147-
148-
[null, 'integer'],
149-
['abc', 'integer'],
150-
['76ggg', 'double'],
151-
['true', 'float'],
152-
['foobar', 'boolean'],
153-
];
154-
}
155-
156-
/**
157-
* @dataProvider castValueInvalidProvider
158-
*/
159-
public function testCastValueInvalid($value, $type): void {
160-
$this->expectException(\InvalidArgumentException::class);
161-
162-
$this->invokePrivate($this->command, 'castValue', [$value, $type]);
163-
}
164116
}

0 commit comments

Comments
 (0)