Skip to content

Commit d47c623

Browse files
Merge branch '4.4'
* 4.4: (21 commits) [appveyor] exclude tty group [HttpFoundation] Add types to private/final/internal methods and constructors. Add types to private/final/internal methods and constructors. SCA: minor code tweaks Tweak output [FrameworkBundle] Added --sort option for TranslationUpdateCommand [HttpClient] fallbackto CURLMOPT_MAXCONNECTS when CURLMOPT_MAX_HOST_CONNECTIONS is not available [DI] generate preload.php file for PHP 7.4 in cache folder Allow version 2 of the contracts package. [Serializer] Allow multi-dimenstion object array in AbstractObjectNormalizer fixed typo [HttpKernel] Fix Apache mod_expires Session Cache-Control issue deprecated not passing dash symbol (-) to STDIN commands [VarDumper] display ellipsed FQCN for nested classes [VarDumper] Display fully qualified title [Mailer] Change the syntax for DSNs using failover or roundrobin Removed workaround introduced in 4.3 [Console] Added support for definition list [OptionsResolver] Display full nested options hierarchy in exceptions New welcome page ...
2 parents e0602f8 + 75ad4ee commit d47c623

File tree

2 files changed

+41
-21
lines changed

2 files changed

+41
-21
lines changed

OptionsResolver.php

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ class OptionsResolver implements Options
103103
*/
104104
private $locked = false;
105105

106+
private $parentsOptions = [];
107+
106108
private static $typeAliases = [
107109
'boolean' => 'bool',
108110
'integer' => 'int',
@@ -423,7 +425,7 @@ public function setDeprecated(string $option, $deprecationMessage = 'The option
423425
}
424426

425427
if (!isset($this->defined[$option])) {
426-
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist, defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
428+
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist, defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
427429
}
428430

429431
if (!\is_string($deprecationMessage) && !$deprecationMessage instanceof \Closure) {
@@ -481,7 +483,7 @@ public function setNormalizer(string $option, \Closure $normalizer)
481483
}
482484

483485
if (!isset($this->defined[$option])) {
484-
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
486+
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
485487
}
486488

487489
$this->normalizers[$option] = [$normalizer];
@@ -526,7 +528,7 @@ public function addNormalizer(string $option, \Closure $normalizer, bool $forceP
526528
}
527529

528530
if (!isset($this->defined[$option])) {
529-
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
531+
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
530532
}
531533

532534
if ($forcePrepend) {
@@ -569,7 +571,7 @@ public function setAllowedValues(string $option, $allowedValues)
569571
}
570572

571573
if (!isset($this->defined[$option])) {
572-
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
574+
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
573575
}
574576

575577
$this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : [$allowedValues];
@@ -610,7 +612,7 @@ public function addAllowedValues(string $option, $allowedValues)
610612
}
611613

612614
if (!isset($this->defined[$option])) {
613-
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
615+
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
614616
}
615617

616618
if (!\is_array($allowedValues)) {
@@ -651,7 +653,7 @@ public function setAllowedTypes(string $option, $allowedTypes)
651653
}
652654

653655
if (!isset($this->defined[$option])) {
654-
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
656+
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
655657
}
656658

657659
$this->allowedTypes[$option] = (array) $allowedTypes;
@@ -686,7 +688,7 @@ public function addAllowedTypes(string $option, $allowedTypes)
686688
}
687689

688690
if (!isset($this->defined[$option])) {
689-
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
691+
throw new UndefinedOptionsException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
690692
}
691693

692694
if (!isset($this->allowedTypes[$option])) {
@@ -793,7 +795,7 @@ public function resolve(array $options = [])
793795
ksort($clone->defined);
794796
ksort($diff);
795797

796-
throw new UndefinedOptionsException(sprintf((\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', implode('", "', array_keys($diff)), implode('", "', array_keys($clone->defined))));
798+
throw new UndefinedOptionsException(sprintf((\count($diff) > 1 ? 'The options "%s" do not exist.' : 'The option "%s" does not exist.').' Defined options are: "%s".', $this->formatOptions(array_keys($diff)), implode('", "', array_keys($clone->defined))));
797799
}
798800

799801
// Override options set by the user
@@ -809,7 +811,7 @@ public function resolve(array $options = [])
809811
if (\count($diff) > 0) {
810812
ksort($diff);
811813

812-
throw new MissingOptionsException(sprintf(\count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', implode('", "', array_keys($diff))));
814+
throw new MissingOptionsException(sprintf(\count($diff) > 1 ? 'The required options "%s" are missing.' : 'The required option "%s" is missing.', $this->formatOptions(array_keys($diff))));
813815
}
814816

815817
// Lock the container
@@ -858,10 +860,10 @@ public function offsetGet($option, bool $triggerDeprecation = true)
858860
// Check whether the option is set at all
859861
if (!isset($this->defaults[$option]) && !\array_key_exists($option, $this->defaults)) {
860862
if (!isset($this->defined[$option])) {
861-
throw new NoSuchOptionException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $option, implode('", "', array_keys($this->defined))));
863+
throw new NoSuchOptionException(sprintf('The option "%s" does not exist. Defined options are: "%s".', $this->formatOptions([$option]), implode('", "', array_keys($this->defined))));
862864
}
863865

864-
throw new NoSuchOptionException(sprintf('The optional option "%s" has no value set. You should make sure it is set with "isset" before reading it.', $option));
866+
throw new NoSuchOptionException(sprintf('The optional option "%s" has no value set. You should make sure it is set with "isset" before reading it.', $this->formatOptions([$option])));
865867
}
866868

867869
$value = $this->defaults[$option];
@@ -870,17 +872,19 @@ public function offsetGet($option, bool $triggerDeprecation = true)
870872
if (isset($this->nested[$option])) {
871873
// If the closure is already being called, we have a cyclic dependency
872874
if (isset($this->calling[$option])) {
873-
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
875+
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling))));
874876
}
875877

876878
if (!\is_array($value)) {
877-
throw new InvalidOptionsException(sprintf('The nested option "%s" with value %s is expected to be of type array, but is of type "%s".', $option, $this->formatValue($value), $this->formatTypeOf($value)));
879+
throw new InvalidOptionsException(sprintf('The nested option "%s" with value %s is expected to be of type array, but is of type "%s".', $this->formatOptions([$option]), $this->formatValue($value), $this->formatTypeOf($value)));
878880
}
879881

880882
// The following section must be protected from cyclic calls.
881883
$this->calling[$option] = true;
882884
try {
883885
$resolver = new self();
886+
$resolver->parentsOptions = $this->parentsOptions;
887+
$resolver->parentsOptions[] = $option;
884888
foreach ($this->nested[$option] as $closure) {
885889
$closure($resolver, $this);
886890
}
@@ -895,7 +899,7 @@ public function offsetGet($option, bool $triggerDeprecation = true)
895899
// If the closure is already being called, we have a cyclic
896900
// dependency
897901
if (isset($this->calling[$option])) {
898-
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
902+
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling))));
899903
}
900904

901905
// The following section must be protected from cyclic
@@ -930,10 +934,10 @@ public function offsetGet($option, bool $triggerDeprecation = true)
930934
$keys = array_keys($invalidTypes);
931935

932936
if (1 === \count($keys) && '[]' === substr($keys[0], -2)) {
933-
throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), $keys[0]));
937+
throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but one of the elements is of type "%s".', $this->formatOptions([$option]), $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), $keys[0]));
934938
}
935939

936-
throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), implode('|', array_keys($invalidTypes))));
940+
throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $this->formatOptions([$option]), $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), implode('|', array_keys($invalidTypes))));
937941
}
938942
}
939943

@@ -987,7 +991,7 @@ public function offsetGet($option, bool $triggerDeprecation = true)
987991
if ($deprecationMessage instanceof \Closure) {
988992
// If the closure is already being called, we have a cyclic dependency
989993
if (isset($this->calling[$option])) {
990-
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
994+
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling))));
991995
}
992996

993997
$this->calling[$option] = true;
@@ -1010,7 +1014,7 @@ public function offsetGet($option, bool $triggerDeprecation = true)
10101014
// If the closure is already being called, we have a cyclic
10111015
// dependency
10121016
if (isset($this->calling[$option])) {
1013-
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', implode('", "', array_keys($this->calling))));
1017+
throw new OptionDefinitionException(sprintf('The options "%s" have a cyclic dependency.', $this->formatOptions(array_keys($this->calling))));
10141018
}
10151019

10161020
// The following section must be protected from cyclic
@@ -1193,4 +1197,20 @@ private function formatValues(array $values): string
11931197

11941198
return implode(', ', $values);
11951199
}
1200+
1201+
private function formatOptions(array $options): string
1202+
{
1203+
if ($this->parentsOptions) {
1204+
$prefix = array_shift($this->parentsOptions);
1205+
if ($this->parentsOptions) {
1206+
$prefix .= sprintf('[%s]', implode('][', $this->parentsOptions));
1207+
}
1208+
1209+
$options = array_map(static function (string $option) use ($prefix): string {
1210+
return sprintf('%s[%s]', $prefix, $option);
1211+
}, $options);
1212+
}
1213+
1214+
return implode('", "', $options);
1215+
}
11961216
}

Tests/OptionsResolverTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,7 +1993,7 @@ public function testIsNestedOption()
19931993
public function testFailsIfUndefinedNestedOption()
19941994
{
19951995
$this->expectException('Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException');
1996-
$this->expectExceptionMessage('The option "foo" does not exist. Defined options are: "host", "port".');
1996+
$this->expectExceptionMessage('The option "database[foo]" does not exist. Defined options are: "host", "port".');
19971997
$this->resolver->setDefaults([
19981998
'name' => 'default',
19991999
'database' => function (OptionsResolver $resolver) {
@@ -2008,7 +2008,7 @@ public function testFailsIfUndefinedNestedOption()
20082008
public function testFailsIfMissingRequiredNestedOption()
20092009
{
20102010
$this->expectException('Symfony\Component\OptionsResolver\Exception\MissingOptionsException');
2011-
$this->expectExceptionMessage('The required option "host" is missing.');
2011+
$this->expectExceptionMessage('The required option "database[host]" is missing.');
20122012
$this->resolver->setDefaults([
20132013
'name' => 'default',
20142014
'database' => function (OptionsResolver $resolver) {
@@ -2023,7 +2023,7 @@ public function testFailsIfMissingRequiredNestedOption()
20232023
public function testFailsIfInvalidTypeNestedOption()
20242024
{
20252025
$this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException');
2026-
$this->expectExceptionMessage('The option "logging" with value null is expected to be of type "bool", but is of type "NULL".');
2026+
$this->expectExceptionMessage('The option "database[logging]" with value null is expected to be of type "bool", but is of type "NULL".');
20272027
$this->resolver->setDefaults([
20282028
'name' => 'default',
20292029
'database' => function (OptionsResolver $resolver) {

0 commit comments

Comments
 (0)