Overview

Namespaces

  • Composer
    • Autoload
  • Guzzle
    • Common
      • Exception
    • Http
      • Curl
      • Exception
      • Message
        • Header
      • QueryAggregator
    • Parser
      • Cookie
      • Message
      • UriTemplate
      • Url
    • Plugin
      • Mock
    • Stream
  • Mockery
    • Adapter
      • Phpunit
    • CountValidator
    • Exception
    • Generator
      • StringManipulation
        • Pass
    • Loader
    • Matcher
  • None
  • Omnipay
    • Common
      • Exception
      • Message
    • Dummy
      • Message
    • Fatzebra
      • Message
  • PHP
  • Symfony
    • Component
      • EventDispatcher
        • Debug
        • DependencyInjection
        • Tests
          • Debug
          • DependencyInjection
      • HttpFoundation
        • File
          • Exception
          • MimeType
        • Session
          • Attribute
          • Flash
          • Storage
            • Handler
            • Proxy
        • Tests
          • File
            • MimeType
          • Session
            • Attribute
            • Flash
            • Storage
              • Handler
              • Proxy
      • Yaml
        • Exception
        • Tests

Classes

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