From f67dab95e2582d24eee84212fabf19a032679299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Wed, 24 Mar 2021 08:42:23 +0100 Subject: [PATCH 1/8] FIx Trying to clone an uncloneable object of class --- src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php b/src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php index 06bdd57b6c..31abcdf589 100644 --- a/src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php +++ b/src/Symfony/Component/Lock/Tests/Store/StoreFactoryTest.php @@ -40,7 +40,7 @@ class StoreFactoryTest extends TestCase public function validConnections() { if (class_exists(\Redis::class)) { - yield [$this->createMock(\Redis::class), RedisStore::class]; + yield [new \Redis(), RedisStore::class]; } if (class_exists(RedisProxy::class)) { yield [$this->createMock(RedisProxy::class), RedisStore::class]; From cf1404a30b3f7b11607f72a5c1f7ff8d72f84d3b Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 25 Mar 2021 18:52:07 +0100 Subject: [PATCH 2/8] [HttpClient] fix using stream_copy_to_stream() with responses cast to php streams --- .../Component/HttpClient/Response/StreamWrapper.php | 2 +- .../HttpClient/Tests/HttpClientTestCase.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php index 961681e4c4..83f1562e84 100644 --- a/src/Symfony/Component/HttpClient/Response/StreamWrapper.php +++ b/src/Symfony/Component/HttpClient/Response/StreamWrapper.php @@ -281,7 +281,7 @@ class StreamWrapper 'uid' => 0, 'gid' => 0, 'rdev' => 0, - 'size' => (int) ($headers['content-length'][0] ?? 0), + 'size' => (int) ($headers['content-length'][0] ?? -1), 'atime' => 0, 'mtime' => strtotime($headers['last-modified'][0] ?? '') ?: 0, 'ctime' => 0, diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index d429934b8b..a3782df49e 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -63,6 +63,19 @@ abstract class HttpClientTestCase extends BaseHttpClientTestCase $this->assertTrue(feof($stream)); } + public function testStreamCopyToStream() + { + $client = $this->getHttpClient(__FUNCTION__); + $response = $client->request('GET', 'http://localhost:8057'); + $h = fopen('php://temp', 'w+'); + stream_copy_to_stream($response->toStream(), $h); + + $this->assertTrue(rewind($h)); + $this->assertSame("{\n \"SER", fread($h, 10)); + $this->assertSame('VER_PROTOCOL', fread($h, 12)); + $this->assertFalse(feof($h)); + } + public function testToStream404() { $client = $this->getHttpClient(__FUNCTION__); From d919f2ce83ad24288284ebf7c78d6df51bf6414a Mon Sep 17 00:00:00 2001 From: Roberto Nygaard Date: Sun, 21 Mar 2021 22:12:55 +0000 Subject: [PATCH 3/8] [HttpKernel] ConfigDataCollector to return known data without the need of a Kernel --- .../DataCollector/ConfigDataCollector.php | 17 +++++---- .../DataCollector/ConfigDataCollectorTest.php | 35 +++++++++++++++++++ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php index d4b298c594..c32c71b747 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/ConfigDataCollector.php @@ -59,12 +59,19 @@ class ConfigDataCollector extends DataCollector implements LateDataCollectorInte */ public function collect(Request $request, Response $response/*, \Throwable $exception = null*/) { + $eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE); + $eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE); + $this->data = [ 'app_name' => $this->name, 'app_version' => $this->version, 'token' => $response->headers->get('X-Debug-Token'), 'symfony_version' => Kernel::VERSION, - 'symfony_state' => 'unknown', + 'symfony_minor_version' => sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION), + 'symfony_lts' => 4 === Kernel::MINOR_VERSION, + 'symfony_state' => $this->determineSymfonyState(), + 'symfony_eom' => $eom->format('F Y'), + 'symfony_eol' => $eol->format('F Y'), 'env' => isset($this->kernel) ? $this->kernel->getEnvironment() : 'n/a', 'debug' => isset($this->kernel) ? $this->kernel->isDebug() : 'n/a', 'php_version' => \PHP_VERSION, @@ -82,14 +89,6 @@ class ConfigDataCollector extends DataCollector implements LateDataCollectorInte foreach ($this->kernel->getBundles() as $name => $bundle) { $this->data['bundles'][$name] = new ClassStub(\get_class($bundle)); } - - $this->data['symfony_state'] = $this->determineSymfonyState(); - $this->data['symfony_minor_version'] = sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION); - $this->data['symfony_lts'] = 4 === Kernel::MINOR_VERSION; - $eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE); - $eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE); - $this->data['symfony_eom'] = $eom->format('F Y'); - $this->data['symfony_eol'] = $eol->format('F Y'); } if (preg_match('~^(\d+(?:\.\d+)*)(.+)?$~', $this->data['php_version'], $matches) && isset($matches[2])) { diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php index df6af954f3..86bd394f56 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/ConfigDataCollectorTest.php @@ -41,6 +41,13 @@ class ConfigDataCollectorTest extends TestCase $this->assertSame(\extension_loaded('xdebug'), $c->hasXDebug()); $this->assertSame(\extension_loaded('Zend OPcache') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN), $c->hasZendOpcache()); $this->assertSame(\extension_loaded('apcu') && filter_var(ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN), $c->hasApcu()); + $this->assertSame(sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION), $c->getSymfonyMinorVersion()); + $this->assertContains($c->getSymfonyState(), ['eol', 'eom', 'dev', 'stable']); + + $eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE)->format('F Y'); + $eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE)->format('F Y'); + $this->assertSame($eom, $c->getSymfonyEom()); + $this->assertSame($eol, $c->getSymfonyEol()); } /** @@ -58,6 +65,34 @@ class ConfigDataCollectorTest extends TestCase $this->assertSame('name', $c->getApplicationName()); $this->assertNull($c->getApplicationVersion()); } + + public function testCollectWithoutKernel() + { + $c = new ConfigDataCollector(); + $c->collect(new Request(), new Response()); + + $this->assertSame('n/a', $c->getEnv()); + $this->assertSame('n/a', $c->isDebug()); + $this->assertSame('config', $c->getName()); + $this->assertMatchesRegularExpression('~^'.preg_quote($c->getPhpVersion(), '~').'~', \PHP_VERSION); + $this->assertMatchesRegularExpression('~'.preg_quote((string) $c->getPhpVersionExtra(), '~').'$~', \PHP_VERSION); + $this->assertSame(\PHP_INT_SIZE * 8, $c->getPhpArchitecture()); + $this->assertSame(class_exists(\Locale::class, false) && \Locale::getDefault() ? \Locale::getDefault() : 'n/a', $c->getPhpIntlLocale()); + $this->assertSame(date_default_timezone_get(), $c->getPhpTimezone()); + $this->assertSame(Kernel::VERSION, $c->getSymfonyVersion()); + $this->assertSame(4 === Kernel::MINOR_VERSION, $c->isSymfonyLts()); + $this->assertNull($c->getToken()); + $this->assertSame(\extension_loaded('xdebug'), $c->hasXDebug()); + $this->assertSame(\extension_loaded('Zend OPcache') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN), $c->hasZendOpcache()); + $this->assertSame(\extension_loaded('apcu') && filter_var(ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN), $c->hasApcu()); + $this->assertSame(sprintf('%s.%s', Kernel::MAJOR_VERSION, Kernel::MINOR_VERSION), $c->getSymfonyMinorVersion()); + $this->assertContains($c->getSymfonyState(), ['eol', 'eom', 'dev', 'stable']); + + $eom = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_MAINTENANCE)->format('F Y'); + $eol = \DateTime::createFromFormat('d/m/Y', '01/'.Kernel::END_OF_LIFE)->format('F Y'); + $this->assertSame($eom, $c->getSymfonyEom()); + $this->assertSame($eol, $c->getSymfonyEol()); + } } class KernelForTest extends Kernel From d0a3c538f474fe467296f19280b1ba1bd897f2f6 Mon Sep 17 00:00:00 2001 From: Roberto Nygaard Date: Thu, 25 Mar 2021 22:22:40 +0000 Subject: [PATCH 4/8] Uses the correct assignment action for console options depending if they are short or long --- src/Symfony/Component/Console/Input/ArrayInput.php | 5 +++-- src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Console/Input/ArrayInput.php b/src/Symfony/Component/Console/Input/ArrayInput.php index 25d2b750b4..bf9a8a455a 100644 --- a/src/Symfony/Component/Console/Input/ArrayInput.php +++ b/src/Symfony/Component/Console/Input/ArrayInput.php @@ -108,12 +108,13 @@ class ArrayInput extends Input $params = []; foreach ($this->parameters as $param => $val) { if ($param && \is_string($param) && '-' === $param[0]) { + $glue = ('-' === $param[1]) ? '=' : ' '; if (\is_array($val)) { foreach ($val as $v) { - $params[] = $param.('' != $v ? '='.$this->escapeToken($v) : ''); + $params[] = $param.('' != $v ? $glue.$this->escapeToken($v) : ''); } } else { - $params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); + $params[] = $param.('' != $val ? $glue.$this->escapeToken($val) : ''); } } else { $params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val); diff --git a/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php index f3eedb3a2d..5777c44b72 100644 --- a/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/ArrayInputTest.php @@ -162,10 +162,10 @@ class ArrayInputTest extends TestCase public function testToString() { $input = new ArrayInput(['-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C"]); - $this->assertEquals('-f -b=bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); + $this->assertEquals('-f -b bar --foo='.escapeshellarg('b a z').' --lala Foo '.escapeshellarg("A\nB'C"), (string) $input); $input = new ArrayInput(['-b' => ['bval_1', 'bval_2'], '--f' => ['fval_1', 'fval_2']]); - $this->assertSame('-b=bval_1 -b=bval_2 --f=fval_1 --f=fval_2', (string) $input); + $this->assertSame('-b bval_1 -b bval_2 --f=fval_1 --f=fval_2', (string) $input); $input = new ArrayInput(['array_arg' => ['val_1', 'val_2']]); $this->assertSame('val_1 val_2', (string) $input); From 83b836dbc90e68e7b088c1c737d393b827baaded Mon Sep 17 00:00:00 2001 From: Warxcell Date: Thu, 18 Mar 2021 15:13:44 +0200 Subject: [PATCH 5/8] IntegerType: Always use en for IntegerToLocalizedStringTransformer Fixes #40456 --- .../IntegerToLocalizedStringTransformer.php | 14 +++++---- .../Form/Extension/Core/Type/IntegerType.php | 2 +- .../Extension/Core/Type/IntegerTypeTest.php | 31 ++++++++++++++++++- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php index 76e3eea8ed..7e0eeea2a5 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/IntegerToLocalizedStringTransformer.php @@ -24,19 +24,21 @@ class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransfo /** * Constructs a transformer. * - * @param bool $grouping Whether thousands should be grouped - * @param int $roundingMode One of the ROUND_ constants in this class + * @param bool $grouping Whether thousands should be grouped + * @param int $roundingMode One of the ROUND_ constants in this class + * @param string|null $locale locale used for transforming */ - public function __construct($grouping = false, $roundingMode = self::ROUND_DOWN) + public function __construct($grouping = false, $roundingMode = self::ROUND_DOWN, $locale = null) { - if (\is_int($grouping) || \is_bool($roundingMode) || 2 < \func_num_args()) { + if (\is_int($grouping) || \is_bool($roundingMode) || \is_int($locale)) { @trigger_error(sprintf('Passing a precision as the first value to %s::__construct() is deprecated since Symfony 4.2 and support for it will be dropped in 5.0.', __CLASS__), \E_USER_DEPRECATED); $grouping = $roundingMode; - $roundingMode = 2 < \func_num_args() ? func_get_arg(2) : self::ROUND_DOWN; + $roundingMode = null !== $locale ? $locale : self::ROUND_DOWN; + $locale = null; } - parent::__construct(0, $grouping, $roundingMode); + parent::__construct(0, $grouping, $roundingMode, $locale); } /** diff --git a/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php b/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php index 540cdb6f79..2fa7c9c642 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/IntegerType.php @@ -25,7 +25,7 @@ class IntegerType extends AbstractType */ public function buildForm(FormBuilderInterface $builder, array $options) { - $builder->addViewTransformer(new IntegerToLocalizedStringTransformer($options['grouping'], $options['rounding_mode'])); + $builder->addViewTransformer(new IntegerToLocalizedStringTransformer($options['grouping'], $options['rounding_mode'], !$options['grouping'] ? 'en' : null)); } /** diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php index a88eb70c5f..0156b21905 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php @@ -17,13 +17,42 @@ class IntegerTypeTest extends BaseTypeTest { public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\IntegerType'; + private $previousLocale; + protected function setUp(): void { IntlTestHelper::requireIntl($this, false); - + $this->previousLocale = \Locale::getDefault(); parent::setUp(); } + protected function tearDown(): void + { + \Locale::setDefault($this->previousLocale); + } + + public function testArabicLocale() + { + \Locale::setDefault('ar'); + + $form = $this->factory->create(static::TESTED_TYPE); + $form->submit('123456'); + + $this->assertSame(123456, $form->getData()); + $this->assertSame('123456', $form->getViewData()); + } + + public function testArabicLocaleNonHtml5() + { + \Locale::setDefault('ar'); + + $form = $this->factory->create(static::TESTED_TYPE, null, ['grouping' => true]); + $form->submit('123456'); + + $this->assertSame(123456, $form->getData()); + $this->assertSame('١٢٣٬٤٥٦', $form->getViewData()); + } + public function testSubmitRejectsFloats() { $form = $this->factory->create(static::TESTED_TYPE); From 5ce5300da335ca0c0b15b1e5c6524c2f94ab72c8 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 27 Mar 2021 18:02:59 +0100 Subject: [PATCH 6/8] error if the input string couldn't be parsed as a date When the Intl polyfill is used instead of the PHP intl extension, the intl_get_error_code() function always returns 0 no matter if the input string could be parsed. --- .../DataTransformer/DateTimeToLocalizedStringTransformer.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index 8d73404b5d..a5ef4f6f06 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -130,6 +130,10 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer } elseif ($timestamp > 253402214400) { // This timestamp represents UTC midnight of 9999-12-31 to prevent 5+ digit years throw new TransformationFailedException('Years beyond 9999 are not supported.'); + } elseif (false === $timestamp) { + // the value couldn't be parsed but the Intl extension didn't report an error code, this + // could be the case when the Intl polyfill is used which always returns 0 as the error code + throw new TransformationFailedException(sprintf('"%s" could not be parsed as a date.', $value)); } try { From bfef4546d313a12af364a1b9680f18f536f0be4f Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sat, 27 Mar 2021 20:49:03 +0100 Subject: [PATCH 7/8] make DateCaster tests timezone-agnostic --- src/Symfony/Component/VarDumper/Caster/DateCaster.php | 2 +- src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Caster/DateCaster.php b/src/Symfony/Component/VarDumper/Caster/DateCaster.php index 6b264c7958..171fbde55c 100644 --- a/src/Symfony/Component/VarDumper/Caster/DateCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/DateCaster.php @@ -92,7 +92,7 @@ class DateCaster if (\PHP_VERSION_ID >= 70107) { // see https://bugs.php.net/74639 foreach (clone $p as $i => $d) { if (self::PERIOD_LIMIT === $i) { - $now = new \DateTimeImmutable(); + $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC')); $dates[] = sprintf('%s more', ($end = $p->getEndDate()) ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) : $p->recurrences - $i diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php index 7f36800d85..63be1681d0 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/DateCasterTest.php @@ -353,7 +353,7 @@ EODUMP; */ public function testCastPeriod($start, $interval, $end, $options, $xPeriod, $xDates) { - $p = new \DatePeriod(new \DateTime($start), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end), $options); + $p = new \DatePeriod(new \DateTime($start, new \DateTimeZone('UTC')), new \DateInterval($interval), \is_int($end) ? $end : new \DateTime($end, new \DateTimeZone('UTC')), $options); $stub = new Stub(); $cast = DateCaster::castPeriod($p, [], $stub, false, 0); From 71da904de056b5bf5b4e5f1cec2948c558f2e0b8 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Sun, 28 Mar 2021 10:05:52 +0200 Subject: [PATCH 8/8] skip intl dependent tests if the extension is missing --- .../Form/Tests/Extension/Core/Type/IntegerTypeTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php index 0156b21905..15cf308d99 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/IntegerTypeTest.php @@ -31,6 +31,9 @@ class IntegerTypeTest extends BaseTypeTest \Locale::setDefault($this->previousLocale); } + /** + * @requires extension intl + */ public function testArabicLocale() { \Locale::setDefault('ar'); @@ -42,6 +45,9 @@ class IntegerTypeTest extends BaseTypeTest $this->assertSame('123456', $form->getViewData()); } + /** + * @requires extension intl + */ public function testArabicLocaleNonHtml5() { \Locale::setDefault('ar');