Manager   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 121
Duplicated Lines 0 %

Test Coverage

Coverage 90.91%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 42
c 1
b 0
f 0
dl 0
loc 121
rs 10
ccs 40
cts 44
cp 0.9091
wmc 14

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getRate() 0 3 1
A getCapacity() 0 3 1
A __construct() 0 6 1
A consume() 0 19 3
A getLimit() 0 3 1
A laodAllowance() 0 17 5
A saveAllowance() 0 8 2
1
<?php
2
namespace RazonYang\TokenBucket;
3
4
use Psr\Log\LoggerInterface;
5
use RazonYang\TokenBucket\Serializer\PhpSerializer;
6
7
/**
8
 * Manager is an abstract bucket manager that implements ManagerInterface.
9
 */
10
abstract class Manager implements ManagerInterface
11
{
12
    /**
13
     * @var LoggerInterface
14
     */
15
    private $logger;
16
17
    /**
18
     * @var int $capacity
19
     */
20
    private $capacity;
21
22 4
    public function getCapacity(): int
23
    {
24 4
        return $this->capacity;
25
    }
26
27
    /**
28
     * @var float $rate
29
     */
30
    private $rate;
31
32 4
    public function getRate(): float
33
    {
34 4
        return $this->rate;
35
    }
36
37
    /**
38
     * @var SerializerInterface $serializer
39
     */
40
    private $serializer;
41
42 14
    public function __construct(int $capacity, float $rate, LoggerInterface $logger, ?SerializerInterface $serializer = null)
43
    {
44 14
        $this->capacity = $capacity;
45 14
        $this->rate = $rate;
46 14
        $this->logger = $logger;
47 14
        $this->serializer = $serializer ?? new PhpSerializer();
48 14
    }
49
50 2
    public function laodAllowance(string $name): array
51
    {
52 2
        $value = $this->load($name);
53 2
        if ($value === false) {
54 1
            return [$this->capacity, 0];
55
        }
56
57
        try {
58 2
            $data = $this->serializer->unserialize($value);
59 1
            if (!is_array($data) || count($data) !== 2) {
60
                throw new \Exception('invalid data');
61
            }
62
63 1
            return $data;
64 1
        } catch (\Throwable $e) {
65 1
            $this->logger->error('Unable to load allowance of {name}: {exception}', ['name' => $name, 'exception' => $e->__toString()]);
66 1
            return [$this->capacity, 0];
67
        }
68
    }
69
70
    /**
71
     * Loads allowance, return data if exists, otherwise false.
72
     *
73
     * @param string $name
74
     *
75
     * @return mixed|false
76
     */
77
    abstract protected function load(string $name);
78
79 1
    public function saveAllowance(string $name, int $allowance, int $timestamp)
80
    {
81
        try {
82 1
            $value = $this->serializer->serialize([$allowance, $timestamp]);
83 1
            $this->save($name, $value);
84
        } catch (\Throwable $e) {
85
            $this->logger->error('Unable to save allowance of {name}: {exception}', ['name' => $name, 'exception' => $e->__toString()]);
86
            return [$this->capacity, 0];
87
        }
88 1
    }
89
90
    /**
91
     * Saves allowance.
92
     *
93
     * @param string $name
94
     * @param mixed  $value
95
     *
96
     * @throws \Throwable throws an exception if save fails.
97
     */
98
    abstract protected function save(string $name, $value);
99
100
    /**
101
     * Consumes a token from the bucket.
102
     *
103
     * @param string $name bucket name.
104
     *
105
     * @return bool returns true on success, otherwise false.
106
     */
107 1
    public function consume(string $name, ?int &$remaining = null, ?int &$reset = null): bool
108
    {
109 1
        list($allowance, $timestamp) = $this->laodAllowance($name);
110 1
        $now = time();
111 1
        $allowance += intval(($now - $timestamp) / $this->rate);
112 1
        if ($allowance > $this->capacity) {
113 1
            $allowance = $this->capacity;
114
        }
115
116 1
        if ($allowance < 1) {
117 1
            $remaining = 0;
118 1
            $reset = intval($this->capacity * $this->rate) - ($now - $timestamp);
119 1
            return false;
120
        }
121
122 1
        $remaining = $allowance - 1;
123 1
        $reset = intval(($this->capacity - $allowance) * $this->rate);
124 1
        $this->saveAllowance($name, $remaining, $now);
125 1
        return true;
126
    }
127
128 3
    public function getLimit(int $period): int
129
    {
130 3
        return min($this->capacity, intval($period / $this->rate));
131
    }
132
}
133