8000 feature #35400 [RFC][DX][OptionsResolver] Allow setting info message … · symfony/symfony@9eb7cb1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9eb7cb1

Browse files
committed
feature #35400 [RFC][DX][OptionsResolver] Allow setting info message per option (yceruto)
This PR was merged into the 5.1-dev branch. Discussion ---------- [RFC][DX][OptionsResolver] Allow setting info message per option | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | TODO This is a DX proposal that will help in debugging/errors to better understand the meaning of one defined option. This is how you'd use it: ```php $resolver = new OptionsResolver(); $resolver->setDefined('date'); $resolver->setAllowedTypes('date', \DateTime::class); $resolver->setInfo('date', 'A future date'); // <-- NEW // ... ``` This information may be useful for those options where their name cannot be intuitive enough, or their purpose is too complex. Here is an example (based on the example above): ```php // ... $resolver->setAllowedValues('date', static function ($value): bool { return $value >= new \DateTime('now'); }); ``` So, if you introduce a date value that does not match the criteria, you will get this error message: **Before:** ``` The option "date" with value DateTime is invalid. ``` Note that the allowed value is not printable in this case, hence the error message cannot be clear at all. **After:** ``` The option "date" with value DateTime is invalid. Info: A future date. ``` Although a more accurate error message can be triggered within the `\Closure` if desired. Also you'll see this info message on `debug:form` command (see tests), then you have in advance the informative description of any option. What do you think? Commits ------- 0477a06 Allow setting one info message per option
2 parents 332fa65 + 0477a06 commit 9eb7cb1

13 files changed

+146
-4
lines changed

src/Symfony/Component/Form/Console/Descriptor/Descriptor.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,15 @@ protected function collectOptions(ResolvedFormTypeInterface $type)
108108

109109
protected function getOptionDefinition(OptionsResolver $optionsResolver, string $option)
110110
{
111-
$definition = [
111+
$definition = [];
112+
113+
if ($info = $optionsResolver->getInfo($option)) {
114+
$definition = [
115+
'info' => $info,
116+
];
117+
}
118+
119+
$definition += [
112120
'required' => $optionsResolver->isRequired($option),
113121
'deprecated' => $optionsResolver->isDeprecated($option),
114122
];

src/Symfony/Component/Form/Console/Descriptor/JsonDescriptor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ protected function describeOption(OptionsResolver $optionsResolver, array $optio
7373
}
7474
}
7575
$map += [
76+
'info' => 'info',
7677
'required' => 'required',
7778
'default' => 'default',
7879
'allowed_types' => 'allowedTypes',

src/Symfony/Component/Form/Console/Descriptor/TextDescriptor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ protected function describeOption(OptionsResolver $optionsResolver, array $optio
114114
];
115115
}
116116
$map += [
117+
'Info' => 'info',
117118
'Required' => 'required',
118119
'Default' => 'default',
119120
'Allowed types' => 'allowedTypes',

src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ public function testDebugCustomFormTypeOption()
151151
Symfony\Component\Form\Tests\Command\FooType (foo)
152152
==================================================
153153
154+
---------------- -----------%s
155+
Info "Info" %s
154156
---------------- -----------%s
155157
Required true %s
156158
---------------- -----------%s
@@ -209,5 +211,6 @@ public function configureOptions(OptionsResolver $resolver)
209211
$resolver->setNormalizer('foo', function (Options $options, $value) {
210212
return (string) $value;
211213
});
214+
$resolver->setInfo('foo', 'Info');
212215
}
213216
}

src/Symfony/Component/Form/Tests/Fixtures/Descriptor/default_option_with_normalizer.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Symfony\Component\Form\Extension\Core\Type\ChoiceType (choice_translation_domain)
33
=================================================================================
44

5+
---------------- -----------%s
6+
Info - %s
57
---------------- -----------%s
68
Required false %s
79
---------------- -----------%s

src/Symfony/Component/Form/Tests/Fixtures/Descriptor/deprecated_option.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (bar)
55
Deprecated true
66
--------------------- -----------------------------------
77
Deprecation message "The option "bar" is deprecated."
8+
--------------------- -----------------------------------
9+
Info -
810
--------------------- -----------------------------------
911
Required false
1012
--------------------- -----------------------------------
@@ -15,4 +17,4 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (bar)
1517
Allowed values -
1618
--------------------- -----------------------------------
1719
Normalizers -
18-
--------------------- -----------------------------------
20+
--------------------- -----------------------------------

src/Symfony/Component/Form/Tests/Fixtures/Descriptor/overridden_option_with_default_closures.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ Symfony\Component\Form\Tests\Console\Descriptor\FooType (empty_data)
22
====================================================================
33

44
---------------- ----------------------%s
5+
Info - %s
6+
---------------- ----------------------%s
57
Required false %s
68
---------------- ----------------------%s
79
Default Value: null %s

src/Symfony/Component/Form/Tests/Fixtures/Descriptor/required_option_with_allowed_values.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Symfony\Component\Form\Tests\Console\Descriptor\FooType (foo)
33
=============================================================
44

5+
---------------- -----------%s
6+
Info - %s
57
---------------- -----------%s
68
Required true %s
79
---------------- -----------%s

src/Symfony/Component/Form/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"symfony/deprecation-contracts": "^2.1",
2121
"symfony/event-dispatcher": "^4.4|^5.0",
2222
"symfony/intl": "^4.4|^5.0",
23-
"symfony/options-resolver": "^5.0",
23+
"symfony/options-resolver": "^5.1",
2424
"symfony/polyfill-ctype": "~1.8",
2525
"symfony/polyfill-mbstring": "~1.0",
2626
"symfony/property-access": "^5.0",

src/Symfony/Component/OptionsResolver/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
-----
66

77
* added fluent configuration of options using `OptionResolver::define()`
8+
* added `setInfo()` and `getInfo()` methods
89

910
5.0.0
1011
-----

src/Symfony/Component/OptionsResolver/OptionConfigurator.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,18 @@ public function required(): self
124124

125125
return $this;
126126
}
127+
128+
/**
129+
* Sets an info message for an option.
130+
*
131+
* @return $this
132+
*
133+
* @throws AccessException If called from a lazy option or normalizer
134+
*/
135+
public function info(string $info): self
136+
{
137+
$this->resolver->setInfo($this->name, $info);
138+
139+
return $this;
140+
}
127141
}

src/Symfony/Component/OptionsResolver/OptionsResolver.php

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ class OptionsResolver implements Options
7171
*/
7272
private $allowedTypes = [];
7373

74+
/**
75+
* A list of info messages for each option.
76 10000 +
*/
77+
private $info = [];
78+
7479
/**
7580
* A list of closures for evaluating lazy options.
7681
*/
@@ -715,6 +720,41 @@ public function define(string $option): OptionConfigurator
715720
return new OptionConfigurator($option, $this);
716721
}
717722

723+
/**
724+
* Sets an info message for an option.
725+
*
726+
* @return $this
727+
*
728+
* @throws UndefinedOptionsException If the option is undefined
729+
* @throws AccessException If called from a lazy option or normalizer
730+
*/
731+
public function setInfo(string $option, string $info): self
732+
{
733+
if ($this->locked) {
734+
throw new AccessException('The Info message cannot be set from a lazy option or normalizer.');
735+
}
736+
737+
if (!isset($this->defined[$option])) {
738+
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
739+
}
740+
741+
$this->info[$option] = $info;
742+
743+
return $this;
744+
}
745+
746+
/**
747+
* Gets the info message for an option.
748+
*/
749+
public function getInfo(string $option): ?string
750+
{
751+
if (!isset($this->defined[$option])) {
752+
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
753+
}
754+
755+
return $this->info[$option] ?? null;
756+
}
757+
718758
/**
719759
* Removes the option with the given name.
720760
*
@@ -734,7 +774,7 @@ public function remove($optionNames)
734774

735775
foreach ((array) $optionNames as $option) {
736776
unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option]);
737-
unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option]);
777+
unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option], $this->info[$option]);
738778
}
739779

740780
return $this;
@@ -763,6 +803,7 @@ public function clear()
763803
$this->allowedTypes = [];
764804
$this->allowedValues = [];
765805
$this->deprecated = [];
806+
$this->info = [];
766807

767808
return $this;
768809
}
@@ -996,6 +1037,10 @@ public function offsetGet($option, bool $triggerDeprecation = true)
9961037
);
9971038
}
9981039

1040+
if (isset($this->info[$option])) {
1041+
$message .= sprintf(' Info: %s.', $ 6851 this->info[$option]);
1042+
}
1043+
9991044
throw new InvalidOptionsException($message);
10001045
}
10011046
}

src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
use PHPUnit\Framework\Assert;
1515
use PHPUnit\Framework\TestCase;
1616
use Symfony\Component\OptionsResolver\Debug\OptionsResolverIntrospector;
17+
use Symfony\Component\OptionsResolver\Exception\AccessException;
1718
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
19+
use Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException;
1820
use Symfony\Component\OptionsResolver\Options;
1921
use Symfony\Component\OptionsResolver\OptionsResolver;
2022

@@ -2399,6 +2401,7 @@ public function testResolveOptionsDefinedByOptionConfigurator()
23992401
->normalize(static function (Options $options, $value) {
24002402
return $value;
24012403
})
2404+
->info('info message')
24022405
;
24032406
$introspector = new OptionsResolverIntrospector($this->resolver);
24042407

@@ -2409,5 +2412,63 @@ public function testResolveOptionsDefinedByOptionConfigurator()
24092412
$this->assertSame(['string', 'bool'], $introspector->getAllowedTypes('foo'));
24102413
$this->assertSame(['bar', 'zab'], $introspector->getAllowedValues('foo'));
24112414
$this->assertCount(1, $introspector->getNormalizers('foo'));
2415+
$this->assertSame('info message', $this->resolver->getInfo('foo'));
2416+
}
2417+
2418+
public function testGetInfo()
2419+
{
2420+
$info = 'The option info message';
2421+
$this->resolver->setDefined('foo');
2422+
$this->resolver->setInfo('foo', $info);
2423+
2424+
$this->assertSame($info, $this->resolver->getInfo('foo'));
2425+
}
2426+
2427+
public function testSetInfoOnNormalization()
2428+
{
2429+
$this->expectException(AccessException::class);
2430+
$this->expectExceptionMessage('The Info message cannot be set from a lazy option or normalizer.');
2431+
2432+
$this->resolver->setDefined('foo');
2433+
$this->resolver->setNormalizer('foo', static function (Options $options, $value) {
2434+
$options->setInfo('foo', 'Info');
2435+
});
2436+
2437+
$this->resolver->resolve(['foo' => 'bar']);
2438+
}
2439+
2440+
public function testSetInfoOnUndefinedOption()
2441+
{
2442+
$this->expectException(UndefinedOptionsException::class);
2443+
$this->expectExceptionMessage('The option "bar" does not exist. Defined options are: "foo".');
2444+
2445+
$this->resolver->setDefined('foo');
2446+
$this->resolver->setInfo('bar', 'The option info message');
2447+
}
2448+
2449+
public function testGetInfoOnUndefinedOption2()
2450+
{
2451+
$this->expectException(UndefinedOptionsException::class);
2452+
$this->expectExceptionMessage('The option "bar" does not exist. Defined options are: "foo".');
2453+
2454+
$this->resolver->setDefined('foo');
2455+
$this->resolver->getInfo('bar');
2456+
}
2457+
2458+
public function testInfoOnInvalidValue()
2459+
{
2460+
$this->expectException(InvalidOptionsException::class);
2461+
$this->expectExceptionMessage('The option "expires" with value DateTime is invalid. Info: A future date time.');
2462+
2463+
$this->resolver
2464+
->setRequired('expires')
2465+
->setInfo('expires', 'A future date time')
2466+
->setAllowedTypes('expires', \DateTime::class)
2467+
->setAllowedValues('expires', static function ($value) {
2468+
return $value >= new \DateTime('now');
2469+
})
2470+
;
2471+
2472+
$this->resolver->resolve(['expires' => new \DateTime('-1 hour')]);
24122473
}
24132474
}

0 commit comments

Comments
 (0)
0