Completed
Push — master ( 9a2ca9...4f5284 )
by Nikola
17s queued 12s
created

ApcuRateLimiter::limitSilently()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 12
cts 12
cp 1
rs 9.6666
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace RateLimit;
6
7
use RateLimit\Exception\CannotUseRateLimiter;
8
use RateLimit\Exception\LimitExceeded;
9
10
final class ApcuRateLimiter implements RateLimiter, SilentRateLimiter
11
{
12
    /** @var string */
13
    private $keyPrefix;
14
15 5
    public function __construct(string $keyPrefix = '')
16
    {
17 5
        if (!\extension_loaded('apcu') || \ini_get('apc.enabled') === '0') {
18
            throw new CannotUseRateLimiter('APCu extension is not loaded or not enabled.');
19
        }
20
21 5
        if (\ini_get('apc.use_request_time') === '1') {
22
            throw new CannotUseRateLimiter('APCu ini configuration "apc.use_request_time" should be set to "0".');
23
        }
24
25 5
        $this->keyPrefix = $keyPrefix;
26 5
    }
27
28 2
    public function limit(string $identifier, Rate $rate): void
29
    {
30 2
        $limitKey = $this->limitKey($identifier, $rate->getInterval());
31
32 2
        $current = $this->getCurrent($limitKey);
33 2
        if ($current >= $rate->getOperations()) {
34 2
            throw LimitExceeded::for($identifier, $rate);
35
        }
36
37 2
        $this->updateCounter($limitKey, $rate->getInterval());
38 2
    }
39
40 3
    public function limitSilently(string $identifier, Rate $rate): Status
41
    {
42 3
        $interval = $rate->getInterval();
43 3
        $limitKey = $this->limitKey($identifier, $interval);
44 3
        $timeKey = $this->timeKey($identifier, $interval);
45
46 3
        $current = $this->getCurrent($limitKey);
47 3
        if ($current <= $rate->getOperations()) {
48 3
            $current = $this->updateCounterAndTime($limitKey, $timeKey, $interval);
49
        }
50
51 3
        return Status::from(
52 3
            $identifier,
53 3
            $current,
54 3
            $rate->getOperations(),
55 3
            \time() + \max(0, $interval - $this->getElapsedTime($timeKey))
56
        );
57
    }
58
59 5
    private function limitKey(string $identifier, int $interval): string
60
    {
61 5
        return \sprintf('%s%s:%d', $this->keyPrefix, $identifier, $interval);
62
    }
63
64 3
    private function timeKey(string $identifier, int $interval): string
65
    {
66 3
        return \sprintf('%s%s:%d:time', $this->keyPrefix, $identifier, $interval);
67
    }
68
69 5
    private function getCurrent(string $limitKey): int
70
    {
71 5
        return (int) \apcu_fetch($limitKey);
72
    }
73
74 3
    private function updateCounterAndTime(string $limitKey, string $timeKey, int $interval): int
75
    {
76 3
        $current = $this->updateCounter($limitKey, $interval);
77
78 3
        if ($current === 1) {
79 3
            \apcu_store($timeKey, \time(), $interval);
80
        }
81
82 3
        return $current;
83
    }
84
85 5
    private function updateCounter(string $limitKey, int $interval): int
86
    {
87 5
        $current = \apcu_inc($limitKey, 1, $success, $interval);
88
89 5
        return $current === false ? 1 : $current;
90
    }
91
92 3
    protected function getElapsedTime(string $timeKey): int
93
    {
94 3
        return \time() - (int) \apcu_fetch($timeKey);
95
    }
96
}
97