Completed
Push — master ( 5d1560...e0507b )
by Timo
02:15
created

RequestLimiter::getCurrentRequestCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 2
rs 9.6666
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 7
    public function __construct(string $host, ?int $maxRequests = self::DEFAULT_MAX_REQUESTS, ?int $requestIntervalSeconds = self::DEFAULT_REQUEST_INTERVAL, StorageInterface $storage = null)
42
    {
43 7
        $this->_storage = $storage ?? new ArrayAdapter();
44 7
        $this->_host = $host;
45 7
        $this->_maxRequestCount = $maxRequests ?? self::DEFAULT_MAX_REQUESTS;
46 7
        $requestInterval = $requestIntervalSeconds ?? self::DEFAULT_REQUEST_INTERVAL;
47
48 7
        $this->_storageKey = $maxRequests . '_' . $requestInterval;
49 7
        $this->_restoreState($requestInterval);
50 7
    }
51
52
    /**
53
     * @param int $requestIntervalSeconds
54
     * @throws \Exception
55
     */
56 7
    private function _restoreState(int $requestIntervalSeconds) : void
57
    {
58 7
        $this->_timekeeper = new TimeKeeper($requestIntervalSeconds);
59
60 7
        $requestInfo = $this->_storage->get($this->_host, $this->_storageKey);
61 7
        if ($requestInfo !== null)
62
        {
63 1
            $this->_requestCount = $requestInfo->requestCount;
64 1
            $this->_timekeeper->setExpiration($requestInfo->expiresAt);
65
        }
66 7
    }
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 6
    public static function create(string $host, ?int $maxRequests = self::DEFAULT_MAX_REQUESTS, ?int $requestIntervalSeconds = self::DEFAULT_REQUEST_INTERVAL, StorageInterface $storage = null) : self
94
    {
95 6
        return new static($host, $maxRequests, $requestIntervalSeconds, $storage);
96
    }
97
98
    /**
99
     * @return bool
100
     * @throws \Exception
101
     */
102 5
    public function canRequest() : bool
103
    {
104 5
        if ($this->_requestCount >= $this->_maxRequestCount)
105
        {
106 2
            return false;
107
        }
108
109 5
        $this->_increment();
110 5
        $this->_save();
111
112 5
        return true;
113
    }
114
115
    /**
116
     * Increment the request counter.
117
     * @throws \Exception
118
     */
119 5
    private function _increment() : void
120
    {
121 5
        $this->_requestCount ++;
122 5
        if ($this->_requestCount === 1)
123
        {
124 5
            $this->_timekeeper->start();
125
        }
126 5
    }
127
128
    /**
129
     * @throws \hamburgscleanest\GuzzleAdvancedThrottle\Exceptions\TimerNotStartedException
130
     */
131 5
    private function _save() : void
132
    {
133 5
        $this->_storage->save(
134 5
            $this->_host,
135 5
            $this->_storageKey,
136 5
            $this->_requestCount,
137 5
            $this->_timekeeper->getExpiration(),
138 5
            $this->getRemainingSeconds()
139
        );
140 5
    }
141
142
    /**
143
     * @return int
144
     * @throws \hamburgscleanest\GuzzleAdvancedThrottle\Exceptions\TimerNotStartedException
145
     */
146 5
    public function getRemainingSeconds() : int
147
    {
148 5
        return $this->_timekeeper->getRemainingSeconds();
149
    }
150
151
    /**
152
     * @return int
153
     * @throws \hamburgscleanest\GuzzleAdvancedThrottle\Exceptions\TimerNotStartedException
154
     */
155 3
    public function getCurrentRequestCount() : int
156
    {
157 3
        if ($this->_timekeeper->isExpired())
158
        {
159 1
            $this->_timekeeper->reset();
160 1
            $this->_requestCount = 0;
161
        }
162
163 3
        return $this->_requestCount;
164
    }
165
}