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

Skip to content

Commit 6fea634

Browse files
bug #27435 [OptionResolver] resolve arrays (Doctrs)
This PR was squashed before being merged into the 3.4 branch (closes #27435). Discussion ---------- [OptionResolver] resolve arrays | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | Option resolver didn't work with nested arrays Before: $resolver->setDefaults([ 'integer' => [ [ 12, 23, ], ], ]); $resolver->setAllowedTypes('integer', 'integer[][]'); Error The option "host" with value array is expected to be of type "integer[][]", but is of type "integer[][]". Option expetcted type `integer[][]` but get... `integer[][]`. So strange Now that case work correct, and we get array (size=1) 'integer' => array (size=1) 0 => array (size=2) 0 => int 12 1 => int 23 Commits ------- 6d4812e [OptionResolver] resolve arrays
2 parents 332b7fd + 6d4812e commit 6fea634

File tree

2 files changed

+223
-36
lines changed

2 files changed

+223
-36
lines changed

src/Symfony/Component/OptionsResolver/OptionsResolver.php

Lines 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

Lines changed: 150 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,7 @@ public function testFailIfSetAllowedTypesFromLazyOption()
483483

484484
/**
485485
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
486-
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "DateTime[]".
486+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "DateTime[]".
487487
*/
488488
public function testResolveFailsIfInvalidTypedArray()
489489
{
@@ -507,7 +507,7 @@ public function testResolveFailsWithNonArray()
507507

508508
/**
509509
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
510-
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but is of type "integer|stdClass|array|DateTime[]".
510+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[]", but one of the elements is of type "stdClass[]".
511511
*/
512512
public function testResolveFailsIfTypedArrayContainsInvalidTypes()
513513
{
@@ -524,7 +524,7 @@ public function testResolveFailsIfTypedArrayContainsInvalidTypes()
524524

525525
/**
526526
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
527-
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but is of type "double[][]".
527+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "double[][]".
528528
*/
529529
public function testResolveFailsWithCorrectLevelsButWrongScalar()
530530
{
@@ -1586,4 +1586,151 @@ public function testCountFailsOutsideResolve()
15861586

15871587
count($this->resolver);
15881588
}
1589+
1590+
public function testNestedArrays()
1591+
{
1592+
$this->resolver->setDefined('foo');
1593+
$this->resolver->setAllowedTypes('foo', 'int[][]');
1594+
1595+
$this->assertEquals(array(
1596+
'foo' => array(
1597+
array(
1598+
1, 2,
1599+
),
1600+
),
1601+
), $this->resolver->resolve(
1602+
array(
1603+
'foo' => array(
1604+
array(1, 2),
1605+
),
1606+
)
1607+
));
1608+
}
1609+
1610+
public function testNested2Arrays()
1611+
{
1612+
$this->resolver->setDefined('foo');
1613+
$this->resolver->setAllowedTypes('foo', 'int[][][][]');
1614+
1615+
$this->assertEquals(array(
1616+
'foo' => array(
1617+
array(
1618+
array(
1619+
array(
1620+
1, 2,
1621+
),
1622+
),
1623+
),
1624+
),
1625+
), $this->resolver->resolve(
1626+
array(
1627+
'foo' => array(
1628+
array(
1629+
array(
1630+
array(1, 2),
1631+
),
1632+
),
1633+
),
1634+
)
1635+
));
1636+
}
1637+
1638+
/**
1639+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1640+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "float[][][][]", but one of the elements is of type "integer[][][][]".
1641+
*/
1642+
public function testNestedArraysException()
1643+
{
1644+
$this->resolver->setDefined('foo');
1645+
$this->resolver->setAllowedTypes('foo', 'float[][][][]');
1646+
1647+
$this->resolver->resolve(
1648+
array(
1649+
'foo' => array(
1650+
array(
1651+
array(
1652+
array(1, 2),
1653+
),
1654+
),
1655+
),
1656+
)
1657+
);
1658+
}
1659+
1660+
/**
1661+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1662+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".
1663+
*/
1664+
public function testNestedArrayException1()
1665+
{
1666+
$this->resolver->setDefined('foo');
1667+
$this->resolver->setAllowedTypes('foo', 'int[][]');
1668+
$this->resolver->resolve(array(
1669+
'foo' => array(
1670+
array(1, true, 'str', array(2, 3)),
1671+
),
1672+
));
1673+
}
1674+
1675+
/**
1676+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1677+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "int[][]", but one of the elements is of type "boolean[][]".
1678+
*/
1679+
public function testNestedArrayException2()
1680+
{
1681+
$this->resolver->setDefined('foo');
1682+
$this->resolver->setAllowedTypes('foo', 'int[][]');
1683+
$this->resolver->resolve(array(
1684+
'foo' => array(
1685+
array(true, 'str', array(2, 3)),
1686+
),
1687+
));
1688+
}
1689+
1690+
/**
1691+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1692+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "string[][]".
1693+
*/
1694+
public function testNestedArrayException3()
1695+
{
1696+
$this->resolver->setDefined('foo');
1697+
$this->resolver->setAllowedTypes('foo', 'string[][][]');
1698+
$this->resolver->resolve(array(
1699+
'foo' => array(
1700+
array('str', array(1, 2)),
1701+
),
1702+
));
1703+
}
1704+
1705+
/**
1706+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1707+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[][][]", but one of the elements is of type "integer[][][]".
1708+
*/
1709+
public function testNestedArrayException4()
1710+
{
1711+
$this->resolver->setDefined('foo');
1712+
$this->resolver->setAllowedTypes('foo', 'string[][][]');
1713+
$this->resolver->resolve(array(
1714+
'foo' => array(
1715+
array(
1716+
array('str'), array(1, 2), ),
1717+
),
1718+
));
1719+
}
1720+
1721+
/**
1722+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1723+
* @expectedExceptionMessage The option "foo" with value array is expected to be of type "string[]", but one of the elements is of type "array[]".
1724+
*/
1725+
public function testNestedArrayException5()
1726+
{
1727+
$this->resolver->setDefined('foo');
1728+
$this->resolver->setAllowedTypes('foo', 'string[]');
1729+
$this->resolver->resolve(array(
1730+
'foo' => array(
1731+
array(
1732+
array('str'), array(1, 2), ),
1733+
),
1734+
));
1735+
}
15891736
}

0 commit comments

Comments
 (0)