Merge branch '3.3' into 3.4

* 3.3:
  fix merge
  fix merge
  fix merge
  Fix 7.2 compat layer
  Fix PHP 7.2 support
  [HttpFoundation] Add missing session.lazy_write config option
  [HttpFoundation] Combine Cache-Control headers
  [Form] fix parsing invalid floating point numbers
  Escape command usage when displaying it in the text descriptor
  Use for=ID on radio/checkbox label.
This commit is contained in:
Nicolas Grekas 2017-10-10 12:38:39 +02:00
commit d3bc436cd2
26 changed files with 185 additions and 40 deletions

View File

@ -25,9 +25,10 @@ matrix:
group: edge
- php: 5.5
- php: 5.6
- php: 7.0
- php: 7.1
env: deps=high
- php: 7.0
- php: 7.2
env: deps=low
fast_finish: true

View File

@ -50,10 +50,14 @@
{% endblock %}
{% block checkbox_label -%}
{%- set label_attr = label_attr|merge({'for': id}) -%}
{{- block('checkbox_radio_label') -}}
{%- endblock checkbox_label %}
{% block radio_label -%}
{%- set label_attr = label_attr|merge({'for': id}) -%}
{{- block('checkbox_radio_label') -}}
{%- endblock radio_label %}

View File

@ -45,6 +45,9 @@ class AppVariableTest extends TestCase
$this->assertEquals('dev', $this->appVariable->getEnvironment());
}
/**
* @runInSeparateProcess
*/
public function testGetSession()
{
$session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock();
@ -166,6 +169,9 @@ class AppVariableTest extends TestCase
$this->assertEquals(array(), $this->appVariable->getFlashes());
}
/**
* @runInSeparateProcess
*/
public function testGetFlashesWithNoSessionStarted()
{
$session = $this->getMockBuilder(Session::class)->disableOriginalConstructor()->getMock();
@ -177,6 +183,9 @@ class AppVariableTest extends TestCase
$this->assertEquals(array(), $this->appVariable->getFlashes());
}
/**
* @runInSeparateProcess
*/
public function testGetFlashes()
{
$flashMessages = $this->setFlashMessages();

View File

@ -24,6 +24,7 @@
"symfony/asset": "~2.8|~3.0|~4.0",
"symfony/finder": "~2.8|~3.0|~4.0",
"symfony/form": "~3.4|~4.0",
"symfony/http-foundation": "^3.3.11|~4.0",
"symfony/http-kernel": "~3.2|~4.0",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/routing": "~2.8|~3.0|~4.0",

View File

@ -55,7 +55,7 @@ class CacheClearCommandTest extends TestCase
$finder = new Finder();
$metaFiles = $finder->files()->in($this->kernel->getCacheDir())->name('*.php.meta');
// simply check that cache is warmed up
$this->assertGreaterThanOrEqual(1, count($metaFiles));
$this->assertNotEmpty($metaFiles);
$configCacheFactory = new ConfigCacheFactory(true);
foreach ($metaFiles as $file) {

View File

@ -373,6 +373,9 @@ abstract class ControllerTraitTest extends TestCase
$this->assertSame(302, $response->getStatusCode());
}
/**
* @runInSeparateProcess
*/
public function testAddFlash()
{
$flashBag = new FlashBag();

View File

@ -2,7 +2,9 @@
namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Validation;
class Article implements NotExistingInterface
{
public $category;
if (!function_exists('__phpunit_run_isolated_test')) {
class Article implements NotExistingInterface
{
public $category;
}
}

View File

@ -23,7 +23,7 @@
"symfony/dependency-injection": "~3.4|~4.0",
"symfony/config": "~3.4|~4.0",
"symfony/event-dispatcher": "^3.3.1|~4.0",
"symfony/http-foundation": "~3.3|~4.0",
"symfony/http-foundation": "^3.3.11|~4.0",
"symfony/http-kernel": "~3.4|~4.0",
"symfony/polyfill-mbstring": "~1.0",
"symfony/filesystem": "~2.8|~3.0|~4.0",

View File

@ -2,6 +2,8 @@
namespace Symfony\Component\Config\Tests\Fixtures;
class BadParent extends MissingParent
{
if (!function_exists('__phpunit_run_isolated_test')) {
class BadParent extends MissingParent
{
}
}

View File

@ -143,7 +143,7 @@ class TextDescriptor extends Descriptor
$this->writeText('<comment>Usage:</comment>', $options);
foreach (array_merge(array($command->getSynopsis(true)), $command->getAliases(), $command->getUsages()) as $usage) {
$this->writeText("\n");
$this->writeText(' '.$usage, $options);
$this->writeText(' '.OutputFormatter::escape($usage), $options);
}
$this->writeText("\n");

View File

@ -1,7 +1,7 @@
<comment>Usage:</comment>
descriptor:command2 [options] [--] <argument_name>
descriptor:command2 -o|--option_name <argument_name>
descriptor:command2 <argument_name>
descriptor:command2 [options] [--] \<argument_name>
descriptor:command2 -o|--option_name \<argument_name>
descriptor:command2 \<argument_name>
<comment>Arguments:</comment>
<info>argument_name</info>

View File

@ -1,7 +1,7 @@
<comment>Usage:</comment>
descriptor:åèä [options] [--] <argument_åèä>
descriptor:åèä -o|--option_name <argument_name>
descriptor:åèä <argument_name>
descriptor:åèä [options] [--] \<argument_åèä>
descriptor:åèä -o|--option_name \<argument_name>
descriptor:åèä \<argument_name>
<comment>Arguments:</comment>
<info>argument_åèä</info>

View File

@ -1,3 +1,5 @@
<?php
throw new \Exception('boo');
if (!function_exists('__phpunit_run_isolated_test')) {
throw new \Exception('boo');
}

View File

@ -13,6 +13,8 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler;
use Symfony\Bug\NotExistClass;
class OptionalServiceClass extends NotExistClass
{
if (!function_exists('__phpunit_run_isolated_test')) {
class OptionalServiceClass extends NotExistClass
{
}
}

View File

@ -458,6 +458,9 @@ class XmlFileLoaderTest extends TestCase
if (extension_loaded('suhosin') && false === strpos(ini_get('suhosin.executor.include.whitelist'), 'phar')) {
$this->markTestSkipped('To run this test, add "phar" to the "suhosin.executor.include.whitelist" settings in your php.ini file.');
}
if (defined('HHVM_VERSION')) {
$this->markTestSkipped('HHVM makes this test conflict with those run in separate processes.');
}
require_once self::$fixturesPath.'/includes/ProjectWithXsdExtensionInPhar.phar';

View File

@ -116,6 +116,7 @@ class PercentToLocalizedStringTransformer implements DataTransformerInterface
return;
}
$position = 0;
$formatter = $this->getNumberFormatter();
$groupSep = $formatter->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL);
$decSep = $formatter->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL);
@ -129,18 +130,44 @@ class PercentToLocalizedStringTransformer implements DataTransformerInterface
$value = str_replace(',', $decSep, $value);
}
if (false !== strpos($value, $decSep)) {
$type = \NumberFormatter::TYPE_DOUBLE;
} else {
$type = \PHP_INT_SIZE === 8 ? \NumberFormatter::TYPE_INT64 : \NumberFormatter::TYPE_INT32;
}
// replace normal spaces so that the formatter can read them
$value = $formatter->parse(str_replace(' ', "\xc2\xa0", $value));
$result = $formatter->parse(str_replace(' ', "\xc2\xa0", $value), $type, $position);
if (intl_is_failure($formatter->getErrorCode())) {
throw new TransformationFailedException($formatter->getErrorMessage());
}
if (self::FRACTIONAL == $this->type) {
$value /= 100;
$result /= 100;
}
return $value;
if (\function_exists('mb_detect_encoding') && false !== $encoding = mb_detect_encoding($value, null, true)) {
$length = mb_strlen($value, $encoding);
$remainder = mb_substr($value, $position, $length, $encoding);
} else {
$length = \strlen($value);
$remainder = substr($value, $position, $length);
}
// After parsing, position holds the index of the character where the
// parsing stopped
if ($position < $length) {
// Check if there are unrecognized characters at the end of the
// number (excluding whitespace characters)
$remainder = trim($remainder, " \t\n\r\0\x0b\xc2\xa0");
if ('' !== $remainder) {
throw new TransformationFailedException(sprintf('The number contains unrecognized characters: "%s"', $remainder));
}
}
return $result;
}
/**

View File

@ -141,10 +141,10 @@ class PercentToLocalizedStringTransformerTest extends TestCase
*/
public function testDecimalSeparatorMayNotBeDotIfGroupingSeparatorIsDot()
{
// Since we test against "de_AT", we need the full implementation
// Since we test against "de_DE", we need the full implementation
IntlTestHelper::requireFullIntl($this, '4.8.1.1');
\Locale::setDefault('de_AT');
\Locale::setDefault('de_DE');
$transformer = new PercentToLocalizedStringTransformer(1, 'integer');
@ -236,4 +236,70 @@ class PercentToLocalizedStringTransformerTest extends TestCase
$this->assertEquals(1234.5, $transformer->reverseTransform('1234,5'));
$this->assertEquals(1234.5, $transformer->reverseTransform('1234.5'));
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testReverseTransformDisallowsLeadingExtraCharacters()
{
$transformer = new PercentToLocalizedStringTransformer();
$transformer->reverseTransform('foo123');
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo3"
*/
public function testReverseTransformDisallowsCenteredExtraCharacters()
{
$transformer = new PercentToLocalizedStringTransformer();
$transformer->reverseTransform('12foo3');
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo8"
* @requires extension mbstring
*/
public function testReverseTransformDisallowsCenteredExtraCharactersMultibyte()
{
// Since we test against other locales, we need the full implementation
IntlTestHelper::requireFullIntl($this, false);
\Locale::setDefault('ru');
$transformer = new PercentToLocalizedStringTransformer();
$transformer->reverseTransform("12\xc2\xa0345,67foo8");
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo"
*/
public function testReverseTransformDisallowsTrailingExtraCharacters()
{
$transformer = new PercentToLocalizedStringTransformer();
$transformer->reverseTransform('123foo');
}
/**
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
* @expectedExceptionMessage The number contains unrecognized characters: "foo"
* @requires extension mbstring
*/
public function testReverseTransformDisallowsTrailingExtraCharactersMultibyte()
{
// Since we test against other locales, we need the full implementation
IntlTestHelper::requireFullIntl($this, false);
\Locale::setDefault('ru');
$transformer = new PercentToLocalizedStringTransformer();
$transformer->reverseTransform("12\xc2\xa0345,678foo");
}
}

View File

@ -147,7 +147,7 @@ class HeaderBag implements \IteratorAggregate, \Countable
}
if ('cache-control' === $key) {
$this->cacheControl = $this->parseCacheControl($values[0]);
$this->cacheControl = $this->parseCacheControl(implode(', ', $this->headers[$key]));
}
}

View File

@ -73,6 +73,7 @@ class NativeSessionStorage implements SessionStorageInterface
* gc_probability, "1"
* hash_bits_per_character, "4"
* hash_function, "0"
* lazy_write, "1"
* name, "PHPSESSID"
* referer_check, ""
* serialize_handler, "php"
@ -98,8 +99,11 @@ class NativeSessionStorage implements SessionStorageInterface
*/
public function __construct(array $options = array(), $handler = null, MetadataBag $metaBag = null)
{
session_cache_limiter(''); // disable by default because it's managed by HeaderBag (if used)
ini_set('session.use_cookies', 1);
$options += array(
// disable by default because it's managed by HeaderBag (if used)
'cache_limiter' => '',
'use_cookies' => 1,
);
session_register_shutdown();
@ -187,6 +191,10 @@ class NativeSessionStorage implements SessionStorageInterface
return false;
}
if (headers_sent()) {
return false;
}
if (null !== $lifetime) {
ini_set('session.cookie_lifetime', $lifetime);
}
@ -324,12 +332,16 @@ class NativeSessionStorage implements SessionStorageInterface
*/
public function setOptions(array $options)
{
if (headers_sent()) {
return;
}
$validOptions = array_flip(array(
'cache_limiter', 'cookie_domain', 'cookie_httponly',
'cookie_lifetime', 'cookie_path', 'cookie_secure',
'entropy_file', 'entropy_length', 'gc_divisor',
'gc_maxlifetime', 'gc_probability', 'hash_bits_per_character',
'hash_function', 'name', 'referer_check',
'hash_function', 'lazy_write', 'name', 'referer_check',
'serialize_handler', 'use_strict_mode', 'use_cookies',
'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
@ -381,6 +393,10 @@ class NativeSessionStorage implements SessionStorageInterface
);
}
if (headers_sent($file, $line)) {
throw new \RuntimeException(sprintf('Failed to set the session handler because headers have already been sent by "%s" at line %d.', $file, $line));
}
// Wrap $saveHandler in proxy and prevent double wrapping of proxy
if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
$saveHandler = new SessionHandlerProxy($saveHandler);

View File

@ -86,6 +86,17 @@ class ResponseHeaderBagTest extends TestCase
$bag = new ResponseHeaderBag();
$bag->set('Last-Modified', 'abcde');
$this->assertEquals('private, must-revalidate', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag();
$bag->set('Cache-Control', array('public', 'must-revalidate'));
$this->assertCount(1, $bag->get('Cache-Control', null, false));
$this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
$bag = new ResponseHeaderBag();
$bag->set('Cache-Control', 'public');
$bag->set('Cache-Control', 'must-revalidate', false);
$this->assertCount(1, $bag->get('Cache-Control', null, false));
$this->assertEquals('must-revalidate, public', $bag->get('Cache-Control'));
}
public function testCacheControlClone()

View File

@ -269,6 +269,9 @@ class PdoSessionHandlerTest extends TestCase
$this->assertSame('', $data, 'Destroyed session returns empty string');
}
/**
* @runInSeparateProcess
*/
public function testSessionGC()
{
$previousLifeTime = ini_set('session.gc_maxlifetime', 1000);

View File

@ -38,8 +38,7 @@ class LocaleListenerTest extends TestCase
public function testLocaleFromRequestAttribute()
{
$request = Request::create('/');
session_name('foo');
$request->cookies->set('foo', 'value');
$request->cookies->set(session_name(), 'value');
$request->attributes->set('_locale', 'es');
$listener = new LocaleListener($this->requestStack, 'fr');

View File

@ -65,8 +65,7 @@ class TestSessionListenerTest extends TestCase
{
$this->sessionHasBeenStarted();
$params = session_get_cookie_params();
session_set_cookie_params(0, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
@ini_set('session.cookie_lifetime', 0);
$response = $this->filterResponse(new Request(), HttpKernelInterface::MASTER_REQUEST);
$cookies = $response->headers->getCookies();

View File

@ -18,7 +18,7 @@
"require": {
"php": "^5.5.9|>=7.0.8",
"symfony/event-dispatcher": "~2.8|~3.0|~4.0",
"symfony/http-foundation": "~3.3|~4.0",
"symfony/http-foundation": "^3.3.11|~4.0",
"symfony/debug": "~2.8|~3.0|~4.0",
"psr/log": "~1.0"
},

View File

@ -1,5 +1,8 @@
<?php
if (function_exists('__phpunit_run_isolated_test')) {
return;
}
/** @var $loader \Symfony\Component\Routing\Loader\PhpFileLoader */
/** @var \Symfony\Component\Routing\RouteCollection $collection */
$collection = $loader->import('validpattern.php');

View File

@ -29,14 +29,6 @@ class NativeSessionTokenStorageTest extends TestCase
*/
private $storage;
public static function setUpBeforeClass()
{
ini_set('session.save_handler', 'files');
ini_set('session.save_path', sys_get_temp_dir());
parent::setUpBeforeClass();
}
protected function setUp()
{
$_SESSION = array();