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 1efc945

Browse filesBrowse files
committed
Added support for nesting options as default value
1 parent 4f4c172 commit 1efc945
Copy full SHA for 1efc945

File tree

3 files changed

+245
-7
lines changed
Filter options

3 files changed

+245
-7
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/OptionsResolver/CHANGELOG.md
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
4.2.0
5+
-----
6+
7+
* added support for nesting options as default value
8+
49
3.4.0
510
-----
611

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/OptionsResolver/OptionsResolver.php
+28-6Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ class OptionsResolver implements Options
3636
*/
3737
private $defaults = array();
3838

39+
/**
40+
* A list of nested options.
41+
*
42+
* @var self[]
43+
*/
44+
private $nested = array();
45+
3946
/**
4047
* The names of required options.
4148
*/
@@ -171,12 +178,17 @@ public function setDefault($option, $value)
171178
// This option is not lazy anymore
172179
unset($this->lazy[$option]);
173180

174-
// Yet undefined options can be marked as resolved, because we only need
175-
// to resolve options with lazy closures, normalizers or validation
176-
// rules, none of which can exist for undefined options
177-
// If the option was resolved before, update the resolved value
178-
if (!isset($this->defined[$option]) || array_key_exists($option, $this->resolved)) {
179-
$this->resolved[$option] = $value;
181+
if ($value instanceof self) {
182+
$this->nested[$option] = $value;
183+
$value = array();
184+
} else {
185+
// Yet undefined options can be marked as resolved, because we only need
186+
// to resolve options with lazy closures, normalizers or validation
187+
// rules, none of which can exist for undefined options
188+
// If the option was resolved before, update the resolved value
189+
if (!isset($this->defined[$option]) || array_key_exists($option, $this->resolved)) {
190+
$this->resolved[$option] = $value;
191+
}
180192
}
181193

182194
$this->defaults[$option] = $value;
@@ -614,6 +626,7 @@ public function clear()
614626

615627
$this->defined = array();
616628
$this->defaults = array();
629+
$this->nested = array();
617630
$this->required = array();
618631
$this->resolved = array();
619632
$this->lazy = array();
@@ -745,6 +758,15 @@ public function offsetGet($option)
745758

746759
$value = $this->defaults[$option];
747760

761+
// Resolve the option if the default value is nested
762+
if (isset($this->nested[$option])) {
763+
if (!\is_array($value)) {
764+
throw new InvalidOptionsException(sprintf('The nested option "%s" with value %s is expected to be of type array, but is of type "%s".', $option, $this->formatValue($value), $this->formatTypeOf($value, 'array')));
765+
}
766+
767+
$value = $this->nested[$option]->resolve($value);
768+
}
769+
748770
// Resolve the option if the default value is lazily evaluated
749771
if (isset($this->lazy[$option])) {
750772
// If the closure is already being called, we have a cyclic

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php
+212-1Lines changed: 212 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,6 @@ public function provideInvalidTypes()
593593
array(false, 'string', 'The option "option" with value false is expected to be of type "string", but is of type "boolean".'),
594594
array(fopen(__FILE__, 'r'), 'string', 'The option "option" with value resource is expected to be of type "string", but is of type "resource".'),
595595
array(array(), 'string', 'The option "option" with value array is expected to be of type "string", but is of type "array".'),
596-
array(new OptionsResolver(), 'string', 'The option "option" with value Symfony\Component\OptionsResolver\OptionsResolver is expected to be of type "string", but is of type "Symfony\Component\OptionsResolver\OptionsResolver".'),
597596
array(42, 'string', 'The option "option" with value 42 is expected to be of type "string", but is of type "integer".'),
598597
array(null, 'string', 'The option "option" with value null is expected to be of type "string", but is of type "NULL".'),
599598
array('bar', '\stdClass', 'The option "option" with value "bar" is expected to be of type "\stdClass", but is of type "string".'),
@@ -1650,4 +1649,216 @@ public function testCountFailsOutsideResolve()
16501649

16511650
count($this->resolver);
16521651
}
1652+
1653+
////////////////////////////////////////////////////////////////////////////
1654+
// Nested Options
1655+
////////////////////////////////////////////////////////////////////////////
1656+
1657+
/**
1658+
* @expectedException \Symfony\Component\OptionsResolver\Exception\UndefinedOptionsException
1659+
* @expectedExceptionMessage The option "baz" does not exist. Defined options are: "bar", "foo".
1660+
*/
1661+
public function testFailIfUndefinedNestedOption()
1662+
{
1663+
$this->resolver
1664+
->setDefaults(array(
1665+
'host' => 'localhost',
1666+
'options' => (new OptionsResolver())
1667+
->setDefined(array('foo', 'bar')),
1668+
))
1669+
;
1670+
1671+
$this->resolver->resolve(array(
1672+
'options' => array('baz' => 'bam'),
1673+
));
1674+
}
1675+
1676+
/**
1677+
* @expectedException \Symfony\Component\OptionsResolver\Exception\MissingOptionsException
1678+
* @expectedExceptionMessage The required option "foo" is missing.
1679+
*/
1680+
public function testFailIfMissingRequiredNestedOption()
1681+
{
1682+
$this->resolver
1683+
->setDefaults(array(
1684+
'host' => 'localhost',
1685+
'options' => (new OptionsResolver())
1686+
->setRequired('foo'),
1687+
))
1688+
;
1689+
1690+
$this->resolver->resolve(array(
1691+
'options' => array(),
1692+
));
1693+
}
1694+
1695+
/**
1696+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1697+
* @expectedExceptionMessage The option "foo" with value null is expected to be of type "bool", but is of type "NULL".
1698+
*/
1699+
public function testFailIfInvalidTypeNestedOption()
1700+
{
1701+
$this->resolver
1702+
->setDefaults(array(
1703+
'host' => 'localhost',
1704+
'options' => (new OptionsResolver())
1705+
->setDefined('foo')
1706+
->setAllowedTypes('foo', 'bool'),
1707+
))
1708+
;
1709+
1710+
$this->resolver->resolve(array(
1711+
'options' => array('foo' => null),
1712+
));
1713+
}
1714+
1715+
/**
1716+
* @expectedException \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException
1717+
* @expectedExceptionMessage The nested option "options" with value null is expected to be of type array, but is of type "NULL".
1718+
*/
1719+
public function testFailIfNotArrayOptions()
1720+
{
1721+
$this->resolver
1722+
->setDefaults(array(
1723+
'host' => 'localhost',
1724+
'options' => (new OptionsResolver())
1725+
->setDefined('foo'),
1726+
))
1727+
;
1728+
1729+
$this->resolver->resolve(array(
1730+
'options' => null,
1731+
));
1732+
}
1733+
1734+
public function testResolveNestedOptionsWithoutDefault()
1735+
{
1736+
$this->resolver
1737+
->setDefaults(array(
1738+
'host' => 'localhost',
1739+
'options' => (new OptionsResolver())
1740+
->setDefined(array('foo', 'bar')),
1741+
))
1742+
;
1743+
1744+
$actualOptions = $this->resolver->resolve();
1745+
$expectedOptions = array(
1746+
'host' => 'localhost',
1747+
'options' => array(),
1748+
);
1749+
1750+
$this->assertSame($expectedOptions, $actualOptions);
1751+
}
1752+
1753+
public function testResolveNestedOptionsWithDefault()
1754+
{
1755+
$this->resolver
1756+
->setDefaults(array(
1757+
'host' => 'localhost',
1758+
'options' => (new OptionsResolver())
1759+
->setDefaults(array(
1760+
'foo' => 4,
1761+
'bar' => false,
1762+
)),
1763+
))
1764+
;
1765+
1766+
$actualOptions = $this->resolver->resolve();
1767+
$expectedOptions = array(
1768+
'host' => 'localhost',
1769+
'options' => array(
1770+
'foo' => 4,
1771+
'bar' => false,
1772+
),
1773+
);
1774+
1775+
$this->assertSame($expectedOptions, $actualOptions);
1776+
}
1777+
1778+
public function testResolveMultipleNestedOptions()
1779+
{
1780+
$this->resolver
1781+
->setDefaults(array(
1782+
'host' => 'localhost',
1783+
'options' => (new OptionsResolver())
1784+
->setDefined('foo')
1785+
->setDefaults(array(
1786+
'bar' => false,
1787+
'baz' => (new OptionsResolver())
1788+
->setDefault('ping', 'pong'),
1789+
)),
1790+
))
1791+
;
1792+
1793+
$actualOptions = $this->resolver->resolve(array(
1794+
'options' => array(
1795+
'foo' => 4,
1796+
'bar' => true,
1797+
'baz' => array('ping' => 'ball'),
1798+
),
1799+
));
1800+
$expectedOptions = array(
1801+
'host' => 'localhost',
1802+
'options' => array(
1803+
'bar' => true,
1804+
'baz' => array('ping' => 'ball'),
1805+
'foo' => 4,
1806+
),
1807+
);
1808+
1809+
$this->assertSame($expectedOptions, $actualOptions);
1810+
}
1811+
1812+
public function testResolveLazyOptionUsingNestedOption()
1813+
{
1814+
$this->resolver
1815+
->setDefaults(array(
1816+
'host' => 'localhost',
1817+
'version' => function (Options $options) {
1818+
return $options['options']['protocol_version'];
1819+
},
1820+
'options' => (new OptionsResolver())
1821+
->setDefault('protocol_version', 5),
1822+
))
1823+
;
1824+
1825+
$actualOptions = $this->resolver->resolve();
1826+
$expectedOptions = array(
1827+
'host' => 'localhost',
1828+
'options' => array('protocol_version' => 5),
1829+
'version' => 5,
1830+
);
1831+
1832+
$this->assertSame($expectedOptions, $actualOptions);
1833+
}
1834+
1835+
public function testNormalizeNestedOptionFromParentLevel()
1836+
{
1837+
$this->resolver
1838+
->setDefaults(array(
1839+
'host' => 'localhost',
1840+
'version' => 4,
1841+
'options' => (new OptionsResolver())
1842+
->setDefined(array('protocol_version')),
1843+
))
1844+
->setNormalizer('options', function (Options $options, $value) {
1845+
if (!isset($value['protocol_version'])) {
1846+
$value['protocol_version'] = $options['version'];
1847+
}
1848+
1849+
return $value;
1850+
})
1851+
;
1852+
1853+
$actualOptions = $this->resolver->resolve();
1854+
$expectedOptions = array(
1855+
'host' => 'localhost',
1856+
'version' => 4,
1857+
'options' => array(
1858+
'protocol_version' => 4,
1859+
),
1860+
);
1861+
1862+
$this->assertSame($expectedOptions, $actualOptions);
1863+
}
16531864
}

0 commit comments

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