Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d066a23

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

File tree

3 files changed

+156
-15
lines changed
Filter options

3 files changed

+156
-15
lines changed

‎src/Symfony/Component/OptionsResolver/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/OptionsResolver/CHANGELOG.md
+1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/OptionsResolver/OptionsResolver.php
+70-15Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php
+85Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.