diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index cae3ae9350..61668b7837 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -61,16 +61,20 @@ class TimeType extends AbstractType } if ('single_text' === $options['widget']) { - // handle seconds ignored by user's browser when with_seconds enabled - // https://codereview.chromium.org/450533009/ - if ($options['with_seconds']) { - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $e) { - $data = $e->getData(); - if ($data && preg_match('/^\d{2}:\d{2}$/', $data)) { - $e->setData($data.':00'); + $builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format)); + + $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $e) use ($options) { + $data = $e->getData(); + if ($data && preg_match('/^(?P\d{2}):(?P\d{2})(?::(?P\d{2})(?:\.\d+)?)?$/', $data, $matches)) { + if ($options['with_seconds']) { + // handle seconds ignored by user's browser when with_seconds enabled + // https://codereview.chromium.org/450533009/ + $e->setData(sprintf('%s:%s:%s', $matches['hours'], $matches['minutes'], isset($matches['seconds']) ? $matches['seconds'] : '00')); + } else { + $e->setData(sprintf('%s:%s', $matches['hours'], $matches['minutes'])); } - }); - } + } + }); if (null !== $options['reference_date']) { $format = 'Y-m-d '.$format; @@ -83,8 +87,6 @@ class TimeType extends AbstractType } }); } - - $builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format)); } else { $hourOptions = $minuteOptions = $secondOptions = [ 'error_bubbling' => true, diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php index 10652b9b98..4ec255a6c6 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -328,6 +328,54 @@ class TimeTypeTest extends BaseTypeTest $this->assertSame('14:09:10', $form->getData()->format('H:i:s')); } + public function testSubmitWithoutSecondsAndBrowserAddingSeconds() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + 'with_seconds' => false, + ]); + + $form->submit('03:04:00'); + + $this->assertEquals('03:04:00', $form->getData()); + $this->assertEquals('03:04', $form->getViewData()); + } + + public function testSubmitWithSecondsAndBrowserAddingMicroseconds() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + 'with_seconds' => true, + ]); + + $form->submit('03:04:00.000'); + + $this->assertEquals('03:04:00', $form->getData()); + $this->assertEquals('03:04:00', $form->getViewData()); + } + + public function testSubmitWithoutSecondsAndBrowserAddingMicroseconds() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'UTC', + 'input' => 'string', + 'widget' => 'single_text', + 'with_seconds' => false, + ]); + + $form->submit('03:04:00.000'); + + $this->assertEquals('03:04:00', $form->getData()); + $this->assertEquals('03:04', $form->getViewData()); + } + public function testSetDataWithoutMinutes() { $form = $this->factory->create(static::TESTED_TYPE, null, [ diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index b037ca0fc2..d20d8b6f32 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -53,7 +53,7 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface, private static $curlVersion; /** - * @param array $defaultOptions Default requests' options + * @param array $defaultOptions Default request's options * @param int $maxHostConnections The maximum number of connections to a single host * @param int $maxPendingPushes The maximum number of pushed responses to accept in the queue * diff --git a/src/Symfony/Component/HttpClient/HttpClient.php b/src/Symfony/Component/HttpClient/HttpClient.php index 3eb3a4c884..b8ce62a233 100644 --- a/src/Symfony/Component/HttpClient/HttpClient.php +++ b/src/Symfony/Component/HttpClient/HttpClient.php @@ -21,7 +21,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; final class HttpClient { /** - * @param array $defaultOptions Default requests' options + * @param array $defaultOptions Default request's options * @param int $maxHostConnections The maximum number of connections to a single host * @param int $maxPendingPushes The maximum number of pushed responses to accept in the queue * diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index 60bced566d..55e208bd7c 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -41,7 +41,7 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac private $multi; /** - * @param array $defaultOptions Default requests' options + * @param array $defaultOptions Default request's options * @param int $maxHostConnections The maximum number of connections to open * * @see HttpClientInterface::OPTIONS_DEFAULTS for available options diff --git a/src/Symfony/Component/Mime/Part/AbstractMultipartPart.php b/src/Symfony/Component/Mime/Part/AbstractMultipartPart.php index 48b8620232..685d250627 100644 --- a/src/Symfony/Component/Mime/Part/AbstractMultipartPart.php +++ b/src/Symfony/Component/Mime/Part/AbstractMultipartPart.php @@ -91,7 +91,7 @@ abstract class AbstractMultipartPart extends AbstractPart private function getBoundary(): string { if (null === $this->boundary) { - $this->boundary = '_=_symfony_'.time().'_'.bin2hex(random_bytes(16)).'_=_'; + $this->boundary = strtr(base64_encode(random_bytes(6)), '+/', '-_'); } return $this->boundary; diff --git a/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php b/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php index a74ecea316..dbc2e6eff9 100644 --- a/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php +++ b/src/Symfony/Component/Mime/Tests/Part/Multipart/FormDataPartTest.php @@ -91,4 +91,16 @@ class FormDataPartTest extends TestCase $this->assertEquals($foo, $parts[0]->bodyToString()); $this->assertEquals($bar, $parts[1]->bodyToString()); } + + public function testBoundaryContentTypeHeader() + { + $f = new FormDataPart([ + 'file' => new DataPart('data.csv', 'data.csv', 'text/csv'), + ]); + $headers = $f->getPreparedHeaders()->toArray(); + $this->assertRegExp( + '/^Content-Type: multipart\/form-data; boundary=[a-zA-Z0-9\-_]{8}$/', + $headers[0] + ); + } }