Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

Response.php

Zuletzt modifiziert: 09.10.2024, 12:58 - Dateigröße: 31.92 KiB


 0001  <?php
 0002   
 0003  /*
 0004   * This file is part of the Symfony package.
 0005   *
 0006   * (c) Fabien Potencier <fabien@symfony.com>
 0007   *
 0008   * For the full copyright and license information, please view the LICENSE
 0009   * file that was distributed with this source code.
 0010   */
 0011   
 0012  namespace Symfony\Component\HttpFoundation;
 0013   
 0014  /**
 0015   * Response represents an HTTP response.
 0016   *
 0017   * @author Fabien Potencier <fabien@symfony.com>
 0018   *
 0019   * @api
 0020   */
 0021  class Response
 0022  {
 0023      /**
 0024       * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
 0025       */
 0026      public $headers;
 0027   
 0028      /**
 0029       * @var string
 0030       */
 0031      protected $content;
 0032   
 0033      /**
 0034       * @var string
 0035       */
 0036      protected $version;
 0037   
 0038      /**
 0039       * @var int
 0040       */
 0041      protected $statusCode;
 0042   
 0043      /**
 0044       * @var string
 0045       */
 0046      protected $statusText;
 0047   
 0048      /**
 0049       * @var string
 0050       */
 0051      protected $charset;
 0052   
 0053      /**
 0054       * Status codes translation table.
 0055       *
 0056       * The list of codes is complete according to the
 0057       * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry}
 0058       * (last updated 2012-02-13).
 0059       *
 0060       * Unless otherwise noted, the status code is defined in RFC2616.
 0061       *
 0062       * @var array
 0063       */
 0064      public static $statusTexts = array(
 0065          100 => 'Continue',
 0066          101 => 'Switching Protocols',
 0067          102 => 'Processing',            // RFC2518
 0068          200 => 'OK',
 0069          201 => 'Created',
 0070          202 => 'Accepted',
 0071          203 => 'Non-Authoritative Information',
 0072          204 => 'No Content',
 0073          205 => 'Reset Content',
 0074          206 => 'Partial Content',
 0075          207 => 'Multi-Status',          // RFC4918
 0076          208 => 'Already Reported',      // RFC5842
 0077          226 => 'IM Used',               // RFC3229
 0078          300 => 'Multiple Choices',
 0079          301 => 'Moved Permanently',
 0080          302 => 'Found',
 0081          303 => 'See Other',
 0082          304 => 'Not Modified',
 0083          305 => 'Use Proxy',
 0084          306 => 'Reserved',
 0085          307 => 'Temporary Redirect',
 0086          308 => 'Permanent Redirect',    // RFC7238
 0087          400 => 'Bad Request',
 0088          401 => 'Unauthorized',
 0089          402 => 'Payment Required',
 0090          403 => 'Forbidden',
 0091          404 => 'Not Found',
 0092          405 => 'Method Not Allowed',
 0093          406 => 'Not Acceptable',
 0094          407 => 'Proxy Authentication Required',
 0095          408 => 'Request Timeout',
 0096          409 => 'Conflict',
 0097          410 => 'Gone',
 0098          411 => 'Length Required',
 0099          412 => 'Precondition Failed',
 0100          413 => 'Request Entity Too Large',
 0101          414 => 'Request-URI Too Long',
 0102          415 => 'Unsupported Media Type',
 0103          416 => 'Requested Range Not Satisfiable',
 0104          417 => 'Expectation Failed',
 0105          418 => 'I\'m a teapot',                                               // RFC2324
 0106          422 => 'Unprocessable Entity',                                        // RFC4918
 0107          423 => 'Locked',                                                      // RFC4918
 0108          424 => 'Failed Dependency',                                           // RFC4918
 0109          425 => 'Reserved for WebDAV advanced collections expired proposal',   // RFC2817
 0110          426 => 'Upgrade Required',                                            // RFC2817
 0111          428 => 'Precondition Required',                                       // RFC6585
 0112          429 => 'Too Many Requests',                                           // RFC6585
 0113          431 => 'Request Header Fields Too Large',                             // RFC6585
 0114          500 => 'Internal Server Error',
 0115          501 => 'Not Implemented',
 0116          502 => 'Bad Gateway',
 0117          503 => 'Service Unavailable',
 0118          504 => 'Gateway Timeout',
 0119          505 => 'HTTP Version Not Supported',
 0120          506 => 'Variant Also Negotiates (Experimental)',                      // RFC2295
 0121          507 => 'Insufficient Storage',                                        // RFC4918
 0122          508 => 'Loop Detected',                                               // RFC5842
 0123          510 => 'Not Extended',                                                // RFC2774
 0124          511 => 'Network Authentication Required',                             // RFC6585
 0125      );
 0126   
 0127      /**
 0128       * Constructor.
 0129       *
 0130       * @param string  $content The response content
 0131       * @param int     $status  The response status code
 0132       * @param array   $headers An array of response headers
 0133       *
 0134       * @throws \InvalidArgumentException When the HTTP status code is not valid
 0135       *
 0136       * @api
 0137       */
 0138      public function __construct($content = '', $status = 200, $headers = array())
 0139      {
 0140          $this->headers = new ResponseHeaderBag($headers);
 0141          $this->setContent($content);
 0142          $this->setStatusCode($status);
 0143          $this->setProtocolVersion('1.0');
 0144          if (!$this->headers->has('Date')) {
 0145              $this->setDate(new \DateTime(null, new \DateTimeZone('UTC')));
 0146          }
 0147      }
 0148   
 0149      /**
 0150       * Factory method for chainability
 0151       *
 0152       * Example:
 0153       *
 0154       *     return Response::create($body, 200)
 0155       *         ->setSharedMaxAge(300);
 0156       *
 0157       * @param string  $content The response content
 0158       * @param int     $status  The response status code
 0159       * @param array   $headers An array of response headers
 0160       *
 0161       * @return Response
 0162       */
 0163      public static function create($content = '', $status = 200, $headers = array())
 0164      {
 0165          return new static($content, $status, $headers);
 0166      }
 0167   
 0168      /**
 0169       * Returns the Response as an HTTP string.
 0170       *
 0171       * The string representation of the Response is the same as the
 0172       * one that will be sent to the client only if the prepare() method
 0173       * has been called before.
 0174       *
 0175       * @return string The Response as an HTTP string
 0176       *
 0177       * @see prepare()
 0178       */
 0179      public function __toString()
 0180      {
 0181          return
 0182              sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
 0183              $this->headers."\r\n".
 0184              $this->getContent();
 0185      }
 0186   
 0187      /**
 0188       * Clones the current Response instance.
 0189       */
 0190      public function __clone()
 0191      {
 0192          $this->headers = clone $this->headers;
 0193      }
 0194   
 0195      /**
 0196       * Prepares the Response before it is sent to the client.
 0197       *
 0198       * This method tweaks the Response to ensure that it is
 0199       * compliant with RFC 2616. Most of the changes are based on
 0200       * the Request that is "associated" with this Response.
 0201       *
 0202       * @param Request $request A Request instance
 0203       *
 0204       * @return Response The current response.
 0205       */
 0206      public function prepare(Request $request)
 0207      {
 0208          $headers = $this->headers;
 0209   
 0210          if ($this->isInformational() || in_array($this->statusCode, array(204, 304))) {
 0211              $this->setContent(null);
 0212              $headers->remove('Content-Type');
 0213              $headers->remove('Content-Length');
 0214          } else {
 0215              // Content-type based on the Request
 0216              if (!$headers->has('Content-Type')) {
 0217                  $format = $request->getRequestFormat();
 0218                  if (null !== $format && $mimeType = $request->getMimeType($format)) {
 0219                      $headers->set('Content-Type', $mimeType);
 0220                  }
 0221              }
 0222   
 0223              // Fix Content-Type
 0224              $charset = $this->charset ?: 'UTF-8';
 0225              if (!$headers->has('Content-Type')) {
 0226                  $headers->set('Content-Type', 'text/html; charset='.$charset);
 0227              } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {
 0228                  // add the charset
 0229                  $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
 0230              }
 0231   
 0232              // Fix Content-Length
 0233              if ($headers->has('Transfer-Encoding')) {
 0234                  $headers->remove('Content-Length');
 0235              }
 0236   
 0237              if ($request->isMethod('HEAD')) {
 0238                  // cf. RFC2616 14.13
 0239                  $length = $headers->get('Content-Length');
 0240                  $this->setContent(null);
 0241                  if ($length) {
 0242                      $headers->set('Content-Length', $length);
 0243                  }
 0244              }
 0245          }
 0246   
 0247          // Fix protocol
 0248          if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
 0249              $this->setProtocolVersion('1.1');
 0250          }
 0251   
 0252          // Check if we need to send extra expire info headers
 0253          if ('1.0' == $this->getProtocolVersion() && 'no-cache' == $this->headers->get('Cache-Control')) {
 0254              $this->headers->set('pragma', 'no-cache');
 0255              $this->headers->set('expires', -1);
 0256          }
 0257   
 0258          $this->ensureIEOverSSLCompatibility($request);
 0259   
 0260          return $this;
 0261      }
 0262   
 0263      /**
 0264       * Sends HTTP headers.
 0265       *
 0266       * @return Response
 0267       */
 0268      public function sendHeaders()
 0269      {
 0270          // headers have already been sent by the developer
 0271          if (headers_sent()) {
 0272              return $this;
 0273          }
 0274   
 0275          // status
 0276          header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText));
 0277   
 0278          // headers
 0279          foreach ($this->headers->allPreserveCase() as $name => $values) {
 0280              foreach ($values as $value) {
 0281                  header($name.': '.$value, false);
 0282              }
 0283          }
 0284   
 0285          // cookies
 0286          foreach ($this->headers->getCookies() as $cookie) {
 0287              setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
 0288          }
 0289   
 0290          return $this;
 0291      }
 0292   
 0293      /**
 0294       * Sends content for the current web response.
 0295       *
 0296       * @return Response
 0297       */
 0298      public function sendContent()
 0299      {
 0300          echo $this->content;
 0301   
 0302          return $this;
 0303      }
 0304   
 0305      /**
 0306       * Sends HTTP headers and content.
 0307       *
 0308       * @return Response
 0309       *
 0310       * @api
 0311       */
 0312      public function send()
 0313      {
 0314          $this->sendHeaders();
 0315          $this->sendContent();
 0316   
 0317          if (function_exists('fastcgi_finish_request')) {
 0318              fastcgi_finish_request();
 0319          } elseif ('cli' !== PHP_SAPI) {
 0320              // ob_get_level() never returns 0 on some Windows configurations, so if
 0321              // the level is the same two times in a row, the loop should be stopped.
 0322              $previous = null;
 0323              $obStatus = ob_get_status(1);
 0324              while (($level = ob_get_level()) > 0 && $level !== $previous) {
 0325                  $previous = $level;
 0326                  if ($obStatus[$level - 1]) {
 0327                      if (version_compare(PHP_VERSION, '5.4', '>=')) {
 0328                          if (isset($obStatus[$level - 1]['flags']) && ($obStatus[$level - 1]['flags'] & PHP_OUTPUT_HANDLER_REMOVABLE)) {
 0329                              ob_end_flush();
 0330                          }
 0331                      } else {
 0332                          if (isset($obStatus[$level - 1]['del']) && $obStatus[$level - 1]['del']) {
 0333                              ob_end_flush();
 0334                          }
 0335                      }
 0336                  }
 0337              }
 0338              flush();
 0339          }
 0340   
 0341          return $this;
 0342      }
 0343   
 0344      /**
 0345       * Sets the response content.
 0346       *
 0347       * Valid types are strings, numbers, and objects that implement a __toString() method.
 0348       *
 0349       * @param mixed $content
 0350       *
 0351       * @return Response
 0352       *
 0353       * @throws \UnexpectedValueException
 0354       *
 0355       * @api
 0356       */
 0357      public function setContent($content)
 0358      {
 0359          if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) {
 0360              throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content)));
 0361          }
 0362   
 0363          $this->content = (string) $content;
 0364   
 0365          return $this;
 0366      }
 0367   
 0368      /**
 0369       * Gets the current response content.
 0370       *
 0371       * @return string Content
 0372       *
 0373       * @api
 0374       */
 0375      public function getContent()
 0376      {
 0377          return $this->content;
 0378      }
 0379   
 0380      /**
 0381       * Sets the HTTP protocol version (1.0 or 1.1).
 0382       *
 0383       * @param string $version The HTTP protocol version
 0384       *
 0385       * @return Response
 0386       *
 0387       * @api
 0388       */
 0389      public function setProtocolVersion($version)
 0390      {
 0391          $this->version = $version;
 0392   
 0393          return $this;
 0394      }
 0395   
 0396      /**
 0397       * Gets the HTTP protocol version.
 0398       *
 0399       * @return string The HTTP protocol version
 0400       *
 0401       * @api
 0402       */
 0403      public function getProtocolVersion()
 0404      {
 0405          return $this->version;
 0406      }
 0407   
 0408      /**
 0409       * Sets the response status code.
 0410       *
 0411       * @param int     $code HTTP status code
 0412       * @param mixed   $text HTTP status text
 0413       *
 0414       * If the status text is null it will be automatically populated for the known
 0415       * status codes and left empty otherwise.
 0416       *
 0417       * @return Response
 0418       *
 0419       * @throws \InvalidArgumentException When the HTTP status code is not valid
 0420       *
 0421       * @api
 0422       */
 0423      public function setStatusCode($code, $text = null)
 0424      {
 0425          $this->statusCode = $code = (int) $code;
 0426          if ($this->isInvalid()) {
 0427              throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
 0428          }
 0429   
 0430          if (null === $text) {
 0431              $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : '';
 0432   
 0433              return $this;
 0434          }
 0435   
 0436          if (false === $text) {
 0437              $this->statusText = '';
 0438   
 0439              return $this;
 0440          }
 0441   
 0442          $this->statusText = $text;
 0443   
 0444          return $this;
 0445      }
 0446   
 0447      /**
 0448       * Retrieves the status code for the current web response.
 0449       *
 0450       * @return int     Status code
 0451       *
 0452       * @api
 0453       */
 0454      public function getStatusCode()
 0455      {
 0456          return $this->statusCode;
 0457      }
 0458   
 0459      /**
 0460       * Sets the response charset.
 0461       *
 0462       * @param string $charset Character set
 0463       *
 0464       * @return Response
 0465       *
 0466       * @api
 0467       */
 0468      public function setCharset($charset)
 0469      {
 0470          $this->charset = $charset;
 0471   
 0472          return $this;
 0473      }
 0474   
 0475      /**
 0476       * Retrieves the response charset.
 0477       *
 0478       * @return string Character set
 0479       *
 0480       * @api
 0481       */
 0482      public function getCharset()
 0483      {
 0484          return $this->charset;
 0485      }
 0486   
 0487      /**
 0488       * Returns true if the response is worth caching under any circumstance.
 0489       *
 0490       * Responses marked "private" with an explicit Cache-Control directive are
 0491       * considered uncacheable.
 0492       *
 0493       * Responses with neither a freshness lifetime (Expires, max-age) nor cache
 0494       * validator (Last-Modified, ETag) are considered uncacheable.
 0495       *
 0496       * @return bool    true if the response is worth caching, false otherwise
 0497       *
 0498       * @api
 0499       */
 0500      public function isCacheable()
 0501      {
 0502          if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
 0503              return false;
 0504          }
 0505   
 0506          if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) {
 0507              return false;
 0508          }
 0509   
 0510          return $this->isValidateable() || $this->isFresh();
 0511      }
 0512   
 0513      /**
 0514       * Returns true if the response is "fresh".
 0515       *
 0516       * Fresh responses may be served from cache without any interaction with the
 0517       * origin. A response is considered fresh when it includes a Cache-Control/max-age
 0518       * indicator or Expires header and the calculated age is less than the freshness lifetime.
 0519       *
 0520       * @return bool    true if the response is fresh, false otherwise
 0521       *
 0522       * @api
 0523       */
 0524      public function isFresh()
 0525      {
 0526          return $this->getTtl() > 0;
 0527      }
 0528   
 0529      /**
 0530       * Returns true if the response includes headers that can be used to validate
 0531       * the response with the origin server using a conditional GET request.
 0532       *
 0533       * @return bool    true if the response is validateable, false otherwise
 0534       *
 0535       * @api
 0536       */
 0537      public function isValidateable()
 0538      {
 0539          return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
 0540      }
 0541   
 0542      /**
 0543       * Marks the response as "private".
 0544       *
 0545       * It makes the response ineligible for serving other clients.
 0546       *
 0547       * @return Response
 0548       *
 0549       * @api
 0550       */
 0551      public function setPrivate()
 0552      {
 0553          $this->headers->removeCacheControlDirective('public');
 0554          $this->headers->addCacheControlDirective('private');
 0555   
 0556          return $this;
 0557      }
 0558   
 0559      /**
 0560       * Marks the response as "public".
 0561       *
 0562       * It makes the response eligible for serving other clients.
 0563       *
 0564       * @return Response
 0565       *
 0566       * @api
 0567       */
 0568      public function setPublic()
 0569      {
 0570          $this->headers->addCacheControlDirective('public');
 0571          $this->headers->removeCacheControlDirective('private');
 0572   
 0573          return $this;
 0574      }
 0575   
 0576      /**
 0577       * Returns true if the response must be revalidated by caches.
 0578       *
 0579       * This method indicates that the response must not be served stale by a
 0580       * cache in any circumstance without first revalidating with the origin.
 0581       * When present, the TTL of the response should not be overridden to be
 0582       * greater than the value provided by the origin.
 0583       *
 0584       * @return bool    true if the response must be revalidated by a cache, false otherwise
 0585       *
 0586       * @api
 0587       */
 0588      public function mustRevalidate()
 0589      {
 0590          return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->has('proxy-revalidate');
 0591      }
 0592   
 0593      /**
 0594       * Returns the Date header as a DateTime instance.
 0595       *
 0596       * @return \DateTime A \DateTime instance
 0597       *
 0598       * @throws \RuntimeException When the header is not parseable
 0599       *
 0600       * @api
 0601       */
 0602      public function getDate()
 0603      {
 0604          return $this->headers->getDate('Date', new \DateTime());
 0605      }
 0606   
 0607      /**
 0608       * Sets the Date header.
 0609       *
 0610       * @param \DateTime $date A \DateTime instance
 0611       *
 0612       * @return Response
 0613       *
 0614       * @api
 0615       */
 0616      public function setDate(\DateTime $date)
 0617      {
 0618          $date->setTimezone(new \DateTimeZone('UTC'));
 0619          $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
 0620   
 0621          return $this;
 0622      }
 0623   
 0624      /**
 0625       * Returns the age of the response.
 0626       *
 0627       * @return int     The age of the response in seconds
 0628       */
 0629      public function getAge()
 0630      {
 0631          if (null !== $age = $this->headers->get('Age')) {
 0632              return (int) $age;
 0633          }
 0634   
 0635          return max(time() - $this->getDate()->format('U'), 0);
 0636      }
 0637   
 0638      /**
 0639       * Marks the response stale by setting the Age header to be equal to the maximum age of the response.
 0640       *
 0641       * @return Response
 0642       *
 0643       * @api
 0644       */
 0645      public function expire()
 0646      {
 0647          if ($this->isFresh()) {
 0648              $this->headers->set('Age', $this->getMaxAge());
 0649          }
 0650   
 0651          return $this;
 0652      }
 0653   
 0654      /**
 0655       * Returns the value of the Expires header as a DateTime instance.
 0656       *
 0657       * @return \DateTime|null A DateTime instance or null if the header does not exist
 0658       *
 0659       * @api
 0660       */
 0661      public function getExpires()
 0662      {
 0663          try {
 0664              return $this->headers->getDate('Expires');
 0665          } catch (\RuntimeException $e) {
 0666              // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past
 0667              return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000');
 0668          }
 0669      }
 0670   
 0671      /**
 0672       * Sets the Expires HTTP header with a DateTime instance.
 0673       *
 0674       * Passing null as value will remove the header.
 0675       *
 0676       * @param \DateTime|null $date A \DateTime instance or null to remove the header
 0677       *
 0678       * @return Response
 0679       *
 0680       * @api
 0681       */
 0682      public function setExpires(\DateTime $date = null)
 0683      {
 0684          if (null === $date) {
 0685              $this->headers->remove('Expires');
 0686          } else {
 0687              $date = clone $date;
 0688              $date->setTimezone(new \DateTimeZone('UTC'));
 0689              $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
 0690          }
 0691   
 0692          return $this;
 0693      }
 0694   
 0695      /**
 0696       * Returns the number of seconds after the time specified in the response's Date
 0697       * header when the response should no longer be considered fresh.
 0698       *
 0699       * First, it checks for a s-maxage directive, then a max-age directive, and then it falls
 0700       * back on an expires header. It returns null when no maximum age can be established.
 0701       *
 0702       * @return int|null     Number of seconds
 0703       *
 0704       * @api
 0705       */
 0706      public function getMaxAge()
 0707      {
 0708          if ($this->headers->hasCacheControlDirective('s-maxage')) {
 0709              return (int) $this->headers->getCacheControlDirective('s-maxage');
 0710          }
 0711   
 0712          if ($this->headers->hasCacheControlDirective('max-age')) {
 0713              return (int) $this->headers->getCacheControlDirective('max-age');
 0714          }
 0715   
 0716          if (null !== $this->getExpires()) {
 0717              return $this->getExpires()->format('U') - $this->getDate()->format('U');
 0718          }
 0719      }
 0720   
 0721      /**
 0722       * Sets the number of seconds after which the response should no longer be considered fresh.
 0723       *
 0724       * This methods sets the Cache-Control max-age directive.
 0725       *
 0726       * @param int     $value Number of seconds
 0727       *
 0728       * @return Response
 0729       *
 0730       * @api
 0731       */
 0732      public function setMaxAge($value)
 0733      {
 0734          $this->headers->addCacheControlDirective('max-age', $value);
 0735   
 0736          return $this;
 0737      }
 0738   
 0739      /**
 0740       * Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
 0741       *
 0742       * This methods sets the Cache-Control s-maxage directive.
 0743       *
 0744       * @param int     $value Number of seconds
 0745       *
 0746       * @return Response
 0747       *
 0748       * @api
 0749       */
 0750      public function setSharedMaxAge($value)
 0751      {
 0752          $this->setPublic();
 0753          $this->headers->addCacheControlDirective('s-maxage', $value);
 0754   
 0755          return $this;
 0756      }
 0757   
 0758      /**
 0759       * Returns the response's time-to-live in seconds.
 0760       *
 0761       * It returns null when no freshness information is present in the response.
 0762       *
 0763       * When the responses TTL is <= 0, the response may not be served from cache without first
 0764       * revalidating with the origin.
 0765       *
 0766       * @return int|null     The TTL in seconds
 0767       *
 0768       * @api
 0769       */
 0770      public function getTtl()
 0771      {
 0772          if (null !== $maxAge = $this->getMaxAge()) {
 0773              return $maxAge - $this->getAge();
 0774          }
 0775      }
 0776   
 0777      /**
 0778       * Sets the response's time-to-live for shared caches.
 0779       *
 0780       * This method adjusts the Cache-Control/s-maxage directive.
 0781       *
 0782       * @param int     $seconds Number of seconds
 0783       *
 0784       * @return Response
 0785       *
 0786       * @api
 0787       */
 0788      public function setTtl($seconds)
 0789      {
 0790          $this->setSharedMaxAge($this->getAge() + $seconds);
 0791   
 0792          return $this;
 0793      }
 0794   
 0795      /**
 0796       * Sets the response's time-to-live for private/client caches.
 0797       *
 0798       * This method adjusts the Cache-Control/max-age directive.
 0799       *
 0800       * @param int     $seconds Number of seconds
 0801       *
 0802       * @return Response
 0803       *
 0804       * @api
 0805       */
 0806      public function setClientTtl($seconds)
 0807      {
 0808          $this->setMaxAge($this->getAge() + $seconds);
 0809   
 0810          return $this;
 0811      }
 0812   
 0813      /**
 0814       * Returns the Last-Modified HTTP header as a DateTime instance.
 0815       *
 0816       * @return \DateTime|null A DateTime instance or null if the header does not exist
 0817       *
 0818       * @throws \RuntimeException When the HTTP header is not parseable
 0819       *
 0820       * @api
 0821       */
 0822      public function getLastModified()
 0823      {
 0824          return $this->headers->getDate('Last-Modified');
 0825      }
 0826   
 0827      /**
 0828       * Sets the Last-Modified HTTP header with a DateTime instance.
 0829       *
 0830       * Passing null as value will remove the header.
 0831       *
 0832       * @param \DateTime|null $date A \DateTime instance or null to remove the header
 0833       *
 0834       * @return Response
 0835       *
 0836       * @api
 0837       */
 0838      public function setLastModified(\DateTime $date = null)
 0839      {
 0840          if (null === $date) {
 0841              $this->headers->remove('Last-Modified');
 0842          } else {
 0843              $date = clone $date;
 0844              $date->setTimezone(new \DateTimeZone('UTC'));
 0845              $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
 0846          }
 0847   
 0848          return $this;
 0849      }
 0850   
 0851      /**
 0852       * Returns the literal value of the ETag HTTP header.
 0853       *
 0854       * @return string|null The ETag HTTP header or null if it does not exist
 0855       *
 0856       * @api
 0857       */
 0858      public function getEtag()
 0859      {
 0860          return $this->headers->get('ETag');
 0861      }
 0862   
 0863      /**
 0864       * Sets the ETag value.
 0865       *
 0866       * @param string|null $etag The ETag unique identifier or null to remove the header
 0867       * @param bool        $weak Whether you want a weak ETag or not
 0868       *
 0869       * @return Response
 0870       *
 0871       * @api
 0872       */
 0873      public function setEtag($etag = null, $weak = false)
 0874      {
 0875          if (null === $etag) {
 0876              $this->headers->remove('Etag');
 0877          } else {
 0878              if (0 !== strpos($etag, '"')) {
 0879                  $etag = '"'.$etag.'"';
 0880              }
 0881   
 0882              $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
 0883          }
 0884   
 0885          return $this;
 0886      }
 0887   
 0888      /**
 0889       * Sets the response's cache headers (validation and/or expiration).
 0890       *
 0891       * Available options are: etag, last_modified, max_age, s_maxage, private, and public.
 0892       *
 0893       * @param array $options An array of cache options
 0894       *
 0895       * @return Response
 0896       *
 0897       * @throws \InvalidArgumentException
 0898       *
 0899       * @api
 0900       */
 0901      public function setCache(array $options)
 0902      {
 0903          if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) {
 0904              throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff))));
 0905          }
 0906   
 0907          if (isset($options['etag'])) {
 0908              $this->setEtag($options['etag']);
 0909          }
 0910   
 0911          if (isset($options['last_modified'])) {
 0912              $this->setLastModified($options['last_modified']);
 0913          }
 0914   
 0915          if (isset($options['max_age'])) {
 0916              $this->setMaxAge($options['max_age']);
 0917          }
 0918   
 0919          if (isset($options['s_maxage'])) {
 0920              $this->setSharedMaxAge($options['s_maxage']);
 0921          }
 0922   
 0923          if (isset($options['public'])) {
 0924              if ($options['public']) {
 0925                  $this->setPublic();
 0926              } else {
 0927                  $this->setPrivate();
 0928              }
 0929          }
 0930   
 0931          if (isset($options['private'])) {
 0932              if ($options['private']) {
 0933                  $this->setPrivate();
 0934              } else {
 0935                  $this->setPublic();
 0936              }
 0937          }
 0938   
 0939          return $this;
 0940      }
 0941   
 0942      /**
 0943       * Modifies the response so that it conforms to the rules defined for a 304 status code.
 0944       *
 0945       * This sets the status, removes the body, and discards any headers
 0946       * that MUST NOT be included in 304 responses.
 0947       *
 0948       * @return Response
 0949       *
 0950       * @see http://tools.ietf.org/html/rfc2616#section-10.3.5
 0951       *
 0952       * @api
 0953       */
 0954      public function setNotModified()
 0955      {
 0956          $this->setStatusCode(304);
 0957          $this->setContent(null);
 0958   
 0959          // remove headers that MUST NOT be included with 304 Not Modified responses
 0960          foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
 0961              $this->headers->remove($header);
 0962          }
 0963   
 0964          return $this;
 0965      }
 0966   
 0967      /**
 0968       * Returns true if the response includes a Vary header.
 0969       *
 0970       * @return bool    true if the response includes a Vary header, false otherwise
 0971       *
 0972       * @api
 0973       */
 0974      public function hasVary()
 0975      {
 0976          return null !== $this->headers->get('Vary');
 0977      }
 0978   
 0979      /**
 0980       * Returns an array of header names given in the Vary header.
 0981       *
 0982       * @return array An array of Vary names
 0983       *
 0984       * @api
 0985       */
 0986      public function getVary()
 0987      {
 0988          if (!$vary = $this->headers->get('Vary', null, false)) {
 0989              return array();
 0990          }
 0991   
 0992          $ret = array();
 0993          foreach ($vary as $item) {
 0994              $ret = array_merge($ret, preg_split('/[\s,]+/', $item));
 0995          }
 0996   
 0997          return $ret;
 0998      }
 0999   
 1000      /**
 1001       * Sets the Vary header.
 1002       *
 1003       * @param string|array $headers
 1004       * @param bool         $replace Whether to replace the actual value of not (true by default)
 1005       *
 1006       * @return Response
 1007       *
 1008       * @api
 1009       */
 1010      public function setVary($headers, $replace = true)
 1011      {
 1012          $this->headers->set('Vary', $headers, $replace);
 1013   
 1014          return $this;
 1015      }
 1016   
 1017      /**
 1018       * Determines if the Response validators (ETag, Last-Modified) match
 1019       * a conditional value specified in the Request.
 1020       *
 1021       * If the Response is not modified, it sets the status code to 304 and
 1022       * removes the actual content by calling the setNotModified() method.
 1023       *
 1024       * @param Request $request A Request instance
 1025       *
 1026       * @return bool    true if the Response validators match the Request, false otherwise
 1027       *
 1028       * @api
 1029       */
 1030      public function isNotModified(Request $request)
 1031      {
 1032          if (!$request->isMethodSafe()) {
 1033              return false;
 1034          }
 1035   
 1036          $notModified   = false;
 1037          $lastModified  = $this->headers->get('Last-Modified');
 1038          $modifiedSince = $request->headers->get('If-Modified-Since');
 1039   
 1040          if ($etags = $request->getEtags()) {
 1041              $notModified = in_array($this->getEtag(), $etags) || in_array('*', $etags);
 1042          }
 1043   
 1044          if ($modifiedSince && $lastModified) {
 1045              $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified);
 1046          }
 1047   
 1048          if ($notModified) {
 1049              $this->setNotModified();
 1050          }
 1051   
 1052          return $notModified;
 1053      }
 1054   
 1055      // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
 1056      /**
 1057       * Is response invalid?
 1058       *
 1059       * @return bool
 1060       *
 1061       * @api
 1062       */
 1063      public function isInvalid()
 1064      {
 1065          return $this->statusCode < 100 || $this->statusCode >= 600;
 1066      }
 1067   
 1068      /**
 1069       * Is response informative?
 1070       *
 1071       * @return bool
 1072       *
 1073       * @api
 1074       */
 1075      public function isInformational()
 1076      {
 1077          return $this->statusCode >= 100 && $this->statusCode < 200;
 1078      }
 1079   
 1080      /**
 1081       * Is response successful?
 1082       *
 1083       * @return bool
 1084       *
 1085       * @api
 1086       */
 1087      public function isSuccessful()
 1088      {
 1089          return $this->statusCode >= 200 && $this->statusCode < 300;
 1090      }
 1091   
 1092      /**
 1093       * Is the response a redirect?
 1094       *
 1095       * @return bool
 1096       *
 1097       * @api
 1098       */
 1099      public function isRedirection()
 1100      {
 1101          return $this->statusCode >= 300 && $this->statusCode < 400;
 1102      }
 1103   
 1104      /**
 1105       * Is there a client error?
 1106       *
 1107       * @return bool
 1108       *
 1109       * @api
 1110       */
 1111      public function isClientError()
 1112      {
 1113          return $this->statusCode >= 400 && $this->statusCode < 500;
 1114      }
 1115   
 1116      /**
 1117       * Was there a server side error?
 1118       *
 1119       * @return bool
 1120       *
 1121       * @api
 1122       */
 1123      public function isServerError()
 1124      {
 1125          return $this->statusCode >= 500 && $this->statusCode < 600;
 1126      }
 1127   
 1128      /**
 1129       * Is the response OK?
 1130       *
 1131       * @return bool
 1132       *
 1133       * @api
 1134       */
 1135      public function isOk()
 1136      {
 1137          return 200 === $this->statusCode;
 1138      }
 1139   
 1140      /**
 1141       * Is the response forbidden?
 1142       *
 1143       * @return bool
 1144       *
 1145       * @api
 1146       */
 1147      public function isForbidden()
 1148      {
 1149          return 403 === $this->statusCode;
 1150      }
 1151   
 1152      /**
 1153       * Is the response a not found error?
 1154       *
 1155       * @return bool
 1156       *
 1157       * @api
 1158       */
 1159      public function isNotFound()
 1160      {
 1161          return 404 === $this->statusCode;
 1162      }
 1163   
 1164      /**
 1165       * Is the response a redirect of some form?
 1166       *
 1167       * @param string $location
 1168       *
 1169       * @return bool
 1170       *
 1171       * @api
 1172       */
 1173      public function isRedirect($location = null)
 1174      {
 1175          return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location'));
 1176      }
 1177   
 1178      /**
 1179       * Is the response empty?
 1180       *
 1181       * @return bool
 1182       *
 1183       * @api
 1184       */
 1185      public function isEmpty()
 1186      {
 1187          return in_array($this->statusCode, array(204, 304));
 1188      }
 1189   
 1190      /**
 1191       * Check if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9
 1192       *
 1193       * @link http://support.microsoft.com/kb/323308
 1194       */
 1195      protected function ensureIEOverSSLCompatibility(Request $request)
 1196      {
 1197          if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) == 1 && true === $request->isSecure()) {
 1198              if (intval(preg_replace("/(MSIE )(.*?);/", "$2", $match[0])) < 9) {
 1199                  $this->headers->remove('Cache-Control');
 1200              }
 1201          }
 1202      }
 1203  }
 1204