Passed
Pull Request — master (#204)
by
unknown
02:16
created

CacheCounter::remainingEmpty()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Web\RateLimiter;
6
7
use Psr\SimpleCache\CacheInterface;
8
9
/**
10
 * CacheCounter implements generic сell rate limit algorithm https://en.wikipedia.org/wiki/Generic_cell_rate_algorithm
11
 */
12
final class CacheCounter
13
{
14
    private int $period;
15
16
    private int $limit;
17
18
    private ?string $id = null;
19
20
    private CacheInterface $storage;
21
22
    private int $arrivalTime;
23
24
    public function __construct(int $limit, int $period, CacheInterface $storage)
25
    {
26
        $this->limit = $limit;
27
        $this->period = $period;
28
        $this->storage = $storage;
29
    }
30
31
    public function setId(string $id): void
32
    {
33
        $this->id = $id;
34
    }
35
36
    public function limitIsReached(): bool
37
    {
38
        $this->checkParams();
39
        $this->arrivalTime = time();
40
        $theoreticalArrivalTime = $this->calculateTheoreticalArrivalTime($this->getStorageValue());
41
        if ($this->remainingEmpty($theoreticalArrivalTime)) {
42
            return true;
43
        }
44
45
        $this->setStorageValue($theoreticalArrivalTime);
46
47
        return false;
48
    }
49
50
    private function checkParams(): void
51
    {
52
        if ($this->id === null) {
53
            throw new \RuntimeException('The counter id not set');
54
        }
55
56
        if ($this->limit < 1) {
57
            throw new \InvalidArgumentException('The limit must be a positive value.');
58
        }
59
60
        if ($this->period < 1) {
61
            throw new \InvalidArgumentException('The period must be a positive value.');
62
        }
63
    }
64
65
    private function getEmissionInterval(): float
66
    {
67
        return (float)($this->period / $this->limit);
68
    }
69
70
    private function calculateTheoreticalArrivalTime(float $theoreticalArrivalTime): float
71
    {
72
        return max($this->arrivalTime, $theoreticalArrivalTime) + $this->getEmissionInterval();
73
    }
74
75
    private function remainingEmpty(float $theoreticalArrivalTime): bool
76
    {
77
        $allowAt = $theoreticalArrivalTime - $this->period;
78
79
        return ((floor($this->arrivalTime - $allowAt) / $this->getEmissionInterval()) + 0.5) < 1;
80
    }
81
82
    private function getStorageValue(): float
83
    {
84
        return $this->storage->get($this->id, (float)$this->arrivalTime);
85
    }
86
87
    private function setStorageValue(float $theoreticalArrivalTime): void
88
    {
89
        $this->storage->set($this->id, $theoreticalArrivalTime);
90
    }
91
}
92