From dca2b69d29961744ff284aeac4f444a8c88d80b5 Mon Sep 17 00:00:00 2001 From: Oleg Golovakhin Date: Mon, 28 May 2018 23:51:54 +0300 Subject: [PATCH] [OptionResolver] resolve arrays --- .../OptionsResolver/OptionsResolver.php | 48 +++++++++++-- .../Tests/OptionsResolverTest.php | 70 +++++++++++++++++++ 2 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 8edd6b5c89791..8cabf6fc83c8d 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -878,8 +878,10 @@ public function offsetGet($option) private function verifyTypes($type, $value, array &$invalidTypes) { if ('[]' === substr($type, -2) && is_array($value)) { - $originalType = $type; - $type = substr($type, 0, -2); + if ($this->verifyArrayType($type, $value, $invalidTypes, $type)) { + return true; + } + $invalidValues = array_filter( // Filter out valid values, keeping invalid values in the resulting array $value, function ($value) use ($type) { @@ -891,7 +893,7 @@ function ($value) use ($type) { return true; } - $invalidTypes[$this->formatTypeOf($value, $originalType)] = true; + $invalidTypes[$this->formatTypeOf($value, $type)] = true; return false; } @@ -907,6 +909,44 @@ function ($value) use ($type) { return false; } + /** + * @param $type + * @param array $value + * @param array $invalidTypes + * + * @return bool + */ + private function verifyArrayType($type, array $value, array &$invalidTypes) + { + $type = substr($type, 0, -2); + + if ('[]' === substr($type, -2)) { + $success = true; + foreach ($value as $item) { + if (!is_array($item)) { + $invalidTypes[$this->formatTypeOf($item, null)] = true; + + return false; + } + + if (!$this->verifyArrayType($type, $item, $invalidTypes)) { + $success = false; + } + } + + return $success; + } + + $invalid = array_filter( // Filter out valid values, keeping invalid values in the resulting array + $value, + function ($value) use ($type) { + return !self::isValueValidType($type, $value); + } + ); + + return !count($invalid); + } + /** * Returns whether a resolved option with the given name exists. * @@ -975,7 +1015,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 */ private function formatTypeOf($value, ?string $type): string { diff --git a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php index 440af8b5787e6..df5f607685da3 100644 --- a/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php +++ b/src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php @@ -1650,4 +1650,74 @@ public function testCountFailsOutsideResolve() count($this->resolver); } + + public function testNestedArrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + 1, 2, + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array(1, 2), + ), + ) + )); + } + + public function testNested2Arrays() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'int[][][][]'); + + $this->assertEquals(array( + 'foo' => array( + array( + array( + array( + 1, 2, + ), + ), + ), + ), + ), $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + )); + } + + /** + * @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException + * @expectedExceptionMessage The option "foo" with value array is expected to be of type "float[][][][]", but is of type "integer[][][][]". + */ + public function testNestedArraysError() + { + $this->resolver->setDefined('foo'); + $this->resolver->setAllowedTypes('foo', 'float[][][][]'); + + $this->resolver->resolve( + array( + 'foo' => array( + array( + array( + array(1, 2), + ), + ), + ), + ) + ); + } }