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