diff --git a/src/Symfony/Bridge/Twig/README.md b/src/Symfony/Bridge/Twig/README.md index eb084147c3..602f5a54c3 100644 --- a/src/Symfony/Bridge/Twig/README.md +++ b/src/Symfony/Bridge/Twig/README.md @@ -1,7 +1,7 @@ Twig Bridge =========== -Provides integration for [Twig](http://twig.sensiolabs.org/) with various +Provides integration for [Twig](https://twig.symfony.com/) with various Symfony components. Resources diff --git a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php index 5e06d54a0a..92a0c075af 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/ChoiceType.php @@ -421,6 +421,7 @@ class ChoiceType extends AbstractType // See https://github.com/symfony/symfony/pull/5582 'data_class' => null, 'choice_translation_domain' => true, + 'trim' => false, )); $resolver->setNormalizer('choices', $choicesNormalizer); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php index cca7397250..2c4c15f410 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php @@ -2567,4 +2567,59 @@ class ChoiceTypeTest extends BaseTypeTest $this->assertEquals('_09name', $view->vars['name']); $this->assertEquals('_09name', $view->vars['full_name']); } + + /** + * @dataProvider provideTrimCases + */ + public function testTrimIsDisabled($multiple, $expanded) + { + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'multiple' => $multiple, + 'expanded' => $expanded, + 'choices' => array( + 'a' => '1', + ), + 'choices_as_values' => true, + )); + + $submittedData = ' 1'; + + $form->submit($multiple ? (array) $submittedData : $submittedData); + + // When the choice does not exist the transformation fails + $this->assertFalse($form->isSynchronized()); + $this->assertNull($form->getData()); + } + + /** + * @dataProvider provideTrimCases + */ + public function testSubmitValueWithWhiteSpace($multiple, $expanded) + { + $valueWhitWhiteSpace = '1 '; + + $form = $this->factory->create(static::TESTED_TYPE, null, array( + 'multiple' => $multiple, + 'expanded' => $expanded, + 'choices' => array( + 'a' => $valueWhitWhiteSpace, + ), + 'choices_as_values' => true, + )); + + $form->submit($multiple ? (array) $valueWhitWhiteSpace : $valueWhitWhiteSpace); + + $this->assertTrue($form->isSynchronized()); + $this->assertSame($multiple ? (array) $valueWhitWhiteSpace : $valueWhitWhiteSpace, $form->getData()); + } + + public function provideTrimCases() + { + return array( + 'Simple' => array(false, false), + 'Multiple' => array(true, false), + 'Simple expanded' => array(false, true), + 'Multiple expanded' => array(true, true), + ); + } } diff --git a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php b/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php index c2ac6768c3..3c35810465 100644 --- a/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php +++ b/src/Symfony/Component/HttpFoundation/File/MimeType/FileBinaryMimeTypeGuesser.php @@ -43,7 +43,21 @@ class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface */ public static function isSupported() { - return '\\' !== DIRECTORY_SEPARATOR && function_exists('passthru') && function_exists('escapeshellarg'); + static $supported = null; + + if (null !== $supported) { + return $supported; + } + + if ('\\' === DIRECTORY_SEPARATOR || !function_exists('passthru') || !function_exists('escapeshellarg')) { + return $supported = false; + } + + ob_start(); + passthru('command -v file', $exitStatus); + $binPath = trim(ob_get_clean()); + + return $supported = 0 === $exitStatus && '' !== $binPath; } /** diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 331aad2610..abf8011794 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -513,13 +513,19 @@ class Response } /** - * Returns true if the response is worth caching under any circumstance. + * Returns true if the response may safely be kept in a shared (surrogate) cache. * * Responses marked "private" with an explicit Cache-Control directive are * considered uncacheable. * * Responses with neither a freshness lifetime (Expires, max-age) nor cache - * validator (Last-Modified, ETag) are considered uncacheable. + * validator (Last-Modified, ETag) are considered uncacheable because there is + * no way to tell when or how to remove them from the cache. + * + * Note that RFC 7231 and RFC 7234 possibly allow for a more permissive implementation, + * for example "status codes that are defined as cacheable by default [...] + * can be reused by a cache with heuristic expiration unless otherwise indicated" + * (https://tools.ietf.org/html/rfc7231#section-6.1) * * @return bool true if the response is worth caching, false otherwise */ diff --git a/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php b/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php index 027b2b1761..672cc893fe 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/ResponseCacheStrategy.php @@ -72,7 +72,7 @@ class ResponseCacheStrategy implements ResponseCacheStrategyInterface $response->setLastModified(null); } - if (!$response->isFresh()) { + if (!$response->isFresh() || !$response->isCacheable()) { $this->cacheable = false; } diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php index 5e4c322223..6d67a17739 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/ResponseCacheStrategyTest.php @@ -175,8 +175,26 @@ class ResponseCacheStrategyTest extends TestCase $cacheStrategy->update($masterResponse); $this->assertTrue($masterResponse->headers->hasCacheControlDirective('private')); - // Not sure if we should pass "max-age: 60" in this case, as long as the response is private and - // that's the more conservative of both the master and embedded response...? + $this->assertFalse($masterResponse->headers->hasCacheControlDirective('public')); + } + + public function testEmbeddingPublicResponseDoesNotMakeMainResponsePublic() + { + $cacheStrategy = new ResponseCacheStrategy(); + + $masterResponse = new Response(); + $masterResponse->setPrivate(); // this is the default, but let's be explicit + $masterResponse->setMaxAge(100); + + $embeddedResponse = new Response(); + $embeddedResponse->setPublic(); + $embeddedResponse->setSharedMaxAge(100); + + $cacheStrategy->add($embeddedResponse); + $cacheStrategy->update($masterResponse); + + $this->assertTrue($masterResponse->headers->hasCacheControlDirective('private')); + $this->assertFalse($masterResponse->headers->hasCacheControlDirective('public')); } public function testResponseIsExiprableWhenEmbeddedResponseCombinesExpiryAndValidation()