15 class_exists(ResponseHeaderBag::class);
136 101 =>
'Switching Protocols',
138 103 =>
'Early Hints',
142 203 =>
'Non-Authoritative Information',
144 205 =>
'Reset Content',
145 206 =>
'Partial Content',
146 207 =>
'Multi-Status',
147 208 =>
'Already Reported',
149 300 =>
'Multiple Choices',
150 301 =>
'Moved Permanently',
153 304 =>
'Not Modified',
155 307 =>
'Temporary Redirect',
156 308 =>
'Permanent Redirect',
157 400 =>
'Bad Request',
158 401 =>
'Unauthorized',
159 402 =>
'Payment Required',
162 405 =>
'Method Not Allowed',
163 406 =>
'Not Acceptable',
164 407 =>
'Proxy Authentication Required',
165 408 =>
'Request Timeout',
168 411 =>
'Length Required',
169 412 =>
'Precondition Failed',
170 413 =>
'Payload Too Large',
171 414 =>
'URI Too Long',
172 415 =>
'Unsupported Media Type',
173 416 =>
'Range Not Satisfiable',
174 417 =>
'Expectation Failed',
175 418 =>
'I\'m a teapot',
176 421 =>
'Misdirected Request',
177 422 =>
'Unprocessable Entity',
179 424 =>
'Failed Dependency',
181 426 =>
'Upgrade Required',
182 428 =>
'Precondition Required',
183 429 =>
'Too Many Requests',
184 431 =>
'Request Header Fields Too Large',
185 451 =>
'Unavailable For Legal Reasons',
186 500 =>
'Internal Server Error',
187 501 =>
'Not Implemented',
188 502 =>
'Bad Gateway',
189 503 =>
'Service Unavailable',
190 504 =>
'Gateway Timeout',
191 505 =>
'HTTP Version Not Supported',
192 506 =>
'Variant Also Negotiates',
193 507 =>
'Insufficient Storage',
194 508 =>
'Loop Detected',
195 510 =>
'Not Extended',
196 511 =>
'Network Authentication Required',
243 sprintf(
'HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText).
"\r\n".
244 $this->headers.
"\r\n".
274 ini_set(
'default_mimetype',
'');
277 if (!
$headers->has(
'Content-Type')) {
278 $format = $request->getRequestFormat(
null);
279 if (
null !== $format && $mimeType = $request->getMimeType($format)) {
280 $headers->set(
'Content-Type', $mimeType);
285 $charset = $this->charset ?:
'UTF-8';
286 if (!
$headers->has(
'Content-Type')) {
288 } elseif (0 === stripos(
$headers->get(
'Content-Type'),
'text/') &&
false === stripos(
$headers->get(
'Content-Type'),
'charset')) {
294 if (
$headers->has(
'Transfer-Encoding')) {
298 if ($request->isMethod(
'HEAD')) {
300 $length =
$headers->get(
'Content-Length');
303 $headers->set(
'Content-Length', $length);
309 if (
'HTTP/1.0' != $request->server->get(
'SERVER_PROTOCOL')) {
315 $headers->set(
'pragma',
'no-cache');
321 if ($request->isSecure()) {
322 foreach (
$headers->getCookies() as $cookie) {
323 $cookie->setSecureDefault(
true);
338 if (headers_sent()) {
343 foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
344 $replace = 0 === strcasecmp($name,
'Content-Type');
345 foreach ($values as $value) {
346 header($name.
': '.$value, $replace, $this->statusCode);
351 foreach ($this->headers->getCookies() as $cookie) {
352 header(
'Set-Cookie: '.$cookie,
false, $this->statusCode);
356 header(sprintf(
'HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText),
true, $this->statusCode);
378 public function send()
383 if (\function_exists(
'fastcgi_finish_request')) {
384 fastcgi_finish_request();
385 } elseif (!\in_array(\PHP_SAPI, [
'cli',
'phpdbg'],
true)) {
386 static::closeOutputBuffers(0,
true);
406 throw new \UnexpectedValueException(sprintf(
'The Response content must be a string or object implementing __toString(), "%s" given.', \gettype(
$content)));
462 $this->statusCode = $code;
464 throw new \InvalidArgumentException(sprintf(
'The HTTP status code "%s" is not valid.', $code));
467 if (
null === $text) {
468 $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] :
'unknown status';
473 if (
false === $text) {
474 $this->statusText =
'';
479 $this->statusText = $text;
537 if (!\in_array($this->statusCode, [200, 203, 300, 301, 302, 404, 410])) {
541 if ($this->headers->hasCacheControlDirective(
'no-store') || $this->headers->getCacheControlDirective(
'private')) {
557 public function isFresh(): bool
559 return $this->
getTtl() > 0;
570 return $this->headers->has(
'Last-Modified') || $this->headers->has(
'ETag');
584 $this->headers->removeCacheControlDirective(
'public');
585 $this->headers->addCacheControlDirective(
'private');
601 $this->headers->addCacheControlDirective(
'public');
602 $this->headers->removeCacheControlDirective(
'private');
617 $this->headers->addCacheControlDirective(
'immutable');
619 $this->headers->removeCacheControlDirective(
'immutable');
632 return $this->headers->hasCacheControlDirective(
'immutable');
647 return $this->headers->hasCacheControlDirective(
'must-revalidate') || $this->headers->hasCacheControlDirective(
'proxy-revalidate');
657 public function getDate(): ?\DateTimeInterface
659 return $this->headers->getDate(
'Date');
669 public function setDate(\DateTimeInterface $date)
671 if ($date instanceof \DateTime) {
672 $date = \DateTimeImmutable::createFromMutable($date);
675 $date = $date->setTimezone(
new \DateTimeZone(
'UTC'));
676 $this->headers->set(
'Date', $date->format(
'D, d M Y H:i:s').
' GMT');
686 public function getAge(): int
688 if (
null !== $age = $this->headers->get(
'Age')) {
692 return max(time() - (
int) $this->
getDate()->format(
'U'), 0);
703 $this->headers->set(
'Age', $this->
getMaxAge());
704 $this->headers->remove(
'Expires');
715 public function getExpires(): ?\DateTimeInterface
718 return $this->headers->getDate(
'Expires');
719 }
catch (\RuntimeException $e) {
721 return \DateTime::createFromFormat(
'U', time() - 172800);
734 public function setExpires(\DateTimeInterface $date =
null)
736 if (
null === $date) {
737 $this->headers->remove(
'Expires');
742 if ($date instanceof \DateTime) {
743 $date = \DateTimeImmutable::createFromMutable($date);
746 $date = $date->setTimezone(
new \DateTimeZone(
'UTC'));
747 $this->headers->set(
'Expires', $date->format(
'D, d M Y H:i:s').
' GMT');
763 if ($this->headers->hasCacheControlDirective(
's-maxage')) {
764 return (
int) $this->headers->getCacheControlDirective(
's-maxage');
767 if ($this->headers->hasCacheControlDirective(
'max-age')) {
768 return (
int) $this->headers->getCacheControlDirective(
'max-age');
772 return (
int) $this->
getExpires()->format(
'U') - (int) $this->
getDate()->format(
'U');
789 $this->headers->addCacheControlDirective(
'max-age', $value);
806 $this->headers->addCacheControlDirective(
's-maxage', $value);
825 return null !== $maxAge ? $maxAge - $this->
getAge() :
null;
837 public function setTtl(
int $seconds)
869 return $this->headers->getDate(
'Last-Modified');
883 if (
null === $date) {
884 $this->headers->remove(
'Last-Modified');
889 if ($date instanceof \DateTime) {
890 $date = \DateTimeImmutable::createFromMutable($date);
893 $date = $date->setTimezone(
new \DateTimeZone(
'UTC'));
894 $this->headers->set(
'Last-Modified', $date->format(
'D, d M Y H:i:s').
' GMT');
904 public function getEtag(): ?string
906 return $this->headers->get(
'ETag');
919 public function setEtag(
string $etag =
null,
bool $weak =
false)
921 if (
null === $etag) {
922 $this->headers->remove(
'Etag');
924 if (0 !== strpos($etag,
'"')) {
925 $etag =
'"'.$etag.
'"';
928 $this->headers->set(
'ETag', (
true === $weak ?
'W/' :
'').$etag);
945 public function setCache(array $options)
947 if ($diff = array_diff(array_keys($options), [
'etag',
'last_modified',
'max_age',
's_maxage',
'private',
'public',
'immutable'])) {
948 throw new \InvalidArgumentException(sprintf(
'Response does not support the following options: "%s".', implode(
'", "', $diff)));
951 if (isset($options[
'etag'])) {
952 $this->
setEtag($options[
'etag']);
955 if (isset($options[
'last_modified'])) {
959 if (isset($options[
'max_age'])) {
963 if (isset($options[
's_maxage'])) {
967 if (isset($options[
'public'])) {
968 if ($options[
'public']) {
975 if (isset($options[
'private'])) {
976 if ($options[
'private']) {
983 if (isset($options[
'immutable'])) {
1008 foreach ([
'Allow',
'Content-Encoding',
'Content-Language',
'Content-Length',
'Content-MD5',
'Content-Type',
'Last-Modified'] as $header) {
1009 $this->headers->remove($header);
1022 return null !== $this->headers->get(
'Vary');
1030 public function getVary(): array
1032 if (!$vary = $this->headers->all(
'Vary')) {
1037 foreach ($vary as $item) {
1038 $ret = array_merge($ret, preg_split(
'/[\s,]+/', $item));
1056 $this->headers->set(
'Vary',
$headers, $replace);
1078 $notModified =
false;
1079 $lastModified = $this->headers->get(
'Last-Modified');
1080 $modifiedSince = $request->headers->
get(
'If-Modified-Since');
1082 if ($etags = $request->
getETags()) {
1083 $notModified = \in_array($this->
getEtag(), $etags) || \in_array(
'*', $etags);
1086 if ($modifiedSince && $lastModified) {
1087 $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified);
1094 return $notModified;
1106 return $this->statusCode < 100 || $this->statusCode >= 600;
1116 return $this->statusCode >= 100 && $this->statusCode < 200;
1126 return $this->statusCode >= 200 && $this->statusCode < 300;
1136 return $this->statusCode >= 300 && $this->statusCode < 400;
1146 return $this->statusCode >= 400 && $this->statusCode < 500;
1156 return $this->statusCode >= 500 && $this->statusCode < 600;
1164 public function isOk(): bool
1194 public function isRedirect(
string $location =
null): bool
1196 return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (
null === $location ?: $location == $this->headers->get(
'Location'));
1204 public function isEmpty(): bool
1206 return \in_array($this->statusCode, [204, 304]);
1218 $status = ob_get_status(
true);
1219 $level = \count($status);
1220 $flags = PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE);
1222 while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s[
'del']) ? !isset($s[
'flags']) || ($s[
'flags'] & $flags) === $flags : $s[
'del'])) {
1240 if (
false !== stripos($this->headers->get(
'Content-Disposition'),
'attachment') && 1 == preg_match(
'/MSIE (.*?);/i', $request->server->get(
'HTTP_USER_AGENT'), $match) &&
true === $request->isSecure()) {
1241 if ((
int) preg_replace(
'/(MSIE )(.*?);/',
'$2', $match[0]) < 9) {
1242 $this->headers->remove(
'Cache-Control');