RequestLimiter::createFromRule()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 3
crap 1
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\Helpers\RequestHelper;
8
use hamburgscleanest\GuzzleAdvancedThrottle\Helpers\UrlHelper;
9
use Illuminate\Support\Str;
10
use Psr\Http\Message\RequestInterface;
11
12
class RequestLimiter
13
{
14
    /** @var int */
15
    private const DEFAULT_MAX_REQUESTS = 120;
16
    /** @var int */
17
    private const DEFAULT_REQUEST_INTERVAL = 60;
18
19
    private string $_host;
20
    private TimeKeeper $_timekeeper;
21
    private int $_requestCount = 0;
22
    private int $_maxRequestCount = self::DEFAULT_MAX_REQUESTS;
23
    private StorageInterface $_storage;
24
    private string $_storageKey;
25
26 33
    public function __construct(string $host, ?int $maxRequests = self::DEFAULT_MAX_REQUESTS, ?int $requestIntervalSeconds = self::DEFAULT_REQUEST_INTERVAL, StorageInterface $storage = null)
27
    {
28 33
        $this->_storage = $storage ?? new ArrayAdapter();
29 33
        $this->_host = UrlHelper::removeTrailingSlash($host);
30 33
        $this->_maxRequestCount = $maxRequests ?? self::DEFAULT_MAX_REQUESTS;
31
32 33
        $requestInterval = $requestIntervalSeconds ?? self::DEFAULT_REQUEST_INTERVAL;
33
34 33
        $this->_storageKey = $this->_maxRequestCount . '_' . $requestInterval;
35 33
        $this->_timekeeper = new TimeKeeper($requestInterval);
36
37 33
        $this->_restoreState();
38 33
    }
39
40 33
    private function _restoreState(): void
41
    {
42 33
        $requestInfo = $this->_storage->get($this->_host, $this->_storageKey);
43 33
        if ($requestInfo === null) {
44 33
            return;
45
        }
46
47 1
        $this->_requestCount = $requestInfo->requestCount;
48 1
        if ($requestInfo->expiresAt !== null) {
49 1
            $this->_timekeeper->setExpiration($requestInfo->expiresAt);
50
        }
51 1
    }
52
53 21
    public static function createFromRule(string $host, array $rule, StorageInterface $storage = null): self
54
    {
55 21
        return new static($host, $rule['max_requests'] ?? null, $rule['request_interval'] ?? null, $storage);
56
    }
57
58 8
    public static function create(string $host, ?int $maxRequests = self::DEFAULT_MAX_REQUESTS, ?int $requestIntervalSeconds = self::DEFAULT_REQUEST_INTERVAL, StorageInterface $storage = null): self
59
    {
60 8
        return new static($host, $maxRequests, $requestIntervalSeconds, $storage);
61
    }
62
63 28
    public function canRequest(RequestInterface $request, array $options = []): bool
64
    {
65 28
        if (!$this->matches(RequestHelper::getHostFromRequestAndOptions($request, $options))) {
66 2
            return true;
67
        }
68
69 28
        if (++$this->_requestCount === 1) {
70 28
            $this->_timekeeper->start();
71
        }
72
73 28
        if ($this->_timekeeper->isExpired()) {
74 2
            $this->_timekeeper->reset();
75 2
            $this->_requestCount = 0;
76
        }
77
78 28
        if ($this->_requestCount > $this->_maxRequestCount) {
79 24
            return false;
80
        }
81
82 22
        $this->_storage->save(
83 22
            $this->_host,
84 22
            $this->_storageKey,
85 22
            $this->_requestCount,
86 22
            $this->_timekeeper
87
        );
88
89 22
        return true;
90
    }
91
92 29
    public function matches(string $host): bool
93
    {
94 29
        return $host === $this->_host || Str::startsWith($host, $this->_host) || Wildcard::matches($this->_host, $host);
95
    }
96
97 23
    public function getRemainingSeconds(): int
98
    {
99 23
        return $this->_timekeeper->getRemainingSeconds();
100
    }
101
102 3
    public function getCurrentRequestCount(): int
103
    {
104 3
        return $this->_requestCount;
105
    }
106
}
107