Overview

Namespaces

  • Contrib
    • Bundle
      • CoverallsBundle
        • Console
        • Entity
      • CoverallsV1Bundle
        • Api
        • Collector
        • Command
        • Config
        • Entity
          • Git
    • Component
      • File
      • Log
      • System
        • Git
  • Guzzle
    • Batch
      • Exception
    • Cache
    • Common
      • Exception
    • Http
      • Curl
      • Exception
      • Message
      • QueryAggregator
    • Inflection
    • Iterator
    • Log
    • Parser
      • Cookie
      • Message
      • UriTemplate
      • Url
    • Plugin
      • Async
      • Backoff
      • Cache
      • Cookie
        • CookieJar
        • Exception
      • CurlAuth
      • ErrorResponse
        • Exception
      • History
      • Log
      • Md5
      • Mock
      • Oauth
    • Service
      • Builder
      • Command
        • Factory
        • LocationVisitor
          • Request
          • Response
      • Description
      • Exception
      • Resource
    • Stream
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Config
        • Definition
          • Builder
          • Exception
        • Exception
        • Loader
        • Resource
        • Util
      • Console
        • Command
        • Formatter
        • Helper
        • Input
        • Output
        • Tester
      • EventDispatcher
        • Debug
      • Finder
        • Adapter
        • Comparator
        • Exception
        • Expression
        • Iterator
        • Shell
      • Stopwatch
      • Yaml
        • Exception

Classes

  • AbstractMessage
  • EntityEnclosingRequest
  • Header
  • HeaderComparison
  • PostFile
  • Request
  • RequestFactory
  • Response

Interfaces

  • EntityEnclosingRequestInterface
  • MessageInterface
  • PostFileInterface
  • RequestFactoryInterface
  • RequestInterface
  • Overview
  • Namespace
  • Class
  • Tree
  • Todo
  1: <?php
  2: 
  3: namespace Guzzle\Http\Message;
  4: 
  5: use Guzzle\Common\Collection;
  6: use Guzzle\Common\Exception\RuntimeException;
  7: use Guzzle\Http\EntityBodyInterface;
  8: use Guzzle\Http\EntityBody;
  9: use Guzzle\Http\Exception\BadResponseException;
 10: use Guzzle\Parser\ParserRegistry;
 11: 
 12: /**
 13:  * Guzzle HTTP response object
 14:  */
 15: class Response extends AbstractMessage
 16: {
 17:     /**
 18:      * @var array Array of reason phrases and their corresponding status codes
 19:      */
 20:     private static $statusTexts = array(
 21:         100 => 'Continue',
 22:         101 => 'Switching Protocols',
 23:         102 => 'Processing',
 24:         200 => 'OK',
 25:         201 => 'Created',
 26:         202 => 'Accepted',
 27:         203 => 'Non-Authoritative Information',
 28:         204 => 'No Content',
 29:         205 => 'Reset Content',
 30:         206 => 'Partial Content',
 31:         207 => 'Multi-Status',
 32:         208 => 'Already Reported',
 33:         226 => 'IM Used',
 34:         300 => 'Multiple Choices',
 35:         301 => 'Moved Permanently',
 36:         302 => 'Found',
 37:         303 => 'See Other',
 38:         304 => 'Not Modified',
 39:         305 => 'Use Proxy',
 40:         307 => 'Temporary Redirect',
 41:         308 => 'Permanent Redirect',
 42:         400 => 'Bad Request',
 43:         401 => 'Unauthorized',
 44:         402 => 'Payment Required',
 45:         403 => 'Forbidden',
 46:         404 => 'Not Found',
 47:         405 => 'Method Not Allowed',
 48:         406 => 'Not Acceptable',
 49:         407 => 'Proxy Authentication Required',
 50:         408 => 'Request Timeout',
 51:         409 => 'Conflict',
 52:         410 => 'Gone',
 53:         411 => 'Length Required',
 54:         412 => 'Precondition Failed',
 55:         413 => 'Request Entity Too Large',
 56:         414 => 'Request-URI Too Long',
 57:         415 => 'Unsupported Media Type',
 58:         416 => 'Requested Range Not Satisfiable',
 59:         417 => 'Expectation Failed',
 60:         422 => 'Unprocessable Entity',
 61:         423 => 'Locked',
 62:         424 => 'Failed Dependency',
 63:         425 => 'Reserved for WebDAV advanced collections expired proposal',
 64:         426 => 'Upgrade required',
 65:         428 => 'Precondition Required',
 66:         429 => 'Too Many Requests',
 67:         431 => 'Request Header Fields Too Large',
 68:         500 => 'Internal Server Error',
 69:         501 => 'Not Implemented',
 70:         502 => 'Bad Gateway',
 71:         503 => 'Service Unavailable',
 72:         504 => 'Gateway Timeout',
 73:         505 => 'HTTP Version Not Supported',
 74:         506 => 'Variant Also Negotiates (Experimental)',
 75:         507 => 'Insufficient Storage',
 76:         508 => 'Loop Detected',
 77:         510 => 'Not Extended',
 78:         511 => 'Network Authentication Required',
 79:     );
 80: 
 81:     /**
 82:      * @var EntityBodyInterface The response body
 83:      */
 84:     protected $body;
 85: 
 86:     /**
 87:      * @var string The reason phrase of the response (human readable code)
 88:      */
 89:     protected $reasonPhrase;
 90: 
 91:     /**
 92:      * @var string The status code of the response
 93:      */
 94:     protected $statusCode;
 95: 
 96:     /**
 97:      * @var string Response protocol
 98:      */
 99:     protected $protocol = 'HTTP';
100: 
101:     /**
102:      * @var array Information about the request
103:      */
104:     protected $info = array();
105: 
106:     /**
107:      * @var RequestInterface Request object that may or may not be set
108:      */
109:     protected $request = null;
110: 
111:     /**
112:      * @var array Cacheable response codes (see RFC 2616:13.4)
113:      */
114:     protected $cacheResponseCodes = array(200, 203, 206, 300, 301, 410);
115: 
116:     /**
117:      * @var Response If a redirect was issued or an intermediate response was issued
118:      */
119:     protected $previous;
120: 
121:     /**
122:      * Create a new Response based on a raw response message
123:      *
124:      * @param string $message Response message
125:      *
126:      * @return Response|bool Returns false on error
127:      */
128:     public static function fromMessage($message)
129:     {
130:         $data = ParserRegistry::getInstance()->getParser('message')->parseResponse($message);
131:         if (!$data) {
132:             return false;
133:         }
134: 
135:         $response = new static($data['code'], $data['headers'], $data['body']);
136:         $response->setProtocol($data['protocol'], $data['version'])
137:                  ->setStatus($data['code'], $data['reason_phrase']);
138: 
139:         // Set the appropriate Content-Length if the one set is inaccurate (e.g. setting to X)
140:         $contentLength = (string) $response->getHeader('Content-Length');
141:         $actualLength = strlen($data['body']);
142:         if (strlen($data['body']) > 0 && $contentLength != $actualLength) {
143:             $response->setHeader('Content-Length', $actualLength);
144:         }
145: 
146:         return $response;
147:     }
148: 
149:     /**
150:      * Construct the response
151:      *
152:      * @param string                              $statusCode The response status code (e.g. 200, 404, etc)
153:      * @param Collection|array                    $headers    The response headers
154:      * @param string|resource|EntityBodyInterface $body       The body of the response
155:      *
156:      * @throws BadResponseException if an invalid response code is given
157:      */
158:     public function __construct($statusCode, $headers = null, $body = null)
159:     {
160:         $this->setStatus($statusCode);
161:         $this->params = new Collection();
162:         $this->body = EntityBody::factory($body !== null ? $body : '');
163: 
164:         if ($headers) {
165:             if (!is_array($headers) && !($headers instanceof Collection)) {
166:                 throw new BadResponseException('Invalid headers argument received');
167:             }
168:             foreach ($headers as $key => $value) {
169:                 $this->addHeaders(array($key => $value));
170:             }
171:         }
172:     }
173: 
174:     /**
175:      * Convert the response object to a string
176:      *
177:      * @return string
178:      */
179:     public function __toString()
180:     {
181:         return $this->getMessage();
182:     }
183: 
184:     /**
185:      * Get the response entity body
186:      *
187:      * @param bool $asString Set to TRUE to return a string of the body rather than a full body object
188:      *
189:      * @return EntityBodyInterface|string
190:      */
191:     public function getBody($asString = false)
192:     {
193:         return $asString ? (string) $this->body : $this->body;
194:     }
195: 
196:     /**
197:      * Set the response entity body
198:      *
199:      * @param EntityBodyInterface|string $body Body to set
200:      *
201:      * @return self
202:      */
203:     public function setBody($body)
204:     {
205:         $this->body = EntityBody::factory($body);
206: 
207:         return $this;
208:     }
209: 
210:     /**
211:      * Set the protocol and protocol version of the response
212:      *
213:      * @param string $protocol Response protocol
214:      * @param string $version  Protocol version
215:      *
216:      * @return Response
217:      */
218:     public function setProtocol($protocol, $version)
219:     {
220:         $this->protocol = $protocol;
221:         $this->protocolVersion = $version;
222: 
223:         return $this;
224:     }
225: 
226:     /**
227:      * Get the protocol used for the response (e.g. HTTP)
228:      *
229:      * @return string
230:      */
231:     public function getProtocol()
232:     {
233:         return $this->protocol ?: 'HTTP';
234:     }
235: 
236:     /**
237:      * Get the HTTP protocol version
238:      *
239:      * @return string
240:      */
241:     public function getProtocolVersion()
242:     {
243:         return $this->protocolVersion ?: '1.1';
244:     }
245: 
246:     /**
247:      * Get a cURL transfer information
248:      *
249:      * @param string $key A single statistic to check
250:      *
251:      * @return array|string|null Returns all stats if no key is set, a single stat if a key is set, or null if a key
252:      *                           is set and not found
253:      * @link http://www.php.net/manual/en/function.curl-getinfo.php
254:      */
255:     public function getInfo($key = null)
256:     {
257:         if ($key === null) {
258:             return $this->info;
259:         } elseif (array_key_exists($key, $this->info)) {
260:             return $this->info[$key];
261:         } else {
262:             return null;
263:         }
264:     }
265: 
266:     /**
267:      * Set the transfer information
268:      *
269:      * @param array $info Array of cURL transfer stats
270:      *
271:      * @return Response
272:      */
273:     public function setInfo(array $info)
274:     {
275:         $this->info = $info;
276: 
277:         return $this;
278:     }
279: 
280:     /**
281:      * Set the response status
282:      *
283:      * @param int    $statusCode   Response status code to set
284:      * @param string $reasonPhrase Response reason phrase
285:      *
286:      * @return Response
287:      * @throws BadResponseException when an invalid response code is received
288:      */
289:     public function setStatus($statusCode, $reasonPhrase = '')
290:     {
291:         $this->statusCode = (int) $statusCode;
292: 
293:         if (!$reasonPhrase && array_key_exists($this->statusCode, self::$statusTexts)) {
294:             $this->reasonPhrase = self::$statusTexts[$this->statusCode];
295:         } else {
296:             $this->reasonPhrase = $reasonPhrase;
297:         }
298: 
299:         return $this;
300:     }
301: 
302:     /**
303:      * Get the response status code
304:      *
305:      * @return integer
306:      */
307:     public function getStatusCode()
308:     {
309:         return $this->statusCode;
310:     }
311: 
312:     /**
313:      * Get the entire response as a string
314:      *
315:      * @return string
316:      */
317:     public function getMessage()
318:     {
319:         $message = $this->getRawHeaders();
320: 
321:         // Only include the body in the message if the size is < 2MB
322:         $size = $this->body->getSize();
323:         if ($size < 2097152) {
324:             $message .= (string) $this->body;
325:         }
326: 
327:         return $message;
328:     }
329: 
330:     /**
331:      * Get the the raw message headers as a string
332:      *
333:      * @return string
334:      */
335:     public function getRawHeaders()
336:     {
337:         $headers = 'HTTP/1.1 ' . $this->statusCode . ' ' . $this->reasonPhrase . "\r\n";
338:         $lines = $this->getHeaderLines();
339:         if (!empty($lines)) {
340:             $headers .= implode("\r\n", $lines) . "\r\n";
341:         }
342: 
343:         return $headers . "\r\n";
344:     }
345: 
346:     /**
347:      * Get the request object (or null) that is associated with this response
348:      *
349:      * @return RequestInterface
350:      */
351:     public function getRequest()
352:     {
353:         return $this->request;
354:     }
355: 
356:     /**
357:      * Get the response reason phrase- a human readable version of the numeric
358:      * status code
359:      *
360:      * @return string
361:      */
362:     public function getReasonPhrase()
363:     {
364:         return $this->reasonPhrase;
365:     }
366: 
367:     /**
368:      * Get the Accept-Ranges HTTP header
369:      *
370:      * @return string Returns what partial content range types this server supports.
371:      */
372:     public function getAcceptRanges()
373:     {
374:         return $this->getHeader('Accept-Ranges', true);
375:     }
376: 
377:     /**
378:      * Get the Age HTTP header
379:      *
380:      * @param bool $headerOnly Set to TRUE to only retrieve the Age header rather than calculating the age
381:      *
382:      * @return integer|null Returns the age the object has been in a proxy cache in seconds.
383:      */
384:     public function getAge($headerOnly = false)
385:     {
386:         $age = $this->getHeader('Age', true);
387: 
388:         if (!$headerOnly && $age === null && $this->getDate()) {
389:             $age = time() - strtotime($this->getDate());
390:         }
391: 
392:         return $age;
393:     }
394: 
395:     /**
396:      * Get the Allow HTTP header
397:      *
398:      * @return string|null Returns valid actions for a specified resource. To be used for a 405 Method not allowed.
399:      */
400:     public function getAllow()
401:     {
402:         return $this->getHeader('Allow', true);
403:     }
404: 
405:     /**
406:      * Check if an HTTP method is allowed by checking the Allow response header
407:      *
408:      * @param string $method Method to check
409:      *
410:      * @return bool
411:      */
412:     public function isMethodAllowed($method)
413:     {
414:         $allow = $this->getHeader('Allow');
415:         if ($allow) {
416:             foreach (explode(',', $allow) as $allowable) {
417:                 if (!strcasecmp(trim($allowable), $method)) {
418:                     return true;
419:                 }
420:             }
421:         }
422: 
423:         return false;
424:     }
425: 
426:     /**
427:      * Get the Cache-Control HTTP header
428:      *
429:      * @return Header|null Returns a Header object that tells all caching mechanisms from server to client whether they
430:      *                     may cache this object.
431:      */
432:     public function getCacheControl()
433:     {
434:         return $this->getHeader('Cache-Control');
435:     }
436: 
437:     /**
438:      * Get the Connection HTTP header
439:      *
440:      * @return string
441:      */
442:     public function getConnection()
443:     {
444:         return $this->getHeader('Connection', true);
445:     }
446: 
447:     /**
448:      * Get the Content-Encoding HTTP header
449:      *
450:      * @return string|null Returns the type of encoding used on the data. One of compress, deflate, gzip, identity.
451:      */
452:     public function getContentEncoding()
453:     {
454:         return $this->getHeader('Content-Encoding', true);
455:     }
456: 
457:     /**
458:      * Get the Content-Language HTTP header
459:      *
460:      * @return string|null Returns the language the content is in.
461:      */
462:     public function getContentLanguage()
463:     {
464:         return $this->getHeader('Content-Language', true);
465:     }
466: 
467:     /**
468:      * Get the Content-Length HTTP header
469:      *
470:      * @return integer Returns the length of the response body in bytes
471:      */
472:     public function getContentLength()
473:     {
474:         return (int) $this->getHeader('Content-Length', true);
475:     }
476: 
477:     /**
478:      * Get the Content-Location HTTP header
479:      *
480:      * @return string|null Returns an alternate location for the returned data (e.g /index.htm)
481:      */
482:     public function getContentLocation()
483:     {
484:         return $this->getHeader('Content-Location', true);
485:     }
486: 
487:     /**
488:      * Get the Content-Disposition HTTP header
489:      *
490:      * @return string|null Returns the Content-Disposition header
491:      */
492:     public function getContentDisposition()
493:     {
494:         return (string) $this->getHeader('Content-Disposition')->setGlue(';');
495:     }
496: 
497:     /**
498:      * Get the Content-MD5 HTTP header
499:      *
500:      * @return string|null Returns a Base64-encoded binary MD5 sum of the content of the response.
501:      */
502:     public function getContentMd5()
503:     {
504:         return $this->getHeader('Content-MD5', true);
505:     }
506: 
507:     /**
508:      * Get the Content-Range HTTP header
509:      *
510:      * @return string Returns where in a full body message this partial message belongs (e.g. bytes 21010-47021/47022).
511:      */
512:     public function getContentRange()
513:     {
514:         return $this->getHeader('Content-Range', true);
515:     }
516: 
517:     /**
518:      * Get the Content-Type HTTP header
519:      *
520:      * @return string Returns the mime type of this content.
521:      */
522:     public function getContentType()
523:     {
524:         return $this->getHeader('Content-Type', true);
525:     }
526: 
527:     /**
528:      * Checks if the Content-Type is of a certain type.  This is useful if the
529:      * Content-Type header contains charset information and you need to know if
530:      * the Content-Type matches a particular type.
531:      *
532:      * @param string $type Content type to check against
533:      *
534:      * @return bool
535:      */
536:     public function isContentType($type)
537:     {
538:         return stripos($this->getContentType(), $type) !== false;
539:     }
540: 
541:     /**
542:      * Get the Date HTTP header
543:      *
544:      * @return string|null Returns the date and time that the message was sent.
545:      */
546:     public function getDate()
547:     {
548:         return $this->getHeader('Date', true);
549:     }
550: 
551:     /**
552:      * Get the ETag HTTP header
553:      *
554:      * @return string|null Returns an identifier for a specific version of a resource, often a Message digest.
555:      */
556:     public function getEtag()
557:     {
558:         return $this->getHeader('ETag', true);
559:     }
560: 
561:     /**
562:      * Get the Expires HTTP header
563:      *
564:      * @return string|null Returns the date/time after which the response is considered stale.
565:      */
566:     public function getExpires()
567:     {
568:         return $this->getHeader('Expires', true);
569:     }
570: 
571:     /**
572:      * Get the Last-Modified HTTP header
573:      *
574:      * @return string|null Returns the last modified date for the requested object, in RFC 2822 format
575:      *                     (e.g. Tue, 15 Nov 1994 12:45:26 GMT)
576:      */
577:     public function getLastModified()
578:     {
579:         return $this->getHeader('Last-Modified', true);
580:     }
581: 
582:     /**
583:      * Get the Location HTTP header
584:      *
585:      * @return string|null Used in redirection, or when a new resource has been created.
586:      */
587:     public function getLocation()
588:     {
589:         return $this->getHeader('Location', true);
590:     }
591: 
592:     /**
593:      * Get the Pragma HTTP header
594:      *
595:      * @return Header|null Returns the implementation-specific headers that may have various effects anywhere along
596:      *                     the request-response chain.
597:      */
598:     public function getPragma()
599:     {
600:         return $this->getHeader('Pragma');
601:     }
602: 
603:     /**
604:      * Get the Proxy-Authenticate HTTP header
605:      *
606:      * @return string|null Authentication to access the proxy (e.g. Basic)
607:      */
608:     public function getProxyAuthenticate()
609:     {
610:         return $this->getHeader('Proxy-Authenticate', true);
611:     }
612: 
613:     /**
614:      * Get the Retry-After HTTP header
615:      *
616:      * @return int|null If an entity is temporarily unavailable, this instructs the client to try again after a
617:      *                  specified period of time.
618:      */
619:     public function getRetryAfter()
620:     {
621:         $time = $this->getHeader('Retry-After', true);
622:         if ($time === null) {
623:             return null;
624:         }
625: 
626:         if (!is_numeric($time)) {
627:             $time = strtotime($time) - time();
628:         }
629: 
630:         return (int) $time;
631:     }
632: 
633:     /**
634:      * Get the Server HTTP header
635:      *
636:      * @return string|null A name for the server
637:      */
638:     public function getServer()
639:     {
640:         return $this->getHeader('Server', true);
641:     }
642: 
643:     /**
644:      * Get the Set-Cookie HTTP header
645:      *
646:      * @return Header|null An HTTP cookie.
647:      */
648:     public function getSetCookie()
649:     {
650:         return $this->getHeader('Set-Cookie');
651:     }
652: 
653:     /**
654:      * Get the Trailer HTTP header
655:      *
656:      * @return string|null The Trailer general field value indicates that the given set of header fields is present in
657:      *                     the trailer of a message encoded with chunked transfer-coding.
658:      */
659:     public function getTrailer()
660:     {
661:         return $this->getHeader('Trailer', true);
662:     }
663: 
664:     /**
665:      * Get the Transfer-Encoding HTTP header
666:      *
667:      * @return string|null The form of encoding used to safely transfer the entity to the user. Currently defined
668:      *                     methods are: chunked
669:      */
670:     public function getTransferEncoding()
671:     {
672:         return $this->getHeader('Transfer-Encoding', true);
673:     }
674: 
675:     /**
676:      * Get the Vary HTTP header
677:      *
678:      * @return string|null Tells downstream proxies how to match future request headers to decide whether the cached
679:      *                     response can be used rather than requesting a fresh one from the origin server.
680:      */
681:     public function getVary()
682:     {
683:         return $this->getHeader('Vary', true);
684:     }
685: 
686:     /**
687:      * Get the Via HTTP header
688:      *
689:      * @return string|null Informs the client of proxies through which the response was sent.
690:      *                     (e.g. 1.0 fred, 1.1 nowhere.com (Apache/1.1))
691:      */
692:     public function getVia()
693:     {
694:         return $this->getHeader('Via', true);
695:     }
696: 
697:     /**
698:      * Get the Warning HTTP header
699:      *
700:      * @return string|null A general warning about possible problems with the entity body.
701:      *                     (e.g. 199 Miscellaneous warning)
702:      */
703:     public function getWarning()
704:     {
705:         return $this->getHeader('Warning', true);
706:     }
707: 
708:     /**
709:      * Get the WWW-Authenticate HTTP header
710:      *
711:      * @return string|null Indicates the authentication scheme that should be used to access the requested entity
712:      *                     (e.g. Basic)
713:      */
714:     public function getWwwAuthenticate()
715:     {
716:         return $this->getHeader('WWW-Authenticate', true);
717:     }
718: 
719:     /**
720:      * Checks if HTTP Status code is a Client Error (4xx)
721:      *
722:      * @return bool
723:      */
724:     public function isClientError()
725:     {
726:         return $this->statusCode >= 400 && $this->statusCode < 500;
727:     }
728: 
729:     /**
730:      * Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
731:      *
732:      * @return boolean
733:      */
734:     public function isError()
735:     {
736:         return $this->isClientError() || $this->isServerError();
737:     }
738: 
739:     /**
740:      * Checks if HTTP Status code is Information (1xx)
741:      *
742:      * @return bool
743:      */
744:     public function isInformational()
745:     {
746:         return $this->statusCode < 200;
747:     }
748: 
749:     /**
750:      * Checks if HTTP Status code is a Redirect (3xx)
751:      *
752:      * @return bool
753:      */
754:     public function isRedirect()
755:     {
756:         return $this->statusCode >= 300 && $this->statusCode < 400;
757:     }
758: 
759:     /**
760:      * Checks if HTTP Status code is Server Error (5xx)
761:      *
762:      * @return bool
763:      */
764:     public function isServerError()
765:     {
766:         return $this->statusCode >= 500 && $this->statusCode < 600;
767:     }
768: 
769:     /**
770:      * Checks if HTTP Status code is Successful (2xx | 304)
771:      *
772:      * @return bool
773:      */
774:     public function isSuccessful()
775:     {
776:         return ($this->statusCode >= 200 && $this->statusCode < 300) || $this->statusCode == 304;
777:     }
778: 
779:     /**
780:      * Set the request object associated with the response
781:      *
782:      * @param RequestInterface $request The request object used to generate the response
783:      *
784:      * @return Response
785:      */
786:     public function setRequest(RequestInterface $request)
787:     {
788:         $this->request = $request;
789: 
790:         return $this;
791:     }
792: 
793:     /**
794:      * Check if the response can be cached
795:      *
796:      * @return bool Returns TRUE if the response can be cached or false if not
797:      */
798:     public function canCache()
799:     {
800:         // Check if the response is cacheable based on the code
801:         if (!in_array((int) $this->getStatusCode(), $this->cacheResponseCodes)) {
802:             return false;
803:         }
804: 
805:         // Make sure a valid body was returned and can be cached
806:         if ((!$this->getBody()->isReadable() || !$this->getBody()->isSeekable())
807:             && ($this->getContentLength() > 0 || $this->getTransferEncoding() == 'chunked')) {
808:             return false;
809:         }
810: 
811:         // Never cache no-store resources (this is a private cache, so private
812:         // can be cached)
813:         if ($this->hasCacheControlDirective('no-store')) {
814:             return false;
815:         }
816: 
817:         return $this->isFresh() || $this->getFreshness() === null || $this->canValidate();
818:     }
819: 
820:     /**
821:      * Gets the number of seconds from the current time in which this response is still considered fresh
822:      *
823:      * @return int|null Returns the number of seconds
824:      */
825:     public function getMaxAge()
826:     {
827:         // s-max-age, then max-age, then Expires
828:         if ($age = $this->getCacheControlDirective('s-maxage')) {
829:             return $age;
830:         }
831: 
832:         if ($age = $this->getCacheControlDirective('max-age')) {
833:             return $age;
834:         }
835: 
836:         if ($this->getHeader('Expires')) {
837:             return strtotime($this->getExpires()) - time();
838:         }
839: 
840:         return null;
841:     }
842: 
843:     /**
844:      * Check if the response is considered fresh.
845:      *
846:      * A response is considered fresh when its age is less than or equal to the freshness lifetime (maximum age) of the
847:      * response.
848:      *
849:      * @return bool|null
850:      */
851:     public function isFresh()
852:     {
853:         $fresh = $this->getFreshness();
854: 
855:         return $fresh === null ? null : $fresh >= 0;
856:     }
857: 
858:     /**
859:      * Check if the response can be validated against the origin server using a conditional GET request.
860:      *
861:      * @return bool
862:      */
863:     public function canValidate()
864:     {
865:         return $this->getEtag() || $this->getLastModified();
866:     }
867: 
868:     /**
869:      * Get the freshness of the response by returning the difference of the maximum lifetime of the response and the
870:      * age of the response (max-age - age).
871:      *
872:      * Freshness values less than 0 mean that the response is no longer fresh and is ABS(freshness) seconds expired.
873:      * Freshness values of greater than zero is the number of seconds until the response is no longer fresh. A NULL
874:      * result means that no freshness information is available.
875:      *
876:      * @return int
877:      */
878:     public function getFreshness()
879:     {
880:         $maxAge = $this->getMaxAge();
881:         $age = $this->getAge();
882: 
883:         return $maxAge && $age ? ($maxAge - $age) : null;
884:     }
885: 
886:     /**
887:      * Get the previous response (e.g. Redirect response)
888:      *
889:      * @return null|Response
890:      */
891:     public function getPreviousResponse()
892:     {
893:         return $this->previous;
894:     }
895: 
896:     /**
897:      * Set the previous response
898:      *
899:      * @param Response $response Response to set
900:      *
901:      * @return self
902:      */
903:     public function setPreviousResponse(Response $response)
904:     {
905:         $this->previous = $response;
906: 
907:         return $this;
908:     }
909: 
910:     /**
911:      * Parse the JSON response body and return an array
912:      *
913:      * @return array|string|int|bool|float
914:      * @throws RuntimeException if the response body is not in JSON format
915:      */
916:     public function json()
917:     {
918:         $data = json_decode((string) $this->body, true);
919:         if (JSON_ERROR_NONE !== json_last_error()) {
920:             throw new RuntimeException('Unable to parse response body into JSON: ' . json_last_error());
921:         }
922: 
923:         return $data === null ? array() : $data;
924:     }
925: 
926:     /**
927:      * Parse the XML response body and return a SimpleXMLElement
928:      *
929:      * @return \SimpleXMLElement
930:      * @throws RuntimeException if the response body is not in XML format
931:      */
932:     public function xml()
933:     {
934:         try {
935:             // Allow XML to be retrieved even if there is no response body
936:             $xml = new \SimpleXMLElement((string) $this->body ?: '<root />');
937:         } catch (\Exception $e) {
938:             throw new RuntimeException('Unable to parse response body into XML: ' . $e->getMessage());
939:         }
940: 
941:         return $xml;
942:     }
943: }
944: 
php-coveralls API documentation generated by ApiGen 2.8.0