[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/ -> Response.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of the Symfony package.
   5   *
   6   * (c) Fabien Potencier <fabien@symfony.com>
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  namespace Symfony\Component\HttpFoundation;
  13  
  14  /**
  15   * Response represents an HTTP response.
  16   *
  17   * @author Fabien Potencier <fabien@symfony.com>
  18   */
  19  class Response
  20  {
  21      /**
  22       * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
  23       */
  24      public $headers;
  25  
  26      /**
  27       * @var string
  28       */
  29      protected $content;
  30  
  31      /**
  32       * @var string
  33       */
  34      protected $version;
  35  
  36      /**
  37       * @var int
  38       */
  39      protected $statusCode;
  40  
  41      /**
  42       * @var string
  43       */
  44      protected $statusText;
  45  
  46      /**
  47       * @var string
  48       */
  49      protected $charset;
  50  
  51      /**
  52       * Status codes translation table.
  53       *
  54       * The list of codes is complete according to the
  55       * {@link http://www.iana.org/assignments/http-status-codes/ Hypertext Transfer Protocol (HTTP) Status Code Registry}
  56       * (last updated 2016-03-01).
  57       *
  58       * Unless otherwise noted, the status code is defined in RFC2616.
  59       *
  60       * @var array
  61       */
  62      public static $statusTexts = array(
  63          100 => 'Continue',
  64          101 => 'Switching Protocols',
  65          102 => 'Processing',            // RFC2518
  66          200 => 'OK',
  67          201 => 'Created',
  68          202 => 'Accepted',
  69          203 => 'Non-Authoritative Information',
  70          204 => 'No Content',
  71          205 => 'Reset Content',
  72          206 => 'Partial Content',
  73          207 => 'Multi-Status',          // RFC4918
  74          208 => 'Already Reported',      // RFC5842
  75          226 => 'IM Used',               // RFC3229
  76          300 => 'Multiple Choices',
  77          301 => 'Moved Permanently',
  78          302 => 'Found',
  79          303 => 'See Other',
  80          304 => 'Not Modified',
  81          305 => 'Use Proxy',
  82          306 => 'Reserved',
  83          307 => 'Temporary Redirect',
  84          308 => 'Permanent Redirect',    // RFC7238
  85          400 => 'Bad Request',
  86          401 => 'Unauthorized',
  87          402 => 'Payment Required',
  88          403 => 'Forbidden',
  89          404 => 'Not Found',
  90          405 => 'Method Not Allowed',
  91          406 => 'Not Acceptable',
  92          407 => 'Proxy Authentication Required',
  93          408 => 'Request Timeout',
  94          409 => 'Conflict',
  95          410 => 'Gone',
  96          411 => 'Length Required',
  97          412 => 'Precondition Failed',
  98          413 => 'Request Entity Too Large',
  99          414 => 'Request-URI Too Long',
 100          415 => 'Unsupported Media Type',
 101          416 => 'Requested Range Not Satisfiable',
 102          417 => 'Expectation Failed',
 103          418 => 'I\'m a teapot',                                               // RFC2324
 104          421 => 'Misdirected Request',                                         // RFC7540
 105          422 => 'Unprocessable Entity',                                        // RFC4918
 106          423 => 'Locked',                                                      // RFC4918
 107          424 => 'Failed Dependency',                                           // RFC4918
 108          425 => 'Reserved for WebDAV advanced collections expired proposal',   // RFC2817
 109          426 => 'Upgrade Required',                                            // RFC2817
 110          428 => 'Precondition Required',                                       // RFC6585
 111          429 => 'Too Many Requests',                                           // RFC6585
 112          431 => 'Request Header Fields Too Large',                             // RFC6585
 113          451 => 'Unavailable For Legal Reasons',                               // RFC7725
 114          500 => 'Internal Server Error',
 115          501 => 'Not Implemented',
 116          502 => 'Bad Gateway',
 117          503 => 'Service Unavailable',
 118          504 => 'Gateway Timeout',
 119          505 => 'HTTP Version Not Supported',
 120          506 => 'Variant Also Negotiates (Experimental)',                      // RFC2295
 121          507 => 'Insufficient Storage',                                        // RFC4918
 122          508 => 'Loop Detected',                                               // RFC5842
 123          510 => 'Not Extended',                                                // RFC2774
 124          511 => 'Network Authentication Required',                             // RFC6585
 125      );
 126  
 127      /**
 128       * Constructor.
 129       *
 130       * @param string $content The response content
 131       * @param int    $status  The response status code
 132       * @param array  $headers An array of response headers
 133       *
 134       * @throws \InvalidArgumentException When the HTTP status code is not valid
 135       */
 136      public function __construct($content = '', $status = 200, $headers = array())
 137      {
 138          $this->headers = new ResponseHeaderBag($headers);
 139          $this->setContent($content);
 140          $this->setStatusCode($status);
 141          $this->setProtocolVersion('1.0');
 142          if (!$this->headers->has('Date')) {
 143              $this->setDate(\DateTime::createFromFormat('U', time(), new \DateTimeZone('UTC')));
 144          }
 145      }
 146  
 147      /**
 148       * Factory method for chainability.
 149       *
 150       * Example:
 151       *
 152       *     return Response::create($body, 200)
 153       *         ->setSharedMaxAge(300);
 154       *
 155       * @param string $content The response content
 156       * @param int    $status  The response status code
 157       * @param array  $headers An array of response headers
 158       *
 159       * @return Response
 160       */
 161      public static function create($content = '', $status = 200, $headers = array())
 162      {
 163          return new static($content, $status, $headers);
 164      }
 165  
 166      /**
 167       * Returns the Response as an HTTP string.
 168       *
 169       * The string representation of the Response is the same as the
 170       * one that will be sent to the client only if the prepare() method
 171       * has been called before.
 172       *
 173       * @return string The Response as an HTTP string
 174       *
 175       * @see prepare()
 176       */
 177      public function __toString()
 178      {
 179          return
 180              sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
 181              $this->headers."\r\n".
 182              $this->getContent();
 183      }
 184  
 185      /**
 186       * Clones the current Response instance.
 187       */
 188      public function __clone()
 189      {
 190          $this->headers = clone $this->headers;
 191      }
 192  
 193      /**
 194       * Prepares the Response before it is sent to the client.
 195       *
 196       * This method tweaks the Response to ensure that it is
 197       * compliant with RFC 2616. Most of the changes are based on
 198       * the Request that is "associated" with this Response.
 199       *
 200       * @param Request $request A Request instance
 201       *
 202       * @return Response The current response.
 203       */
 204      public function prepare(Request $request)
 205      {
 206          $headers = $this->headers;
 207  
 208          if ($this->isInformational() || $this->isEmpty()) {
 209              $this->setContent(null);
 210              $headers->remove('Content-Type');
 211              $headers->remove('Content-Length');
 212          } else {
 213              // Content-type based on the Request
 214              if (!$headers->has('Content-Type')) {
 215                  $format = $request->getRequestFormat();
 216                  if (null !== $format && $mimeType = $request->getMimeType($format)) {
 217                      $headers->set('Content-Type', $mimeType);
 218                  }
 219              }
 220  
 221              // Fix Content-Type
 222              $charset = $this->charset ?: 'UTF-8';
 223              if (!$headers->has('Content-Type')) {
 224                  $headers->set('Content-Type', 'text/html; charset='.$charset);
 225              } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {
 226                  // add the charset
 227                  $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
 228              }
 229  
 230              // Fix Content-Length
 231              if ($headers->has('Transfer-Encoding')) {
 232                  $headers->remove('Content-Length');
 233              }
 234  
 235              if ($request->isMethod('HEAD')) {
 236                  // cf. RFC2616 14.13
 237                  $length = $headers->get('Content-Length');
 238                  $this->setContent(null);
 239                  if ($length) {
 240                      $headers->set('Content-Length', $length);
 241                  }
 242              }
 243          }
 244  
 245          // Fix protocol
 246          if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
 247              $this->setProtocolVersion('1.1');
 248          }
 249  
 250          // Check if we need to send extra expire info headers
 251          if ('1.0' == $this->getProtocolVersion() && 'no-cache' == $this->headers->get('Cache-Control')) {
 252              $this->headers->set('pragma', 'no-cache');
 253              $this->headers->set('expires', -1);
 254          }
 255  
 256          $this->ensureIEOverSSLCompatibility($request);
 257  
 258          return $this;
 259      }
 260  
 261      /**
 262       * Sends HTTP headers.
 263       *
 264       * @return Response
 265       */
 266      public function sendHeaders()
 267      {
 268          // headers have already been sent by the developer
 269          if (headers_sent()) {
 270              return $this;
 271          }
 272  
 273          // headers
 274          foreach ($this->headers->allPreserveCase() as $name => $values) {
 275              foreach ($values as $value) {
 276                  header($name.': '.$value, false);
 277              }
 278          }
 279  
 280          // status
 281          header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
 282  
 283          // cookies
 284          foreach ($this->headers->getCookies() as $cookie) {
 285              setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
 286          }
 287  
 288          return $this;
 289      }
 290  
 291      /**
 292       * Sends content for the current web response.
 293       *
 294       * @return Response
 295       */
 296      public function sendContent()
 297      {
 298          echo $this->content;
 299  
 300          return $this;
 301      }
 302  
 303      /**
 304       * Sends HTTP headers and content.
 305       *
 306       * @return Response
 307       */
 308      public function send()
 309      {
 310          $this->sendHeaders();
 311          $this->sendContent();
 312  
 313          if (function_exists('fastcgi_finish_request')) {
 314              fastcgi_finish_request();
 315          } elseif ('cli' !== PHP_SAPI) {
 316              // ob_get_level() never returns 0 on some Windows configurations, so if
 317              // the level is the same two times in a row, the loop should be stopped.
 318              $previous = null;
 319              $obStatus = ob_get_status(1);
 320              while (($level = ob_get_level()) > 0 && $level !== $previous) {
 321                  $previous = $level;
 322                  if ($obStatus[$level - 1]) {
 323                      if (PHP_VERSION_ID >= 50400) {
 324                          if (isset($obStatus[$level - 1]['flags']) && ($obStatus[$level - 1]['flags'] & PHP_OUTPUT_HANDLER_REMOVABLE)) {
 325                              ob_end_flush();
 326                          }
 327                      } else {
 328                          if (isset($obStatus[$level - 1]['del']) && $obStatus[$level - 1]['del']) {
 329                              ob_end_flush();
 330                          }
 331                      }
 332                  }
 333              }
 334              flush();
 335          }
 336  
 337          return $this;
 338      }
 339  
 340      /**
 341       * Sets the response content.
 342       *
 343       * Valid types are strings, numbers, and objects that implement a __toString() method.
 344       *
 345       * @param mixed $content
 346       *
 347       * @return Response
 348       *
 349       * @throws \UnexpectedValueException
 350       */
 351      public function setContent($content)
 352      {
 353          if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) {
 354              throw new \UnexpectedValueException(sprintf('The Response content must be a string or object implementing __toString(), "%s" given.', gettype($content)));
 355          }
 356  
 357          $this->content = (string) $content;
 358  
 359          return $this;
 360      }
 361  
 362      /**
 363       * Gets the current response content.
 364       *
 365       * @return string Content
 366       */
 367      public function getContent()
 368      {
 369          return $this->content;
 370      }
 371  
 372      /**
 373       * Sets the HTTP protocol version (1.0 or 1.1).
 374       *
 375       * @param string $version The HTTP protocol version
 376       *
 377       * @return Response
 378       */
 379      public function setProtocolVersion($version)
 380      {
 381          $this->version = $version;
 382  
 383          return $this;
 384      }
 385  
 386      /**
 387       * Gets the HTTP protocol version.
 388       *
 389       * @return string The HTTP protocol version
 390       */
 391      public function getProtocolVersion()
 392      {
 393          return $this->version;
 394      }
 395  
 396      /**
 397       * Sets the response status code.
 398       *
 399       * @param int   $code HTTP status code
 400       * @param mixed $text HTTP status text
 401       *
 402       * If the status text is null it will be automatically populated for the known
 403       * status codes and left empty otherwise.
 404       *
 405       * @return Response
 406       *
 407       * @throws \InvalidArgumentException When the HTTP status code is not valid
 408       */
 409      public function setStatusCode($code, $text = null)
 410      {
 411          $this->statusCode = $code = (int) $code;
 412          if ($this->isInvalid()) {
 413              throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
 414          }
 415  
 416          if (null === $text) {
 417              $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status';
 418  
 419              return $this;
 420          }
 421  
 422          if (false === $text) {
 423              $this->statusText = '';
 424  
 425              return $this;
 426          }
 427  
 428          $this->statusText = $text;
 429  
 430          return $this;
 431      }
 432  
 433      /**
 434       * Retrieves the status code for the current web response.
 435       *
 436       * @return int Status code
 437       */
 438      public function getStatusCode()
 439      {
 440          return $this->statusCode;
 441      }
 442  
 443      /**
 444       * Sets the response charset.
 445       *
 446       * @param string $charset Character set
 447       *
 448       * @return Response
 449       */
 450      public function setCharset($charset)
 451      {
 452          $this->charset = $charset;
 453  
 454          return $this;
 455      }
 456  
 457      /**
 458       * Retrieves the response charset.
 459       *
 460       * @return string Character set
 461       */
 462      public function getCharset()
 463      {
 464          return $this->charset;
 465      }
 466  
 467      /**
 468       * Returns true if the response is worth caching under any circumstance.
 469       *
 470       * Responses marked "private" with an explicit Cache-Control directive are
 471       * considered uncacheable.
 472       *
 473       * Responses with neither a freshness lifetime (Expires, max-age) nor cache
 474       * validator (Last-Modified, ETag) are considered uncacheable.
 475       *
 476       * @return bool true if the response is worth caching, false otherwise
 477       */
 478      public function isCacheable()
 479      {
 480          if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
 481              return false;
 482          }
 483  
 484          if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) {
 485              return false;
 486          }
 487  
 488          return $this->isValidateable() || $this->isFresh();
 489      }
 490  
 491      /**
 492       * Returns true if the response is "fresh".
 493       *
 494       * Fresh responses may be served from cache without any interaction with the
 495       * origin. A response is considered fresh when it includes a Cache-Control/max-age
 496       * indicator or Expires header and the calculated age is less than the freshness lifetime.
 497       *
 498       * @return bool true if the response is fresh, false otherwise
 499       */
 500      public function isFresh()
 501      {
 502          return $this->getTtl() > 0;
 503      }
 504  
 505      /**
 506       * Returns true if the response includes headers that can be used to validate
 507       * the response with the origin server using a conditional GET request.
 508       *
 509       * @return bool true if the response is validateable, false otherwise
 510       */
 511      public function isValidateable()
 512      {
 513          return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
 514      }
 515  
 516      /**
 517       * Marks the response as "private".
 518       *
 519       * It makes the response ineligible for serving other clients.
 520       *
 521       * @return Response
 522       */
 523      public function setPrivate()
 524      {
 525          $this->headers->removeCacheControlDirective('public');
 526          $this->headers->addCacheControlDirective('private');
 527  
 528          return $this;
 529      }
 530  
 531      /**
 532       * Marks the response as "public".
 533       *
 534       * It makes the response eligible for serving other clients.
 535       *
 536       * @return Response
 537       */
 538      public function setPublic()
 539      {
 540          $this->headers->addCacheControlDirective('public');
 541          $this->headers->removeCacheControlDirective('private');
 542  
 543          return $this;
 544      }
 545  
 546      /**
 547       * Returns true if the response must be revalidated by caches.
 548       *
 549       * This method indicates that the response must not be served stale by a
 550       * cache in any circumstance without first revalidating with the origin.
 551       * When present, the TTL of the response should not be overridden to be
 552       * greater than the value provided by the origin.
 553       *
 554       * @return bool true if the response must be revalidated by a cache, false otherwise
 555       */
 556      public function mustRevalidate()
 557      {
 558          return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate');
 559      }
 560  
 561      /**
 562       * Returns the Date header as a DateTime instance.
 563       *
 564       * @return \DateTime A \DateTime instance
 565       *
 566       * @throws \RuntimeException When the header is not parseable
 567       */
 568      public function getDate()
 569      {
 570          return $this->headers->getDate('Date', new \DateTime());
 571      }
 572  
 573      /**
 574       * Sets the Date header.
 575       *
 576       * @param \DateTime $date A \DateTime instance
 577       *
 578       * @return Response
 579       */
 580      public function setDate(\DateTime $date)
 581      {
 582          $date->setTimezone(new \DateTimeZone('UTC'));
 583          $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
 584  
 585          return $this;
 586      }
 587  
 588      /**
 589       * Returns the age of the response.
 590       *
 591       * @return int The age of the response in seconds
 592       */
 593      public function getAge()
 594      {
 595          if (null !== $age = $this->headers->get('Age')) {
 596              return (int) $age;
 597          }
 598  
 599          return max(time() - $this->getDate()->format('U'), 0);
 600      }
 601  
 602      /**
 603       * Marks the response stale by setting the Age header to be equal to the maximum age of the response.
 604       *
 605       * @return Response
 606       */
 607      public function expire()
 608      {
 609          if ($this->isFresh()) {
 610              $this->headers->set('Age', $this->getMaxAge());
 611          }
 612  
 613          return $this;
 614      }
 615  
 616      /**
 617       * Returns the value of the Expires header as a DateTime instance.
 618       *
 619       * @return \DateTime|null A DateTime instance or null if the header does not exist
 620       */
 621      public function getExpires()
 622      {
 623          try {
 624              return $this->headers->getDate('Expires');
 625          } catch (\RuntimeException $e) {
 626              // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past
 627              return \DateTime::createFromFormat(DATE_RFC2822, 'Sat, 01 Jan 00 00:00:00 +0000');
 628          }
 629      }
 630  
 631      /**
 632       * Sets the Expires HTTP header with a DateTime instance.
 633       *
 634       * Passing null as value will remove the header.
 635       *
 636       * @param \DateTime|null $date A \DateTime instance or null to remove the header
 637       *
 638       * @return Response
 639       */
 640      public function setExpires(\DateTime $date = null)
 641      {
 642          if (null === $date) {
 643              $this->headers->remove('Expires');
 644          } else {
 645              $date = clone $date;
 646              $date->setTimezone(new \DateTimeZone('UTC'));
 647              $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
 648          }
 649  
 650          return $this;
 651      }
 652  
 653      /**
 654       * Returns the number of seconds after the time specified in the response's Date
 655       * header when the response should no longer be considered fresh.
 656       *
 657       * First, it checks for a s-maxage directive, then a max-age directive, and then it falls
 658       * back on an expires header. It returns null when no maximum age can be established.
 659       *
 660       * @return int|null Number of seconds
 661       */
 662      public function getMaxAge()
 663      {
 664          if ($this->headers->hasCacheControlDirective('s-maxage')) {
 665              return (int) $this->headers->getCacheControlDirective('s-maxage');
 666          }
 667  
 668          if ($this->headers->hasCacheControlDirective('max-age')) {
 669              return (int) $this->headers->getCacheControlDirective('max-age');
 670          }
 671  
 672          if (null !== $this->getExpires()) {
 673              return $this->getExpires()->format('U') - $this->getDate()->format('U');
 674          }
 675      }
 676  
 677      /**
 678       * Sets the number of seconds after which the response should no longer be considered fresh.
 679       *
 680       * This methods sets the Cache-Control max-age directive.
 681       *
 682       * @param int $value Number of seconds
 683       *
 684       * @return Response
 685       */
 686      public function setMaxAge($value)
 687      {
 688          $this->headers->addCacheControlDirective('max-age', $value);
 689  
 690          return $this;
 691      }
 692  
 693      /**
 694       * Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
 695       *
 696       * This methods sets the Cache-Control s-maxage directive.
 697       *
 698       * @param int $value Number of seconds
 699       *
 700       * @return Response
 701       */
 702      public function setSharedMaxAge($value)
 703      {
 704          $this->setPublic();
 705          $this->headers->addCacheControlDirective('s-maxage', $value);
 706  
 707          return $this;
 708      }
 709  
 710      /**
 711       * Returns the response's time-to-live in seconds.
 712       *
 713       * It returns null when no freshness information is present in the response.
 714       *
 715       * When the responses TTL is <= 0, the response may not be served from cache without first
 716       * revalidating with the origin.
 717       *
 718       * @return int|null The TTL in seconds
 719       */
 720      public function getTtl()
 721      {
 722          if (null !== $maxAge = $this->getMaxAge()) {
 723              return $maxAge - $this->getAge();
 724          }
 725      }
 726  
 727      /**
 728       * Sets the response's time-to-live for shared caches.
 729       *
 730       * This method adjusts the Cache-Control/s-maxage directive.
 731       *
 732       * @param int $seconds Number of seconds
 733       *
 734       * @return Response
 735       */
 736      public function setTtl($seconds)
 737      {
 738          $this->setSharedMaxAge($this->getAge() + $seconds);
 739  
 740          return $this;
 741      }
 742  
 743      /**
 744       * Sets the response's time-to-live for private/client caches.
 745       *
 746       * This method adjusts the Cache-Control/max-age directive.
 747       *
 748       * @param int $seconds Number of seconds
 749       *
 750       * @return Response
 751       */
 752      public function setClientTtl($seconds)
 753      {
 754          $this->setMaxAge($this->getAge() + $seconds);
 755  
 756          return $this;
 757      }
 758  
 759      /**
 760       * Returns the Last-Modified HTTP header as a DateTime instance.
 761       *
 762       * @return \DateTime|null A DateTime instance or null if the header does not exist
 763       *
 764       * @throws \RuntimeException When the HTTP header is not parseable
 765       */
 766      public function getLastModified()
 767      {
 768          return $this->headers->getDate('Last-Modified');
 769      }
 770  
 771      /**
 772       * Sets the Last-Modified HTTP header with a DateTime instance.
 773       *
 774       * Passing null as value will remove the header.
 775       *
 776       * @param \DateTime|null $date A \DateTime instance or null to remove the header
 777       *
 778       * @return Response
 779       */
 780      public function setLastModified(\DateTime $date = null)
 781      {
 782          if (null === $date) {
 783              $this->headers->remove('Last-Modified');
 784          } else {
 785              $date = clone $date;
 786              $date->setTimezone(new \DateTimeZone('UTC'));
 787              $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
 788          }
 789  
 790          return $this;
 791      }
 792  
 793      /**
 794       * Returns the literal value of the ETag HTTP header.
 795       *
 796       * @return string|null The ETag HTTP header or null if it does not exist
 797       */
 798      public function getEtag()
 799      {
 800          return $this->headers->get('ETag');
 801      }
 802  
 803      /**
 804       * Sets the ETag value.
 805       *
 806       * @param string|null $etag The ETag unique identifier or null to remove the header
 807       * @param bool        $weak Whether you want a weak ETag or not
 808       *
 809       * @return Response
 810       */
 811      public function setEtag($etag = null, $weak = false)
 812      {
 813          if (null === $etag) {
 814              $this->headers->remove('Etag');
 815          } else {
 816              if (0 !== strpos($etag, '"')) {
 817                  $etag = '"'.$etag.'"';
 818              }
 819  
 820              $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
 821          }
 822  
 823          return $this;
 824      }
 825  
 826      /**
 827       * Sets the response's cache headers (validation and/or expiration).
 828       *
 829       * Available options are: etag, last_modified, max_age, s_maxage, private, and public.
 830       *
 831       * @param array $options An array of cache options
 832       *
 833       * @return Response
 834       *
 835       * @throws \InvalidArgumentException
 836       */
 837      public function setCache(array $options)
 838      {
 839          if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) {
 840              throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_values($diff))));
 841          }
 842  
 843          if (isset($options['etag'])) {
 844              $this->setEtag($options['etag']);
 845          }
 846  
 847          if (isset($options['last_modified'])) {
 848              $this->setLastModified($options['last_modified']);
 849          }
 850  
 851          if (isset($options['max_age'])) {
 852              $this->setMaxAge($options['max_age']);
 853          }
 854  
 855          if (isset($options['s_maxage'])) {
 856              $this->setSharedMaxAge($options['s_maxage']);
 857          }
 858  
 859          if (isset($options['public'])) {
 860              if ($options['public']) {
 861                  $this->setPublic();
 862              } else {
 863                  $this->setPrivate();
 864              }
 865          }
 866  
 867          if (isset($options['private'])) {
 868              if ($options['private']) {
 869                  $this->setPrivate();
 870              } else {
 871                  $this->setPublic();
 872              }
 873          }
 874  
 875          return $this;
 876      }
 877  
 878      /**
 879       * Modifies the response so that it conforms to the rules defined for a 304 status code.
 880       *
 881       * This sets the status, removes the body, and discards any headers
 882       * that MUST NOT be included in 304 responses.
 883       *
 884       * @return Response
 885       *
 886       * @see http://tools.ietf.org/html/rfc2616#section-10.3.5
 887       */
 888      public function setNotModified()
 889      {
 890          $this->setStatusCode(304);
 891          $this->setContent(null);
 892  
 893          // remove headers that MUST NOT be included with 304 Not Modified responses
 894          foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
 895              $this->headers->remove($header);
 896          }
 897  
 898          return $this;
 899      }
 900  
 901      /**
 902       * Returns true if the response includes a Vary header.
 903       *
 904       * @return bool true if the response includes a Vary header, false otherwise
 905       */
 906      public function hasVary()
 907      {
 908          return null !== $this->headers->get('Vary');
 909      }
 910  
 911      /**
 912       * Returns an array of header names given in the Vary header.
 913       *
 914       * @return array An array of Vary names
 915       */
 916      public function getVary()
 917      {
 918          if (!$vary = $this->headers->get('Vary', null, false)) {
 919              return array();
 920          }
 921  
 922          $ret = array();
 923          foreach ($vary as $item) {
 924              $ret = array_merge($ret, preg_split('/[\s,]+/', $item));
 925          }
 926  
 927          return $ret;
 928      }
 929  
 930      /**
 931       * Sets the Vary header.
 932       *
 933       * @param string|array $headers
 934       * @param bool         $replace Whether to replace the actual value or not (true by default)
 935       *
 936       * @return Response
 937       */
 938      public function setVary($headers, $replace = true)
 939      {
 940          $this->headers->set('Vary', $headers, $replace);
 941  
 942          return $this;
 943      }
 944  
 945      /**
 946       * Determines if the Response validators (ETag, Last-Modified) match
 947       * a conditional value specified in the Request.
 948       *
 949       * If the Response is not modified, it sets the status code to 304 and
 950       * removes the actual content by calling the setNotModified() method.
 951       *
 952       * @param Request $request A Request instance
 953       *
 954       * @return bool true if the Response validators match the Request, false otherwise
 955       */
 956      public function isNotModified(Request $request)
 957      {
 958          if (!$request->isMethodSafe()) {
 959              return false;
 960          }
 961  
 962          $notModified = false;
 963          $lastModified = $this->headers->get('Last-Modified');
 964          $modifiedSince = $request->headers->get('If-Modified-Since');
 965  
 966          if ($etags = $request->getETags()) {
 967              $notModified = in_array($this->getEtag(), $etags) || in_array('*', $etags);
 968          }
 969  
 970          if ($modifiedSince && $lastModified) {
 971              $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified);
 972          }
 973  
 974          if ($notModified) {
 975              $this->setNotModified();
 976          }
 977  
 978          return $notModified;
 979      }
 980  
 981      /**
 982       * Is response invalid?
 983       *
 984       * @return bool
 985       *
 986       * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
 987       */
 988      public function isInvalid()
 989      {
 990          return $this->statusCode < 100 || $this->statusCode >= 600;
 991      }
 992  
 993      /**
 994       * Is response informative?
 995       *
 996       * @return bool
 997       */
 998      public function isInformational()
 999      {
1000          return $this->statusCode >= 100 && $this->statusCode < 200;
1001      }
1002  
1003      /**
1004       * Is response successful?
1005       *
1006       * @return bool
1007       */
1008      public function isSuccessful()
1009      {
1010          return $this->statusCode >= 200 && $this->statusCode < 300;
1011      }
1012  
1013      /**
1014       * Is the response a redirect?
1015       *
1016       * @return bool
1017       */
1018      public function isRedirection()
1019      {
1020          return $this->statusCode >= 300 && $this->statusCode < 400;
1021      }
1022  
1023      /**
1024       * Is there a client error?
1025       *
1026       * @return bool
1027       */
1028      public function isClientError()
1029      {
1030          return $this->statusCode >= 400 && $this->statusCode < 500;
1031      }
1032  
1033      /**
1034       * Was there a server side error?
1035       *
1036       * @return bool
1037       */
1038      public function isServerError()
1039      {
1040          return $this->statusCode >= 500 && $this->statusCode < 600;
1041      }
1042  
1043      /**
1044       * Is the response OK?
1045       *
1046       * @return bool
1047       */
1048      public function isOk()
1049      {
1050          return 200 === $this->statusCode;
1051      }
1052  
1053      /**
1054       * Is the response forbidden?
1055       *
1056       * @return bool
1057       */
1058      public function isForbidden()
1059      {
1060          return 403 === $this->statusCode;
1061      }
1062  
1063      /**
1064       * Is the response a not found error?
1065       *
1066       * @return bool
1067       */
1068      public function isNotFound()
1069      {
1070          return 404 === $this->statusCode;
1071      }
1072  
1073      /**
1074       * Is the response a redirect of some form?
1075       *
1076       * @param string $location
1077       *
1078       * @return bool
1079       */
1080      public function isRedirect($location = null)
1081      {
1082          return in_array($this->statusCode, array(201, 301, 302, 303, 307, 308)) && (null === $location ?: $location == $this->headers->get('Location'));
1083      }
1084  
1085      /**
1086       * Is the response empty?
1087       *
1088       * @return bool
1089       */
1090      public function isEmpty()
1091      {
1092          return in_array($this->statusCode, array(204, 304));
1093      }
1094  
1095      /**
1096       * Check if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9.
1097       *
1098       * @link http://support.microsoft.com/kb/323308
1099       */
1100      protected function ensureIEOverSSLCompatibility(Request $request)
1101      {
1102          if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) == 1 && true === $request->isSecure()) {
1103              if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) {
1104                  $this->headers->remove('Cache-Control');
1105              }
1106          }
1107      }
1108  }


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1