Completed
Push — master ( f6901d...949ee8 )
by David
03:53 queued 01:16
created

RetryPlugin::handleRequest()   B

Complexity

Conditions 5
Paths 1

Size

Total Lines 51
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 5.0101

Importance

Changes 0
Metric Value
dl 0
loc 51
ccs 25
cts 27
cp 0.9259
rs 8.6588
c 0
b 0
f 0
cc 5
eloc 26
nc 1
nop 3
crap 5.0101

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Http\Client\Common\Plugin;
4
5
use Http\Client\Common\Deferred;
6
use Http\Client\Common\Plugin;
7
use Http\Client\Exception;
8
use Psr\Http\Message\RequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Symfony\Component\OptionsResolver\OptionsResolver;
11
12
/**
13
 * Retry the request if an exception is thrown.
14
 *
15
 * By default will retry only one time.
16
 *
17
 * @author Joel Wurtz <[email protected]>
18
 */
19
final class RetryPlugin implements Plugin
20
{
21
    /**
22
     * Number of retry before sending an exception.
23
     *
24
     * @var int
25
     */
26
    private $retry;
27
28
    /**
29
     * @var callable
30
     */
31
    private $delay;
32
33
    /**
34
     * @var callable
35
     */
36
    private $decider;
37
38
    /**
39
     * Store the retry counter for each request.
40
     *
41
     * @var array
42
     */
43
    private $retryStorage = [];
44
45
    /**
46
     * @param array $config {
47
     *
48
     *     @var int $retries Number of retries to attempt if an exception occurs before letting the exception bubble up.
49
     *     @var callable $decider A callback that gets a request and an exception to decide after a failure whether the request should be retried.
50
     *     @var callable $delay A callback that gets a request, an exception and the number of retries and returns how many microseconds we should wait before trying again.
51
     * }
52
     */
53 7
    public function __construct(array $config = [])
54
    {
55 7
        $resolver = new OptionsResolver();
56 7
        $resolver->setDefaults([
57 7
            'retries' => 1,
58
            'decider' => function (RequestInterface $request, Exception $e) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $e is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
59 3
                return true;
60 7
            },
61
            'delay' => __CLASS__.'::defaultDelay',
62
        ]);
63 7
        $resolver->setAllowedTypes('retries', 'int');
64 7
        $resolver->setAllowedTypes('decider', 'callable');
65 7
        $resolver->setAllowedTypes('delay', 'callable');
66 7
        $options = $resolver->resolve($config);
67
68 7
        $this->retry = $options['retries'];
69 7
        $this->decider = $options['decider'];
70 7
        $this->delay = $options['delay'];
71 7
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 4
    public function handleRequest(RequestInterface $request, callable $next, callable $first)
77
    {
78 4
        $chainIdentifier = spl_object_hash((object) $first);
79
80 4
        $promise = $next($request);
81
        $deferred = new Deferred(function () use ($promise) {
82
            $promise->wait(false);
83 4
        });
84
85
        $onFulfilled = function (ResponseInterface $response) use ($chainIdentifier, $deferred) {
86 3
            if (array_key_exists($chainIdentifier, $this->retryStorage)) {
87 2
                unset($this->retryStorage[$chainIdentifier]);
88
            }
89
90 3
            $deferred->resolve($response);
91
92 3
            return $response;
93 4
        };
94
95 4
        $onRejected = function (Exception $exception) use ($request, $next, $onFulfilled, &$onRejected, $chainIdentifier, $deferred) {
96 3
            if (!array_key_exists($chainIdentifier, $this->retryStorage)) {
97 3
                $this->retryStorage[$chainIdentifier] = 0;
98
            }
99
100 3
            if ($this->retryStorage[$chainIdentifier] >= $this->retry) {
101 1
                unset($this->retryStorage[$chainIdentifier]);
102
103 1
                $deferred->reject($exception);
104
105 1
                throw $exception;
106
            }
107
108 3
            if (!call_user_func($this->decider, $request, $exception)) {
109
                throw $exception;
110
            }
111
112 3
            $time = call_user_func($this->delay, $request, $exception, $this->retryStorage[$chainIdentifier]);
113 3
            usleep($time);
114
115
            // Retry in synchrone
116 3
            ++$this->retryStorage[$chainIdentifier];
117
118 3
            $next($request)->then($onFulfilled, $onRejected);
119
120 3
            throw $exception;
121 4
        };
122
123 4
        $promise->then($onFulfilled, $onRejected);
124
125 4
        return $deferred;
126
    }
127
128
    /**
129
     * @param RequestInterface $request
130
     * @param Exception        $e
131
     * @param int              $retries The number of retries we made before. First time this get called it will be 0.
132
     *
133
     * @return int
134
     */
135 4
    public static function defaultDelay(RequestInterface $request, Exception $e, $retries)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $e is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
136
    {
137 4
        return pow(2, $retries) * 500000;
138
    }
139
}
140