This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php

300 lines
7.8 KiB
PHP
Raw Normal View History

<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpFoundation;
/**
* ResponseHeaderBag is a container for Response HTTP headers.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ResponseHeaderBag extends HeaderBag
{
2014-10-22 19:27:13 +01:00
const COOKIES_FLAT = 'flat';
const COOKIES_ARRAY = 'array';
const DISPOSITION_ATTACHMENT = 'attachment';
2014-10-22 19:27:13 +01:00
const DISPOSITION_INLINE = 'inline';
2019-01-16 20:35:37 +00:00
protected $computedCacheControl = [];
protected $cookies = [];
protected $headerNames = [];
2019-01-16 20:35:37 +00:00
public function __construct(array $headers = [])
{
parent::__construct($headers);
if (!isset($this->headers['cache-control'])) {
$this->set('Cache-Control', '');
}
/* RFC2616 - 14.18 says all Responses need to have a Date */
if (!isset($this->headers['date'])) {
$this->initDate();
}
}
2011-06-08 11:12:55 +01:00
/**
* Returns the headers, with original capitalizations.
*
* @return array An array of headers
*/
public function allPreserveCase()
{
2019-01-16 20:35:37 +00:00
$headers = [];
foreach ($this->all() as $name => $value) {
$headers[isset($this->headerNames[$name]) ? $this->headerNames[$name] : $name] = $value;
}
return $headers;
}
public function allPreserveCaseWithoutCookies()
{
$headers = $this->allPreserveCase();
if (isset($this->headerNames['set-cookie'])) {
unset($headers[$this->headerNames['set-cookie']]);
}
return $headers;
}
/**
* {@inheritdoc}
*/
2019-01-16 20:35:37 +00:00
public function replace(array $headers = [])
{
2019-01-16 20:35:37 +00:00
$this->headerNames = [];
parent::replace($headers);
if (!isset($this->headers['cache-control'])) {
$this->set('Cache-Control', '');
}
if (!isset($this->headers['date'])) {
$this->initDate();
}
}
/**
* {@inheritdoc}
*/
public function all()
{
$headers = parent::all();
foreach ($this->getCookies() as $cookie) {
$headers['set-cookie'][] = (string) $cookie;
}
return $headers;
}
/**
* {@inheritdoc}
*/
public function set($key, $values, $replace = true)
{
$uniqueKey = str_replace('_', '-', strtolower($key));
if ('set-cookie' === $uniqueKey) {
if ($replace) {
2019-01-16 20:35:37 +00:00
$this->cookies = [];
}
foreach ((array) $values as $cookie) {
$this->setCookie(Cookie::fromString($cookie));
}
$this->headerNames[$uniqueKey] = $key;
return;
}
$this->headerNames[$uniqueKey] = $key;
parent::set($key, $values, $replace);
// ensure the cache-control header has sensible defaults
2019-01-16 20:35:37 +00:00
if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true)) {
$computed = $this->computeCacheControlValue();
2019-01-16 20:35:37 +00:00
$this->headers['cache-control'] = [$computed];
$this->headerNames['cache-control'] = 'Cache-Control';
$this->computedCacheControl = $this->parseCacheControl($computed);
}
}
/**
* {@inheritdoc}
*/
made some method name changes to have a better coherence throughout the framework When an object has a "main" many relation with related "things" (objects, parameters, ...), the method names are normalized: * get() * set() * all() * replace() * remove() * clear() * isEmpty() * add() * register() * count() * keys() The classes below follow this method naming convention: * BrowserKit\CookieJar -> Cookie * BrowserKit\History -> Request * Console\Application -> Command * Console\Application\Helper\HelperSet -> HelperInterface * DependencyInjection\Container -> services * DependencyInjection\ContainerBuilder -> services * DependencyInjection\ParameterBag\ParameterBag -> parameters * DependencyInjection\ParameterBag\FrozenParameterBag -> parameters * DomCrawler\Form -> FormField * EventDispatcher\Event -> parameters * Form\FieldGroup -> Field * HttpFoundation\HeaderBag -> headers * HttpFoundation\ParameterBag -> parameters * HttpFoundation\Session -> attributes * HttpKernel\Profiler\Profiler -> DataCollectorInterface * Routing\RouteCollection -> Route * Security\Authentication\AuthenticationProviderManager -> AuthenticationProviderInterface * Templating\Engine -> HelperInterface * Translation\MessageCatalogue -> messages The usage of these methods are only allowed when it is clear that there is a main relation: * a CookieJar has many Cookies; * a Container has many services and many parameters (as services is the main relation, we use the naming convention for this relation); * a Console Input has many arguments and many options. There is no "main" relation, and so the naming convention does not apply. For many relations where the convention does not apply, the following methods must be used instead (where XXX is the name of the related thing): * get() -> getXXX() * set() -> setXXX() * all() -> getXXXs() * replace() -> setXXXs() * remove() -> removeXXX() * clear() -> clearXXX() * isEmpty() -> isEmptyXXX() * add() -> addXXX() * register() -> registerXXX() * count() -> countXXX() * keys()
2010-11-23 08:42:19 +00:00
public function remove($key)
{
$uniqueKey = str_replace('_', '-', strtolower($key));
unset($this->headerNames[$uniqueKey]);
if ('set-cookie' === $uniqueKey) {
2019-01-16 20:35:37 +00:00
$this->cookies = [];
return;
}
parent::remove($key);
if ('cache-control' === $uniqueKey) {
2019-01-16 20:35:37 +00:00
$this->computedCacheControl = [];
}
if ('date' === $uniqueKey) {
$this->initDate();
}
}
/**
* {@inheritdoc}
*/
public function hasCacheControlDirective($key)
{
return \array_key_exists($key, $this->computedCacheControl);
}
/**
* {@inheritdoc}
*/
public function getCacheControlDirective($key)
{
return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
}
public function setCookie(Cookie $cookie)
{
$this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
$this->headerNames['set-cookie'] = 'Set-Cookie';
}
/**
2014-12-21 17:00:50 +00:00
* Removes a cookie from the array, but does not unset it in the browser.
*
* @param string $name
* @param string $path
* @param string $domain
*/
public function removeCookie($name, $path = '/', $domain = null)
{
if (null === $path) {
$path = '/';
}
unset($this->cookies[$domain][$path][$name]);
if (empty($this->cookies[$domain][$path])) {
unset($this->cookies[$domain][$path]);
if (empty($this->cookies[$domain])) {
unset($this->cookies[$domain]);
}
}
if (empty($this->cookies)) {
unset($this->headerNames['set-cookie']);
}
}
/**
2014-12-21 17:00:50 +00:00
* Returns an array with all cookies.
*
* @param string $format
*
* @return Cookie[]
*
* @throws \InvalidArgumentException When the $format is invalid
*/
public function getCookies($format = self::COOKIES_FLAT)
{
2019-01-16 20:35:37 +00:00
if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) {
throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY])));
}
if (self::COOKIES_ARRAY === $format) {
return $this->cookies;
}
2019-01-16 20:35:37 +00:00
$flattenedCookies = [];
foreach ($this->cookies as $path) {
foreach ($path as $cookies) {
foreach ($cookies as $cookie) {
$flattenedCookies[] = $cookie;
}
}
}
return $flattenedCookies;
}
/**
2014-12-21 17:00:50 +00:00
* Clears a cookie in the browser.
*
* @param string $name
* @param string $path
* @param string $domain
* @param bool $secure
* @param bool $httpOnly
*/
public function clearCookie($name, $path = '/', $domain = null, $secure = false, $httpOnly = true)
{
$this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null));
}
/**
* @see HeaderUtils::makeDisposition()
*/
public function makeDisposition($disposition, $filename, $filenameFallback = '')
{
return HeaderUtils::makeDisposition((string) $disposition, (string) $filename, (string) $filenameFallback);
}
/**
* Returns the calculated value of the cache-control header.
*
* This considers several other headers and calculates or modifies the
* cache-control header to a sensible, conservative value.
*
* @return string
*/
protected function computeCacheControlValue()
{
if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
return 'no-cache, private';
}
if (!$this->cacheControl) {
// conservative by default
return 'private, must-revalidate';
}
$header = $this->getCacheControlHeader();
if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
return $header;
}
// public if s-maxage is defined, private otherwise
if (!isset($this->cacheControl['s-maxage'])) {
return $header.', private';
}
return $header;
}
private function initDate()
{
$now = \DateTime::createFromFormat('U', time());
$now->setTimezone(new \DateTimeZone('UTC'));
$this->set('Date', $now->format('D, d M Y H:i:s').' GMT');
}
2011-06-08 11:16:48 +01:00
}