Skip to content

Commit 0169801

Browse files
committed
introduce resolveOnly to not always merge all defaults
1 parent a117950 commit 0169801

3 files changed

Lines changed: 94 additions & 39 deletions

File tree

src/Options.php

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public function __construct(array $options = [])
5858

5959
$this->configureOptions($this->resolver);
6060

61-
$this->options = $this->resolveWithLogger($options);
61+
$this->options = $this->resolver->resolve($options, $this->getLoggerOrNullLogger($options));
6262
}
6363

6464
/**
@@ -268,10 +268,15 @@ public function getLogger(): ?LoggerInterface
268268

269269
/**
270270
* Helper to always get a logger instance even if it was not set.
271+
*
272+
* it will check for a logger using the following order:
273+
* 1. the passed `$options`
274+
* 2. already configured `logger` option
275+
* 3. `NullLogger` as fallback
271276
*/
272-
public function getLoggerOrNullLogger(): LoggerInterface
277+
public function getLoggerOrNullLogger(array $options = []): LoggerInterface
273278
{
274-
return $this->getLogger() ?? new NullLogger();
279+
return $options['logger'] ?? $this->getLogger() ?? new NullLogger();
275280
}
276281

277282
/**
@@ -1167,24 +1172,6 @@ private function validateContextLinesOption(?int $contextLines): bool
11671172
return $contextLines === null || $contextLines >= 0;
11681173
}
11691174

1170-
/**
1171-
* Calls the resolve method of the internal resolver with a logger so that
1172-
* validation failures can be logged and investigated.
1173-
*
1174-
* @param array<string, mixed> $options
1175-
*
1176-
* @return array<string, mixed>
1177-
*/
1178-
private function resolveWithLogger(array $options = []): array
1179-
{
1180-
/**
1181-
* @var LoggerInterface $logger
1182-
*/
1183-
$logger = $options['logger'] ?? $this->getLoggerOrNullLogger();
1184-
1185-
return $this->resolver->resolve($options, $logger);
1186-
}
1187-
11881175
/**
11891176
* Merges the passed options with the current options and resolves them.
11901177
* The result is stored back onto the class field.
@@ -1193,9 +1180,9 @@ private function resolveWithLogger(array $options = []): array
11931180
*/
11941181
private function updateOptions(array $override = []): self
11951182
{
1196-
$options = array_merge($this->options, $override);
1183+
$resolved = $this->resolver->resolveOnly($override, $this->getLoggerOrNullLogger($override));
11971184

1198-
$this->options = $this->resolveWithLogger($options);
1185+
$this->options = array_merge($this->options, $resolved);
11991186

12001187
return $this;
12011188
}

src/OptionsResolver.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,21 @@ public function setNormalizer(string $path, callable $normalizer): void
109109
*/
110110
public function resolve(array $options = [], ?LoggerInterface $logger = null): array
111111
{
112-
$result = $this->defaults;
112+
return array_merge($this->defaults, $this->resolveOnly($options, $logger));
113+
}
114+
115+
/**
116+
* Resolves passed options against the defaults but in contrast to {@see self::resolve}, it will not merge
117+
* defaults into the result. This means that the returning array will always be equal or smaller than
118+
* the input array.
119+
*
120+
* @param array<string, mixed> $options
121+
*
122+
* @return array<string, mixed>
123+
*/
124+
public function resolveOnly(array $options = [], ?LoggerInterface $logger = null): array
125+
{
126+
$result = [];
113127

114128
foreach ($options as $option => $value) {
115129
if (!\array_key_exists($option, $this->defaults)) {
@@ -124,6 +138,7 @@ public function resolve(array $options = [], ?LoggerInterface $logger = null): a
124138
if ($logger !== null) {
125139
$logger->debug(\sprintf('Invalid value for option "%s". Using default value.', $option));
126140
}
141+
$result[$option] = $this->defaults[$option];
127142
continue;
128143
}
129144

tests/OptionResolverTest.php

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,17 @@ public function testNormalizers(array $defaults, array $options, array $normaliz
9898
$this->assertEquals($result, $expectedResult);
9999
}
100100

101+
/**
102+
* @dataProvider resolveOnlyTestProvider
103+
*/
104+
public function testResolveOnly(array $defaults, array $options, array $expectedResult): void
105+
{
106+
$resolver = new OptionsResolver();
107+
$resolver->setDefaults($defaults);
108+
$result = $resolver->resolveOnly($options);
109+
$this->assertEquals($expectedResult, $result);
110+
}
111+
101112
public function testNormalizerReturnsInvalidType()
102113
{
103114
$resolver = new OptionsResolver();
@@ -242,7 +253,30 @@ public function allowedValueTestProvider(): \Generator
242253
];
243254
}
244255

245-
public function normalizerTestProvider()
256+
public function resolveOnlyTestProvider(): \Generator
257+
{
258+
$defaults = ['foo' => 'bar', 'test' => 'example', 'a' => 'b'];
259+
260+
yield 'Result only contains passed values' => [
261+
$defaults,
262+
['foo' => 'test'],
263+
['foo' => 'test'],
264+
];
265+
266+
yield 'Result does not contain invalid key' => [
267+
$defaults,
268+
['foo' => 'test', 'example' => 'abcde'],
269+
['foo' => 'test'],
270+
];
271+
272+
yield 'Empty input returns empty output' => [
273+
$defaults,
274+
[],
275+
[],
276+
];
277+
}
278+
279+
public function normalizerTestProvider(): \Generator
246280
{
247281
yield 'Normalizes successful' => [
248282
['a' => 'b'],
@@ -254,21 +288,9 @@ public function normalizerTestProvider()
254288
];
255289
}
256290

257-
public function testDebugLogsProduced()
291+
public function testDebugLogsProduced(): void
258292
{
259-
$logger = new class extends AbstractLogger {
260-
private $logs = [];
261-
262-
public function log($level, $message, array $context = []): void
263-
{
264-
$this->logs[] = $message;
265-
}
266-
267-
public function getLogs(): array
268-
{
269-
return $this->logs;
270-
}
271-
};
293+
$logger = new TestLogger();
272294
$resolver = new OptionsResolver();
273295
$resolver->setAllowedValues('test', ['foo']);
274296
$resolver->setDefaults([
@@ -282,4 +304,35 @@ public function getLogs(): array
282304
$this->assertCount(2, $logger->getLogs());
283305
$this->assertEquals('Invalid value for option "test". Using default value.', $logger->getLogs()[1]);
284306
}
307+
308+
public function testDebugLogsProducedForResolveOnly(): void
309+
{
310+
$logger = new TestLogger();
311+
$resolver = new OptionsResolver();
312+
$resolver->setAllowedValues('test', ['foo']);
313+
$resolver->setDefaults(['test' => 'foo', 'abc' => 'def', 'bar' => 'baz']);
314+
315+
$resolver->resolveOnly(['example' => 'test'], $logger);
316+
$this->assertCount(1, $logger->getLogs());
317+
$this->assertEquals('Option "example" does not exist and will be ignored', $logger->getLogs()[0]);
318+
319+
$resolver->resolveOnly(['test' => 'abc'], $logger);
320+
$this->assertCount(2, $logger->getLogs());
321+
$this->assertEquals('Invalid value for option "test". Using default value.', $logger->getLogs()[1]);
322+
}
323+
}
324+
325+
class TestLogger extends AbstractLogger
326+
{
327+
private $logs = [];
328+
329+
public function log($level, $message, array $context = []): void
330+
{
331+
$this->logs[] = $message;
332+
}
333+
334+
public function getLogs(): array
335+
{
336+
return $this->logs;
337+
}
285338
}

0 commit comments

Comments
 (0)