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

Browse filesBrowse 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
Copy full SHA for 6fea634

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
@@ -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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.