From d4de104ec8a529f54fe3270671c7fca1befb967e Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 14:33:45 +0100 Subject: [PATCH 01/22] Suggested fix for issue #15524 --- .../OptionsResolver/OptionsResolver.php | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index bcaea9e4cbc0f..82cd6d50943d5 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -794,19 +794,8 @@ public function offsetGet($option) $valid = false; foreach ($this->allowedTypes[$option] as $type) { - $type = isset(self::$typeAliases[$type]) ? self::$typeAliases[$type] : $type; - - if (function_exists($isFunction = 'is_'.$type)) { - if ($isFunction($value)) { - $valid = true; - break; - } - - continue; - } - - if ($value instanceof $type) { - $valid = true; + $valid = $this->verifyAllowedType($type, $value); + if ($valid) { break; } } @@ -818,7 +807,7 @@ public function offsetGet($option) $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), - $this->formatTypeOf($value) + $this->formatTypeOf($value, $offendingType) )); } } @@ -895,6 +884,44 @@ public function offsetGet($option) return $value; } + /** + * Verify value is of the allowed type. Recursive method to support + * typed array notation like ClassName[], or scalar arrays (int[]) + * + * @param string $type the required allowedType string + * + * @param mixed $value the value + * + * @return bool Whether or not $value if of the allowed type + * + */ + private function verifyAllowedType($type, $value) + { + $valid = false; + + $type = isset(self::$typeAliases[$type]) ? self::$typeAliases[$type] : $type; + + if (substr($type, -2) === '[]') { + if (is_array($value)) { + $subType = substr($type, 0, -2); + foreach ($value as $v) { + $valid = $this->validateType($subType, $v); + if (!$valid) { + break; + } + } + } + } else if (function_exists($isFunction = 'is_'.$type)) { + if ($isFunction($value)) { + $valid = true; + } + } else if ($value instanceof $type) { + $valid = true; + } + + return $valid; + } + /** * Returns whether a resolved option with the given name exists. * From 82f825c4c25850367bce03eaf3c3bf1c505fa34b Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 14:41:41 +0100 Subject: [PATCH 02/22] Fix style issues (apply patch) --- src/Symfony/Component/OptionsResolver/OptionsResolver.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 82cd6d50943d5..446210180f930 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -886,14 +886,12 @@ public function offsetGet($option) /** * Verify value is of the allowed type. Recursive method to support - * typed array notation like ClassName[], or scalar arrays (int[]) + * typed array notation like ClassName[], or scalar arrays (int[]). * - * @param string $type the required allowedType string - * - * @param mixed $value the value + * @param string $type the required allowedType string + * @param mixed $value the value * * @return bool Whether or not $value if of the allowed type - * */ private function verifyAllowedType($type, $value) { From c42c1f497ac4c983227b7ae10475635b6c64da3d Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 14:45:51 +0100 Subject: [PATCH 03/22] Remove unused variable from method call --- src/Symfony/Component/OptionsResolver/OptionsResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 446210180f930..04838caeadf56 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -807,7 +807,7 @@ public function offsetGet($option) $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), - $this->formatTypeOf($value, $offendingType) + $this->formatTypeOf($value) )); } } From 3609d4461d358c4de1e6bfd94a68dbf9c82e7e66 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 15:11:30 +0100 Subject: [PATCH 04/22] Fix recursive call + add simple testcase --- .../Component/OptionsResolver/OptionsResolver.php | 2 +- .../OptionsResolver/Tests/OptionsResolverTest.php | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 04838caeadf56..a17da897b6b6d 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -903,7 +903,7 @@ private function verifyAllowedType($type, $value) if (is_array($value)) { $subType = substr($type, 0, -2); foreach ($value as $v) { - $valid = $this->validateType($subType, $v); + $valid = $this->verifyAllowedType($subType, $v); if (!$valid) { break; } diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 4f21bbaa3b824..d20d2c0a19906 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -498,8 +498,22 @@ public function testFailIfSetAllowedTypesFromLazyOption() $this->resolver->resolve(); } + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "array". + */ + public function testResolveFailsIfInvalidTypedArray() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[]'); + + $this->resolver->resolve(array('foo' => array(new \DateTime))); + } + /** * @dataProvider provideInvalidTypes + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value 42 is expected to be of type "string", but is of type "integer". */ public function testResolveFailsIfInvalidType($actualType, $allowedType, $exceptionMessage) { From 6b1c23034dab678be6c102b33c5ba00410f50896 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 15:12:48 +0100 Subject: [PATCH 05/22] Fix coding style --- .../Component/OptionsResolver/Tests/OptionsResolverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index d20d2c0a19906..1e24cda96e9a1 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -507,7 +507,7 @@ public function testResolveFailsIfInvalidTypedArray() $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[]'); - $this->resolver->resolve(array('foo' => array(new \DateTime))); + $this->resolver->resolve(array('foo' => array(new \DateTime()))); } /** From 0194c25a0e16715a9c9d70958d9cc346e2e45dd6 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 15:20:42 +0100 Subject: [PATCH 06/22] Add 2 successful testcases --- .../Tests/OptionsResolverTest.php | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 1e24cda96e9a1..3eb87139c45c4 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -565,6 +565,46 @@ public function testResolveSucceedsIfValidTypeMultiple() $this->assertNotEmpty($this->resolver->resolve()); } + public function testResolveSucceedsIfTypedArray() + { + $this->resolver->setDefault('foo', null); + $this->resolver->setAllowedTypes('foo', array('null', '\DateTime[]')); + + $data = array( + 'foo' => array( + new \DateTime(), + new \DateTime(), + ) + ); + $result = $this->resolver->resolve($data); + $this->assertEquals($data, $result); + } + + public function testResolveSucceedsNestedTypedArray() + { + $this->resolver->setDefault('foo', null); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $expect = array( + 'foo' => array( + range(1, 10), + ), + ); + $result = $this->resolver->resolve($expect); + $this->assertEquals($expect, $result); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + */ + public function testResolveFailsIfNotInstanceOfClass() + { + $this->resolver->setDefault('foo', 'bar'); + $this->resolver->setAllowedTypes('foo', '\stdClass'); + + $this->resolver->resolve(); + } + public function testResolveSucceedsIfInstanceOfClass() { $this->resolver->setDefault('foo', new \stdClass()); From ca213297cac49413fbf754b9ccbb0b7bc5592170 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 15:22:37 +0100 Subject: [PATCH 07/22] Fix coding style --- .../Component/OptionsResolver/Tests/OptionsResolverTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 3eb87139c45c4..59c8b50c2ec96 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -571,10 +571,10 @@ public function testResolveSucceedsIfTypedArray() $this->resolver->setAllowedTypes('foo', array('null', '\DateTime[]')); $data = array( - 'foo' => array( + 'foo' => array( new \DateTime(), new \DateTime(), - ) + ), ); $result = $this->resolver->resolve($data); $this->assertEquals($data, $result); From 6b639cf06fed5f2c4b2dfc4dda0dbacda97a88be Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 15:38:15 +0100 Subject: [PATCH 08/22] Conform to coding standards (PSR-2 => elseif) --- src/Symfony/Component/OptionsResolver/OptionsResolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index a17da897b6b6d..966cddfcdec9d 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -909,11 +909,11 @@ private function verifyAllowedType($type, $value) } } } - } else if (function_exists($isFunction = 'is_'.$type)) { + } elseif (function_exists($isFunction = 'is_'.$type)) { if ($isFunction($value)) { $valid = true; } - } else if ($value instanceof $type) { + } elseif ($value instanceof $type) { $valid = true; } From 8ff6aac9cd933c434aea4d4523b2640eb7bb1eeb Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 16:38:45 +0100 Subject: [PATCH 09/22] Add early returns in response to review --- .../OptionsResolver/OptionsResolver.php | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 966cddfcdec9d..2b9f6b03f72b7 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -895,29 +895,28 @@ public function offsetGet($option) */ private function verifyAllowedType($type, $value) { - $valid = false; - - $type = isset(self::$typeAliases[$type]) ? self::$typeAliases[$type] : $type; - if (substr($type, -2) === '[]') { - if (is_array($value)) { - $subType = substr($type, 0, -2); - foreach ($value as $v) { - $valid = $this->verifyAllowedType($subType, $v); - if (!$valid) { - break; - } - } + //allowed type is typed array + if (!is_array($value)) { + return false; } - } elseif (function_exists($isFunction = 'is_'.$type)) { - if ($isFunction($value)) { - $valid = true; + $subType = substr($type, 0, -2); + foreach ($value as $v) { + //recursive call -> check subtype + if (!$this->verifyAllowedType($subType, $v)) { + return false; + } } - } elseif ($value instanceof $type) { - $valid = true; + //value was array, subtypes all matched -> allowed type OK + return true; } - return $valid; + $type = isset(self::$typeAliases[$type]) ? self::$typeAliases[$type] : $type; + + if (function_exists($isFunction = 'is_'.$type)) { + return $isFunction($value); + } + return ($value instanceof $type); } /** From 4ab68e0771f13c106676b4548cfdcdd41ce94ba1 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 2 Sep 2015 16:45:13 +0100 Subject: [PATCH 10/22] Add whitespace before return statement --- src/Symfony/Component/OptionsResolver/OptionsResolver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 2b9f6b03f72b7..918b6370f4499 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -916,6 +916,7 @@ private function verifyAllowedType($type, $value) if (function_exists($isFunction = 'is_'.$type)) { return $isFunction($value); } + return ($value instanceof $type); } From 2685d2ee5b7b3b0d929fcf83d6dfdf15dd8813b5 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Tue, 8 Sep 2015 11:01:34 +0100 Subject: [PATCH 11/22] Format complex types in exception message, too --- .../OptionsResolver/OptionsResolver.php | 46 ++++++++++++++++++- .../Tests/OptionsResolverTest.php | 2 +- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 918b6370f4499..9e86e99575a18 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -807,7 +807,7 @@ public function offsetGet($option) $option, $this->formatValue($value), implode('" or "', $this->allowedTypes[$option]), - $this->formatTypeOf($value) + $this->formatTypeOf($value, $option) )); } } @@ -989,14 +989,56 @@ public function count() * non-technical people. * * @param mixed $value The value to return the type of + * @param string $option The option that holds the value * * @return string The type of the value */ - private function formatTypeOf($value) + private function formatTypeOf($value, $option) { + if (is_array($value)) { + foreach ($this->allowedTypes[$option] as $type) { + if (substr($type, -2) === '[]') { + return $this->formatComplexTypeOf($value, $type); + } + } + } return is_object($value) ? get_class($value) : gettype($value); } + /** + * Returns a string represnetation of the complex type of the value + * + * This method should be called in formatTypeOf, if there is a complex allowed type + * for an array value defined to get a more explicit exception message + * + * @param array $value The value to return the complex type of + * @param string $type the expected type + * + * @return string the complex type of the value + */ + private function formatComplexTypeOf(array $value, $type) + { + $suffix = '[]'; + $type = substr($type, 0, -2); + while (substr($type, -2) === '[]') { + $value = array_shift($value); + if (!is_array($value)) { + //expected a nested array, but we've already hit a scalar + break; + } + $type = substr($type, 0, -2); + $suffix .= '[]'; + } + if (is_array($value)) { + $value = array_shift($value);//get first element + } + return sprintf( + '%s%s', + is_object($value) ? get_class($value) : gettype($value), + $suffix + ); + } + /** * Returns a string representation of the value. * diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 59c8b50c2ec96..1f351d5596950 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -500,7 +500,7 @@ public function testFailIfSetAllowedTypesFromLazyOption() /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "array". + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "DateTime[]". */ public function testResolveFailsIfInvalidTypedArray() { From 1fc5ddb6c58b299e7a0bf901488ee90589215e79 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Tue, 8 Sep 2015 11:03:47 +0100 Subject: [PATCH 12/22] Fix coding style --- .../Component/OptionsResolver/OptionsResolver.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 9e86e99575a18..4be0e7183fe8a 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -988,7 +988,7 @@ public function count() * parameters should usually not be included in messages aimed at * non-technical people. * - * @param mixed $value The value to return the type of + * @param mixed $value The value to return the type of * @param string $option The option that holds the value * * @return string The type of the value @@ -1002,17 +1002,18 @@ private function formatTypeOf($value, $option) } } } + return is_object($value) ? get_class($value) : gettype($value); } /** - * Returns a string represnetation of the complex type of the value + * Returns a string represnetation of the complex type of the value. * * This method should be called in formatTypeOf, if there is a complex allowed type * for an array value defined to get a more explicit exception message * - * @param array $value The value to return the complex type of - * @param string $type the expected type + * @param array $value The value to return the complex type of + * @param string $type the expected type * * @return string the complex type of the value */ @@ -1032,6 +1033,7 @@ private function formatComplexTypeOf(array $value, $type) if (is_array($value)) { $value = array_shift($value);//get first element } + return sprintf( '%s%s', is_object($value) ? get_class($value) : gettype($value), From 0368246c5e33ca86af54968237ce1699ad387f33 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Tue, 8 Sep 2015 11:27:35 +0100 Subject: [PATCH 13/22] Fix typo --- src/Symfony/Component/OptionsResolver/OptionsResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 4be0e7183fe8a..22bb10f615191 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -1007,7 +1007,7 @@ private function formatTypeOf($value, $option) } /** - * Returns a string represnetation of the complex type of the value. + * Returns a string representation of the complex type of the value. * * This method should be called in formatTypeOf, if there is a complex allowed type * for an array value defined to get a more explicit exception message From 82d463f2e9d9b6bd9bd886c96a03ca2aef0c5417 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Tue, 8 Sep 2015 18:20:02 +0100 Subject: [PATCH 14/22] Format the exception message clearer: in case complex typed arrays contain valid and invalid arguments, list all the values they contain + add test case --- .../OptionsResolver/OptionsResolver.php | 17 +++++++++++++---- .../Tests/OptionsResolverTest.php | 17 +++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 22bb10f615191..7a54a711d4004 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -993,9 +993,9 @@ public function count() * * @return string The type of the value */ - private function formatTypeOf($value, $option) + private function formatTypeOf($value, $option = null) { - if (is_array($value)) { + if (is_array($value) && $option) { foreach ($this->allowedTypes[$option] as $type) { if (substr($type, -2) === '[]') { return $this->formatComplexTypeOf($value, $type); @@ -1031,12 +1031,21 @@ private function formatComplexTypeOf(array $value, $type) $suffix .= '[]'; } if (is_array($value)) { - $value = array_shift($value);//get first element + $subTypes = array(); + foreach ($value as $v) { + $v = $this->formatTypeOf($v); + if (!isset($subTypes[$v])) { + $subTypes[$v] = $v;//build unique map from the off + } + } + $vType = implode('|', $subTypes); + } else { + $vType = is_object($value) ? get_class($value) : gettype($value); } return sprintf( '%s%s', - is_object($value) ? get_class($value) : gettype($value), + $vType, $suffix ); } diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 1f351d5596950..ea32f1a99ecc2 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -512,6 +512,23 @@ public function testResolveFailsIfInvalidTypedArray() /** * @dataProvider provideInvalidTypes + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]". + */ + public function testResolveFailsIfTypedArrayContainsInvalidTypes() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[]'); + $values = range(1, 5); + $values[] = new \stdClass; + $values[] = array(); + $values[] = new \DateTime(); + $values[] = 123; + + $this->resolver->resolve(array('foo' => $values)); + } + + /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException * @expectedExceptionMessage The option "foo" with value 42 is expected to be of type "string", but is of type "integer". */ From 56163711deb6d7ffb387e89aafadd1b01de38a77 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Tue, 8 Sep 2015 18:21:21 +0100 Subject: [PATCH 15/22] Coding style -> new + () --- .../Component/OptionsResolver/Tests/OptionsResolverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index ea32f1a99ecc2..510d0602f8aec 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -520,7 +520,7 @@ public function testResolveFailsIfTypedArrayContainsInvalidTypes() $this->resolver->setDefined('foo'); $this->resolver->setAllowedTypes('foo', 'int[]'); $values = range(1, 5); - $values[] = new \stdClass; + $values[] = new \stdClass(); $values[] = array(); $values[] = new \DateTime(); $values[] = 123; From 7c2613e385e433a4000d5e9fbd3015940b607789 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Tue, 8 Sep 2015 18:47:07 +0100 Subject: [PATCH 16/22] Add another test case, mainly to force another build --- .../Tests/OptionsResolverTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 510d0602f8aec..6d58aed683c52 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -512,6 +512,24 @@ public function testResolveFailsIfInvalidTypedArray() /** * @dataProvider provideInvalidTypes + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but is of type "double[][]". + */ + public function testResolveFailsWithCorrectLevelsButWrongScalar() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $this->resolver->resolve( + array( + 'foo' => array( + array(1.2), + ), + ) + ); + } + + /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]". */ From 33b1e9c22d78d1b6f7cfef8f3bfdcf2a311ce6f4 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Tue, 8 Sep 2015 18:48:30 +0100 Subject: [PATCH 17/22] Fix indentation --- .../Component/OptionsResolver/Tests/OptionsResolverTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 6d58aed683c52..9d04f0fd891bb 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -522,13 +522,13 @@ public function testResolveFailsWithCorrectLevelsButWrongScalar() $this->resolver->resolve( array( - 'foo' => array( + 'foo' => array( array(1.2), ), ) ); } - + /** * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]". From 4579f177f9c007a9288dba6e10ca66c5347cae8c Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Fri, 11 Sep 2015 16:48:50 +0100 Subject: [PATCH 18/22] Add document feature in changelog --- src/Symfony/Component/OptionsResolver/CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Symfony/Component/OptionsResolver/CHANGELOG.md b/src/Symfony/Component/OptionsResolver/CHANGELOG.md index 5f6d15b2c7ddc..17f910d79bba7 100644 --- a/src/Symfony/Component/OptionsResolver/CHANGELOG.md +++ b/src/Symfony/Component/OptionsResolver/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +2.8.0 +----- + + * Added typed array support as allowed type + 2.6.0 ----- From b18975777e5e074e981658835e62444b0267d73a Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 16 Dec 2015 15:19:10 +0000 Subject: [PATCH 19/22] Use mb_substr instead of substr --- .../Component/OptionsResolver/OptionsResolver.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 7a54a711d4004..fd188c0965184 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -895,12 +895,12 @@ public function offsetGet($option) */ private function verifyAllowedType($type, $value) { - if (substr($type, -2) === '[]') { + if (mb_substr($type, -2) === '[]') { //allowed type is typed array if (!is_array($value)) { return false; } - $subType = substr($type, 0, -2); + $subType = mb_substr($type, 0, -2); foreach ($value as $v) { //recursive call -> check subtype if (!$this->verifyAllowedType($subType, $v)) { @@ -997,7 +997,7 @@ private function formatTypeOf($value, $option = null) { if (is_array($value) && $option) { foreach ($this->allowedTypes[$option] as $type) { - if (substr($type, -2) === '[]') { + if (mb_substr($type, -2) === '[]') { return $this->formatComplexTypeOf($value, $type); } } @@ -1020,14 +1020,14 @@ private function formatTypeOf($value, $option = null) private function formatComplexTypeOf(array $value, $type) { $suffix = '[]'; - $type = substr($type, 0, -2); - while (substr($type, -2) === '[]') { + $type = mb_substr($type, 0, -2); + while (mb_substr($type, -2) === '[]') { $value = array_shift($value); if (!is_array($value)) { //expected a nested array, but we've already hit a scalar break; } - $type = substr($type, 0, -2); + $type = mb_substr($type, 0, -2); $suffix .= '[]'; } if (is_array($value)) { From 84c895418c9f80ec168246c0aa719f23822932b0 Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Wed, 16 Dec 2015 15:21:55 +0000 Subject: [PATCH 20/22] Update changelog -> merge master => target is now 3.1 --- src/Symfony/Component/OptionsResolver/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/OptionsResolver/CHANGELOG.md b/src/Symfony/Component/OptionsResolver/CHANGELOG.md index 17f910d79bba7..136e032bfab46 100644 --- a/src/Symfony/Component/OptionsResolver/CHANGELOG.md +++ b/src/Symfony/Component/OptionsResolver/CHANGELOG.md @@ -1,7 +1,7 @@ CHANGELOG ========= -2.8.0 +3.1.0 ----- * Added typed array support as allowed type From c79c17ba1057045832aaee2a0ecbcec52721eb6b Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Fri, 18 Dec 2015 17:40:39 +0000 Subject: [PATCH 21/22] Fix typo in docblock --- src/Symfony/Component/OptionsResolver/OptionsResolver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index fd188c0965184..31ce65acb28f8 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -891,7 +891,7 @@ public function offsetGet($option) * @param string $type the required allowedType string * @param mixed $value the value * - * @return bool Whether or not $value if of the allowed type + * @return bool Whether the $value is of the allowed type */ private function verifyAllowedType($type, $value) { From c03ccdf8a6c2f1cfa235025dc0c825beae78fb6e Mon Sep 17 00:00:00 2001 From: Elias Van Ootegem Date: Fri, 18 Dec 2015 18:01:03 +0000 Subject: [PATCH 22/22] Fix dataprovider annotation -> rebase conflict --- .../Component/OptionsResolver/Tests/OptionsResolverTest.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 9d04f0fd891bb..9f6446da27cb1 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -511,7 +511,6 @@ public function testResolveFailsIfInvalidTypedArray() } /** - * @dataProvider provideInvalidTypes * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException * @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but is of type "double[][]". */ @@ -547,8 +546,7 @@ public function testResolveFailsIfTypedArrayContainsInvalidTypes() } /** - * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * @expectedExceptionMessage The option "foo" with value 42 is expected to be of type "string", but is of type "integer". + * @dataProvider provideInvalidTypes */ public function testResolveFailsIfInvalidType($actualType, $allowedType, $exceptionMessage) {