diff --git a/CHANGELOG.md b/CHANGELOG.md index 374c31889..752dd6c14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ CHANGELOG ========= +8.0 +--- + + * Remove the following deprecated session options from `NativeSessionStorage`: `referer_check`, `use_only_cookies`, `use_trans_sid`, `sid_length`, `sid_bits_per_character`, `trans_sid_hosts`, `trans_sid_tags` + +7.4 +--- + + * Deprecate using `Request::sendHeaders()` after headers have already been sent; use a `StreamedResponse` instead + 7.3 --- diff --git a/HeaderUtils.php b/HeaderUtils.php index a7079be9a..37953af4f 100644 --- a/HeaderUtils.php +++ b/HeaderUtils.php @@ -164,7 +164,7 @@ public static function unquote(string $s): string */ public static function makeDisposition(string $disposition, string $filename, string $filenameFallback = ''): string { - if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE])) { + if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE], true)) { throw new \InvalidArgumentException(\sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE)); } diff --git a/IpUtils.php b/IpUtils.php index 11a43238b..f67e314ab 100644 --- a/IpUtils.php +++ b/IpUtils.php @@ -196,7 +196,7 @@ public static function anonymize(string $ip/* , int $v4Bytes = 1, int $v6Bytes = throw new \InvalidArgumentException('Cannot anonymize more than 4 bytes for IPv4 and 16 bytes for IPv6.'); } - /** + /* * If the IP contains a % symbol, then it is a local-link address with scoping according to RFC 4007 * In that case, we only care about the part before the % symbol, as the following functions, can only work with * the IP address itself. As the scope can leak information (containing interface name), we do not want to diff --git a/Request.php b/Request.php index dba930a24..2f8f0add4 100644 --- a/Request.php +++ b/Request.php @@ -1351,7 +1351,7 @@ public function isMethod(string $method): bool */ public function isMethodSafe(): bool { - return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE']); + return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE'], true); } /** @@ -1359,7 +1359,7 @@ public function isMethodSafe(): bool */ public function isMethodIdempotent(): bool { - return \in_array($this->getMethod(), ['HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE']); + return \in_array($this->getMethod(), ['HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE'], true); } /** @@ -1369,7 +1369,7 @@ public function isMethodIdempotent(): bool */ public function isMethodCacheable(): bool { - return \in_array($this->getMethod(), ['GET', 'HEAD']); + return \in_array($this->getMethod(), ['GET', 'HEAD'], true); } /** diff --git a/Response.php b/Response.php index 638b5bf60..ebbfbc4ec 100644 --- a/Response.php +++ b/Response.php @@ -261,7 +261,7 @@ public function prepare(Request $request): static } // Fix Content-Type - $charset = $this->charset ?: 'UTF-8'; + $charset = $this->charset ?: 'utf-8'; if (!$headers->has('Content-Type')) { $headers->set('Content-Type', 'text/html; charset='.$charset); } elseif (0 === stripos($headers->get('Content-Type') ?? '', 'text/') && false === stripos($headers->get('Content-Type') ?? '', 'charset')) { @@ -317,6 +317,12 @@ public function sendHeaders(?int $statusCode = null): static { // headers have already been sent by the developer if (headers_sent()) { + if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) { + $statusCode ??= $this->statusCode; + trigger_deprecation('symfony/http-foundation', '7.4', 'Trying to use "%s::sendHeaders()" after headers have already been sent is deprecated will throw a PHP warning in 8.0. Use a "StreamedResponse" instead.', static::class); + // header(\sprintf('HTTP/%s %s %s', $this->version, $statusCode, $this->statusText), true, $statusCode); + } + return $this; } @@ -539,7 +545,7 @@ public function getCharset(): ?string */ public function isCacheable(): bool { - if (!\in_array($this->statusCode, [200, 203, 300, 301, 302, 404, 410])) { + if (!\in_array($this->statusCode, [200, 203, 300, 301, 302, 404, 410], true)) { return false; } @@ -1248,7 +1254,7 @@ public function isNotFound(): bool */ public function isRedirect(?string $location = null): bool { - return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (null === $location ?: $location == $this->headers->get('Location')); + return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308], true) && (null === $location ?: $location == $this->headers->get('Location')); } /** @@ -1258,7 +1264,7 @@ public function isRedirect(?string $location = null): bool */ public function isEmpty(): bool { - return \in_array($this->statusCode, [204, 304]); + return \in_array($this->statusCode, [204, 304], true); } /** diff --git a/ResponseHeaderBag.php b/ResponseHeaderBag.php index b2bdb500c..7df73f7fd 100644 --- a/ResponseHeaderBag.php +++ b/ResponseHeaderBag.php @@ -194,7 +194,7 @@ public function removeCookie(string $name, ?string $path = '/', ?string $domain */ public function getCookies(string $format = self::COOKIES_FLAT): array { - if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) { + if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY], true)) { throw new \InvalidArgumentException(\sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY]))); } diff --git a/Session/Storage/NativeSessionStorage.php b/Session/Storage/NativeSessionStorage.php index 5c0851347..4077160e0 100644 --- a/Session/Storage/NativeSessionStorage.php +++ b/Session/Storage/NativeSessionStorage.php @@ -62,16 +62,9 @@ class NativeSessionStorage implements SessionStorageInterface * gc_probability, "1" * lazy_write, "1" * name, "PHPSESSID" - * referer_check, "" (deprecated since Symfony 7.2, to be removed in Symfony 8.0) * serialize_handler, "php" * use_strict_mode, "1" * use_cookies, "1" - * use_only_cookies, "1" (deprecated since Symfony 7.2, to be removed in Symfony 8.0) - * use_trans_sid, "0" (deprecated since Symfony 7.2, to be removed in Symfony 8.0) - * sid_length, "32" (@deprecated since Symfony 7.2, to be removed in 8.0) - * sid_bits_per_character, "5" (@deprecated since Symfony 7.2, to be removed in 8.0) - * trans_sid_hosts, $_SERVER['HTTP_HOST'] (deprecated since Symfony 7.2, to be removed in Symfony 8.0) - * trans_sid_tags, "a=href,area=href,frame=src,form=" (deprecated since Symfony 7.2, to be removed in Symfony 8.0) */ public function __construct(array $options = [], AbstractProxy|\SessionHandlerInterface|null $handler = null, ?MetadataBag $metaBag = null) { @@ -122,16 +115,12 @@ public function start(): bool * * ---------- Part 1 * - * The part `[a-zA-Z0-9,-]` is related to the PHP ini directive `session.sid_bits_per_character` defined as 6. + * The part `[a-zA-Z0-9,-]` corresponds to the character range when PHP's `session.sid_bits_per_character` is set to 6. * See https://php.net/session.configuration#ini.session.sid-bits-per-character - * Allowed values are integers such as: - * - 4 for range `a-f0-9` - * - 5 for range `a-v0-9` (@deprecated since Symfony 7.2, it will default to 4 and the option will be ignored in Symfony 8.0) - * - 6 for range `a-zA-Z0-9,-` (@deprecated since Symfony 7.2, it will default to 4 and the option will be ignored in Symfony 8.0) * * ---------- Part 2 * - * The part `{22,250}` is related to the PHP ini directive `session.sid_length`. + * The part `{22,250}` defines the acceptable length range for session IDs. * See https://php.net/session.configuration#ini.session.sid-length * Allowed values are integers between 22 and 256, but we use 250 for the max. * @@ -139,8 +128,6 @@ public function start(): bool * - The length of Windows and Linux filenames is limited to 255 bytes. Then the max must not exceed 255. * - The session filename prefix is `sess_`, a 5 bytes string. Then the max must not exceed 255 - 5 = 250. * - * This is @deprecated since Symfony 7.2, the sid length will default to 32 and the option will be ignored in Symfony 8.0. - * * ---------- Conclusion * * The parts 1 and 2 prevent the warning below: @@ -323,17 +310,11 @@ public function setOptions(array $options): void 'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly', 'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite', 'gc_divisor', 'gc_maxlifetime', 'gc_probability', - 'lazy_write', 'name', 'referer_check', + 'lazy_write', 'name', 'serialize_handler', 'use_strict_mode', 'use_cookies', - 'use_only_cookies', 'use_trans_sid', - 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags', ]); foreach ($options as $key => $value) { - if (\in_array($key, ['referer_check', 'use_only_cookies', 'use_trans_sid', 'trans_sid_hosts', 'trans_sid_tags', 'sid_length', 'sid_bits_per_character'], true)) { - trigger_deprecation('symfony/http-foundation', '7.2', 'NativeSessionStorage\'s "%s" option is deprecated and will be ignored in Symfony 8.0.', $key); - } - if (isset($validOptions[$key])) { if ('cookie_secure' === $key && 'auto' === $value) { continue; diff --git a/Tests/RequestTest.php b/Tests/RequestTest.php index 5cfb980a7..62284e725 100644 --- a/Tests/RequestTest.php +++ b/Tests/RequestTest.php @@ -1559,7 +1559,7 @@ public static function providePreferredLanguage(): iterable yield '"fr_FR" is selected as "fr" is a similar dialect (2)' => ['fr_FR', 'ja-JP,fr;q=0.5,en_US;q=0.3', ['en_US', 'fr_FR']]; yield '"fr_FR" is selected as "fr_CA" is a similar dialect and has a greater "q" compared to "en_US" (2)' => ['fr_FR', 'ja-JP,fr_CA;q=0.7,ru-ru;q=0.3', ['en_US', 'fr_FR']]; yield '"fr_FR" is selected as "fr_CA" is a similar dialect and has a greater "q" compared to "en"' => ['fr_FR', 'ja-JP,fr_CA;q=0.7,en;q=0.5', ['en_US', 'fr_FR']]; - yield '"fr_FR" is selected as is is an exact match as well as "en_US", but with a greater "q" parameter' => ['fr_FR', 'en-us;q=0.5,fr-fr', ['en_US', 'fr_FR']]; + yield '"fr_FR" is selected as it is an exact match as well as "en_US", but with a greater "q" parameter' => ['fr_FR', 'en-us;q=0.5,fr-fr', ['en_US', 'fr_FR']]; yield '"hi_IN" is selected as "hi_Latn_IN" is a similar dialect' => ['hi_IN', 'fr-fr,hi_Latn_IN;q=0.5', ['hi_IN', 'en_US']]; yield '"hi_Latn_IN" is selected as "hi_IN" is a similar dialect' => ['hi_Latn_IN', 'fr-fr,hi_IN;q=0.5', ['hi_Latn_IN', 'en_US']]; yield '"en_US" is selected as "en_Latn_US+variants+extensions" is a similar dialect' => ['en_US', 'en-latn-us-fonapi-u-nu-numerical-x-private,fr;q=0.5', ['fr_FR', 'en_US']]; diff --git a/Tests/ResponseTest.php b/Tests/ResponseTest.php index 2c761a4f8..26ce83df1 100644 --- a/Tests/ResponseTest.php +++ b/Tests/ResponseTest.php @@ -63,7 +63,7 @@ public function testSend() public function testGetCharset() { $response = new Response(); - $charsetOrigin = 'UTF-8'; + $charsetOrigin = 'utf-8'; $response->setCharset($charsetOrigin); $charset = $response->getCharset(); $this->assertEquals($charsetOrigin, $charset); @@ -534,7 +534,7 @@ public function testDefaultContentType() $response = new Response('foo'); $response->prepare(new Request()); - $this->assertSame('text/html; charset=UTF-8', $response->headers->get('Content-Type')); + $this->assertSame('text/html; charset=utf-8', $response->headers->get('Content-Type')); } public function testContentTypeCharset() @@ -545,7 +545,7 @@ public function testContentTypeCharset() // force fixContentType() to be called $response->prepare(new Request()); - $this->assertEquals('text/css; charset=UTF-8', $response->headers->get('Content-Type')); + $this->assertEquals('text/css; charset=utf-8', $response->headers->get('Content-Type')); } public function testContentTypeIsNull() @@ -565,7 +565,7 @@ public function testPrepareDoesNothingIfContentTypeIsSet() $response->prepare(new Request()); - $this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('content-type')); + $this->assertEquals('text/plain; charset=utf-8', $response->headers->get('content-type')); } public function testPrepareDoesNothingIfRequestFormatIsNotDefined() @@ -574,7 +574,7 @@ public function testPrepareDoesNothingIfRequestFormatIsNotDefined() $response->prepare(new Request()); - $this->assertEquals('text/html; charset=UTF-8', $response->headers->get('content-type')); + $this->assertEquals('text/html; charset=utf-8', $response->headers->get('content-type')); } /** @@ -588,7 +588,7 @@ public function testPrepareDoesNotSetContentTypeBasedOnRequestAcceptHeader() $request->headers->set('Accept', 'application/json'); $response->prepare($request); - $this->assertSame('text/html; charset=UTF-8', $response->headers->get('content-type')); + $this->assertSame('text/html; charset=utf-8', $response->headers->get('content-type')); } public function testPrepareSetContentType() @@ -1021,7 +1021,7 @@ public function testSettersAreChainable() $setters = [ 'setProtocolVersion' => '1.0', - 'setCharset' => 'UTF-8', + 'setCharset' => 'utf-8', 'setPublic' => null, 'setPrivate' => null, 'setDate' => $this->createDateTimeNow(), diff --git a/Tests/Session/Storage/NativeSessionStorageTest.php b/Tests/Session/Storage/NativeSessionStorageTest.php index 11c489f6b..55f95d0c4 100644 --- a/Tests/Session/Storage/NativeSessionStorageTest.php +++ b/Tests/Session/Storage/NativeSessionStorageTest.php @@ -217,32 +217,6 @@ public function testCacheExpireOption() $this->assertSame('200', \ini_get('session.cache_expire')); } - /** - * @group legacy - * - * The test must only be removed when the "session.trans_sid_tags" option is removed from PHP or when the "trans_sid_tags" option is no longer supported by the native session storage. - */ - public function testTransSidTagsOption() - { - $this->expectUserDeprecationMessage('Since symfony/http-foundation 7.2: NativeSessionStorage\'s "trans_sid_tags" option is deprecated and will be ignored in Symfony 8.0.'); - - $previousErrorHandler = set_error_handler(function ($errno, $errstr) use (&$previousErrorHandler) { - if ('ini_set(): Usage of session.trans_sid_tags INI setting is deprecated' !== $errstr) { - return $previousErrorHandler ? $previousErrorHandler(...\func_get_args()) : false; - } - }); - - try { - $this->getStorage([ - 'trans_sid_tags' => 'a=href', - ]); - } finally { - restore_error_handler(); - } - - $this->assertSame('a=href', \ini_get('session.trans_sid_tags')); - } - public function testSetSaveHandler() { $initialSaveHandler = ini_set('session.save_handler', 'files'); @@ -365,27 +339,4 @@ public function testSaveHandlesNullSessionGracefully() $this->addToAssertionCount(1); } - /** - * @group legacy - */ - public function testPassingDeprecatedOptions() - { - $this->expectUserDeprecationMessage('Since symfony/http-foundation 7.2: NativeSessionStorage\'s "sid_length" option is deprecated and will be ignored in Symfony 8.0.'); - $this->expectUserDeprecationMessage('Since symfony/http-foundation 7.2: NativeSessionStorage\'s "sid_bits_per_character" option is deprecated and will be ignored in Symfony 8.0.'); - $this->expectUserDeprecationMessage('Since symfony/http-foundation 7.2: NativeSessionStorage\'s "referer_check" option is deprecated and will be ignored in Symfony 8.0.'); - $this->expectUserDeprecationMessage('Since symfony/http-foundation 7.2: NativeSessionStorage\'s "use_only_cookies" option is deprecated and will be ignored in Symfony 8.0.'); - $this->expectUserDeprecationMessage('Since symfony/http-foundation 7.2: NativeSessionStorage\'s "use_trans_sid" option is deprecated and will be ignored in Symfony 8.0.'); - $this->expectUserDeprecationMessage('Since symfony/http-foundation 7.2: NativeSessionStorage\'s "trans_sid_hosts" option is deprecated and will be ignored in Symfony 8.0.'); - $this->expectUserDeprecationMessage('Since symfony/http-foundation 7.2: NativeSessionStorage\'s "trans_sid_tags" option is deprecated and will be ignored in Symfony 8.0.'); - - $this->getStorage([ - 'sid_length' => 42, - 'sid_bits_per_character' => 6, - 'referer_check' => 'foo', - 'use_only_cookies' => 'foo', - 'use_trans_sid' => 'foo', - 'trans_sid_hosts' => 'foo', - 'trans_sid_tags' => 'foo', - ]); - } } diff --git a/composer.json b/composer.json index a86b21b7c..8bf522136 100644 --- a/composer.json +++ b/composer.json @@ -16,25 +16,23 @@ } ], "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php83": "^1.27" + "symfony/polyfill-mbstring": "^1.1" }, "require-dev": { "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", - "symfony/clock": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0" + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0" }, "conflict": { - "doctrine/dbal": "<3.6", - "symfony/cache": "<6.4.12|>=7.0,<7.1.5" + "doctrine/dbal": "<3.6" }, "autoload": { "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" },
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: