Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit d066a23

Browse files
pierredupnicolas-grekas
authored andcommitted
Support array of types in allowed type
1 parent 0f5e38c commit d066a23

File tree

3 files changed

+156
-15
lines changed

3 files changed

+156
-15
lines changed

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 `OptionsResolverIntrospector` to inspect options definitions inside an `OptionsResolver` instance
8+
* added array of types support in allowed types (e.g int[])
89

910
2.6.0
1011
-----

src/Symfony/Component/OptionsResolver/OptionsResolver.php

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -792,21 +792,12 @@ public function offsetGet($option)
792792
// Validate the type of the resolved option
793793
if (isset($this->allowedTypes[$option])) {
794794
$valid = false;
795+
$invalidTypes = array();
795796

796797
foreach ($this->allowedTypes[$option] as $type) {
797798
$type = isset(self::$typeAliases[$type]) ? self::$typeAliases[$type] : $type;
798799

799-
if (function_exists($isFunction = 'is_'.$type)) {
800-
if ($isFunction($value)) {
801-
$valid = true;
802-
break;
803-
}
804-
805-
continue;
806-
}
807-
808-
if ($value instanceof $type) {
809-
$valid = true;
800+
if ($valid = $this->verifyTypes($type, $value, $invalidTypes)) {
810801
break;
811802
}
812803
}
@@ -818,7 +809,7 @@ public function offsetGet($option)
818809
$option,
819810
$this->formatValue($value),
820811
implode('" or "', $this->allowedTypes[$option]),
821-
$this->formatTypeOf($value)
812+
implode('|', array_keys($invalidTypes))
822813
));
823814
}
824815
}
@@ -895,6 +886,45 @@ public function offsetGet($option)
895886
return $value;
896887
}
897888

889+
/**
890+
* @param string $type
891+
* @param mixed $value
892+
* @param array &$invalidTypes
893+
*
894+
* @return bool
895+
*/
896+
private function verifyTypes($type, $value, array &$invalidTypes)
897+
{
898+
if ('[]' === substr($type, -2) && is_array($value)) {
899+
$originalType = $type;
900+
$type = substr($type, 0, -2);
901+
$invalidValues = array_filter( // Filter out valid values, keeping invalid values in the resulting array
902+
$value,
903+
function ($value) use ($type) {
904+
return (function_exists($isFunction = 'is_'.$type) && !$isFunction($value)) || !$value instanceof $type;
905+
}
906+
);
907+
908+
if (!$invalidValues) {
909+
return true;
910+
}
911+
912+
$invalidTypes[$this->formatTypeOf($value, $originalType)] = true;
913+
914+
return false;
915+
}
916+
917+
if ((function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type) {
918+
return true;
919+
}
920+
921+
if (!$invalidTypes) {
922+
$invalidTypes[$this->formatTypeOf($value, null)] = true;
923+
}
924+
925+
return false;
926+
}
927+
898928
/**
899929
* Returns whether a resolved option with the given name exists.
900930
*
@@ -963,13 +993,38 @@ public function count()
963993
* parameters should usually not be included in messages aimed at
964994
* non-technical people.
965995
*
966-
* @param mixed $value The value to return the type of
996+
* @param mixed $value The value to return the type of
997+
* @param string $type
967998
*
968999
* @return string The type of the value
9691000
*/
970-
private function formatTypeOf($value)
1001+
private function formatTypeOf($value, $type)
9711002
{
972-
return is_object($value) ? get_class($value) : gettype($value);
1003+
$suffix = '';
1004+
1005+
if ('[]' === substr($type, -2)) {
1006+
$suffix = '[]';
1007+
$type = substr($type, 0, -2);
1008+
while ('[]' === substr($type, -2)) {
1009+
$type = substr($type, 0, -2);
1010+
$value = array_shift($value);
1011+
if (!is_array($value)) {
1012+
break;
1013+
}
1014+
$suffix .= '[]';
1015+
}
1016+
1017+
if (is_array($value)) {
1018+
$subTypes = array();
1019+
foreach ($value as $val) {
1020+
$subTypes[$this->formatTypeOf($val, null)] = true;
1021+
}
1022+
1023+
return implode('|', array_keys($subTypes)).$suffix;
1024+
}
1025+
}
1026+
1027+
return (is_object($value) ? get_class($value) : gettype($value)).$suffix;
9731028
}
9741029

9751030
/**

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

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,65 @@ public function testFailIfSetAllowedTypesFromLazyOption()
500500
$this->resolver->resolve();
501501
}
502502

503+
/**
504+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
505+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "DateTime[]".
506+
*/
507+
public function testResolveFailsIfInvalidTypedArray()
508+
{
509+
$this->resolver->setDefined('foo');
510+
$this->resolver->setAllowedTypes('foo', 'int[]');
511+
512+
$this->resolver->resolve(array('foo' => array(new \DateTime())));
513+
}
514+
515+
/**
516+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
517+
* @expectedExceptionMessage The option "foo" with value "bar" is expected to be of type "int[]", but is of type "string".
518+
*/
519+
public function testResolveFailsWithNonArray()
520+
{
521+
$this->resolver->setDefined('foo');
522+
$this->resolver->setAllowedTypes('foo', 'int[]');
523+
524+
$this->resolver->resolve(array('foo' => 'bar'));
525+
}
526+
527+
/**
528+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
529+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]".
530+
*/
531+
public function testResolveFailsIfTypedArrayContainsInvalidTypes()
532+
{
533+
$this->resolver->setDefined('foo');
534+
$this->resolver->setAllowedTypes('foo', 'int[]');
535+
$values = range(1, 5);
536+
$values[] = new \stdClass();
537+
$values[] = array();
538+
$values[] = new \DateTime();
539+
$values[] = 123;
540+
541+
$this->resolver->resolve(array('foo' => $values));
542+
}
543+
544+
/**
545+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
546+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but is of type "double[][]".
547+
*/
548+
public function testResolveFailsWithCorrectLevelsButWrongScalar()
549+
{
550+
$this->resolver->setDefined('foo');
551+
$this->resolver->setAllowedTypes('foo', 'int[][]');
552+
553+
$this->resolver->resolve(
554+
array(
555+
'foo' => array(
556+
array(1.2),
557+
),
558+
)
559+
);
560+
}
561+
503562
/**
504563
* @dataProvider provideInvalidTypes
505564
*/
@@ -568,6 +627,32 @@ public function testResolveSucceedsIfInstanceOfClass()
568627
$this->assertNotEmpty($this->resolver->resolve());
569628
}
570629

630+
public function testResolveSucceedsIfTypedArray()
631+
{
632+
$this->resolver->setDefault('foo', null);
633+
$this->resolver->setAllowedTypes('foo', array('null', 'DateTime[]'));
634+
635+
$data = array(
636+
'foo' => array(
637+
new \DateTime(),
638+
new \DateTime(),
639+
),
640+
);
641+
$result = $this->resolver->resolve($data);
642+
$this->assertEquals($data, $result);
643+
}
644+
645+
/**
646+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
647+
*/
648+
public function testResolveFailsIfNotInstanceOfClass()
649+
{
650+
$this->resolver->setDefault('foo', 'bar');
651+
$this->resolver->setAllowedTypes('foo', '\stdClass');
652+
653+
$this->resolver->resolve();
654+
}
655+
571656
////////////////////////////////////////////////////////////////////////////
572657
// addAllowedTypes()
573658
////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)