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

Skip to content

Commit ff582b8

Browse files
committed
bug #16705 [Form] Deprecated setting "choices_as_values" to "false" (webmozart)
This PR was merged into the 2.7 branch. Discussion ---------- [Form] Deprecated setting "choices_as_values" to "false" | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This is #16681 for 2.7, i.e. without the deprecations trigger Commits ------- 3ab8189 [Form] Deprecated setting "choices_as_values" to "false"
2 parents d65b924 + 3ab8189 commit ff582b8

File tree

12 files changed

+298
-51
lines changed

12 files changed

+298
-51
lines changed

src/Symfony/Component/Form/ChoiceList/ArrayChoiceList.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ public function __construct($choices, $value = null)
7474
$choices = iterator_to_array($choices);
7575
}
7676

77+
if (null === $value && $this->castableToString($choices)) {
78+
$value = function ($choice) {
79+
return (string) $choice;
80+
};
81+
}
82+
7783
if (null !== $value) {
7884
// If a deterministic value generator was passed, use it later
7985
$this->valueCallback = $value;
@@ -207,4 +213,35 @@ protected function flatten(array $choices, $value, &$choicesByValues, &$keysByVa
207213
$structuredValues[$key] = $choiceValue;
208214
}
209215
}
216+
217+
/**
218+
* Checks whether the given choices can be cast to strings without
219+
* generating duplicates.
220+
*
221+
* @param array $choices The choices.
222+
* @param array|null $cache The cache for previously checked entries. Internal
223+
*
224+
* @return bool Returns true if the choices can be cast to strings and
225+
* false otherwise.
226+
*/
227+
private function castableToString(array $choices, array &$cache = array())
228+
{
229+
foreach ($choices as $choice) {
230+
if (is_array($choice)) {
231+
if (!$this->castableToString($choice, $cache)) {
232+
return false;
233+
}
234+
235+
continue;
236+
} elseif (!is_scalar($choice)) {
237+
return false;
238+
} elseif (isset($cache[(string) $choice])) {
239+
return false;
240+
}
241+
242+
$cache[(string) $choice] = true;
243+
}
244+
245+
return true;
246+
}
210247
}

src/Symfony/Component/Form/Extension/Core/Type/CountryType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class CountryType extends AbstractType
2323
public function configureOptions(OptionsResolver $resolver)
2424
{
2525
$resolver->setDefaults(array(
26-
'choices' => Intl::getRegionBundle()->getCountryNames(),
26+
'choices' => array_flip(Intl::getRegionBundle()->getCountryNames()),
27+
'choices_as_values' => true,
2728
'choice_translation_domain' => false,
2829
));
2930
}

src/Symfony/Component/Form/Extension/Core/Type/CurrencyType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class CurrencyType extends AbstractType
2323
public function configureOptions(OptionsResolver $resolver)
2424
{
2525
$resolver->setDefaults(array(
26-
'choices' => Intl::getCurrencyBundle()->getCurrencyNames(),
26+
'choices' => array_flip(Intl::getCurrencyBundle()->getCurrencyNames()),
27+
'choices_as_values' => true,
2728
'choice_translation_domain' => false,
2829
));
2930
}

src/Symfony/Component/Form/Extension/Core/Type/DateType.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,13 @@ public function buildForm(FormBuilderInterface $builder, array $options)
8888
if ('choice' === $options['widget']) {
8989
// Only pass a subset of the options to children
9090
$yearOptions['choices'] = $this->formatTimestamps($formatter, '/y+/', $this->listYears($options['years']));
91+
$yearOptions['choices_as_values'] = true;
9192
$yearOptions['placeholder'] = $options['placeholder']['year'];
9293
$monthOptions['choices'] = $this->formatTimestamps($formatter, '/[M|L]+/', $this->listMonths($options['months']));
94+
$monthOptions['choices_as_values'] = true;
9395
$monthOptions['placeholder'] = $options['placeholder']['month'];
9496
$dayOptions['choices'] = $this->formatTimestamps($formatter, '/d+/', $this->listDays($options['days']));
97+
$dayOptions['choices_as_values'] = true;
9598
$dayOptions['placeholder'] = $options['placeholder']['day'];
9699
}
97100

@@ -262,6 +265,7 @@ private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $
262265
{
263266
$pattern = $formatter->getPattern();
264267
$timezone = $formatter->getTimezoneId();
268+
$formattedTimestamps = array();
265269

266270
if ($setTimeZone = PHP_VERSION_ID >= 50500 || method_exists($formatter, 'setTimeZone')) {
267271
$formatter->setTimeZone('UTC');
@@ -272,8 +276,8 @@ private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $
272276
if (preg_match($regex, $pattern, $matches)) {
273277
$formatter->setPattern($matches[0]);
274278

275-
foreach ($timestamps as $key => $timestamp) {
276-
$timestamps[$key] = $formatter->format($timestamp);
279+
foreach ($timestamps as $timestamp => $choice) {
280+
$formattedTimestamps[$formatter->format($timestamp)] = $choice;
277281
}
278282

279283
// I'd like to clone the formatter above, but then we get a
@@ -287,7 +291,7 @@ private function formatTimestamps(\IntlDateFormatter $formatter, $regex, array $
287291
$formatter->setTimeZoneId($timezone);
288292
}
289293

290-
return $timestamps;
294+
return $formattedTimestamps;
291295
}
292296

293297
private function listYears(array $years)
@@ -296,7 +300,7 @@ private function listYears(array $years)
296300

297301
foreach ($years as $year) {
298302
if (false !== $y = gmmktime(0, 0, 0, 6, 15, $year)) {
299-
$result[$year] = $y;
303+
$result[$y] = $year;
300304
}
301305
}
302306

@@ -308,7 +312,7 @@ private function listMonths(array $months)
308312
$result = array();
309313

310314
foreach ($months as $month) {
311-
$result[$month] = gmmktime(0, 0, 0, $month, 15);
315+
$result[gmmktime(0, 0, 0, $month, 15)] = $month;
312316
}
313317

314318
return $result;
@@ -319,7 +323,7 @@ private function listDays(array $days)
319323
$result = array();
320324

321325
foreach ($days as $day) {
322-
$result[$day] = gmmktime(0, 0, 0, 5, $day);
326+
$result[gmmktime(0, 0, 0, 5, $day)] = $day;
323327
}
324328

325329
return $result;

src/Symfony/Component/Form/Extension/Core/Type/LanguageType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class LanguageType extends AbstractType
2323
public function configureOptions(OptionsResolver $resolver)
2424
{
2525
$resolver->setDefaults(array(
26-
'choices' => Intl::getLanguageBundle()->getLanguageNames(),
26+
'choices' => array_flip(Intl::getLanguageBundle()->getLanguageNames()),
27+
'choices_as_values' => true,
2728
'choice_translation_domain' => false,
2829
));
2930
}

src/Symfony/Component/Form/Extension/Core/Type/LocaleType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class LocaleType extends AbstractType
2323
public function configureOptions(OptionsResolver $resolver)
2424
{
2525
$resolver->setDefaults(array(
26-
'choices' => Intl::getLocaleBundle()->getLocaleNames(),
26+
'choices' => array_flip(Intl::getLocaleBundle()->getLocaleNames()),
27+
'choices_as_values' => true,
2728
'choice_translation_domain' => false,
2829
));
2930
}

src/Symfony/Component/Form/Extension/Core/Type/TimeType.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,30 +58,33 @@ public function buildForm(FormBuilderInterface $builder, array $options)
5858
$hours = $minutes = array();
5959

6060
foreach ($options['hours'] as $hour) {
61-
$hours[$hour] = str_pad($hour, 2, '0', STR_PAD_LEFT);
61+
$hours[str_pad($hour, 2, '0', STR_PAD_LEFT)] = $hour;
6262
}
6363

6464
// Only pass a subset of the options to children
6565
$hourOptions['choices'] = $hours;
66+
$hourOptions['choices_as_values'] = true;
6667
$hourOptions['placeholder'] = $options['placeholder']['hour'];
6768

6869
if ($options['with_minutes']) {
6970
foreach ($options['minutes'] as $minute) {
70-
$minutes[$minute] = str_pad($minute, 2, '0', STR_PAD_LEFT);
71+
$minutes[str_pad($minute, 2, '0', STR_PAD_LEFT)] = $minute;
7172
}
7273

7374
$minuteOptions['choices'] = $minutes;
75+
$minuteOptions['choices_as_values'] = true;
7476
$minuteOptions['placeholder'] = $options['placeholder']['minute'];
7577
}
7678

7779
if ($options['with_seconds']) {
7880
$seconds = array();
7981

8082
foreach ($options['seconds'] as $second) {
81-
$seconds[$second] = str_pad($second, 2, '0', STR_PAD_LEFT);
83+
$seconds[str_pad($second, 2, '0', STR_PAD_LEFT)] = $second;
8284
}
8385

8486
$secondOptions['choices'] = $seconds;
87+
$secondOptions['choices_as_values'] = true;
8588
$secondOptions['placeholder'] = $options['placeholder']['second'];
8689
}
8790

src/Symfony/Component/Form/Extension/Core/Type/TimezoneType.php

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,21 @@ class TimezoneType extends AbstractType
2323
*/
2424
private static $timezones;
2525

26+
/**
27+
* Stores the available timezone choices.
28+
*
29+
* @var array
30+
*/
31+
private static $flippedTimezones;
32+
2633
/**
2734
* {@inheritdoc}
2835
*/
2936
public function configureOptions(OptionsResolver $resolver)
3037
{
3138
$resolver->setDefaults(array(
32-
'choices' => self::getTimezones(),
39+
'choices' => self::getFlippedTimezones(),
40+
'choices_as_values' => true,
3341
'choice_translation_domain' => false,
3442
));
3543
}
@@ -85,4 +93,40 @@ public static function getTimezones()
8593

8694
return static::$timezones;
8795
}
96+
97+
/**
98+
* Returns the timezone choices.
99+
*
100+
* The choices are generated from the ICU function
101+
* \DateTimeZone::listIdentifiers(). They are cached during a single request,
102+
* so multiple timezone fields on the same page don't lead to unnecessary
103+
* overhead.
104+
*
105+
* @return array The timezone choices
106+
*/
107+
private static function getFlippedTimezones()
108+
{
109+
if (null === self::$timezones) {
110+
self::$timezones = array();
111+
112+
foreach (\DateTimeZone::listIdentifiers() as $timezone) {
113+
$parts = explode('/', $timezone);
114+
115+
if (count($parts) > 2) {
116+
$region = $parts[0];
117+
$name = $parts[1].' - '.$parts[2];
118+
} elseif (count($parts) > 1) {
119+
$region = $parts[0];
120+
$name = $parts[1];
121+
} else {
122+
$region = 'Other';
123+
$name = $parts[0];
124+
}
125+
126+
self::$timezones[$region][str_replace('_', ' ', $name)] = $timezone;
127+
}
128+
}
129+
130+
return self::$timezones;
131+
}
88132
}

src/Symfony/Component/Form/Tests/ChoiceList/ArrayChoiceListTest.php

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,56 @@ public function testCreateChoiceListWithValueCallback()
6565
$this->assertSame(array(1 => ':foo', 2 => ':baz'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 'baz')));
6666
}
6767

68+
public function testCreateChoiceListWithoutValueCallbackAndDuplicateFreeToStringChoices()
69+
{
70+
$choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => 'bar', 10 => 123));
71+
72+
$this->assertSame(array('foo', 'bar', '123'), $choiceList->getValues());
73+
$this->assertSame(array('foo' => 'foo', 'bar' => 'bar', '123' => 123), $choiceList->getChoices());
74+
$this->assertSame(array('foo' => 2, 'bar' => 7, '123' => 10), $choiceList->getOriginalKeys());
75+
$this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => 'foo', 2 => '123')));
76+
$this->assertSame(array(1 => 'foo', 2 => '123'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123)));
77+
}
78+
79+
public function testCreateChoiceListWithoutValueCallbackAndToStringDuplicates()
80+
{
81+
$choiceList = new ArrayChoiceList(array(2 => 'foo', 7 => '123', 10 => 123));
82+
83+
$this->assertSame(array('0', '1', '2'), $choiceList->getValues());
84+
$this->assertSame(array('0' => 'foo', '1' => '123', '2' => 123), $choiceList->getChoices());
85+
$this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys());
86+
$this->assertSame(array(1 => 'foo', 2 => 123), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2')));
87+
$this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => 123)));
88+
}
89+
90+
public function testCreateChoiceListWithoutValueCallbackAndMixedChoices()
91+
{
92+
$object = new \stdClass();
93+
$choiceList = new ArrayChoiceList(array(2 => 'foo', 5 => array(7 => '123'), 10 => $object));
94+
95+
$this->assertSame(array('0', '1', '2'), $choiceList->getValues());
96+
$this->assertSame(array('0' => 'foo', '1' => '123', '2' => $object), $choiceList->getChoices());
97+
$this->assertSame(array('0' => 2, '1' => 7, '2' => 10), $choiceList->getOriginalKeys());
98+
$this->assertSame(array(1 => 'foo', 2 => $object), $choiceList->getChoicesForValues(array(1 => '0', 2 => '2')));
99+
$this->assertSame(array(1 => '0', 2 => '2'), $choiceList->getValuesForChoices(array(1 => 'foo', 2 => $object)));
100+
}
101+
68102
public function testCreateChoiceListWithGroupedChoices()
69103
{
70104
$choiceList = new ArrayChoiceList(array(
71105
'Group 1' => array('A' => 'a', 'B' => 'b'),
72106
'Group 2' => array('C' => 'c', 'D' => 'd'),
73107
));
74108

75-
$this->assertSame(array('0', '1', '2', '3'), $choiceList->getValues());
109+
$this->assertSame(array('a', 'b', 'c', 'd'), $choiceList->getValues());
76110
$this->assertSame(array(
77-
'Group 1' => array('A' => '0', 'B' => '1'),
78-
'Group 2' => array('C' => '2', 'D' => '3'),
111+
'Group 1' => array('A' => 'a', 'B' => 'b'),
112+
'Group 2' => array('C' => 'c', 'D' => 'd'),
79113
), $choiceList->getStructuredValues());
80-
$this->assertSame(array(0 => 'a', 1 => 'b', 2 => 'c', 3 => 'd'), $choiceList->getChoices());
81-
$this->assertSame(array(0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D'), $choiceList->getOriginalKeys());
82-
$this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getChoicesForValues(array(1 => '0', 2 => '1')));
83-
$this->assertSame(array(1 => '0', 2 => '1'), $choiceList->getValuesForChoices(array(1 => 'a', 2 => 'b')));
114+
$this->assertSame(array('a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd'), $choiceList->getChoices());
115+
$this->assertSame(array('a' => 'A', 'b' => 'B', 'c' => 'C', 'd' => 'D'), $choiceList->getOriginalKeys());
116+
$this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getChoicesForValues(array(1 => 'a', 2 => 'b')));
117+
$this->assertSame(array(1 => 'a', 2 => 'b'), $choiceList->getValuesForChoices(array(1 => 'a', 2 => 'b')));
84118
}
85119

86120
public function testCompareChoicesByIdentityByDefault()

src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoiceToValueTransformerTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class ChoiceToValueTransformerTest extends \PHPUnit_Framework_TestCase
2020

2121
protected function setUp()
2222
{
23-
$list = new ArrayChoiceList(array('', 0, 'X'));
23+
$list = new ArrayChoiceList(array('', false, 'X'));
2424

2525
$this->transformer = new ChoiceToValueTransformer($list);
2626
}
@@ -35,7 +35,7 @@ public function transformProvider()
3535
return array(
3636
// more extensive test set can be found in FormUtilTest
3737
array('', '0'),
38-
array(0, '1'),
38+
array(false, '1'),
3939
);
4040
}
4141

@@ -53,7 +53,7 @@ public function reverseTransformProvider()
5353
// values are expected to be valid choice keys already and stay
5454
// the same
5555
array('0', ''),
56-
array('1', 0),
56+
array('1', false),
5757
array('2', 'X'),
5858
);
5959
}

src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/ChoicesToValuesTransformerTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class ChoicesToValuesTransformerTest extends \PHPUnit_Framework_TestCase
2020

2121
protected function setUp()
2222
{
23-
$list = new ArrayChoiceList(array('A', 'B', 'C'));
23+
$list = new ArrayChoiceList(array('', false, 'X'));
2424
$this->transformer = new ChoicesToValuesTransformer($list);
2525
}
2626

@@ -31,7 +31,7 @@ protected function tearDown()
3131

3232
public function testTransform()
3333
{
34-
$in = array('A', 'B', 'C');
34+
$in = array('', false, 'X');
3535
$out = array('0', '1', '2');
3636

3737
$this->assertSame($out, $this->transformer->transform($in));
@@ -54,7 +54,7 @@ public function testReverseTransform()
5454
{
5555
// values are expected to be valid choices and stay the same
5656
$in = array('0', '1', '2');
57-
$out = array('A', 'B', 'C');
57+
$out = array('', false, 'X');
5858

5959
$this->assertSame($out, $this->transformer->reverseTransform($in));
6060
}

0 commit comments

Comments
 (0)