ExponentialBackoff::setDelayFunction()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace SchulzeFelix\AdWords;
4
5
/**
6
 * Exponential backoff implementation.
7
 */
8
class ExponentialBackoff
9
{
10
    const MAX_DELAY_MICROSECONDS = 120000000;
11
12
    /**
13
     * @var int
14
     */
15
    private $retries;
16
17
    /**
18
     * @var callable
19
     */
20
    private $retryFunction;
21
22
    /**
23
     * @var callable
24
     */
25
    private $delayFunction;
26
27
    /**
28
     * @param int $retries [optional] Number of retries for a failed request.
29
     * @param callable $retryFunction [optional] returns bool for whether or not to retry
30
     */
31
    public function __construct($retries = null, callable $retryFunction = null)
32
    {
33
        $this->retries = $retries !== null ? (int) $retries : 3;
34
        $this->retryFunction = $retryFunction;
35
        $this->delayFunction = function ($delay) {
36
            usleep($delay);
37
        };
38
    }
39
40
    /**
41
     * Executes the retry process.
42
     *
43
     * @param callable $function
44
     * @param array $arguments [optional]
45
     * @return mixed
46
     * @throws \Exception The last exception caught while retrying.
47
     */
48
    public function execute(callable $function, array $arguments = [])
49
    {
50
        $delayFunction = $this->delayFunction;
51
        $retryAttempt = 0;
52
        $exception = null;
53
54
        while (true) {
55
            try {
56
                return call_user_func_array($function, $arguments);
57
            } catch (\Exception $exception) {
58
                if ($this->retryFunction) {
59
                    if (! call_user_func($this->retryFunction, $exception)) {
60
                        throw $exception;
61
                    }
62
                }
63
64
                if (in_array($exception->getCode(), [0, 400, 403])) {
65
                    break;
66
                }
67
68
69
                if ($retryAttempt >= $this->retries) {
70
                    break;
71
                }
72
73
                $delayFunction($this->calculateDelay($retryAttempt));
74
                $retryAttempt++;
75
            }
76
        }
77
78
        throw $exception;
79
    }
80
81
    /**
82
     * @param callable $delayFunction
83
     * @return void
84
     */
85
    public function setDelayFunction(callable $delayFunction)
86
    {
87
        $this->delayFunction = $delayFunction;
88
    }
89
90
    /**
91
     * Calculates exponential delay.
92
     *
93
     * @param int $attempt
94
     * @return int
95
     */
96
    private function calculateDelay($attempt)
97
    {
98
        return min(
99
            mt_rand(0, 1000000) + (pow(2, $attempt) * 1000000),
100
            self::MAX_DELAY_MICROSECONDS
101
        );
102
    }
103
}
104