diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 6462e2c15a7e..fe84ba5dad29 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -499,7 +499,7 @@ public function handleError($type, $message, $file, $line) if ($this->isRecursive) { $log = 0; } else { - if (!\defined('HHVM_VERSION')) { + if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404) && !\defined('HHVM_VERSION')) { $currentErrorHandler = set_error_handler('var_dump'); restore_error_handler(); } @@ -511,7 +511,7 @@ public function handleError($type, $message, $file, $line) } finally { $this->isRecursive = false; - if (!\defined('HHVM_VERSION')) { + if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404) && !\defined('HHVM_VERSION')) { set_error_handler($currentErrorHandler); } } diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index b45a6d08c5b6..fcead4f84e01 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -329,8 +329,6 @@ public function testHandleDeprecation() $handler = new ErrorHandler(); $handler->setDefaultLogger($logger); @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, []); - - restore_error_handler(); } public function testHandleException() diff --git a/src/Symfony/Component/ErrorHandler/ErrorHandler.php b/src/Symfony/Component/ErrorHandler/ErrorHandler.php index c98eea89e5c8..0c8edfcd9d3b 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorHandler.php +++ b/src/Symfony/Component/ErrorHandler/ErrorHandler.php @@ -519,7 +519,7 @@ public function handleError(int $type, string $message, string $file, int $line) if ($this->isRecursive) { $log = 0; } else { - if (!\defined('HHVM_VERSION')) { + if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404)) { $currentErrorHandler = set_error_handler('var_dump'); restore_error_handler(); } @@ -531,7 +531,7 @@ public function handleError(int $type, string $message, string $file, int $line) } finally { $this->isRecursive = false; - if (!\defined('HHVM_VERSION')) { + if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404)) { set_error_handler($currentErrorHandler); } } diff --git a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php index 70710302fd97..28b311549272 100644 --- a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php @@ -363,8 +363,6 @@ public function testHandleDeprecation() $handler = new ErrorHandler(); $handler->setDefaultLogger($logger); @$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, []); - - restore_error_handler(); } /** @@ -618,6 +616,10 @@ public function errorHandlerWhenLoggingProvider(): iterable public function testAssertQuietEval() { + if ('-1' === ini_get('zend.assertions')) { + $this->markTestSkipped('zend.assertions is forcibly disabled'); + } + $ini = [ ini_set('zend.assertions', 1), ini_set('assert.active', 1), diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index 325f61f11232..4d7eed8b0c43 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -117,11 +117,16 @@ public function reverseTransform($value) // date-only patterns require parsing to be done in UTC, as midnight might not exist in the local timezone due // to DST changes $dateOnly = $this->isPatternDateOnly(); + $dateFormatter = $this->getIntlDateFormatter($dateOnly); - $timestamp = $this->getIntlDateFormatter($dateOnly)->parse($value); + try { + $timestamp = @$dateFormatter->parse($value); + } catch (\IntlException $e) { + throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); + } if (0 != intl_get_error_code()) { - throw new TransformationFailedException(intl_get_error_message()); + throw new TransformationFailedException(intl_get_error_message(), intl_get_error_code()); } 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.'); diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index 808571cbef41..c9821bfe7475 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -27,6 +27,12 @@ protected function setUp(): void { parent::setUp(); + // Normalize intl. configuration settings. + if (\extension_loaded('intl')) { + $this->iniSet('intl.use_exceptions', 0); + $this->iniSet('intl.error_level', 0); + } + // Since we test against "de_AT", we need the full implementation IntlTestHelper::requireFullIntl($this, '57.1'); @@ -322,4 +328,44 @@ public function testReverseTransformFiveDigitYearsWithTimestamp() $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, null, \IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd HH:mm:ss'); $transformer->reverseTransform('20107-03-21 12:34:56'); } + + public function testReverseTransformWrapsIntlErrorsWithErrorLevel() + { + if (!\extension_loaded('intl')) { + $this->markTestSkipped('intl extension is not loaded'); + } + + $this->iniSet('intl.error_level', E_WARNING); + + $this->expectException('Symfony\Component\Form\Exception\TransformationFailedException'); + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->reverseTransform('12345'); + } + + public function testReverseTransformWrapsIntlErrorsWithExceptions() + { + if (!\extension_loaded('intl')) { + $this->markTestSkipped('intl extension is not loaded'); + } + + $this->iniSet('intl.use_exceptions', 1); + + $this->expectException('Symfony\Component\Form\Exception\TransformationFailedException'); + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->reverseTransform('12345'); + } + + public function testReverseTransformWrapsIntlErrorsWithExceptionsAndErrorLevel() + { + if (!\extension_loaded('intl')) { + $this->markTestSkipped('intl extension is not loaded'); + } + + $this->iniSet('intl.use_exceptions', 1); + $this->iniSet('intl.error_level', E_WARNING); + + $this->expectException('Symfony\Component\Form\Exception\TransformationFailedException'); + $transformer = new DateTimeToLocalizedStringTransformer(); + $transformer->reverseTransform('12345'); + } } diff --git a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php index cdc66990488d..e71034abac98 100644 --- a/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php +++ b/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php @@ -252,10 +252,13 @@ public function getCookies($format = self::COOKIES_FLAT) * @param string $domain * @param bool $secure * @param bool $httpOnly + * @param string $sameSite */ - public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true) + public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true/*, $sameSite = null*/) { - $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null)); + $sameSite = \func_num_args() > 5 ? func_get_arg(5) : null; + + $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, $sameSite)); } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php index 4d924412232c..18b3c36a6ae9 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php @@ -128,6 +128,14 @@ public function testClearCookieSecureNotHttpOnly() $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure', $bag); } + public function testClearCookieSamesite() + { + $bag = new ResponseHeaderBag([]); + + $bag->clearCookie('foo', '/', null, true, false, 'none'); + $this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0; path=/; secure; samesite=none', $bag); + } + public function testReplace() { $bag = new ResponseHeaderBag([]); diff --git a/src/Symfony/Component/Process/Process.php b/src/Symfony/Component/Process/Process.php index aa9d50065f89..db4c2919dff5 100644 --- a/src/Symfony/Component/Process/Process.php +++ b/src/Symfony/Component/Process/Process.php @@ -336,7 +336,7 @@ public function start(callable $callback = null, array $env = []) throw new RuntimeException(sprintf('The provided cwd "%s" does not exist.', $this->cwd)); } - $this->process = proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $options); + $this->process = @proc_open($commandline, $descriptors, $this->processPipes->pipes, $this->cwd, $envPairs, $options); if (!\is_resource($this->process)) { throw new RuntimeException('Unable to launch a new process.'); diff --git a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php index a76bf6ef91eb..a151de825aa4 100644 --- a/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php +++ b/src/Symfony/Component/Security/Guard/GuardAuthenticatorHandler.php @@ -127,7 +127,7 @@ public function setSessionAuthenticationStrategy(SessionAuthenticationStrategyIn private function migrateSession(Request $request, TokenInterface $token, ?string $providerKey) { - if (!$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession() || \in_array($providerKey, $this->statelessProviderKeys, true)) { + if (\in_array($providerKey, $this->statelessProviderKeys, true) || !$this->sessionStrategy || !$request->hasSession() || !$request->hasPreviousSession()) { return; } diff --git a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php index 0256ec22ff16..5a6c3c26d089 100644 --- a/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php +++ b/src/Symfony/Component/Security/Guard/Tests/GuardAuthenticatorHandlerTest.php @@ -153,6 +153,25 @@ public function testSessionStrategyIsNotCalledWhenStateless() $handler->authenticateWithToken($this->token, $this->request, 'some_provider_key'); } + /** + * @requires function \Symfony\Component\HttpFoundation\Request::setSessionFactory + */ + public function testSessionIsNotInstantiatedOnStatelessFirewall() + { + $sessionFactory = $this->getMockBuilder(\stdClass::class) + ->setMethods(['__invoke']) + ->getMock(); + + $sessionFactory->expects($this->never()) + ->method('__invoke'); + + $this->request->setSessionFactory($sessionFactory); + + $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher, ['stateless_provider_key']); + $handler->setSessionAuthenticationStrategy($this->sessionStrategy); + $handler->authenticateWithToken($this->token, $this->request, 'stateless_provider_key'); + } + protected function setUp(): void { $this->tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock(); diff --git a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php index 6ae15ed9f306..41d61732ac92 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/AbstractRememberMeServices.php @@ -39,6 +39,7 @@ abstract class AbstractRememberMeServices implements RememberMeServicesInterface protected $options = [ 'secure' => false, 'httponly' => true, + 'samesite' => null, ]; private $providerKey; private $secret; @@ -276,7 +277,7 @@ protected function cancelCookie(Request $request) $this->logger->debug('Clearing remember-me cookie.', ['name' => $this->options['name']]); } - $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain'], $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'] ?? null)); + $request->attributes->set(self::COOKIE_ATTR_NAME, new Cookie($this->options['name'], null, 1, $this->options['path'], $this->options['domain'], $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, $this->options['samesite'])); } /** diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 61b1257b4a46..167a09474135 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -86,7 +86,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request) $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, - $this->options['samesite'] ?? null + $this->options['samesite'] ) ); @@ -121,7 +121,7 @@ protected function onLoginSuccess(Request $request, Response $response, TokenInt $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, - $this->options['samesite'] ?? null + $this->options['samesite'] ) ); } diff --git a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php index 6db2a78330d7..8dfce95ae45b 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/TokenBasedRememberMeServices.php @@ -83,7 +83,7 @@ protected function onLoginSuccess(Request $request, Response $response, TokenInt $this->options['secure'] ?? $request->isSecure(), $this->options['httponly'], false, - $this->options['samesite'] ?? null + $this->options['samesite'] ) ); } diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf index 8f8d2d0a0fe9..674ccf5c30ea 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.en.xlf @@ -374,6 +374,14 @@ The number of elements in this collection should be a multiple of {{ compared_value }}. The number of elements in this collection should be a multiple of {{ compared_value }}. + + This value should satisfy at least one of the following constraints: + This value should satisfy at least one of the following constraints: + + + Each element of this collection should satisfy its own set of constraints. + Each element of this collection should satisfy its own set of constraints. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf index 24ef5548da44..57b07b2b5546 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf @@ -374,6 +374,14 @@ The number of elements in this collection should be a multiple of {{ compared_value }}. El número de elementos en esta colección debería ser múltiplo de {{ compared_value }}. + + This value should satisfy at least one of the following constraints: + Este valor debería satisfacer al menos una de las siguientes restricciones: + + + Each element of this collection should satisfy its own set of constraints. + Cada elemento de esta colección debería satisfacer su propio conjunto de restricciones. + diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf index df93abd0b9ee..afd3f73263c7 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.pl.xlf @@ -374,6 +374,14 @@ The number of elements in this collection should be a multiple of {{ compared_value }}. Liczba elementów w tym zbiorze powinna być wielokrotnością {{ compared_value }}. + + This value should satisfy at least one of the following constraints: + Ta wartość powinna spełniać co najmniej jedną z następujących reguł: + + + Each element of this collection should satisfy its own set of constraints. + Każdy element w tym zbiorze powinien spełniać własny zestaw reguł. +