2015-08-17 17:00:26 -07:00
< ? 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
{
const COOKIES_FLAT = 'flat' ;
const COOKIES_ARRAY = 'array' ;
const DISPOSITION_ATTACHMENT = 'attachment' ;
const DISPOSITION_INLINE = 'inline' ;
protected $computedCacheControl = array ();
protected $cookies = array ();
protected $headerNames = array ();
public function __construct ( array $headers = array ())
{
parent :: __construct ( $headers );
if ( ! isset ( $this -> headers [ 'cache-control' ])) {
$this -> set ( 'Cache-Control' , '' );
}
2018-11-23 12:29:20 +00:00
/* RFC2616 - 14.18 says all Responses need to have a Date */
if ( ! isset ( $this -> headers [ 'date' ])) {
$this -> initDate ();
2015-08-17 17:00:26 -07:00
}
}
/**
* Returns the headers , with original capitalizations .
*
* @ return array An array of headers
*/
public function allPreserveCase ()
{
2018-11-23 12:29:20 +00:00
$headers = array ();
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 ;
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function replace ( array $headers = array ())
{
$this -> headerNames = array ();
parent :: replace ( $headers );
if ( ! isset ( $this -> headers [ 'cache-control' ])) {
$this -> set ( 'Cache-Control' , '' );
}
2018-11-23 12:29:20 +00:00
if ( ! isset ( $this -> headers [ 'date' ])) {
$this -> initDate ();
}
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
2018-11-23 12:29:20 +00:00
public function all ()
2015-08-17 17:00:26 -07:00
{
2018-11-23 12:29:20 +00:00
$headers = parent :: all ();
foreach ( $this -> getCookies () as $cookie ) {
$headers [ 'set-cookie' ][] = ( string ) $cookie ;
}
2015-08-17 17:00:26 -07:00
2018-11-23 12:29:20 +00:00
return $headers ;
}
/**
* { @ inheritdoc }
*/
public function set ( $key , $values , $replace = true )
{
2016-04-20 09:56:34 -07:00
$uniqueKey = str_replace ( '_' , '-' , strtolower ( $key ));
2018-11-23 12:29:20 +00:00
if ( 'set-cookie' === $uniqueKey ) {
if ( $replace ) {
$this -> cookies = array ();
}
foreach (( array ) $values as $cookie ) {
$this -> setCookie ( Cookie :: fromString ( $cookie ));
}
$this -> headerNames [ $uniqueKey ] = $key ;
return ;
}
2015-08-17 17:00:26 -07:00
$this -> headerNames [ $uniqueKey ] = $key ;
2018-11-23 12:29:20 +00:00
parent :: set ( $key , $values , $replace );
2015-08-17 17:00:26 -07:00
// ensure the cache-control header has sensible defaults
2018-11-23 12:29:20 +00:00
if ( \in_array ( $uniqueKey , array ( 'cache-control' , 'etag' , 'last-modified' , 'expires' ), true )) {
2015-08-17 17:00:26 -07:00
$computed = $this -> computeCacheControlValue ();
$this -> headers [ 'cache-control' ] = array ( $computed );
$this -> headerNames [ 'cache-control' ] = 'Cache-Control' ;
$this -> computedCacheControl = $this -> parseCacheControl ( $computed );
}
}
/**
* { @ inheritdoc }
*/
public function remove ( $key )
{
2016-04-20 09:56:34 -07:00
$uniqueKey = str_replace ( '_' , '-' , strtolower ( $key ));
2015-08-17 17:00:26 -07:00
unset ( $this -> headerNames [ $uniqueKey ]);
2018-11-23 12:29:20 +00:00
if ( 'set-cookie' === $uniqueKey ) {
$this -> cookies = array ();
return ;
}
parent :: remove ( $key );
2015-08-17 17:00:26 -07:00
if ( 'cache-control' === $uniqueKey ) {
$this -> computedCacheControl = array ();
}
2018-11-23 12:29:20 +00:00
if ( 'date' === $uniqueKey ) {
$this -> initDate ();
}
2015-08-17 17:00:26 -07:00
}
/**
* { @ 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 ;
2018-11-23 12:29:20 +00:00
$this -> headerNames [ 'set-cookie' ] = 'Set-Cookie' ;
2015-08-17 17:00:26 -07: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 ]);
}
}
2018-11-23 12:29:20 +00:00
if ( empty ( $this -> cookies )) {
unset ( $this -> headerNames [ 'set-cookie' ]);
}
2015-08-17 17:00:26 -07:00
}
/**
* Returns an array with all cookies .
*
* @ param string $format
*
2018-11-23 12:29:20 +00:00
* @ return Cookie []
2017-02-02 16:28:38 -08:00
*
* @ throws \InvalidArgumentException When the $format is invalid
2015-08-17 17:00:26 -07:00
*/
public function getCookies ( $format = self :: COOKIES_FLAT )
{
2018-11-23 12:29:20 +00:00
if ( ! \in_array ( $format , array ( self :: COOKIES_FLAT , self :: COOKIES_ARRAY ))) {
2015-08-17 17:00:26 -07:00
throw new \InvalidArgumentException ( sprintf ( 'Format "%s" invalid (%s).' , $format , implode ( ', ' , array ( self :: COOKIES_FLAT , self :: COOKIES_ARRAY ))));
}
if ( self :: COOKIES_ARRAY === $format ) {
return $this -> cookies ;
}
$flattenedCookies = array ();
foreach ( $this -> cookies as $path ) {
foreach ( $path as $cookies ) {
foreach ( $cookies as $cookie ) {
$flattenedCookies [] = $cookie ;
}
}
}
return $flattenedCookies ;
}
/**
* 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 ));
}
/**
* Generates a HTTP Content - Disposition field - value .
*
* @ param string $disposition One of " inline " or " attachment "
* @ param string $filename A unicode string
* @ param string $filenameFallback A string containing only ASCII characters that
* is semantically equivalent to $filename . If the filename is already ASCII ,
* it can be omitted , or just copied from $filename
*
2017-02-02 16:28:38 -08:00
* @ return string A string suitable for use as a Content - Disposition field - value
2015-08-17 17:00:26 -07:00
*
* @ throws \InvalidArgumentException
*
* @ see RFC 6266
*/
public function makeDisposition ( $disposition , $filename , $filenameFallback = '' )
{
2018-11-23 12:29:20 +00:00
if ( ! \in_array ( $disposition , array ( self :: DISPOSITION_ATTACHMENT , self :: DISPOSITION_INLINE ))) {
2015-08-17 17:00:26 -07:00
throw new \InvalidArgumentException ( sprintf ( 'The disposition must be either "%s" or "%s".' , self :: DISPOSITION_ATTACHMENT , self :: DISPOSITION_INLINE ));
}
if ( '' == $filenameFallback ) {
$filenameFallback = $filename ;
}
// filenameFallback is not ASCII.
if ( ! preg_match ( '/^[\x20-\x7e]*$/' , $filenameFallback )) {
throw new \InvalidArgumentException ( 'The filename fallback must only contain ASCII characters.' );
}
// percent characters aren't safe in fallback.
if ( false !== strpos ( $filenameFallback , '%' )) {
throw new \InvalidArgumentException ( 'The filename fallback cannot contain the "%" character.' );
}
// path separators aren't allowed in either.
if ( false !== strpos ( $filename , '/' ) || false !== strpos ( $filename , '\\' ) || false !== strpos ( $filenameFallback , '/' ) || false !== strpos ( $filenameFallback , '\\' )) {
throw new \InvalidArgumentException ( 'The filename and the fallback cannot contain the "/" and "\\" characters.' );
}
$output = sprintf ( '%s; filename="%s"' , $disposition , str_replace ( '"' , '\\"' , $filenameFallback ));
if ( $filename !== $filenameFallback ) {
$output .= sprintf ( " ; filename*=utf-8''%s " , rawurlencode ( $filename ));
}
return $output ;
}
/**
* 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' )) {
2018-11-23 12:29:20 +00:00
return 'no-cache, private' ;
2015-08-17 17:00:26 -07:00
}
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 ;
}
2018-11-23 12:29:20 +00:00
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' );
}
2015-08-17 17:00:26 -07:00
}