Passed
Push — master ( 23d826...e7e63f )
by Vince
01:30
created

limiter::getThrottle()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 4
eloc 13
c 2
b 0
f 0
nc 3
nop 0
dl 0
loc 20
rs 9.8333
1
<?php
2
/**
3
 * ==================================
4
 * Responsible PHP API
5
 * ==================================
6
 *
7
 * @link Git https://github.com/vince-scarpa/responsibleAPI.git
8
 *
9
 * @api Responible API
10
 * @package responsible\core\throttle
11
 *
12
 * @author Vince scarpa <[email protected]>
13
 *
14
 */
15
namespace responsible\core\throttle;
16
17
use responsible\core\exception;
18
use responsible\core\throttle;
19
use responsible\core\user;
20
21
class limiter
22
{
23
    /**
24
     * [$capacity Bucket volume]
25
     * @var integer
26
     */
27
    private $capacity = 100;
28
29
    /**
30
     * [$leakRate Constant rate at which the bucket will leak]
31
     * @var integer
32
     */
33
    private $leakRate = 1;
34
35
    /**
36
     * [$unpacked]
37
     */
38
    private $unpacked
39
40
    /**
41
     * [$timeframe Durations are in seconds]
42
     * @var array
43
     */
44
    private static $timeframe = [
0 ignored issues
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_PRIVATE, expecting ',' or ';' on line 44 at column 4
Loading history...
45
        'SECOND' => 1,
46
        'MINUTE' => 60,
47
        'HOUR' => 3600,
48
        'DAY' => 86400,
49
        'CUSTOM' => 0,
50
    ];
51
52
    /**
53
     * [$window Timeframe window]
54
     * @var integer
55
     */
56
    private $window;
57
58
    /**
59
     * [$unlimited Rate limiter bypass if true]
60
     * @var boolean
61
     */
62
    private $unlimited = false;
63
64
    /**
65
     * [$scope Set the default scope]
66
     * @var string
67
     */
68
    private $scope = 'private';
69
70
    /**
71
     * [setupOptions Set any Responsible API options]
72
     * @return self
73
     */
74
    public function setupOptions()
75
    {
76
        $options = $this->getOptions();
77
78
        if (isset($options['rateLimit'])) {
79
            $this->setCapacity($options['rateLimit']);
80
        }
81
82
        if (isset($options['rateWindow'])) {
83
            $this->setTimeframe($options['rateWindow']);
84
        }
85
86
        if (isset($options['leak']) && !$options['leak']) {
87
            $options['leakRate'] = 0;
88
        }
89
90
        if (isset($options['leakRate'])) {
91
            $this->setLeakRate($options['leakRate']);
92
        }
93
94
        if (isset($options['unlimited']) && ($options['unlimited'] == 1 || $options['unlimited'] == true)) {
95
            $this->setUnlimited();
96
        }
97
98
        if (isset($options['requestType']) && $options['requestType'] == 'debug') {
99
            $this->setUnlimited();
100
        }
101
102
        if( isset($this->account->scope) &&
103
            ($this->account->scope == 'anonymous' || $this->account->scope == 'public')
104
        ) {
105
           $this->scope = $this->account->scope;
106
        }
107
108
        return $this;
109
    }
110
111
    /**
112
     * [throttleRequest Build the Responsible API throttle]
113
     * @return void
114
     */
115
    public function throttleRequest()
116
    {
117
        if ($this->isUnlimited() || $this->scope !== 'private') {
118
            return true;
119
        }
120
121
        /**
122
         * [$unpack Unpack the account bucket data]
123
         */
124
        $this->unpacked = (new throttle\tokenPack)->unpack(
125
            $this->getAccount()->bucket
126
        );
127
        if (empty($this->unpacked)) {
128
            $this->unpacked = array(
129
                'drops' => 1,
130
                'time' => $this->getAccount()->access,
131
            );
132
        }
133
134
        $this->bucket = (new throttle\tokenBucket())
135
            ->setTimeframe($this->getTimeframe())
136
            ->setCapacity($this->getCapacity())
137
            ->setLeakRate($this->getLeakRate())
138
            ->pour($this->unpacked['drops'], $this->unpacked['time'])
139
        ;
140
141
        /**
142
         * Check if the bucket still has capacity to fill
143
         */
144
        if ($this->bucket->capacity()) {
145
            $this->bucket->pause(false);
146
            $this->bucket->fill();
147
        } else {
148
            if ($this->getLeakRate() <= 0) {
149
                if ($this->unpacked['pauseAccess'] == false) {
150
                    $this->bucket->pause(true);
151
                    $this->save();
152
                }
153
154
                if ($this->bucket->refill($this->getAccount()->access)) {
155
                    $this->save();
156
                }
157
            }
158
159
            (new exception\errorException)->error('TOO_MANY_REQUESTS');
160
        }
161
162
        $this->save();
163
    }
164
165
    /**
166
     * [updateBucket Store the buckets token data and user access time]
167
     * @return void
168
     */
169
    private function save()
170
    {
171
        $this->packed = (new throttle\tokenPack)->pack(
172
            $this->bucket->getTokenData()
173
        );
174
175
        /**
176
         * [Update account access]
177
         */
178
        /*$user = (new user\account($this->getAccount()->account_id))
179
            ->setBucketToken($this->packed)
180
            ->updateAccountAccess();*/
181
182
        $user = (new user\user)
183
            ->setAccountID($this->getAccount()->account_id)
184
            ->setBucketToken($this->packed)
185
            ->updateAccountAccess()
186
        ;
187
    }
188
189
    /**
190
     * [getThrottle Return a list of the throttled results]
191
     * @return array
192
     */
193
    public function getThrottle()
194
    {
195
        if ($this->isUnlimited() || $this->scope !== 'private') {
196
            return array(
197
                'unlimited' => true,
198
            );
199
        }
200
201
        $windowFrame = (is_string($this->getTimeframe()))
202
        ? $this->getTimeframe()
203
        : $this->getTimeframe() . 'secs'
204
        ;
205
206
        return array(
207
            'limit' => $this->getCapacity(),
208
            'leakRate' => $this->getLeakRate(),
209
            'leak' => $this->bucket->getLeakage(),
210
            'lastAccess' => $this->getLastAccessDate(),
211
            'description' => $this->getCapacity() . ' requests per ' . $windowFrame,
212
            'bucket' => $this->bucket->getTokenData(),
213
        );
214
    }
215
216
    /**
217
     * [getLastAccessDate Get the last recorded access in date format]
218
     * @return string
219
     */
220
    private function getLastAccessDate()
221
    {
222
        if (isset($this->bucket->getTokenData()['time'])) {
223
            return date('m/d/y h:i:sa', $this->bucket->getTokenData()['time']);
224
        }
225
226
        return 'Can\'t be converted';
227
    }
228
229
    /**
230
     * [setAccount Set the requests account]
231
     */
232
    public function setAccount($account)
233
    {
234
        $this->account = $account;
235
        return $this;
236
    }
237
238
    /**
239
     * [getAccount Get the requests account]
240
     */
241
    public function getAccount()
242
    {
243
        return $this->account;
244
    }
245
246
    /**
247
     * [options Responsible API options]
248
     * @param array $options
249
     */
250
    public function options($options)
251
    {
252
        $this->options = $options;
253
        return $this;
254
    }
255
256
    /**
257
     * [getOptions Get the stored Responsible API options]
258
     * @return array
259
     */
260
    private function getOptions()
261
    {
262
        return $this->options;
263
    }
264
265
    /**
266
     * [setCapacity Set the buckets capacity]
267
     * @param integer $capacity
268
     */
269
    public function setCapacity($capacity)
270
    {
271
        $this->capacity = $capacity;
272
    }
273
274
    /**
275
     * [getCapacity Get the buckets capacity]
276
     * @return integer
277
     */
278
    public function getCapacity()
279
    {
280
        return $this->capacity;
281
    }
282
283
    /**
284
     * [setTimeframe Set the window timeframe]
285
     * @param string|integer $timeframe
286
     */
287
    public function setTimeframe($timeframe)
288
    {
289
        if (is_numeric($timeframe)) {
290
            self::$timeframe['CUSTOM'] = $timeframe;
291
            $this->window = self::$timeframe['CUSTOM'];
292
            return;
293
        }
294
295
        if (isset(self::$timeframe[$timeframe])) {
296
            $this->window = self::$timeframe[$timeframe];
297
            return;
298
        }
299
300
        $this->window = self::$timeframe['MINUTE'];
301
    }
302
303
    /**
304
     * [getTimeframe Get the timeframe window]
305
     * @return integer
306
     */
307
    public function getTimeframe()
308
    {
309
        return $this->window;
310
    }
311
312
    /**
313
     * [setLeakRate Set the buckets leak rate]
314
     * Options: slow, medium, normal, default, fast or custom positive integer
315
     * @param string|integer $leakRate
316
     */
317
    private function setLeakRate($leakRate)
318
    {
319
        $this->leakRate = $leakRate;
320
    }
321
322
    /**
323
     * [getLeakRate Get the buckets leak rate]
324
     * @return string|integer
325
     */
326
    private function getLeakRate()
327
    {
328
        return $this->leakRate;
329
    }
330
331
    /**
332
     * [setUnlimited Rate limiter bypass]
333
     */
334
    private function setUnlimited()
335
    {
336
        $this->unlimited = true;
337
    }
338
339
    /**
340
     * [isUnlimited Check if the Responsible API is set to unlimited]
341
     * @return boolean
342
     */
343
    private function isUnlimited()
344
    {
345
        return $this->unlimited;
346
    }
347
}
348