diff --git a/CHANGELOG.md b/CHANGELOG.md index 374c31889..ca58a4032 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +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/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/composer.json b/composer.json index a86b21b7c..9fe4c1a4b 100644 --- a/composer.json +++ b/composer.json @@ -24,13 +24,13 @@ "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": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" }, "conflict": { "doctrine/dbal": "<3.6",
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: