Request   F
last analyzed

Complexity

Total Complexity 105

Size/Duplication

Total Lines 626
Duplicated Lines 1.92 %

Coupling/Cohesion

Components 3
Dependencies 19

Importance

Changes 0
Metric Value
wmc 105
lcom 3
cbo 19
dl 12
loc 626
rs 1.4967
c 0
b 0
f 0

49 Methods

Rating   Name   Duplication   Size   Complexity  
A getAllEvents() 0 19 1
C __construct() 0 25 7
A __clone() 0 14 2
A __toString() 0 4 1
A onRequestError() 0 6 1
A setClient() 0 6 1
A getClient() 0 4 1
A getRawHeaders() 0 8 2
A setUrl() 0 20 4
A send() 0 8 2
A getResponse() 0 4 1
A getQuery() 0 6 2
A getMethod() 0 4 1
A getScheme() 0 4 1
A setScheme() 0 6 1
A getHost() 0 4 1
A setHost() 0 7 1
A getProtocolVersion() 0 4 1
A setProtocolVersion() 0 6 1
A getPath() 0 4 1
A setPath() 0 6 1
A getPort() 0 4 1
B setPort() 0 14 6
A getUsername() 0 4 1
A getPassword() 0 4 1
B setAuth() 0 40 5
A getResource() 0 9 2
A getUrl() 0 4 2
A getState() 0 4 1
C setState() 0 36 12
A getCurlOptions() 0 4 1
A startResponse() 0 8 1
B setResponse() 0 23 5
A setResponseBody() 0 15 3
A getResponseBody() 0 8 2
A isResponseBodyRepeatable() 0 5 2
A getCookies() 0 9 2
A getCookie() 0 6 2
A addCookie() 0 13 2
A removeCookie() 0 12 4
A setEventDispatcher() 0 7 1
A getEventDispatcher() 0 8 2
A dispatch() 12 12 1
A addSubscriber() 0 6 1
A getEventArray() 0 7 1
B processResponse() 0 43 6
A canCache() 0 10 2
A setIsRedirect() 0 6 1
A isRedirect() 0 5 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Request often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Request, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Guzzle\Http\Message;
4
5
use Guzzle\Common\Version;
6
use Guzzle\Common\Event;
7
use Guzzle\Common\Collection;
8
use Guzzle\Common\Exception\RuntimeException;
9
use Guzzle\Common\Exception\InvalidArgumentException;
10
use Guzzle\Http\Exception\RequestException;
11
use Guzzle\Http\Exception\BadResponseException;
12
use Guzzle\Http\ClientInterface;
13
use Guzzle\Http\EntityBody;
14
use Guzzle\Http\EntityBodyInterface;
15
use Guzzle\Http\Message\Header\HeaderInterface;
16
use Guzzle\Http\Url;
17
use Guzzle\Parser\ParserRegistry;
18
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
19
use Symfony\Component\EventDispatcher\EventDispatcher;
20
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
21
22
/**
23
 * HTTP request class to send requests
24
 */
25
class Request extends AbstractMessage implements RequestInterface
26
{
27
    /** @var EventDispatcherInterface */
28
    protected $eventDispatcher;
29
30
    /** @var Url HTTP Url */
31
    protected $url;
32
33
    /** @var string HTTP method (GET, PUT, POST, DELETE, HEAD, OPTIONS, TRACE) */
34
    protected $method;
35
36
    /** @var ClientInterface */
37
    protected $client;
38
39
    /** @var Response Response of the request */
40
    protected $response;
41
42
    /** @var EntityBodyInterface Response body */
43
    protected $responseBody;
44
45
    /** @var string State of the request object */
46
    protected $state;
47
48
    /** @var string Authentication username */
49
    protected $username;
50
51
    /** @var string Auth password */
52
    protected $password;
53
54
    /** @var Collection cURL specific transfer options */
55
    protected $curlOptions;
56
57
    /** @var bool */
58
    protected $isRedirect = false;
59
60
    public static function getAllEvents()
61
    {
62
        return array(
63
            // Called when receiving or uploading data through cURL
64
            'curl.callback.read', 'curl.callback.write', 'curl.callback.progress',
65
            // Cloning a request
66
            'request.clone',
67
            // About to send the request, sent request, completed transaction
68
            'request.before_send', 'request.sent', 'request.complete',
69
            // A request received a successful response
70
            'request.success',
71
            // A request received an unsuccessful response
72
            'request.error',
73
            // An exception is being thrown because of an unsuccessful response
74
            'request.exception',
75
            // Received response status line
76
            'request.receive.status_line'
77
        );
78
    }
79
80
    /**
81
     * @param string           $method  HTTP method
82
     * @param string|Url       $url     HTTP URL to connect to. The URI scheme, host header, and URI are parsed from the
83
     *                                  full URL. If query string parameters are present they will be parsed as well.
84
     * @param array|Collection $headers HTTP headers
85
     */
86
    public function __construct($method, $url, $headers = array())
87
    {
88
        parent::__construct();
89
        $this->method = strtoupper($method);
90
        $this->curlOptions = new Collection();
91
        $this->setUrl($url);
92
93
        if ($headers) {
94
            // Special handling for multi-value headers
95
            foreach ($headers as $key => $value) {
96
                // Deal with collisions with Host and Authorization
97
                if ($key == 'host' || $key == 'Host') {
98
                    $this->setHeader($key, $value);
99
                } elseif ($value instanceof HeaderInterface) {
100
                    $this->addHeader($key, $value);
101
                } else {
102
                    foreach ((array) $value as $v) {
103
                        $this->addHeader($key, $v);
104
                    }
105
                }
106
            }
107
        }
108
109
        $this->setState(self::STATE_NEW);
110
    }
111
112
    public function __clone()
113
    {
114
        if ($this->eventDispatcher) {
115
            $this->eventDispatcher = clone $this->eventDispatcher;
116
        }
117
        $this->curlOptions = clone $this->curlOptions;
118
        $this->params = clone $this->params;
119
        $this->url = clone $this->url;
120
        $this->response = $this->responseBody = null;
121
        $this->headers = clone $this->headers;
0 ignored issues
show
Documentation Bug introduced by
It seems like clone $this->headers of type object is incompatible with the declared type array of property $headers.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
122
123
        $this->setState(RequestInterface::STATE_NEW);
124
        $this->dispatch('request.clone', array('request' => $this));
125
    }
126
127
    /**
128
     * Get the HTTP request as a string
129
     *
130
     * @return string
131
     */
132
    public function __toString()
133
    {
134
        return $this->getRawHeaders() . "\r\n\r\n";
135
    }
136
137
    /**
138
     * Default method that will throw exceptions if an unsuccessful response is received.
139
     *
140
     * @param Event $event Received
141
     * @throws BadResponseException if the response is not successful
142
     */
143
    public static function onRequestError(Event $event)
144
    {
145
        $e = BadResponseException::factory($event['request'], $event['response']);
146
        $event['request']->setState(self::STATE_ERROR, array('exception' => $e) + $event->toArray());
147
        throw $e;
148
    }
149
150
    public function setClient(ClientInterface $client)
151
    {
152
        $this->client = $client;
153
154
        return $this;
155
    }
156
157
    public function getClient()
158
    {
159
        return $this->client;
160
    }
161
162
    public function getRawHeaders()
163
    {
164
        $protocolVersion = $this->protocolVersion ?: '1.1';
165
166
        return trim($this->method . ' ' . $this->getResource()) . ' '
167
            . strtoupper(str_replace('https', 'http', $this->url->getScheme()))
168
            . '/' . $protocolVersion . "\r\n" . implode("\r\n", $this->getHeaderLines());
169
    }
170
171
    public function setUrl($url)
172
    {
173
        if ($url instanceof Url) {
174
            $this->url = $url;
175
        } else {
176
            $this->url = Url::factory($url);
177
        }
178
179
        // Update the port and host header
180
        $this->setPort($this->url->getPort());
181
182
        if ($this->url->getUsername() || $this->url->getPassword()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->url->getUsername() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
Bug Best Practice introduced by
The expression $this->url->getPassword() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
183
            $this->setAuth($this->url->getUsername(), $this->url->getPassword());
184
            // Remove the auth info from the URL
185
            $this->url->setUsername(null);
186
            $this->url->setPassword(null);
187
        }
188
189
        return $this;
190
    }
191
192
    public function send()
193
    {
194
        if (!$this->client) {
195
            throw new RuntimeException('A client must be set on the request');
196
        }
197
198
        return $this->client->send($this);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->client->send($this); of type Guzzle\Http\Message\Response|array adds the type array to the return on line 198 which is incompatible with the return type declared by the interface Guzzle\Http\Message\RequestInterface::send of type Guzzle\Http\Message\Response.
Loading history...
199
    }
200
201
    public function getResponse()
202
    {
203
        return $this->response;
204
    }
205
206
    public function getQuery($asString = false)
207
    {
208
        return $asString
0 ignored issues
show
Bug Compatibility introduced by
The expression $asString ? (string) $th...$this->url->getQuery(); of type string|Guzzle\Http\QueryString adds the type string to the return on line 208 which is incompatible with the return type declared by the interface Guzzle\Http\Message\RequestInterface::getQuery of type Guzzle\Http\QueryString.
Loading history...
209
            ? (string) $this->url->getQuery()
210
            : $this->url->getQuery();
211
    }
212
213
    public function getMethod()
214
    {
215
        return $this->method;
216
    }
217
218
    public function getScheme()
219
    {
220
        return $this->url->getScheme();
221
    }
222
223
    public function setScheme($scheme)
224
    {
225
        $this->url->setScheme($scheme);
226
227
        return $this;
228
    }
229
230
    public function getHost()
231
    {
232
        return $this->url->getHost();
233
    }
234
235
    public function setHost($host)
236
    {
237
        $this->url->setHost($host);
238
        $this->setPort($this->url->getPort());
239
240
        return $this;
241
    }
242
243
    public function getProtocolVersion()
244
    {
245
        return $this->protocolVersion;
246
    }
247
248
    public function setProtocolVersion($protocol)
249
    {
250
        $this->protocolVersion = $protocol;
251
252
        return $this;
253
    }
254
255
    public function getPath()
256
    {
257
        return '/' . ltrim($this->url->getPath(), '/');
258
    }
259
260
    public function setPath($path)
261
    {
262
        $this->url->setPath($path);
263
264
        return $this;
265
    }
266
267
    public function getPort()
268
    {
269
        return $this->url->getPort();
270
    }
271
272
    public function setPort($port)
273
    {
274
        $this->url->setPort($port);
275
276
        // Include the port in the Host header if it is not the default port for the scheme of the URL
277
        $scheme = $this->url->getScheme();
278
        if ($port && (($scheme == 'http' && $port != 80) || ($scheme == 'https' && $port != 443))) {
279
            $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost() . ':' . $port);
280
        } else {
281
            $this->headers['host'] = $this->headerFactory->createHeader('Host', $this->url->getHost());
282
        }
283
284
        return $this;
285
    }
286
287
    public function getUsername()
288
    {
289
        return $this->username;
290
    }
291
292
    public function getPassword()
293
    {
294
        return $this->password;
295
    }
296
297
    public function setAuth($user, $password = '', $scheme = CURLAUTH_BASIC)
298
    {
299
        static $authMap = array(
300
            'basic'  => CURLAUTH_BASIC,
301
            'digest' => CURLAUTH_DIGEST,
302
            'ntlm'   => CURLAUTH_NTLM,
303
            'any'    => CURLAUTH_ANY
304
        );
305
306
        // If we got false or null, disable authentication
307
        if (!$user) {
308
            $this->password = $this->username = null;
309
            $this->removeHeader('Authorization');
310
            $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
311
            return $this;
312
        }
313
314
        if (!is_numeric($scheme)) {
315
            $scheme = strtolower($scheme);
316
            if (!isset($authMap[$scheme])) {
317
                throw new InvalidArgumentException($scheme . ' is not a valid authentication type');
318
            }
319
            $scheme = $authMap[$scheme];
320
        }
321
322
        $this->username = $user;
0 ignored issues
show
Documentation Bug introduced by
It seems like $user can also be of type boolean. However, the property $username is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
323
        $this->password = $password;
324
325
        // Bypass CURL when using basic auth to promote connection reuse
326
        if ($scheme == CURLAUTH_BASIC) {
327
            $this->getCurlOptions()->remove(CURLOPT_HTTPAUTH);
328
            $this->setHeader('Authorization', 'Basic ' . base64_encode($this->username . ':' . $this->password));
329
        } else {
330
            $this->getCurlOptions()
331
                ->set(CURLOPT_HTTPAUTH, $scheme)
332
                ->set(CURLOPT_USERPWD, $this->username . ':' . $this->password);
333
        }
334
335
        return $this;
336
    }
337
338
    public function getResource()
339
    {
340
        $resource = $this->getPath();
341
        if ($query = (string) $this->url->getQuery()) {
342
            $resource .= '?' . $query;
343
        }
344
345
        return $resource;
346
    }
347
348
    public function getUrl($asObject = false)
349
    {
350
        return $asObject ? clone $this->url : (string) $this->url;
351
    }
352
353
    public function getState()
354
    {
355
        return $this->state;
356
    }
357
358
    public function setState($state, array $context = array())
359
    {
360
        $oldState = $this->state;
361
        $this->state = $state;
362
363
        switch ($state) {
364
            case self::STATE_NEW:
365
                $this->response = null;
366
                break;
367
            case self::STATE_TRANSFER:
368
                if ($oldState !== $state) {
369
                    // Fix Content-Length and Transfer-Encoding collisions
370
                    if ($this->hasHeader('Transfer-Encoding') && $this->hasHeader('Content-Length')) {
371
                        $this->removeHeader('Transfer-Encoding');
372
                    }
373
                    $this->dispatch('request.before_send', array('request' => $this));
374
                }
375
                break;
376
            case self::STATE_COMPLETE:
377
                if ($oldState !== $state) {
378
                    $this->processResponse($context);
379
                    $this->responseBody = null;
380
                }
381
                break;
382
            case self::STATE_ERROR:
383
                if (isset($context['exception'])) {
384
                    $this->dispatch('request.exception', array(
385
                        'request'   => $this,
386
                        'response'  => isset($context['response']) ? $context['response'] : $this->response,
387
                        'exception' => isset($context['exception']) ? $context['exception'] : null
388
                    ));
389
                }
390
        }
391
392
        return $this->state;
393
    }
394
395
    public function getCurlOptions()
396
    {
397
        return $this->curlOptions;
398
    }
399
400
    public function startResponse(Response $response)
401
    {
402
        $this->state = self::STATE_TRANSFER;
403
        $response->setEffectiveUrl((string) $this->getUrl());
404
        $this->response = $response;
405
406
        return $this;
407
    }
408
409
    public function setResponse(Response $response, $queued = false)
410
    {
411
        $response->setEffectiveUrl((string) $this->url);
412
413
        if ($queued) {
414
            $ed = $this->getEventDispatcher();
415
            $ed->addListener('request.before_send', $f = function ($e) use ($response, &$f, $ed) {
416
                $e['request']->setResponse($response);
417
                $ed->removeListener('request.before_send', $f);
418
            }, -9999);
419
        } else {
420
            $this->response = $response;
421
            // If a specific response body is specified, then use it instead of the response's body
422
            if ($this->responseBody && !$this->responseBody->getCustomData('default') && !$response->isRedirect()) {
423
                $this->getResponseBody()->write((string) $this->response->getBody());
424
            } else {
425
                $this->responseBody = $this->response->getBody();
426
            }
427
            $this->setState(self::STATE_COMPLETE);
428
        }
429
430
        return $this;
431
    }
432
433
    public function setResponseBody($body)
434
    {
435
        // Attempt to open a file for writing if a string was passed
436
        if (is_string($body)) {
437
            // @codeCoverageIgnoreStart
438
            if (!($body = fopen($body, 'w+'))) {
439
                throw new InvalidArgumentException('Could not open ' . $body . ' for writing');
440
            }
441
            // @codeCoverageIgnoreEnd
442
        }
443
444
        $this->responseBody = EntityBody::factory($body);
0 ignored issues
show
Bug introduced by
It seems like $body defined by parameter $body on line 433 can also be of type object<Guzzle\Http\EntityBodyInterface>; however, Guzzle\Http\EntityBody::factory() does only seem to accept string|resource|object<Guzzle\Http\EntityBody>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
445
446
        return $this;
447
    }
448
449
    public function getResponseBody()
450
    {
451
        if ($this->responseBody === null) {
452
            $this->responseBody = EntityBody::factory()->setCustomData('default', true);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Guzzle\Http\EntityBody:...omData('default', true) of type object<Guzzle\Stream\Stream> is incompatible with the declared type object<Guzzle\Http\EntityBodyInterface> of property $responseBody.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
453
        }
454
455
        return $this->responseBody;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->responseBody; of type Guzzle\Stream\Stream|Guz...ttp\EntityBodyInterface adds the type Guzzle\Stream\Stream to the return on line 455 which is incompatible with the return type declared by the interface Guzzle\Http\Message\Requ...erface::getResponseBody of type Guzzle\Http\EntityBodyInterface.
Loading history...
456
    }
457
458
    /**
459
     * Determine if the response body is repeatable (readable + seekable)
460
     *
461
     * @return bool
462
     * @deprecated Use getResponseBody()->isSeekable()
463
     * @codeCoverageIgnore
464
     */
465
    public function isResponseBodyRepeatable()
466
    {
467
        Version::warn(__METHOD__ . ' is deprecated. Use $request->getResponseBody()->isRepeatable()');
468
        return !$this->responseBody ? true : $this->responseBody->isRepeatable();
469
    }
470
471
    public function getCookies()
472
    {
473
        if ($cookie = $this->getHeader('Cookie')) {
474
            $data = ParserRegistry::getInstance()->getParser('cookie')->parseCookie($cookie);
475
            return $data['cookies'];
476
        }
477
478
        return array();
479
    }
480
481
    public function getCookie($name)
482
    {
483
        $cookies = $this->getCookies();
484
485
        return isset($cookies[$name]) ? $cookies[$name] : null;
486
    }
487
488
    public function addCookie($name, $value)
489
    {
490
        if (!$this->hasHeader('Cookie')) {
491
            $this->setHeader('Cookie', "{$name}={$value}");
492
        } else {
493
            $this->getHeader('Cookie')->add("{$name}={$value}");
494
        }
495
496
        // Always use semicolons to separate multiple cookie headers
497
        $this->getHeader('Cookie')->setGlue(';');
498
499
        return $this;
500
    }
501
502
    public function removeCookie($name)
503
    {
504
        if ($cookie = $this->getHeader('Cookie')) {
505
            foreach ($cookie as $cookieValue) {
506
                if (strpos($cookieValue, $name . '=') === 0) {
507
                    $cookie->removeValue($cookieValue);
508
                }
509
            }
510
        }
511
512
        return $this;
513
    }
514
515
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
516
    {
517
        $this->eventDispatcher = $eventDispatcher;
518
        $this->eventDispatcher->addListener('request.error', array(__CLASS__, 'onRequestError'), -255);
519
520
        return $this;
521
    }
522
523
    public function getEventDispatcher()
524
    {
525
        if (!$this->eventDispatcher) {
526
            $this->setEventDispatcher(new EventDispatcher());
527
        }
528
529
        return $this->eventDispatcher;
530
    }
531
532 View Code Duplication
    public function dispatch($eventName, array $context = array())
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
533
    {
534
        $context['request'] = $this;
535
536
        $dispatcher = $this->getEventDispatcher();
537
538
        $event = new Event($context);
539
        $event->setEventDispatcher($dispatcher);
540
        $event->setName($eventName);
541
542
        return $dispatcher->dispatch($eventName, $event);
543
    }
544
545
    public function addSubscriber(EventSubscriberInterface $subscriber)
546
    {
547
        $this->getEventDispatcher()->addSubscriber($subscriber);
548
549
        return $this;
550
    }
551
552
    /**
553
     * Get an array containing the request and response for event notifications
554
     *
555
     * @return array
556
     */
557
    protected function getEventArray()
558
    {
559
        return array(
560
            'request'  => $this,
561
            'response' => $this->response
562
        );
563
    }
564
565
    /**
566
     * Process a received response
567
     *
568
     * @param array $context Contextual information
569
     * @throws RequestException|BadResponseException on unsuccessful responses
570
     */
571
    protected function processResponse(array $context = array())
572
    {
573
        if (!$this->response) {
574
            // If no response, then processResponse shouldn't have been called
575
            $e = new RequestException('Error completing request');
576
            $e->setRequest($this);
577
            throw $e;
578
        }
579
580
        $this->state = self::STATE_COMPLETE;
581
582
        // A request was sent, but we don't know if we'll send more or if the final response will be successful
583
        $this->dispatch('request.sent', $this->getEventArray() + $context);
584
585
        // Some response processors will remove the response or reset the state (example: ExponentialBackoffPlugin)
586
        if ($this->state == RequestInterface::STATE_COMPLETE) {
587
588
            // The request completed, so the HTTP transaction is complete
589
            $this->dispatch('request.complete', $this->getEventArray());
590
591
            // If the response is bad, allow listeners to modify it or throw exceptions. You can change the response by
592
            // modifying the Event object in your listeners or calling setResponse() on the request
593
            if ($this->response->isError()) {
594
                $dispatcher = $this->getEventDispatcher();
595
                $eventName = 'request.error';
596
597
                $event = new Event($this->getEventArray());
598
                $event->setEventDispatcher($dispatcher);
599
                $event->setName($eventName);
600
601
                $dispatcher->dispatch($eventName, $event);
602
                // Allow events of request.error to quietly change the response
603
                if ($event['response'] !== $this->response) {
604
                    $this->response = $event['response'];
605
                }
606
            }
607
608
            // If a successful response was received, dispatch an event
609
            if ($this->response->isSuccessful()) {
610
                $this->dispatch('request.success', $this->getEventArray());
611
            }
612
        }
613
    }
614
615
    /**
616
     * @deprecated Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy
617
     * @codeCoverageIgnore
618
     */
619
    public function canCache()
620
    {
621
        Version::warn(__METHOD__ . ' is deprecated. Use Guzzle\Plugin\Cache\DefaultCanCacheStrategy.');
622
        if (class_exists('Guzzle\Plugin\Cache\DefaultCanCacheStrategy')) {
623
            $canCache = new \Guzzle\Plugin\Cache\DefaultCanCacheStrategy();
624
            return $canCache->canCacheRequest($this);
625
        } else {
626
            return false;
627
        }
628
    }
629
630
    /**
631
     * @deprecated Use the history plugin (not emitting a warning as this is built-into the RedirectPlugin for now)
632
     * @codeCoverageIgnore
633
     */
634
    public function setIsRedirect($isRedirect)
635
    {
636
        $this->isRedirect = $isRedirect;
637
638
        return $this;
639
    }
640
641
    /**
642
     * @deprecated Use the history plugin
643
     * @codeCoverageIgnore
644
     */
645
    public function isRedirect()
646
    {
647
        Version::warn(__METHOD__ . ' is deprecated. Use the HistoryPlugin to track this.');
648
        return $this->isRedirect;
649
    }
650
}
651