RateLimit   A
last analyzed

Complexity

Total Complexity 11

Size/Duplication

Total Lines 86
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 11
eloc 37
c 1
b 0
f 0
dl 0
loc 86
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 3
A call() 0 44 5
A fail() 0 10 2
A addHeaders() 0 2 1
1
<?php
2
3
namespace CodeBlog\RateLimit;
4
5
/**
6
 * Class CodeBlog RateLimit
7
 *
8
 * @author Whallysson Avelino <https://github.com/whallysson>
9
 * @package CodeBlog\RateLimit
10
 */
11
class RateLimit extends VariableCache {
12
13
    /**
14
     * Requests for hours
15
     * @var type interger
0 ignored issues
show
Bug introduced by
The type CodeBlog\RateLimit\type was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
     */
17
    private $maxResquests = 100;
18
    private $timeReset = 3600; // 01h
19
20
    public function __construct(string $cacheFolder, string $key, int $max = null, $reset = null) {
21
        parent::__construct($cacheFolder);
22
        $this->maxResquests = (int)($max ? $max : $this->maxResquests);
0 ignored issues
show
Documentation Bug introduced by
It seems like (int)$max ? $max : $this->maxResquests of type integer is incompatible with the declared type CodeBlog\RateLimit\type of property $maxResquests.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
23
        $this->timeReset = ($reset ? $reset : $this->timeReset);
24
25
        $this->call($key);
26
    }
27
28
    public function call($key) {
29
        if ($key) {
30
31
            $data = $this->fetch($key);
32
            if (false === $data) {
33
34
                // First time or previous perion expired,
35
                // initialize and save a new entry
36
                $remaining = $this->maxResquests - 1;
37
                $variables = [
38
                    'remaining' => $remaining,
39
                    'created' => time(),
40
                ];
41
                $reset = $this->timeReset;
42
43
                $this->store($key, $variables, $this->timeReset);
0 ignored issues
show
Bug introduced by
It seems like $this->timeReset can also be of type mixed; however, parameter $expire of CodeBlog\RateLimit\VariableCache::store() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

43
                $this->store($key, $variables, /** @scrutinizer ignore-type */ $this->timeReset);
Loading history...
44
            } else {
45
                // Take the current entry and update it
46
                $remaining = ((--$data['remaining'] >= 0) ? $data['remaining'] : -1);
47
                $variables = [
48
                    'remaining' => $remaining,
49
                    'created' => $data['created'],
50
                ];
51
                $reset = (($data['created'] + $this->timeReset) - time());
52
53
                $this->store($key, $variables, $reset);
54
            }
55
56
            // Set rating headers
57
            $this->addHeaders('X-Rate-Limit-Limit', $this->maxResquests);
58
            $this->addHeaders('X-Rate-Limit-Reset', $reset);
59
            $this->addHeaders('X-Rate-Limit-Remaining', $remaining);
60
61
            // Check if the current key is allowed to pass
62
            if ($remaining < 0) {
63
                // Rewrite remaining headers
64
                $this->addHeaders('X-Rate-Limit-Remaining', 0);
65
66
                // Exits with status "429 Too Many Requests" (see doc below)
67
                $this->fail();
68
            }
69
        } else {
70
            // Exits with status "429 Too Many Requests" (see doc below)
71
            $this->fail();
72
        }
73
    }
74
75
    protected function addHeaders($headers, $value) {
76
        header("{$headers}: {$value}");
77
    }
78
79
    /**
80
     * Exits with status "429 Too Many Requests"
81
     *
82
     * Work around on Apache's issue: it does not support
83
     * status code 429 until version 2.4
84
     *
85
     * @link http://stackoverflow.com/questions/17735514/php-apache-silently-converting-http-429-and-others-to-500
86
     */
87
    protected function fail() {
88
        http_response_code(429);
89
        header('HTTP/1.1 429 Too Many Requests', false, 429);
90
91
        // Write the remaining headers
92
        foreach (getallheaders() as $key => $value) {
93
            $this->addHeaders($key, $value);
94
        }
95
96
        throw new \Exception('Too Many Requests. Rate limit reached');
97
    }
98
99
}
100