Skip to content

Commit 9f64a0f

Browse files
author
Robin Chalas
committed
Merge branch '2.8' into 3.4
* 2.8: [Intl] Fixed the broken link [Routing] Fix trailing slash redirection for non-safe verbs [Debug] Fix bad registration of exception handler, leading to mem leak [Form] Fixed empty data on expanded ChoiceType and FileType
2 parents a48849e + 0293f37 commit 9f64a0f

19 files changed

+415
-104
lines changed

src/Symfony/Component/Debug/ErrorHandler.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,20 @@ public static function register(self $handler = null, $replace = true)
136136
}
137137
if (!$replace && $prev) {
138138
restore_error_handler();
139+
$handlerIsRegistered = is_array($prev) && $handler === $prev[0];
140+
} else {
141+
$handlerIsRegistered = true;
139142
}
140-
if (is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] === $handler) {
143+
if (is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] instanceof self) {
141144
restore_exception_handler();
145+
if (!$handlerIsRegistered) {
146+
$handler = $prev[0];
147+
} elseif ($handler !== $prev[0] && $replace) {
148+
set_exception_handler(array($handler, 'handleException'));
149+
$p = $prev[0]->setExceptionHandler(null);
150+
$handler->setExceptionHandler($p);
151+
$prev[0]->setExceptionHandler($p);
152+
}
142153
} else {
143154
$handler->setExceptionHandler($prev);
144155
}

src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public function testRegister()
3535

3636
$newHandler = new ErrorHandler();
3737

38-
$this->assertSame($newHandler, ErrorHandler::register($newHandler, false));
38+
$this->assertSame($handler, ErrorHandler::register($newHandler, false));
3939
$h = set_error_handler('var_dump');
4040
restore_error_handler();
4141
$this->assertSame(array($handler, 'handleError'), $h);

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
use Symfony\Component\Form\Extension\Core\EventListener\MergeCollectionListener;
3232
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoiceToValueTransformer;
3333
use Symfony\Component\Form\Extension\Core\DataTransformer\ChoicesToValuesTransformer;
34-
use Symfony\Component\Form\Util\FormUtil;
3534
use Symfony\Component\OptionsResolver\Options;
3635
use Symfony\Component\OptionsResolver\OptionsResolver;
3736

@@ -87,12 +86,12 @@ public function buildForm(FormBuilderInterface $builder, array $options)
8786
$form = $event->getForm();
8887
$data = $event->getData();
8988

89+
// Since the type always use mapper an empty array will not be
90+
// considered as empty in Form::submit(), we need to evaluate
91+
// empty data here so its value is submitted to sub forms
9092
if (null === $data) {
9193
$emptyData = $form->getConfig()->getEmptyData();
92-
93-
if (false === FormUtil::isEmpty($emptyData) && array() !== $emptyData) {
94-
$data = is_callable($emptyData) ? call_user_func($emptyData, $form, $data) : $emptyData;
95-
}
94+
$data = $emptyData instanceof \Closure ? $emptyData($form, $data) : $emptyData;
9695
}
9796

9897
// Convert the submitted data to a string, if scalar, before

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ class FileType extends AbstractType
2727
*/
2828
public function buildForm(FormBuilderInterface $builder, array $options)
2929
{
30+
// Ensure that submitted data is always an uploaded file or an array of some
3031
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) {
3132
$form = $event->getForm();
3233
$requestHandler = $form->getConfig()->getRequestHandler();
33-
$data = null;
3434

3535
if ($options['multiple']) {
3636
$data = array();
@@ -46,19 +46,16 @@ public function buildForm(FormBuilderInterface $builder, array $options)
4646
}
4747
}
4848

49-
// submitted data for an input file (not required) without choosing any file
50-
if (array(null) === $data || array() === $data) {
49+
// Since the array is never considered empty in the view data format
50+
// on submission, we need to evaluate the configured empty data here
51+
if (array() === $data) {
5152
$emptyData = $form->getConfig()->getEmptyData();
52-
53-
$data = is_callable($emptyData) ? call_user_func($emptyData, $form, $data) : $emptyData;
53+
$data = $emptyData instanceof \Closure ? $emptyData($form, $data) : $emptyData;
5454
}
5555

5656
$event->setData($data);
5757
} elseif (!$requestHandler->isFileUpload($event->getData())) {
58-
$emptyData = $form->getConfig()->getEmptyData();
59-
60-
$data = is_callable($emptyData) ? call_user_func($emptyData, $form, $data) : $emptyData;
61-
$event->setData($data);
58+
$event->setData(null);
6259
}
6360
});
6461
}

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

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,20 @@ public function testSubmitSingleChoiceWithEmptyData()
598598
$this->assertSame('test', $form->getData());
599599
}
600600

601+
public function testSubmitSingleChoiceWithEmptyDataAndInitialData()
602+
{
603+
$form = $this->factory->create(static::TESTED_TYPE, 'initial', array(
604+
'multiple' => false,
605+
'expanded' => false,
606+
'choices' => array('initial', 'test'),
607+
'empty_data' => 'test',
608+
));
609+
610+
$form->submit(null);
611+
612+
$this->assertSame('test', $form->getData());
613+
}
614+
601615
public function testSubmitMultipleChoiceWithEmptyData()
602616
{
603617
$form = $this->factory->create(static::TESTED_TYPE, null, array(
@@ -612,6 +626,34 @@ public function testSubmitMultipleChoiceWithEmptyData()
612626
$this->assertSame(array('test'), $form->getData());
613627
}
614628

629+
public function testSubmitMultipleChoiceWithEmptyDataAndInitialEmptyArray()
630+
{
631+
$form = $this->factory->create(static::TESTED_TYPE, array(), array(
632+
'multiple' => true,
633+
'expanded' => false,
634+
'choices' => array('test'),
635+
'empty_data' => array('test'),
636+
));
637+
638+
$form->submit(null);
639+
640+
$this->assertSame(array('test'), $form->getData());
641+
}
642+
643+
public function testSubmitMultipleChoiceWithEmptyDataAndInitialData()
644+
{
645+
$form = $this->factory->create(static::TESTED_TYPE, array('initial'), array(
646+
'multiple' => true,
647+
'expanded' => false,
648+
'choices' => array('initial', 'test'),
649+
'empty_data' => array('test'),
650+
));
651+
652+
$form->submit(null);
653+
654+
$this->assertSame(array('test'), $form->getData());
655+
}
656+
615657
public function testSubmitSingleChoiceExpandedWithEmptyData()
616658
{
617659
$form = $this->factory->create(static::TESTED_TYPE, null, array(
@@ -626,6 +668,20 @@ public function testSubmitSingleChoiceExpandedWithEmptyData()
626668
$this->assertSame('test', $form->getData());
627669
}
628670

671+
public function testSubmitSingleChoiceExpandedWithEmptyDataAndInitialData()
672+
{
673+
$form = $this->factory->create(static::TESTED_TYPE, 'initial', array(
674+
'multiple' => false,
675+
'expanded' => true,
676+
'choices' => array('initial', 'test'),
677+
'empty_data' => 'test',
678+
));
679+
680+
$form->submit(null);
681+
682+
$this->assertSame('test', $form->getData());
683+
}
684+
629685
public function testSubmitMultipleChoiceExpandedWithEmptyData()
630686
{
631687
$form = $this->factory->create(static::TESTED_TYPE, null, array(
@@ -640,6 +696,49 @@ public function testSubmitMultipleChoiceExpandedWithEmptyData()
640696
$this->assertSame(array('test'), $form->getData());
641697
}
642698

699+
public function testSubmitMultipleChoiceExpandedWithEmptyDataAndInitialEmptyArray()
700+
{
701+
$form = $this->factory->create(static::TESTED_TYPE, array(), array(
702+
'multiple' => true,
703+
'expanded' => true,
704+
'choices' => array('test'),
705+
'empty_data' => array('test'),
706+
));
707+
708+
$form->submit(null);
709+
710+
$this->assertSame(array('test'), $form->getData());
711+
}
712+
713+
public function testSubmitMultipleChoiceExpandedWithEmptyDataAndInitialData()
714+
{
715+
$form = $this->factory->create(static::TESTED_TYPE, array('init'), array(
716+
'multiple' => true,
717+
'expanded' => true,
718+
'choices' => array('init', 'test'),
719+
'empty_data' => array('test'),
720+
));
721+
722+
$form->submit(null);
723+
724+
$this->assertSame(array('test'), $form->getData());
725+
}
726+
727+
/**
728+
* @group legacy
729+
*/
730+
public function testLegacyNullChoices()
731+
{
732+
$form = $this->factory->create(static::TESTED_TYPE, null, array(
733+
'multiple' => false,
734+
'expanded' => false,
735+
'choices' => null,
736+
));
737+
$this->assertNull($form->getConfig()->getOption('choices'));
738+
$this->assertFalse($form->getConfig()->getOption('multiple'));
739+
$this->assertFalse($form->getConfig()->getOption('expanded'));
740+
}
741+
643742
public function testSubmitMultipleNonExpanded()
644743
{
645744
$form = $this->factory->create(static::TESTED_TYPE, null, array(

src/Symfony/Component/Intl/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A PHP replacement layer for the C intl extension that also provides access to
55
the localization data of the ICU library.
66

77
The replacement layer is limited to the locale "en". If you want to use other
8-
locales, you should [install the intl PHP extension] [0] instead.
8+
locales, you should [install the intl PHP extension][0] instead.
99

1010
Resources
1111
---------

src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,14 @@ public function match(\$rawPathinfo)
102102
\$pathinfo = rawurldecode(\$rawPathinfo);
103103
\$trimmedPathinfo = rtrim(\$pathinfo, '/');
104104
\$context = \$this->context;
105-
\$request = \$this->request;
105+
\$request = \$this->request ?: \$this->createRequest(\$pathinfo);
106106
\$requestMethod = \$canonicalMethod = \$context->getMethod();
107107
\$scheme = \$context->getScheme();
108108
109109
if ('HEAD' === \$requestMethod) {
110110
\$canonicalMethod = 'GET';
111111
}
112112
113-
114113
$code
115114
116115
throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
@@ -361,7 +360,11 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren
361360

362361
if ($hasTrailingSlash) {
363362
$code .= <<<EOF
364-
if (substr(\$pathinfo, -1) !== '/') {
363+
if ('/' === substr(\$pathinfo, -1)) {
364+
// no-op
365+
} elseif (!in_array(\$this->context->getMethod(), array('HEAD', 'GET'))) {
366+
goto $gotoname;
367+
} else {
365368
return array_replace(\$ret, \$this->redirect(\$rawPathinfo.'/', '$name'));
366369
}
367370
@@ -391,7 +394,7 @@ private function compileRoute(Route $route, $name, $supportsRedirections, $paren
391394
}
392395
$code .= " }\n";
393396

394-
if ($methods) {
397+
if ($methods || $hasTrailingSlash) {
395398
$code .= " $gotoname:\n";
396399
}
397400

src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher0.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ public function match($rawPathinfo)
2121
$pathinfo = rawurldecode($rawPathinfo);
2222
$trimmedPathinfo = rtrim($pathinfo, '/');
2323
$context = $this->context;
24-
$request = $this->request;
24+
$request = $this->request ?: $this->createRequest($pathinfo);
2525
$requestMethod = $canonicalMethod = $context->getMethod();
2626
$scheme = $context->getScheme();
2727

2828
if ('HEAD' === $requestMethod) {
2929
$canonicalMethod = 'GET';
3030
}
3131

32-
3332
if ('/' === $pathinfo) {
3433
throw new Symfony\Component\Routing\Exception\NoConfigurationException();
3534
}

src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher1.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ public function match($rawPathinfo)
2121
$pathinfo = rawurldecode($rawPathinfo);
2222
$trimmedPathinfo = rtrim($pathinfo, '/');
2323
$context = $this->context;
24-
$request = $this->request;
24+
$request = $this->request ?: $this->createRequest($pathinfo);
2525
$requestMethod = $canonicalMethod = $context->getMethod();
2626
$scheme = $context->getScheme();
2727

2828
if ('HEAD' === $requestMethod) {
2929
$canonicalMethod = 'GET';
3030
}
3131

32-
3332
if (0 === strpos($pathinfo, '/foo')) {
3433
// foo
3534
if (preg_match('#^/foo/(?P<bar>baz|symfony)$#s', $pathinfo, $matches)) {

src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ public function match($rawPathinfo)
2121
$pathinfo = rawurldecode($rawPathinfo);
2222
$trimmedPathinfo = rtrim($pathinfo, '/');
2323
$context = $this->context;
24-
$request = $this->request;
24+
$request = $this->request ?: $this->createRequest($pathinfo);
2525
$requestMethod = $canonicalMethod = $context->getMethod();
2626
$scheme = $context->getScheme();
2727

2828
if ('HEAD' === $requestMethod) {
2929
$canonicalMethod = 'GET';
3030
}
3131

32-
3332
if (0 === strpos($pathinfo, '/foo')) {
3433
// foo
3534
if (preg_match('#^/foo/(?P<bar>baz|symfony)$#s', $pathinfo, $matches)) {
@@ -83,24 +82,34 @@ public function match($rawPathinfo)
8382
// baz3
8483
if ('/test/baz3' === $trimmedPathinfo) {
8584
$ret = array('_route' => 'baz3');
86-
if (substr($pathinfo, -1) !== '/') {
85+
if ('/' === substr($pathinfo, -1)) {
86+
// no-op
87+
} elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) {
88+
goto not_baz3;
89+
} else {
8790
return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz3'));
8891
}
8992

9093
return $ret;
9194
}
95+
not_baz3:
9296

9397
}
9498

9599
// baz4
96100
if (preg_match('#^/test/(?P<foo>[^/]++)/?$#s', $pathinfo, $matches)) {
97101
$ret = $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
98-
if (substr($pathinfo, -1) !== '/') {
102+
if ('/' === substr($pathinfo, -1)) {
103+
// no-op
104+
} elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) {
105+
goto not_baz4;
106+
} else {
99107
return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz4'));
100108
}
101109

102110
return $ret;
103111
}
112+
not_baz4:
104113

105114
// baz5
106115
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
@@ -179,12 +188,17 @@ public function match($rawPathinfo)
179188
// hey
180189
if ('/multi/hey' === $trimmedPathinfo) {
181190
$ret = array('_route' => 'hey');
182-
if (substr($pathinfo, -1) !== '/') {
191+
if ('/' === substr($pathinfo, -1)) {
192+
// no-op
193+
} elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) {
194+
goto not_hey;
195+
} else {
183196
return array_replace($ret, $this->redirect($rawPathinfo.'/', 'hey'));
184197
}
185198

186199
return $ret;
187200
}
201+
not_hey:
188202

189203
// overridden2
190204
if ('/multi/new' === $pathinfo) {

0 commit comments

Comments
 (0)