From 1be68a752a6bc9190c492a54cd996481b9d9367e Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Mon, 28 Oct 2019 15:33:22 -0400 Subject: [PATCH] Fix an error message to be more accurate --- .../OptionsResolver/OptionsResolver.php | 30 ++++++++++--------- .../Tests/OptionsResolverTest.php | 23 ++++++++------ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index f47aef128451..e9de7800b654 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -917,7 +917,7 @@ public function offsetGet($option/*, bool $triggerDeprecation = true*/) // Validate the type of the resolved option if (isset($this->allowedTypes[$option])) { - $valid = false; + $valid = true; $invalidTypes = []; foreach ($this->allowedTypes[$option] as $type) { @@ -929,13 +929,18 @@ public function offsetGet($option/*, bool $triggerDeprecation = true*/) } if (!$valid) { - $keys = array_keys($invalidTypes); - - if (1 === \count($keys) && '[]' === substr($keys[0], -2)) { - 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])); + $fmtActualValue = $this->formatValue($value); + $fmtAllowedTypes = implode('" or "', $this->allowedTypes[$option]); + $fmtProvidedTypes = implode('|', array_keys($invalidTypes)); + $allowedContainsArrayType = \count(array_filter($this->allowedTypes[$option], static function ($item) { + return '[]' === substr(self::$typeAliases[$item] ?? $item, -2); + })) > 0; + + if (\is_array($value) && $allowedContainsArrayType) { + 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, $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes)); } - 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)))); + throw new InvalidOptionsException(sprintf('The option "%s" with value %s is expected to be of type "%s", but is of type "%s".', $option, $fmtActualValue, $fmtAllowedTypes, $fmtProvidedTypes)); } } @@ -1040,26 +1045,23 @@ private function verifyTypes(string $type, $value, array &$invalidTypes, int $le { if (\is_array($value) && '[]' === substr($type, -2)) { $type = substr($type, 0, -2); + $valid = true; foreach ($value as $val) { if (!$this->verifyTypes($type, $val, $invalidTypes, $level + 1)) { - return false; + $valid = false; } } - return true; + return $valid; } if (('null' === $type && null === $value) || (\function_exists($func = 'is_'.$type) && $func($value)) || $value instanceof $type) { return true; } - if (!$invalidTypes) { - $suffix = ''; - while (\strlen($suffix) < $level * 2) { - $suffix .= '[]'; - } - $invalidTypes[$this->formatTypeOf($value).$suffix] = true; + if (!$invalidTypes || $level > 0) { + $invalidTypes[$this->formatTypeOf($value)] = true; } return false; diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 43ebf408e90b..0211b22b26c0 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -776,7 +776,7 @@ public function testFailIfSetAllowedTypesFromLazyOption() public function testResolveFailsIfInvalidTypedArray() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[]'); @@ -796,7 +796,7 @@ public function testResolveFailsWithNonArray() public function testResolveFailsIfTypedArrayContainsInvalidTypes() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass|array|DateTime".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[]'); $values = range(1, 5); @@ -811,7 +811,7 @@ public function testResolveFailsIfTypedArrayContainsInvalidTypes() public function testResolveFailsWithCorrectLevelsButWrongScalar() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[][]'); @@ -847,6 +847,11 @@ public function provideInvalidTypes() [42, 'string', 'The option "option" with value 42 is expected to be of type "string", but is of type "integer".'], [null, 'string', 'The option "option" with value null is expected to be of type "string", but is of type "NULL".'], ['bar', '\stdClass', 'The option "option" with value "bar" is expected to be of type "\stdClass", but is of type "string".'], + [['foo', 12], 'string[]', 'The option "option" with value array is expected to be of type "string[]", but one of the elements is of type "integer".'], + [123, ['string[]', 'string'], 'The option "option" with value 123 is expected to be of type "string[]" or "string", but is of type "integer".'], + [[null], ['string[]', 'string'], 'The option "option" with value array is expected to be of type "string[]" or "string", but one of the elements is of type "NULL".'], + [['string', null], ['string[]', 'string'], 'The option "option" with value array is expected to be of type "string[]" or "string", but one of the elements is of type "NULL".'], + [[\stdClass::class], ['string'], 'The option "option" with value array is expected to be of type "string", but is of type "array".'], ]; } @@ -1898,7 +1903,7 @@ public function testNested2Arrays() public function testNestedArraysException() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'float[][][][]'); @@ -1916,7 +1921,7 @@ public function testNestedArraysException() public function testNestedArrayException1() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean|string|array".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[][]'); $this->resolver->resolve([ @@ -1929,7 +1934,7 @@ public function testNestedArrayException1() public function testNestedArrayException2() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean|string|array".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[][]'); $this->resolver->resolve([ @@ -1942,7 +1947,7 @@ public function testNestedArrayException2() public function testNestedArrayException3() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string|integer".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'string[][][]'); $this->resolver->resolve([ @@ -1955,7 +1960,7 @@ public function testNestedArrayException3() public function testNestedArrayException4() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'string[][][]'); $this->resolver->resolve([ @@ -1969,7 +1974,7 @@ public function testNestedArrayException4() public function testNestedArrayException5() { $this->expectException('Symfony\Component\OptionsResolver\Exception\InvalidOptionsException'); - $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]".'); + $this->expectExceptionMessage('The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array".'); $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'string[]'); $this->resolver->resolve([