Skip to content

Commit d94c322

Browse files
Merge branch '2.7' into 2.8
* 2.7: [ci] Force update of ./phpunit deps [Form] Fixed: Duplicate choice labels are remembered when using "choices_as_values" = false [Process] Don't catch RuntimeException when it complicates tests debugging [appveyor] Workaround transient segfault when APCu is enabled
2 parents cdfae4f + 60b02fc commit d94c322

File tree

6 files changed

+113
-34
lines changed

6 files changed

+113
-34
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ before_install:
3939
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then phpenv config-rm xdebug.ini; fi;
4040
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then echo "extension = mongo.so" >> $INI_FILE; fi;
4141
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then echo "extension = memcache.so" >> $INI_FILE; fi;
42-
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.7 && echo "apc.enable_cli = 1" >> $INI_FILE) || echo "Let's continue without apcu extension"; fi;
42+
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then (echo yes | pecl install -f apcu-4.0.8 && echo "apc.enable_cli = 1" >> $INI_FILE) || echo "Let's continue without apcu extension"; fi;
4343
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then pecl install -f memcached-2.1.0 || echo "Let's continue without memcached extension"; fi;
4444
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]] && [ "$deps" = "no" ]; then (cd src/Symfony/Component/Debug/Resources/ext && phpize && ./configure && make && echo "extension = $(pwd)/modules/symfony_debug.so" >> $INI_FILE); fi;
4545
- if [[ "$TRAVIS_PHP_VERSION" != "hhvm" ]]; then echo "extension = ldap.so" >> $INI_FILE; fi;

appveyor.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ install:
2626
- IF %PHP%==1 cd ext
2727
- IF %PHP%==1 appveyor DownloadFile http://nebm.ist.utl.pt/~glopes/misc/intl_win/php_intl-3.0.0-5.3-nts-vc9-x86.zip
2828
- IF %PHP%==1 7z x php_intl-3.0.0-5.3-nts-vc9-x86.zip -y >nul
29-
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/apcu/4.0.7/php_apcu-4.0.7-5.3-nts-vc9-x86.zip
30-
- IF %PHP%==1 7z x php_apcu-4.0.7-5.3-nts-vc9-x86.zip -y >nul
29+
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/apcu/4.0.8/php_apcu-4.0.8-5.3-nts-vc9-x86.zip
30+
- IF %PHP%==1 7z x php_apcu-4.0.8-5.3-nts-vc9-x86.zip -y >nul
3131
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/pecl/releases/memcache/3.0.8/php_memcache-3.0.8-5.3-nts-vc9-x86.zip
3232
- IF %PHP%==1 7z x php_memcache-3.0.8-5.3-nts-vc9-x86.zip -y >nul
3333
- IF %PHP%==1 del /Q *.zip

phpunit

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
// Please update when phpunit needs to be reinstalled with fresh deps:
14-
// Cache-Id-Version: 2015-11-18 14:14 UTC
14+
// Cache-Id-Version: 2015-11-28 09:05 UTC
1515

1616
use Symfony\Component\Process\ProcessUtils;
1717

@@ -164,7 +164,8 @@ if (isset($argv[1]) && 'symfony' === $argv[1]) {
164164
unlink($file);
165165
}
166166

167-
if ($procStatus) {
167+
// Fail on any individual component failures but ignore STATUS_STACK_BUFFER_OVERRUN (-1073740791) on Windows when APCu is enabled
168+
if ($procStatus && ('\\' !== DIRECTORY_SEPARATOR || !extension_loaded('apcu') || !ini_get('apc.enable_cli') || -1073740791 !== $procStatus)) {
168169
$exit = 1;
169170
echo "\033[41mKO\033[0m $component\n\n";
170171
} else {

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

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ public function finishView(FormView $view, FormInterface $form, array $options)
238238
*/
239239
public function configureOptions(OptionsResolver $resolver)
240240
{
241+
$choiceLabels = array();
241242
$choiceListFactory = $this->choiceListFactory;
242243

243244
$emptyData = function (Options $options) {
@@ -252,6 +253,44 @@ public function configureOptions(OptionsResolver $resolver)
252253
return $options['required'] ? null : '';
253254
};
254255

256+
// BC closure, to be removed in 3.0
257+
$choicesNormalizer = function (Options $options, $choices) use (&$choiceLabels) {
258+
// Unset labels from previous invocations
259+
$choiceLabels = array();
260+
261+
// This closure is irrelevant when "choices_as_values" is set to true
262+
if ($options['choices_as_values']) {
263+
return $choices;
264+
}
265+
266+
ChoiceType::normalizeLegacyChoices($choices, $choiceLabels);
267+
268+
return $choices;
269+
};
270+
271+
// BC closure, to be removed in 3.0
272+
$choiceLabel = function (Options $options) use (&$choiceLabels) {
273+
// If the choices contain duplicate labels, the normalizer of the
274+
// "choices" option stores them in the $choiceLabels variable
275+
276+
// Trigger the normalizer
277+
$options->offsetGet('choices');
278+
279+
// Pick labels from $choiceLabels if available
280+
// Don't invoke count() to avoid creating a copy of the array (yet)
281+
if ($choiceLabels) {
282+
// Don't pass the labels by reference. We do want to create a
283+
// copy here so that every form has an own version of that
284+
// variable (contrary to the global reference shared by all
285+
// forms)
286+
return function ($choice, $key) use ($choiceLabels) {
287+
return $choiceLabels[$key];
288+
};
289+
}
290+
291+
return;
292+
};
293+
255294
$choiceListNormalizer = function (Options $options, $choiceList) use ($choiceListFactory) {
256295
if ($choiceList) {
257296
@trigger_error('The "choice_list" option is deprecated since version 2.7 and will be removed in 3.0. Use "choice_loader" instead.', E_USER_DEPRECATED);
@@ -330,7 +369,7 @@ public function configureOptions(OptionsResolver $resolver)
330369
'choices' => array(),
331370
'choices_as_values' => false,
332371
'choice_loader' => null,
333-
'choice_label' => null,
372+
'choice_label' => $choiceLabel,
334373
'choice_name' => null,
335374
'choice_value' => null,
336375
'choice_attr' => null,
@@ -348,6 +387,7 @@ public function configureOptions(OptionsResolver $resolver)
348387
'choice_translation_domain' => true,
349388
));
350389

390+
$resolver->setNormalizer('choices', $choicesNormalizer);
351391
$resolver->setNormalizer('choice_list', $choiceListNormalizer);
352392
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
353393
$resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer);
@@ -471,4 +511,34 @@ private function createChoiceListView(ChoiceListInterface $choiceList, array $op
471511
$options['choice_attr']
472512
);
473513
}
514+
515+
/**
516+
* When "choices_as_values" is set to false, the choices are in the keys and
517+
* their labels in the values. Labels may occur twice. The form component
518+
* flips the choices array in the new implementation, so duplicate labels
519+
* are lost. Store them in a utility array that is used from the
520+
* "choice_label" closure by default.
521+
*
522+
* @param array $choices The choice labels indexed by choices.
523+
* Labels are replaced by generated keys.
524+
* @param array $choiceLabels The array that receives the choice labels
525+
* indexed by generated keys.
526+
* @param int|null $nextKey The next generated key.
527+
*
528+
* @internal Public only to be accessible from closures on PHP 5.3. Don't
529+
* use this method, as it may be removed without notice.
530+
*/
531+
public static function normalizeLegacyChoices(array &$choices, array &$choiceLabels, &$nextKey = 0)
532+
{
533+
foreach ($choices as $choice => &$choiceLabel) {
534+
if (is_array($choiceLabel)) {
535+
self::normalizeLegacyChoices($choiceLabel, $choiceLabels, $nextKey);
536+
continue;
537+
}
538+
539+
$choiceLabels[$nextKey] = $choiceLabel;
540+
$choices[$choice] = $nextKey;
541+
++$nextKey;
542+
}
543+
}
474544
}

src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,6 +1828,23 @@ public function testPassChoiceDataToView()
18281828
), $view->vars['choices']);
18291829
}
18301830

1831+
/**
1832+
* @group legacy
1833+
*/
1834+
public function testDuplicateChoiceLabels()
1835+
{
1836+
$form = $this->factory->create('choice', null, array(
1837+
'choices' => array('a' => 'A', 'b' => 'B', 'c' => 'A'),
1838+
));
1839+
$view = $form->createView();
1840+
1841+
$this->assertEquals(array(
1842+
new ChoiceView('a', 'a', 'A'),
1843+
new ChoiceView('b', 'b', 'B'),
1844+
new ChoiceView('c', 'c', 'A'),
1845+
), $view->vars['choices']);
1846+
}
1847+
18311848
public function testAdjustFullNameForMultipleNonExpanded()
18321849
{
18331850
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', null, array(

src/Symfony/Component/Process/Tests/SimpleProcessTest.php

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -152,46 +152,37 @@ public function testSignalWithWrongNonIntSignal()
152152

153153
public function testStopTerminatesProcessCleanly()
154154
{
155-
try {
156-
$process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"');
157-
$process->run(function () use ($process) {
158-
$process->stop();
159-
});
160-
} catch (\RuntimeException $e) {
161-
$this->fail('A call to stop() is not expected to cause wait() to throw a RuntimeException');
162-
}
155+
$process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"');
156+
$process->run(function () use ($process) {
157+
$process->stop();
158+
});
159+
$this->assertTrue(true, 'A call to stop() is not expected to cause wait() to throw a RuntimeException');
163160
}
164161

165162
public function testKillSignalTerminatesProcessCleanly()
166163
{
167164
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
168165

169-
try {
170-
$process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"');
171-
$process->run(function () use ($process) {
172-
if ($process->isRunning()) {
173-
$process->signal(defined('SIGKILL') ? SIGKILL : 9);
174-
}
175-
});
176-
} catch (\RuntimeException $e) {
177-
$this->fail('A call to signal() is not expected to cause wait() to throw a RuntimeException');
178-
}
166+
$process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"');
167+
$process->run(function () use ($process) {
168+
if ($process->isRunning()) {
169+
$process->signal(defined('SIGKILL') ? SIGKILL : 9);
170+
}
171+
});
172+
$this->assertTrue(true, 'A call to signal() is not expected to cause wait() to throw a RuntimeException');
179173
}
180174

181175
public function testTermSignalTerminatesProcessCleanly()
182176
{
183177
$this->expectExceptionIfPHPSigchild('Symfony\Component\Process\Exception\RuntimeException', 'This PHP has been compiled with --enable-sigchild. The process can not be signaled.');
184178

185-
try {
186-
$process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"');
187-
$process->run(function () use ($process) {
188-
if ($process->isRunning()) {
189-
$process->signal(defined('SIGTERM') ? SIGTERM : 15);
190-
}
191-
});
192-
} catch (\RuntimeException $e) {
193-
$this->fail('A call to signal() is not expected to cause wait() to throw a RuntimeException');
194-
}
179+
$process = $this->getProcess(self::$phpBin.' -r "echo \'foo\'; sleep(1); echo \'bar\';"');
180+
$process->run(function () use ($process) {
181+
if ($process->isRunning()) {
182+
$process->signal(defined('SIGTERM') ? SIGTERM : 15);
183+
}
184+
});
185+
$this->assertTrue(true, 'A call to signal() is not expected to cause wait() to throw a RuntimeException');
195186
}
196187

197188
public function testStopWithTimeoutIsActuallyWorking()

0 commit comments

Comments
 (0)