ResponseHeaderBag.php

Namespace

Symfony\Component\HttpFoundation

File

drupal/core/vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
View source
<?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>
 *
 * @api
 */
class ResponseHeaderBag extends HeaderBag {
  const COOKIES_FLAT = 'flat';
  const COOKIES_ARRAY = 'array';
  const DISPOSITION_ATTACHMENT = 'attachment';
  const DISPOSITION_INLINE = 'inline';

  /**
   * @var array
   */
  protected $computedCacheControl = array();

  /**
   * @var array
   */
  protected $cookies = array();

  /**
   * Constructor.
   *
   * @param array $headers An array of HTTP headers
   *
   * @api
   */
  public function __construct(array $headers = array()) {
    parent::__construct($headers);
    if (!isset($this->headers['cache-control'])) {
      $this
        ->set('cache-control', '');
    }
  }

  /**
   * {@inheritdoc}
   */
  public function __toString() {
    $cookies = '';
    foreach ($this
      ->getCookies() as $cookie) {
      $cookies .= 'Set-Cookie: ' . $cookie . "\r\n";
    }
    return parent::__toString() . $cookies;
  }

  /**
   * {@inheritdoc}
   *
   * @api
   */
  public function replace(array $headers = array()) {
    parent::replace($headers);
    if (!isset($this->headers['cache-control'])) {
      $this
        ->set('cache-control', '');
    }
  }

  /**
   * {@inheritdoc}
   *
   * @api
   */
  public function set($key, $values, $replace = true) {
    parent::set($key, $values, $replace);

    // ensure the cache-control header has sensible defaults
    if (in_array(strtr(strtolower($key), '_', '-'), array(
      'cache-control',
      'etag',
      'last-modified',
      'expires',
    ))) {
      $computed = $this
        ->computeCacheControlValue();
      $this->headers['cache-control'] = array(
        $computed,
      );
      $this->computedCacheControl = $this
        ->parseCacheControl($computed);
    }
  }

  /**
   * {@inheritdoc}
   *
   * @api
   */
  public function remove($key) {
    parent::remove($key);
    if ('cache-control' === strtr(strtolower($key), '_', '-')) {
      $this->computedCacheControl = array();
    }
  }

  /**
   * {@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;
  }

  /**
   * Sets a cookie.
   *
   * @param Cookie $cookie
   *
   * @api
   */
  public function setCookie(Cookie $cookie) {
    $this->cookies[$cookie
      ->getDomain()][$cookie
      ->getPath()][$cookie
      ->getName()] = $cookie;
  }

  /**
   * Removes a cookie from the array, but does not unset it in the browser
   *
   * @param string $name
   * @param string $path
   * @param string $domain
   *
   * @api
   */
  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]);
      }
    }
  }

  /**
   * Returns an array with all cookies
   *
   * @param string $format
   *
   * @throws \InvalidArgumentException When the $format is invalid
   *
   * @return array
   *
   * @api
   */
  public function getCookies($format = self::COOKIES_FLAT) {
    if (!in_array($format, array(
      self::COOKIES_FLAT,
      self::COOKIES_ARRAY,
    ))) {
      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
   *
   * @api
   */
  public function clearCookie($name, $path = '/', $domain = null) {
    $this
      ->setCookie(new Cookie($name, null, 1, $path, $domain));
  }

  /**
   * 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
   *
   * @return string A string suitable for use as a Content-Disposition field-value.
   *
   * @throws \InvalidArgumentException
   * @see RFC 6266
   */
  public function makeDisposition($disposition, $filename, $filenameFallback = '') {
    if (!in_array($disposition, array(
      self::DISPOSITION_ATTACHMENT,
      self::DISPOSITION_INLINE,
    ))) {
      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')) {
      return 'no-cache';
    }
    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;
  }

}

Classes

Namesort descending Description
ResponseHeaderBag ResponseHeaderBag is a container for Response HTTP headers.