Merge branch '5.2' into 5.x

* 5.2:
  skip intl dependent tests if the extension is missing
  make DateCaster tests timezone-agnostic
  error if the input string couldn't be parsed as a date
  IntegerType: Always use en for IntegerToLocalizedStringTransformer Fixes #40456
  Uses the correct assignment action for console options depending if they are short or long
  [HttpKernel] ConfigDataCollector to return known data without the need of a Kernel
  [HttpClient] fix using stream_copy_to_stream() with responses cast to php streams
  FIx Trying to clone an uncloneable object of class
This commit is contained in:
Christian Flothmann 2021-03-28 11:44:11 +02:00
commit 42a56528e9
13 changed files with 111 additions and 23 deletions

View File

@ -108,12 +108,13 @@ class ArrayInput extends Input
$params = []; $params = [];
foreach ($this->parameters as $param => $val) { foreach ($this->parameters as $param => $val) {
if ($param && \is_string($param) && '-' === $param[0]) { if ($param && \is_string($param) && '-' === $param[0]) {
$glue = ('-' === $param[1]) ? '=' : ' ';
if (\is_array($val)) { if (\is_array($val)) {
foreach ($val as $v) { foreach ($val as $v) {
$params[] = $param.('' != $v ? '='.$this->escapeToken($v) : ''); $params[] = $param.('' != $v ? $glue.$this->escapeToken($v) : '');
} }
} else { } else {
$params[] = $param.('' != $val ? '='.$this->escapeToken($val) : ''); $params[] = $param.('' != $val ? $glue.$this->escapeToken($val) : '');
} }
} else { } else {
$params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val); $params[] = \is_array($val) ? implode(' ', array_map([$this, 'escapeToken'], $val)) : $this->escapeToken($val);

View File

@ -162,10 +162,10 @@ class ArrayInputTest extends TestCase
public function testToString() public function testToString()
{ {
$input = new ArrayInput(['-f' => null, '-b' => 'bar', '--foo' => 'b a z', '--lala' => null, 'test' => 'Foo', 'test2' => "A\nB'C"]); $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']]); $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']]); $input = new ArrayInput(['array_arg' => ['val_1', 'val_2']]);
$this->assertSame('val_1 val_2', (string) $input); $this->assertSame('val_1 val_2', (string) $input);

View File

@ -130,6 +130,10 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
} elseif ($timestamp > 253402214400) { } elseif ($timestamp > 253402214400) {
// This timestamp represents UTC midnight of 9999-12-31 to prevent 5+ digit years // This timestamp represents UTC midnight of 9999-12-31 to prevent 5+ digit years
throw new TransformationFailedException('Years beyond 9999 are not supported.'); 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 { try {

View File

@ -24,12 +24,13 @@ class IntegerToLocalizedStringTransformer extends NumberToLocalizedStringTransfo
/** /**
* Constructs a transformer. * Constructs a transformer.
* *
* @param bool $grouping Whether thousands should be grouped * @param bool $grouping Whether thousands should be grouped
* @param int $roundingMode One of the ROUND_ constants in this class * @param int $roundingMode One of the ROUND_ constants in this class
* @param string|null $locale locale used for transforming
*/ */
public function __construct(?bool $grouping = false, ?int $roundingMode = \NumberFormatter::ROUND_DOWN) public function __construct(?bool $grouping = false, ?int $roundingMode = \NumberFormatter::ROUND_DOWN, ?string $locale)
{ {
parent::__construct(0, $grouping, $roundingMode); parent::__construct(0, $grouping, $roundingMode, $locale);
} }
/** /**

View File

@ -26,7 +26,7 @@ class IntegerType extends AbstractType
*/ */
public function buildForm(FormBuilderInterface $builder, array $options) 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));
} }
/** /**

View File

@ -17,13 +17,48 @@ class IntegerTypeTest extends BaseTypeTest
{ {
public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\IntegerType'; public const TESTED_TYPE = 'Symfony\Component\Form\Extension\Core\Type\IntegerType';
private $previousLocale;
protected function setUp(): void protected function setUp(): void
{ {
IntlTestHelper::requireIntl($this, false); IntlTestHelper::requireIntl($this, false);
$this->previousLocale = \Locale::getDefault();
parent::setUp(); parent::setUp();
} }
protected function tearDown(): void
{
\Locale::setDefault($this->previousLocale);
}
/**
* @requires extension intl
*/
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());
}
/**
* @requires extension intl
*/
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() public function testSubmitRejectsFloats()
{ {
$form = $this->factory->create(static::TESTED_TYPE); $form = $this->factory->create(static::TESTED_TYPE);

View File

@ -289,7 +289,7 @@ class StreamWrapper
'uid' => 0, 'uid' => 0,
'gid' => 0, 'gid' => 0,
'rdev' => 0, 'rdev' => 0,
'size' => (int) ($headers['content-length'][0] ?? 0), 'size' => (int) ($headers['content-length'][0] ?? -1),
'atime' => 0, 'atime' => 0,
'mtime' => strtotime($headers['last-modified'][0] ?? '') ?: 0, 'mtime' => strtotime($headers['last-modified'][0] ?? '') ?: 0,
'ctime' => 0, 'ctime' => 0,

View File

@ -75,6 +75,19 @@ abstract class HttpClientTestCase extends BaseHttpClientTestCase
$this->assertTrue(feof($stream)); $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() public function testToStream404()
{ {
$client = $this->getHttpClient(__FUNCTION__); $client = $this->getHttpClient(__FUNCTION__);

View File

@ -42,10 +42,17 @@ class ConfigDataCollector extends DataCollector implements LateDataCollectorInte
*/ */
public function collect(Request $request, Response $response, \Throwable $exception = null) 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 = [ $this->data = [
'token' => $response->headers->get('X-Debug-Token'), 'token' => $response->headers->get('X-Debug-Token'),
'symfony_version' => Kernel::VERSION, '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', 'env' => isset($this->kernel) ? $this->kernel->getEnvironment() : 'n/a',
'debug' => isset($this->kernel) ? $this->kernel->isDebug() : 'n/a', 'debug' => isset($this->kernel) ? $this->kernel->isDebug() : 'n/a',
'php_version' => \PHP_VERSION, 'php_version' => \PHP_VERSION,
@ -63,14 +70,6 @@ class ConfigDataCollector extends DataCollector implements LateDataCollectorInte
foreach ($this->kernel->getBundles() as $name => $bundle) { foreach ($this->kernel->getBundles() as $name => $bundle) {
$this->data['bundles'][$name] = new ClassStub(\get_class($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])) { if (preg_match('~^(\d+(?:\.\d+)*)(.+)?$~', $this->data['php_version'], $matches) && isset($matches[2])) {

View File

@ -41,6 +41,41 @@ class ConfigDataCollectorTest extends TestCase
$this->assertSame(\extension_loaded('xdebug'), $c->hasXDebug()); $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('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(\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());
}
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());
} }
} }

View File

@ -43,7 +43,7 @@ class StoreFactoryTest extends TestCase
public function validConnections() public function validConnections()
{ {
if (class_exists(\Redis::class)) { if (class_exists(\Redis::class)) {
yield [$this->createMock(\Redis::class), RedisStore::class]; yield [new \Redis(), RedisStore::class];
} }
if (class_exists(RedisProxy::class)) { if (class_exists(RedisProxy::class)) {
yield [$this->createMock(RedisProxy::class), RedisStore::class]; yield [$this->createMock(RedisProxy::class), RedisStore::class];

View File

@ -91,7 +91,7 @@ class DateCaster
$dates = []; $dates = [];
foreach (clone $p as $i => $d) { foreach (clone $p as $i => $d) {
if (self::PERIOD_LIMIT === $i) { if (self::PERIOD_LIMIT === $i) {
$now = new \DateTimeImmutable(); $now = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
$dates[] = sprintf('%s more', ($end = $p->getEndDate()) $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'))) ? 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 : $p->recurrences - $i

View File

@ -341,7 +341,7 @@ EODUMP;
*/ */
public function testCastPeriod($start, $interval, $end, $options, $xPeriod, $xDates) 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(); $stub = new Stub();
$cast = DateCaster::castPeriod($p, [], $stub, false, 0); $cast = DateCaster::castPeriod($p, [], $stub, false, 0);