Completed
Push — master ( 258294...5d1560 )
by Timo
02:13
created

RequestLimiter::createFromRule()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 2
dl 0
loc 8
ccs 4
cts 4
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace hamburgscleanest\GuzzleAdvancedThrottle;
4
5
use hamburgscleanest\GuzzleAdvancedThrottle\Cache\Adapters\ArrayAdapter;
6
use hamburgscleanest\GuzzleAdvancedThrottle\Cache\Interfaces\StorageInterface;
7
use hamburgscleanest\GuzzleAdvancedThrottle\Exceptions\HostNotDefinedException;
8
9
/**
10
 * Class RequestLimiter
11
 * @package hamburgscleanest\GuzzleAdvancedThrottle
12
 */
13
class RequestLimiter
14
{
15
16
    /** @var int */
17
    private const DEFAULT_MAX_REQUESTS = 120;
18
    /** @var int */
19
    private const DEFAULT_REQUEST_INTERVAL = 60;
20
    /** @var string */
21
    private $_host;
22
    /** @var TimeKeeper */
23
    private $_timekeeper;
24
    /** @var int */
25
    private $_requestCount = 0;
26
    /** @var int */
27
    private $_maxRequestCount;
28
    /** @var StorageInterface */
29
    private $_storage;
30
    /** @var string */
31
    private $_storageKey;
32
33
    /**
34
     * RequestLimiter constructor.
35
     * @param string $host
36
     * @param int $maxRequests
37
     * @param int $requestIntervalSeconds
38
     * @param StorageInterface|null $storage
39
     * @throws \Exception
40
     */
41 6
    public function __construct(string $host, ?int $maxRequests = self::DEFAULT_MAX_REQUESTS, ?int $requestIntervalSeconds = self::DEFAULT_REQUEST_INTERVAL, StorageInterface $storage = null)
42
    {
43 6
        $this->_storage = $storage ?? new ArrayAdapter();
44 6
        $this->_host = $host;
45 6
        $this->_maxRequestCount = $maxRequests ?? self::DEFAULT_MAX_REQUESTS;
46 6
        $requestInterval = $requestIntervalSeconds ?? self::DEFAULT_REQUEST_INTERVAL;
47
48 6
        $this->_storageKey = $maxRequests . '_' . $requestInterval;
49 6
        $this->_restoreState($requestInterval);
50 6
    }
51
52
    /**
53
     * @param int $requestIntervalSeconds
54
     * @throws \Exception
55
     */
56 6
    private function _restoreState(int $requestIntervalSeconds) : void
57
    {
58 6
        $this->_timekeeper = new TimeKeeper($requestIntervalSeconds);
59
60 6
        $requestInfo = $this->_storage->get($this->_host, $this->_storageKey);
61 6
        if ($requestInfo !== null)
62
        {
63 1
            $this->_requestCount = $requestInfo->requestCount;
64 1
            $this->_timekeeper->setExpiration($requestInfo->expiresAt);
65
        }
66 6
    }
67
68
    /**
69
     * @param array $rule
70
     * @param StorageInterface|null $storage
71
     * @return RequestLimiter
72
     * @throws \hamburgscleanest\GuzzleAdvancedThrottle\Exceptions\HostNotDefinedException
73
     * @throws \Exception
74
     */
75 2
    public static function createFromRule(array $rule, StorageInterface $storage = null) : self
76
    {
77 2
        if (!isset($rule['host']))
78
        {
79 1
            throw new HostNotDefinedException();
80
        }
81
82 1
        return new static($rule['host'], $rule['max_requests'] ?? null, $rule['request_interval'] ?? null, $storage);
83
    }
84
85
    /**
86
     * @param string $host
87
     * @param int $maxRequests
88
     * @param int $requestIntervalSeconds
89
     * @param StorageInterface|null $storage
90
     * @return RequestLimiter
91
     * @throws \Exception
92
     */
93 5
    public static function create(string $host, ?int $maxRequests = self::DEFAULT_MAX_REQUESTS, ?int $requestIntervalSeconds = self::DEFAULT_REQUEST_INTERVAL, StorageInterface $storage = null) : self
94
    {
95 5
        return new static($host, $maxRequests, $requestIntervalSeconds, $storage);
96
    }
97
98
    /**
99
     * @return bool
100
     * @throws \Exception
101
     */
102 4
    public function canRequest() : bool
103
    {
104 4
        if ($this->_requestCount >= $this->_maxRequestCount)
105
        {
106 2
            return false;
107
        }
108
109 4
        $this->_increment();
110 4
        $this->_save();
111
112 4
        return true;
113
    }
114
115
    /**
116
     * Increment the request counter.
117
     * @throws \Exception
118
     */
119 4
    private function _increment() : void
120
    {
121 4
        $this->_requestCount ++;
122 4
        if ($this->_requestCount === 1)
123
        {
124 4
            $this->_timekeeper->start();
125
        }
126 4
    }
127
128
    /**
129
     * @throws \hamburgscleanest\GuzzleAdvancedThrottle\Exceptions\TimerNotStartedException
130
     */
131 4
    private function _save() : void
132
    {
133 4
        $this->_storage->save(
134 4
            $this->_host,
135 4
            $this->_storageKey,
136 4
            $this->_requestCount,
137 4
            $this->_timekeeper->getExpiration(),
138 4
            $this->getRemainingSeconds()
139
        );
140 4
    }
141
142
    /**
143
     * @return int
144
     * @throws \hamburgscleanest\GuzzleAdvancedThrottle\Exceptions\TimerNotStartedException
145
     */
146 4
    public function getRemainingSeconds() : ? int
147
    {
148 4
        return $this->_timekeeper->getRemainingSeconds();
149
    }
150
151
    /**
152
     * @return int
153
     * @throws \hamburgscleanest\GuzzleAdvancedThrottle\Exceptions\TimerNotStartedException
154
     */
155 2
    public function getCurrentRequestCount() : int
156
    {
157 2
        if ($this->_timekeeper->isExpired())
158
        {
159
            $this->_timekeeper->reset();
160
            $this->_requestCount = 0;
161
        }
162
163 2
        return $this->_requestCount;
164
    }
165
}