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 6d4812e

Browse filesBrowse files
Doctrsnicolas-grekas
authored andcommitted
[OptionResolver] resolve arrays
1 parent 98934e4 commit 6d4812e
Copy full SHA for 6d4812e

File tree

2 files changed

+223
-36
lines changed
Filter options

2 files changed

+223
-36
lines changed

‎src/Symfony/Component/OptionsResolver/OptionsResolver.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/OptionsResolver/OptionsResolver.php
+73-33Lines changed: 73 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ public function setAllowedValues($option, $allowedValues)
433433
));
434434
}
435435

436-
$this->allowedValues[$option] = is_array($allowedValues) ? $allowedValues : array($allowedValues);
436+
$this->allowedValues[$option] = \is_array($allowedValues) ? $allowedValues : array($allowedValues);
437437

438438
// Make sure the option is processed
439439
unset($this->resolved[$option]);
@@ -785,14 +785,13 @@ public function offsetGet($option)
785785
}
786786

787787
if (!$valid) {
788-
throw new InvalidOptionsException(sprintf(
789-
'The option "%s" with value %s is expected to be of type '.
790-
'"%s", but is of type "%s".',
791-
$option,
792-
$this->formatValue($value),
793-
implode('" or "', $this->allowedTypes[$option]),
794-
implode('|', array_keys($invalidTypes))
795-
));
788+
$keys = array_keys($invalidTypes);
789+
790+
if (1 === \count($keys) && '[]' === substr($keys[0], -2)) {
791+
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]));
792+
}
793+
794+
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))));
796795
}
797796
}
798797

@@ -877,23 +876,8 @@ public function offsetGet($option)
877876
*/
878877
private function verifyTypes($type, $value, array &$invalidTypes)
879878
{
880-
if ('[]' === substr($type, -2) && is_array($value)) {
881-
$originalType = $type;
882-
$type = substr($type, 0, -2);
883-
$invalidValues = array_filter( // Filter out valid values, keeping invalid values in the resulting array
884-
$value,
885-
function ($value) use ($type) {
886-
return !self::isValueValidType($type, $value);
887-
}
888-
);
889-
890-
if (!$invalidValues) {
891-
return true;
892-
}
893-
894-
$invalidTypes[$this->formatTypeOf($value, $originalType)] = true;
895-
896-
return false;
879+
if (\is_array($value) && '[]' === substr($type, -2)) {
880+
return $this->verifyArrayType($type, $value, $invalidTypes);
897881
}
898882

899883
if (self::isValueValidType($type, $value)) {
@@ -907,6 +891,46 @@ function ($value) use ($type) {
907891
return false;
908892
}
909893

894+
/**
895+
* @return bool
896+
*/
897+
private function verifyArrayType($type, array $value, array &$invalidTypes, $level = 0)
898+
{
899+
$type = substr($type, 0, -2);
900+
901+
$suffix = '[]';
902+
while (\strlen($suffix) <= $level * 2) {
903+
$suffix .= '[]';
904+
}
905+
906+
if ('[]' === substr($type, -2)) {
907+
$success = true;
908+
foreach ($value as $item) {
909+
if (!\is_array($item)) {
910+
$invalidTypes[$this->formatTypeOf($item, null).$suffix] = true;
911+
912+
return false;
913+
}
914+
915+
if (!$this->verifyArrayType($type, $item, $invalidTypes, $level + 1)) {
916+
$success = false;
917+
}
918+
}
919+
920+
return $success;
921+
}
922+
923+
foreach ($value as $item) {
924+
if (!self::isValueValidType($type, $item)) {
925+
$invalidTypes[$this->formatTypeOf($item, $type).$suffix] = $value;
926+
927+
return false;
928+
}
929+
}
930+
931+
return true;
932+
}
933+
910934
/**
911935
* Returns whether a resolved option with the given name exists.
912936
*
@@ -990,13 +1014,13 @@ private function formatTypeOf($value, $type)
9901014
while ('[]' === substr($type, -2)) {
9911015
$type = substr($type, 0, -2);
9921016
$value = array_shift($value);
993-
if (!is_array($value)) {
1017+
if (!\is_array($value)) {
9941018
break;
9951019
}
9961020
$suffix .= '[]';
9971021
}
9981022

999-
if (is_array($value)) {
1023+
if (\is_array($value)) {
10001024
$subTypes = array();
10011025
foreach ($value as $val) {
10021026
$subTypes[$this->formatTypeOf($val, null)] = true;
@@ -1006,7 +1030,7 @@ private function formatTypeOf($value, $type)
10061030
}
10071031
}
10081032

1009-
return (is_object($value) ? get_class($value) : gettype($value)).$suffix;
1033+
return (\is_object($value) ? get_class($value) : gettype($value)).$suffix;
10101034
}
10111035

10121036
/**
@@ -1022,19 +1046,19 @@ private function formatTypeOf($value, $type)
10221046
*/
10231047
private function formatValue($value)
10241048
{
1025-
if (is_object($value)) {
1049+
if (\is_object($value)) {
10261050
return get_class($value);
10271051
}
10281052

1029-
if (is_array($value)) {
1053+
if (\is_array($value)) {
10301054
return 'array';
10311055
}
10321056

1033-
if (is_string($value)) {
1057+
if (\is_string($value)) {
10341058
return '"'.$value.'"';
10351059
}
10361060

1037-
if (is_resource($value)) {
1061+
if (\is_resource($value)) {
10381062
return 'resource';
10391063
}
10401064

@@ -1078,4 +1102,20 @@ private static function isValueValidType($type, $value)
10781102
{
10791103
return (function_exists($isFunction = 'is_'.$type) && $isFunction($value)) || $value instanceof $type;
10801104
}
1105+
1106+
/**
1107+
* @return array
1108+
*/
1109+
private function getInvalidValues(array $arrayValues, $type)
1110+
{
1111+
$invalidValues = array();
1112+
1113+
foreach ($arrayValues as $key => $value) {
1114+
if (!self::isValueValidType($type, $value)) {
1115+
$invalidValues[$key] = $value;
1116+
}
1117+
}
1118+
1119+
return $invalidValues;
1120+
}
10811121
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php
+150-3Lines changed: 150 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ public function testFailIfSetAllowedTypesFromLazyOption()
511511

512512
/**
513513
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
514-
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "DateTime[]".
514+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]".
515515
*/
516516
public function testResolveFailsIfInvalidTypedArray()
517517
{
@@ -535,7 +535,7 @@ public function testResolveFailsWithNonArray()
535535

536536
/**
537537
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
538-
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]".
538+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]".
539539
*/
540540
public function testResolveFailsIfTypedArrayContainsInvalidTypes()
541541
{
@@ -552,7 +552,7 @@ public function testResolveFailsIfTypedArrayContainsInvalidTypes()
552552

553553
/**
554554
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
555-
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but is of type "double[][]".
555+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]".
556556
*/
557557
public function testResolveFailsWithCorrectLevelsButWrongScalar()
558558
{
@@ -1650,4 +1650,151 @@ public function testCountFailsOutsideResolve()
16501650

16511651
count($this->resolver);
16521652
}
1653+
1654+
public function testNestedArrays()
1655+
{
1656+
$this->resolver->setDefined('foo');
1657+
$this->resolver->setAllowedTypes('foo', 'int[][]');
1658+
1659+
$this->assertEquals(array(
1660+
'foo' => array(
1661+
array(
1662+
1, 2,
1663+
),
1664+
),
1665+
), $this->resolver->resolve(
1666+
array(
1667+
'foo' => array(
1668+
array(1, 2),
1669+
),
1670+
)
1671+
));
1672+
}
1673+
1674+
public function testNested2Arrays()
1675+
{
1676+
$this->resolver->setDefined('foo');
1677+
$this->resolver->setAllowedTypes('foo', 'int[][][][]');
1678+
1679+
$this->assertEquals(array(
1680+
'foo' => array(
1681+
array(
1682+
array(
1683+
array(
1684+
1, 2,
1685+
),
1686+
),
1687+
),
1688+
),
1689+
), $this->resolver->resolve(
1690+
array(
1691+
'foo' => array(
1692+
array(
1693+
array(
1694+
array(1, 2),
1695+
),
1696+
),
1697+
),
1698+
)
1699+
));
1700+
}
1701+
1702+
/**
1703+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1704+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]".
1705+
*/
1706+
public function testNestedArraysException()
1707+
{
1708+
$this->resolver->setDefined('foo');
1709+
$this->resolver->setAllowedTypes('foo', 'float[][][][]');
1710+
1711+
$this->resolver->resolve(
1712+
array(
1713+
'foo' => array(
1714+
array(
1715+
array(
1716+
array(1, 2),
1717+
),
1718+
),
1719+
),
1720+
)
1721+
);
1722+
}
1723+
1724+
/**
1725+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1726+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".
1727+
*/
1728+
public function testNestedArrayException1()
1729+
{
1730+
$this->resolver->setDefined('foo');
1731+
$this->resolver->setAllowedTypes('foo', 'int[][]');
1732+
$this->resolver->resolve(array(
1733+
'foo' => array(
1734+
array(1, true, 'str', array(2, 3)),
1735+
),
1736+
));
1737+
}
1738+
1739+
/**
1740+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1741+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".
1742+
*/
1743+
public function testNestedArrayException2()
1744+
{
1745+
$this->resolver->setDefined('foo');
1746+
$this->resolver->setAllowedTypes('foo', 'int[][]');
1747+
$this->resolver->resolve(array(
1748+
'foo' => array(
1749+
array(true, 'str', array(2, 3)),
1750+
),
1751+
));
1752+
}
1753+
1754+
/**
1755+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1756+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]".
1757+
*/
1758+
public function testNestedArrayException3()
1759+
{
1760+
$this->resolver->setDefined('foo');
1761+
$this->resolver->setAllowedTypes('foo', 'string[][][]');
1762+
$this->resolver->resolve(array(
1763+
'foo' => array(
1764+
array('str', array(1, 2)),
1765+
),
1766+
));
1767+
}
1768+
1769+
/**
1770+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1771+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]".
1772+
*/
1773+
public function testNestedArrayException4()
1774+
{
1775+
$this->resolver->setDefined('foo');
1776+
$this->resolver->setAllowedTypes('foo', 'string[][][]');
1777+
$this->resolver->resolve(array(
1778+
'foo' => array(
1779+
array(
1780+
array('str'), array(1, 2), ),
1781+
),
1782+
));
1783+
}
1784+
1785+
/**
1786+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1787+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]".
1788+
*/
1789+
public function testNestedArrayException5()
1790+
{
1791+
$this->resolver->setDefined('foo');
1792+
$this->resolver->setAllowedTypes('foo', 'string[]');
1793+
$this->resolver->resolve(array(
1794+
'foo' => array(
1795+
array(
1796+
array('str'), array(1, 2), ),
1797+
),
1798+
));
1799+
}
16531800
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.