Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

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