Response.php 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpFoundation;
  11. /**
  12. * Response represents an HTTP response.
  13. *
  14. * @author Fabien Potencier <fabien@symfony.com>
  15. *
  16. * @api
  17. */
  18. class Response
  19. {
  20. /**
  21. * @var \Symfony\Component\HttpFoundation\ResponseHeaderBag
  22. */
  23. public $headers;
  24. protected $content;
  25. protected $version;
  26. protected $statusCode;
  27. protected $statusText;
  28. protected $charset;
  29. static public $statusTexts = array(
  30. 100 => 'Continue',
  31. 101 => 'Switching Protocols',
  32. 200 => 'OK',
  33. 201 => 'Created',
  34. 202 => 'Accepted',
  35. 203 => 'Non-Authoritative Information',
  36. 204 => 'No Content',
  37. 205 => 'Reset Content',
  38. 206 => 'Partial Content',
  39. 300 => 'Multiple Choices',
  40. 301 => 'Moved Permanently',
  41. 302 => 'Found',
  42. 303 => 'See Other',
  43. 304 => 'Not Modified',
  44. 305 => 'Use Proxy',
  45. 307 => 'Temporary Redirect',
  46. 400 => 'Bad Request',
  47. 401 => 'Unauthorized',
  48. 402 => 'Payment Required',
  49. 403 => 'Forbidden',
  50. 404 => 'Not Found',
  51. 405 => 'Method Not Allowed',
  52. 406 => 'Not Acceptable',
  53. 407 => 'Proxy Authentication Required',
  54. 408 => 'Request Timeout',
  55. 409 => 'Conflict',
  56. 410 => 'Gone',
  57. 411 => 'Length Required',
  58. 412 => 'Precondition Failed',
  59. 413 => 'Request Entity Too Large',
  60. 414 => 'Request-URI Too Long',
  61. 415 => 'Unsupported Media Type',
  62. 416 => 'Requested Range Not Satisfiable',
  63. 417 => 'Expectation Failed',
  64. 418 => 'I\'m a teapot',
  65. 500 => 'Internal Server Error',
  66. 501 => 'Not Implemented',
  67. 502 => 'Bad Gateway',
  68. 503 => 'Service Unavailable',
  69. 504 => 'Gateway Timeout',
  70. 505 => 'HTTP Version Not Supported',
  71. );
  72. /**
  73. * Constructor.
  74. *
  75. * @param string $content The response content
  76. * @param integer $status The response status code
  77. * @param array $headers An array of response headers
  78. *
  79. * @api
  80. */
  81. public function __construct($content = '', $status = 200, $headers = array())
  82. {
  83. $this->headers = new ResponseHeaderBag($headers);
  84. $this->setContent($content);
  85. $this->setStatusCode($status);
  86. $this->setProtocolVersion('1.0');
  87. if (!$this->headers->has('Date')) {
  88. $this->setDate(new \DateTime(null, new \DateTimeZone('UTC')));
  89. }
  90. }
  91. /**
  92. * Returns the response content as it will be sent (with the headers).
  93. *
  94. * @return string The response content
  95. */
  96. public function __toString()
  97. {
  98. $this->prepare();
  99. return
  100. sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
  101. $this->headers."\r\n".
  102. $this->getContent();
  103. }
  104. /**
  105. * Clones the current Response instance.
  106. */
  107. public function __clone()
  108. {
  109. $this->headers = clone $this->headers;
  110. }
  111. /**
  112. * Prepares the Response before it is sent to the client.
  113. *
  114. * This method tweaks the Response to ensure that it is
  115. * compliant with RFC 2616.
  116. */
  117. public function prepare()
  118. {
  119. if ($this->isInformational() || in_array($this->statusCode, array(204, 304))) {
  120. $this->setContent('');
  121. }
  122. // Fix Content-Type
  123. $charset = $this->charset ?: 'UTF-8';
  124. if (!$this->headers->has('Content-Type')) {
  125. $this->headers->set('Content-Type', 'text/html; charset='.$charset);
  126. } elseif ('text/' === substr($this->headers->get('Content-Type'), 0, 5) && false === strpos($this->headers->get('Content-Type'), 'charset')) {
  127. // add the charset
  128. $this->headers->set('Content-Type', $this->headers->get('Content-Type').'; charset='.$charset);
  129. }
  130. // Fix Content-Length
  131. if ($this->headers->has('Transfer-Encoding')) {
  132. $this->headers->remove('Content-Length');
  133. }
  134. }
  135. /**
  136. * Sends HTTP headers.
  137. */
  138. public function sendHeaders()
  139. {
  140. // headers have already been sent by the developer
  141. if (headers_sent()) {
  142. return;
  143. }
  144. $this->prepare();
  145. // status
  146. header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText));
  147. // headers
  148. foreach ($this->headers->all() as $name => $values) {
  149. foreach ($values as $value) {
  150. header($name.': '.$value, false);
  151. }
  152. }
  153. // cookies
  154. foreach ($this->headers->getCookies() as $cookie) {
  155. setcookie($cookie->getName(), $cookie->getValue(), $cookie->getExpiresTime(), $cookie->getPath(), $cookie->getDomain(), $cookie->isSecure(), $cookie->isHttpOnly());
  156. }
  157. }
  158. /**
  159. * Sends content for the current web response.
  160. */
  161. public function sendContent()
  162. {
  163. echo $this->content;
  164. }
  165. /**
  166. * Sends HTTP headers and content.
  167. *
  168. * @api
  169. */
  170. public function send()
  171. {
  172. $this->sendHeaders();
  173. $this->sendContent();
  174. if (function_exists('fastcgi_finish_request')) {
  175. fastcgi_finish_request();
  176. }
  177. }
  178. /**
  179. * Sets the response content
  180. *
  181. * Valid types are strings, numbers, and objects that implement a __toString() method.
  182. *
  183. * @param mixed $content
  184. *
  185. * @api
  186. */
  187. public function setContent($content)
  188. {
  189. if (null !== $content && !is_string($content) && !is_numeric($content) && !is_callable(array($content, '__toString'))) {
  190. throw new \UnexpectedValueException('The Response content must be a string or object implementing __toString(), "'.gettype($content).'" given.');
  191. }
  192. $this->content = (string) $content;
  193. }
  194. /**
  195. * Gets the current response content
  196. *
  197. * @return string Content
  198. *
  199. * @api
  200. */
  201. public function getContent()
  202. {
  203. return $this->content;
  204. }
  205. /**
  206. * Sets the HTTP protocol version (1.0 or 1.1).
  207. *
  208. * @param string $version The HTTP protocol version
  209. *
  210. * @api
  211. */
  212. public function setProtocolVersion($version)
  213. {
  214. $this->version = $version;
  215. }
  216. /**
  217. * Gets the HTTP protocol version.
  218. *
  219. * @return string The HTTP protocol version
  220. *
  221. * @api
  222. */
  223. public function getProtocolVersion()
  224. {
  225. return $this->version;
  226. }
  227. /**
  228. * Sets response status code.
  229. *
  230. * @param integer $code HTTP status code
  231. * @param string $text HTTP status text
  232. *
  233. * @throws \InvalidArgumentException When the HTTP status code is not valid
  234. *
  235. * @api
  236. */
  237. public function setStatusCode($code, $text = null)
  238. {
  239. $this->statusCode = (int) $code;
  240. if ($this->isInvalid()) {
  241. throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
  242. }
  243. $this->statusText = false === $text ? '' : (null === $text ? self::$statusTexts[$this->statusCode] : $text);
  244. }
  245. /**
  246. * Retrieves status code for the current web response.
  247. *
  248. * @return string Status code
  249. *
  250. * @api
  251. */
  252. public function getStatusCode()
  253. {
  254. return $this->statusCode;
  255. }
  256. /**
  257. * Sets response charset.
  258. *
  259. * @param string $charset Character set
  260. *
  261. * @api
  262. */
  263. public function setCharset($charset)
  264. {
  265. $this->charset = $charset;
  266. }
  267. /**
  268. * Retrieves the response charset.
  269. *
  270. * @return string Character set
  271. *
  272. * @api
  273. */
  274. public function getCharset()
  275. {
  276. return $this->charset;
  277. }
  278. /**
  279. * Returns true if the response is worth caching under any circumstance.
  280. *
  281. * Responses marked "private" with an explicit Cache-Control directive are
  282. * considered uncacheable.
  283. *
  284. * Responses with neither a freshness lifetime (Expires, max-age) nor cache
  285. * validator (Last-Modified, ETag) are considered uncacheable.
  286. *
  287. * @return Boolean true if the response is worth caching, false otherwise
  288. *
  289. * @api
  290. */
  291. public function isCacheable()
  292. {
  293. if (!in_array($this->statusCode, array(200, 203, 300, 301, 302, 404, 410))) {
  294. return false;
  295. }
  296. if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) {
  297. return false;
  298. }
  299. return $this->isValidateable() || $this->isFresh();
  300. }
  301. /**
  302. * Returns true if the response is "fresh".
  303. *
  304. * Fresh responses may be served from cache without any interaction with the
  305. * origin. A response is considered fresh when it includes a Cache-Control/max-age
  306. * indicator or Expiration header and the calculated age is less than the freshness lifetime.
  307. *
  308. * @return Boolean true if the response is fresh, false otherwise
  309. *
  310. * @api
  311. */
  312. public function isFresh()
  313. {
  314. return $this->getTtl() > 0;
  315. }
  316. /**
  317. * Returns true if the response includes headers that can be used to validate
  318. * the response with the origin server using a conditional GET request.
  319. *
  320. * @return Boolean true if the response is validateable, false otherwise
  321. *
  322. * @api
  323. */
  324. public function isValidateable()
  325. {
  326. return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
  327. }
  328. /**
  329. * Marks the response as "private".
  330. *
  331. * It makes the response ineligible for serving other clients.
  332. *
  333. * @api
  334. */
  335. public function setPrivate()
  336. {
  337. $this->headers->removeCacheControlDirective('public');
  338. $this->headers->addCacheControlDirective('private');
  339. }
  340. /**
  341. * Marks the response as "public".
  342. *
  343. * It makes the response eligible for serving other clients.
  344. *
  345. * @api
  346. */
  347. public function setPublic()
  348. {
  349. $this->headers->addCacheControlDirective('public');
  350. $this->headers->removeCacheControlDirective('private');
  351. }
  352. /**
  353. * Returns true if the response must be revalidated by caches.
  354. *
  355. * This method indicates that the response must not be served stale by a
  356. * cache in any circumstance without first revalidating with the origin.
  357. * When present, the TTL of the response should not be overridden to be
  358. * greater than the value provided by the origin.
  359. *
  360. * @return Boolean true if the response must be revalidated by a cache, false otherwise
  361. *
  362. * @api
  363. */
  364. public function mustRevalidate()
  365. {
  366. return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->has('must-proxy-revalidate');
  367. }
  368. /**
  369. * Returns the Date header as a DateTime instance.
  370. *
  371. * @return \DateTime A \DateTime instance
  372. *
  373. * @throws \RuntimeException when the header is not parseable
  374. *
  375. * @api
  376. */
  377. public function getDate()
  378. {
  379. return $this->headers->getDate('Date');
  380. }
  381. /**
  382. * Sets the Date header.
  383. *
  384. * @param \DateTime $date A \DateTime instance
  385. *
  386. * @api
  387. */
  388. public function setDate(\DateTime $date)
  389. {
  390. $date->setTimezone(new \DateTimeZone('UTC'));
  391. $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
  392. }
  393. /**
  394. * Returns the age of the response.
  395. *
  396. * @return integer The age of the response in seconds
  397. */
  398. public function getAge()
  399. {
  400. if ($age = $this->headers->get('Age')) {
  401. return $age;
  402. }
  403. return max(time() - $this->getDate()->format('U'), 0);
  404. }
  405. /**
  406. * Marks the response stale by setting the Age header to be equal to the maximum age of the response.
  407. *
  408. * @api
  409. */
  410. public function expire()
  411. {
  412. if ($this->isFresh()) {
  413. $this->headers->set('Age', $this->getMaxAge());
  414. }
  415. }
  416. /**
  417. * Returns the value of the Expires header as a DateTime instance.
  418. *
  419. * @return \DateTime A DateTime instance
  420. *
  421. * @api
  422. */
  423. public function getExpires()
  424. {
  425. return $this->headers->getDate('Expires');
  426. }
  427. /**
  428. * Sets the Expires HTTP header with a \DateTime instance.
  429. *
  430. * If passed a null value, it removes the header.
  431. *
  432. * @param \DateTime $date A \DateTime instance
  433. *
  434. * @api
  435. */
  436. public function setExpires(\DateTime $date = null)
  437. {
  438. if (null === $date) {
  439. $this->headers->remove('Expires');
  440. } else {
  441. $date = clone $date;
  442. $date->setTimezone(new \DateTimeZone('UTC'));
  443. $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
  444. }
  445. }
  446. /**
  447. * Sets the number of seconds after the time specified in the response's Date
  448. * header when the the response should no longer be considered fresh.
  449. *
  450. * First, it checks for a s-maxage directive, then a max-age directive, and then it falls
  451. * back on an expires header. It returns null when no maximum age can be established.
  452. *
  453. * @return integer|null Number of seconds
  454. *
  455. * @api
  456. */
  457. public function getMaxAge()
  458. {
  459. if ($age = $this->headers->getCacheControlDirective('s-maxage')) {
  460. return $age;
  461. }
  462. if ($age = $this->headers->getCacheControlDirective('max-age')) {
  463. return $age;
  464. }
  465. if (null !== $this->getExpires()) {
  466. return $this->getExpires()->format('U') - $this->getDate()->format('U');
  467. }
  468. return null;
  469. }
  470. /**
  471. * Sets the number of seconds after which the response should no longer be considered fresh.
  472. *
  473. * This methods sets the Cache-Control max-age directive.
  474. *
  475. * @param integer $value A number of seconds
  476. *
  477. * @api
  478. */
  479. public function setMaxAge($value)
  480. {
  481. $this->headers->addCacheControlDirective('max-age', $value);
  482. }
  483. /**
  484. * Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
  485. *
  486. * This methods sets the Cache-Control s-maxage directive.
  487. *
  488. * @param integer $value A number of seconds
  489. *
  490. * @api
  491. */
  492. public function setSharedMaxAge($value)
  493. {
  494. $this->setPublic();
  495. $this->headers->addCacheControlDirective('s-maxage', $value);
  496. }
  497. /**
  498. * Returns the response's time-to-live in seconds.
  499. *
  500. * It returns null when no freshness information is present in the response.
  501. *
  502. * When the responses TTL is <= 0, the response may not be served from cache without first
  503. * revalidating with the origin.
  504. *
  505. * @return integer The TTL in seconds
  506. *
  507. * @api
  508. */
  509. public function getTtl()
  510. {
  511. if ($maxAge = $this->getMaxAge()) {
  512. return $maxAge - $this->getAge();
  513. }
  514. return null;
  515. }
  516. /**
  517. * Sets the response's time-to-live for shared caches.
  518. *
  519. * This method adjusts the Cache-Control/s-maxage directive.
  520. *
  521. * @param integer $seconds The number of seconds
  522. *
  523. * @api
  524. */
  525. public function setTtl($seconds)
  526. {
  527. $this->setSharedMaxAge($this->getAge() + $seconds);
  528. }
  529. /**
  530. * Sets the response's time-to-live for private/client caches.
  531. *
  532. * This method adjusts the Cache-Control/max-age directive.
  533. *
  534. * @param integer $seconds The number of seconds
  535. *
  536. * @api
  537. */
  538. public function setClientTtl($seconds)
  539. {
  540. $this->setMaxAge($this->getAge() + $seconds);
  541. }
  542. /**
  543. * Returns the Last-Modified HTTP header as a DateTime instance.
  544. *
  545. * @return \DateTime A DateTime instance
  546. *
  547. * @api
  548. */
  549. public function getLastModified()
  550. {
  551. return $this->headers->getDate('Last-Modified');
  552. }
  553. /**
  554. * Sets the Last-Modified HTTP header with a \DateTime instance.
  555. *
  556. * If passed a null value, it removes the header.
  557. *
  558. * @param \DateTime $date A \DateTime instance
  559. *
  560. * @api
  561. */
  562. public function setLastModified(\DateTime $date = null)
  563. {
  564. if (null === $date) {
  565. $this->headers->remove('Last-Modified');
  566. } else {
  567. $date = clone $date;
  568. $date->setTimezone(new \DateTimeZone('UTC'));
  569. $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
  570. }
  571. }
  572. /**
  573. * Returns the literal value of ETag HTTP header.
  574. *
  575. * @return string The ETag HTTP header
  576. *
  577. * @api
  578. */
  579. public function getEtag()
  580. {
  581. return $this->headers->get('ETag');
  582. }
  583. /**
  584. * Sets the ETag value.
  585. *
  586. * @param string $etag The ETag unique identifier
  587. * @param Boolean $weak Whether you want a weak ETag or not
  588. *
  589. * @api
  590. */
  591. public function setEtag($etag = null, $weak = false)
  592. {
  593. if (null === $etag) {
  594. $this->headers->remove('Etag');
  595. } else {
  596. if (0 !== strpos($etag, '"')) {
  597. $etag = '"'.$etag.'"';
  598. }
  599. $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
  600. }
  601. }
  602. /**
  603. * Sets Response cache headers (validation and/or expiration).
  604. *
  605. * Available options are: etag, last_modified, max_age, s_maxage, private, and public.
  606. *
  607. * @param array $options An array of cache options
  608. *
  609. * @api
  610. */
  611. public function setCache(array $options)
  612. {
  613. if ($diff = array_diff(array_keys($options), array('etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public'))) {
  614. throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', array_keys($diff))));
  615. }
  616. if (isset($options['etag'])) {
  617. $this->setEtag($options['etag']);
  618. }
  619. if (isset($options['last_modified'])) {
  620. $this->setLastModified($options['last_modified']);
  621. }
  622. if (isset($options['max_age'])) {
  623. $this->setMaxAge($options['max_age']);
  624. }
  625. if (isset($options['s_maxage'])) {
  626. $this->setSharedMaxAge($options['s_maxage']);
  627. }
  628. if (isset($options['public'])) {
  629. if ($options['public']) {
  630. $this->setPublic();
  631. } else {
  632. $this->setPrivate();
  633. }
  634. }
  635. if (isset($options['private'])) {
  636. if ($options['private']) {
  637. $this->setPrivate();
  638. } else {
  639. $this->setPublic();
  640. }
  641. }
  642. }
  643. /**
  644. * Modifies the response so that it conforms to the rules defined for a 304 status code.
  645. *
  646. * This sets the status, removes the body, and discards any headers
  647. * that MUST NOT be included in 304 responses.
  648. *
  649. * @see http://tools.ietf.org/html/rfc2616#section-10.3.5
  650. *
  651. * @api
  652. */
  653. public function setNotModified()
  654. {
  655. $this->setStatusCode(304);
  656. $this->setContent(null);
  657. // remove headers that MUST NOT be included with 304 Not Modified responses
  658. foreach (array('Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified') as $header) {
  659. $this->headers->remove($header);
  660. }
  661. }
  662. /**
  663. * Returns true if the response includes a Vary header.
  664. *
  665. * @return true if the response includes a Vary header, false otherwise
  666. *
  667. * @api
  668. */
  669. public function hasVary()
  670. {
  671. return (Boolean) $this->headers->get('Vary');
  672. }
  673. /**
  674. * Returns an array of header names given in the Vary header.
  675. *
  676. * @return array An array of Vary names
  677. *
  678. * @api
  679. */
  680. public function getVary()
  681. {
  682. if (!$vary = $this->headers->get('Vary')) {
  683. return array();
  684. }
  685. return is_array($vary) ? $vary : preg_split('/[\s,]+/', $vary);
  686. }
  687. /**
  688. * Sets the Vary header.
  689. *
  690. * @param string|array $headers
  691. * @param Boolean $replace Whether to replace the actual value of not (true by default)
  692. *
  693. * @api
  694. */
  695. public function setVary($headers, $replace = true)
  696. {
  697. $this->headers->set('Vary', $headers, $replace);
  698. }
  699. /**
  700. * Determines if the Response validators (ETag, Last-Modified) matches
  701. * a conditional value specified in the Request.
  702. *
  703. * If the Response is not modified, it sets the status code to 304 and
  704. * remove the actual content by calling the setNotModified() method.
  705. *
  706. * @param Request $request A Request instance
  707. *
  708. * @return Boolean true if the Response validators matches the Request, false otherwise
  709. *
  710. * @api
  711. */
  712. public function isNotModified(Request $request)
  713. {
  714. $lastModified = $request->headers->get('If-Modified-Since');
  715. $notModified = false;
  716. if ($etags = $request->getEtags()) {
  717. $notModified = (in_array($this->getEtag(), $etags) || in_array('*', $etags)) && (!$lastModified || $this->headers->get('Last-Modified') == $lastModified);
  718. } elseif ($lastModified) {
  719. $notModified = $lastModified == $this->headers->get('Last-Modified');
  720. }
  721. if ($notModified) {
  722. $this->setNotModified();
  723. }
  724. return $notModified;
  725. }
  726. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
  727. /**
  728. * @api
  729. */
  730. public function isInvalid()
  731. {
  732. return $this->statusCode < 100 || $this->statusCode >= 600;
  733. }
  734. /**
  735. * @api
  736. */
  737. public function isInformational()
  738. {
  739. return $this->statusCode >= 100 && $this->statusCode < 200;
  740. }
  741. /**
  742. * @api
  743. */
  744. public function isSuccessful()
  745. {
  746. return $this->statusCode >= 200 && $this->statusCode < 300;
  747. }
  748. /**
  749. * @api
  750. */
  751. public function isRedirection()
  752. {
  753. return $this->statusCode >= 300 && $this->statusCode < 400;
  754. }
  755. /**
  756. * @api
  757. */
  758. public function isClientError()
  759. {
  760. return $this->statusCode >= 400 && $this->statusCode < 500;
  761. }
  762. /**
  763. * @api
  764. */
  765. public function isServerError()
  766. {
  767. return $this->statusCode >= 500 && $this->statusCode < 600;
  768. }
  769. /**
  770. * @api
  771. */
  772. public function isOk()
  773. {
  774. return 200 === $this->statusCode;
  775. }
  776. /**
  777. * @api
  778. */
  779. public function isForbidden()
  780. {
  781. return 403 === $this->statusCode;
  782. }
  783. /**
  784. * @api
  785. */
  786. public function isNotFound()
  787. {
  788. return 404 === $this->statusCode;
  789. }
  790. /**
  791. * @api
  792. */
  793. public function isRedirect($location = null)
  794. {
  795. return in_array($this->statusCode, array(201, 301, 302, 303, 307)) && (null === $location ?: $location == $this->headers->get('Location'));
  796. }
  797. /**
  798. * @api
  799. */
  800. public function isEmpty()
  801. {
  802. return in_array($this->statusCode, array(201, 204, 304));
  803. }
  804. }