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. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
Response.php
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