limiter   A
last analyzed

Complexity

Total Complexity 35

Size/Duplication

Total Lines 289
Duplicated Lines 0 %

Importance

Changes 19
Bugs 0 Features 0
Metric Value
eloc 107
c 19
b 0
f 0
dl 0
loc 289
rs 9.6
wmc 35

13 Methods

Rating   Name   Duplication   Size   Complexity  
A packerObj() 0 3 1
A getAccount() 0 13 4
A bucketObj() 0 3 1
A __construct() 0 12 3
A getThrottle() 0 26 5
A setAccount() 0 4 1
A save() 0 18 2
A unpackBucket() 0 20 2
A throttlePause() 0 19 4
A throttleRequest() 0 19 4
A setupOptions() 0 24 4
A getLastAccessDate() 0 12 2
A getMockAccount() 0 17 2
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
use responsible\core\server;
21
use responsible\core\headers\header;
22
23
class limiter extends limiterOptions
24
{
25
    /**
26
     * [$unpacked]
27
     */
28
    private $unpacked;
29
30
    /**
31
     * [$packed]
32
     * @var string
33
     */
34
    private $packed;
35
36
    /**
37
     * [$bucket]
38
     * @var object
39
     */
40
    private $bucket;
41
42
    /**
43
     * [$tokenPacker Token packer class object]
44
     * @var object
45
     */
46
    private $tokenPacker;
47
48
    public function __construct($limit = null, $rate = null)
49
    {
50
        if (!is_null($limit)) {
51
            $this->setCapacity($limit);
52
        }
53
54
        if (!is_null($rate)) {
55
            $this->setTimeframe($rate);
56
        }
57
58
        $this->bucket = new throttle\tokenBucket;
59
        $this->tokenPacker = new throttle\tokenPack;
60
    }
61
62
    /**
63
     * [setupOptions Set any Responsible API options]
64
     * @return self
65
     */
66
    public function setupOptions()
67
    {
68
        $options = $this->getOptions();
69
70
        $server = new server([], $options);
71
        $this->isMockTest = $server->isMockTest();
72
73
        $this->setCapacity();
74
75
        $this->setTimeframe();
76
77
        $this->setLeakRate();
78
79
        $this->setUnlimited();
80
81
        $this->setDebugMode();
82
83
        if (isset($this->account->scope) &&
84
            ($this->account->scope == 'anonymous' || $this->account->scope == 'public')
85
        ) {
86
            $this->scope = $this->account->scope;
87
        }
88
89
        return $this;
90
    }
91
92
    /**
93
     * [throttleRequest Build the Responsible API throttle]
94
     * @return boolean|void
95
     */
96
    public function throttleRequest()
97
    {
98
        if ($this->isUnlimited() || $this->scope !== 'private') {
99
            return true;
100
        }
101
102
        $bucket = $this->bucketObj();
103
104
        $this->unpackBucket();
105
        
106
        if ($bucket->capacity()) {
107
            $bucket->pause(false);
108
            $bucket->fill();
109
110
        } else {
111
            $this->throttlePause();
112
        }
113
114
        $this->save();
115
    }
116
117
    /**
118
     * [throttlePause Throttle the limiter when there are too many requests]
119
     * @return void
120
     */
121
    private function throttlePause()
122
    {
123
        $account = $this->getAccount();
124
        $bucket = $this->bucketObj();
125
126
        if ($this->getLeakRate() <= 0) {
127
            if ($this->unpacked['pauseAccess'] == false) {
128
                $bucket->pause(true);
129
                $this->save();
130
            }
131
132
            if ($bucket->refill($account->access)) {
133
                $this->save();
134
            }
135
        }
136
137
        (new exception\errorException)
138
                ->setOptions($this->getOptions())
139
                ->error('TOO_MANY_REQUESTS');
140
    }
141
142
    /**
143
     * Unpack the account bucket data
144
     */
145
    private function unpackBucket()
146
    {
147
        $account = $this->getAccount();
148
        $bucket = $this->bucketObj();
149
        $packer = $this->packerObj();
150
151
        $this->unpacked = $packer->unpack(
152
            $account->bucket
153
        );
154
        if (empty($this->unpacked)) {
155
            $this->unpacked = array(
156
                'drops' => 1,
157
                'time' => $account->access,
158
            );
159
        }
160
161
        $bucket->setTimeframe($this->getTimeframe())
162
            ->setCapacity($this->getCapacity())
163
            ->setLeakRate($this->getLeakRate())
164
            ->pour($this->unpacked['drops'], $this->unpacked['time'])
165
        ;
166
    }
167
168
    /**
169
     * [getThrottle Return a list of the throttled results]
170
     * @return array
171
     */
172
    public function getThrottle()
173
    {
174
        if ($this->isUnlimited() || $this->scope !== 'private') {
175
            return array(
176
                'unlimited' => true,
177
            );
178
        }
179
180
        $bucket = $this->bucketObj();
181
182
        $windowFrame = (is_string($this->getTimeframe()))
183
        ? $this->getTimeframe()
184
        : $this->getTimeframe() . 'secs'
185
        ;
186
187
        if (is_null($bucket)) {
188
            return;
189
        }
190
191
        return array(
192
            'limit' => $this->getCapacity(),
193
            'leakRate' => $this->getLeakRate(),
194
            'leak' => $bucket->getLeakage(),
195
            'lastAccess' => $this->getLastAccessDate(),
196
            'description' => $this->getCapacity() . ' requests per ' . $windowFrame,
197
            'bucket' => $bucket->getTokenData(),
198
        );
199
    }
200
201
    /**
202
     * [updateBucket Store the buckets token data and user access time]
203
     * @return void
204
     */
205
    private function save()
206
    {
207
        $bucket = $this->bucketObj();
208
        $packer = $this->packerObj();
209
210
        $this->packed = $packer->pack(
211
            $bucket->getTokenData()
212
        );
213
214
        if($this->isMockTest) {
215
            return;
216
        }
217
        
218
        // @codeCoverageIgnoreStart
219
        (new user\user)
220
            ->setAccountID($this->getAccount()->account_id)
221
            ->setBucketToken($this->packed)
222
            ->updateAccountAccess()
223
        ;
224
        // @codeCoverageIgnoreEnd
225
    }
226
227
    /**
228
     * [getLastAccessDate Get the last recorded access in date format]
229
     * @return string
230
     */
231
    private function getLastAccessDate()
232
    {
233
        $bucket = $this->bucketObj();
234
235
        if (isset($bucket->getTokenData()['time'])) {
236
            return date('m/d/y h:i:sa', $bucket->getTokenData()['time']);
237
        }
238
239
        /**
240
         * @codeCoverageIgnore
241
         */
242
        return 'Can\'t be converted';
243
    }
244
245
    /**
246
     * [setAccount Set the requests account]
247
     * @return self
248
     */
249
    public function setAccount($account)
250
    {
251
        $this->account = $account;
252
        return $this;
253
    }
254
255
    /**
256
     * [getAccount Get the requests account]
257
     */
258
    public function getAccount()
259
    {
260
        if($this->isMockTest) {
261
            $this->getMockAccount();
262
        }
263
264
        if (is_null($this->account) || empty($this->account)) {
265
            $header = new header;
266
            $header->setOptions($this->getOptions());
267
            $header->unauthorised();
268
        }
269
270
        return $this->account;
271
    }
272
273
    /**
274
     * Build a mock account for testing
275
     * @return void
276
     */
277
    private function getMockAccount()
278
    {
279
        $bucket = $this->bucketObj();
280
        $packer = $this->packerObj();
281
282
        $mockAccount = [];
283
284
        if(!isset($mockAccount['bucket'])) {
285
            $mockAccount['bucket'] = $packer->pack(
286
                $bucket->getTokenData()
287
            );
288
        }
289
290
        $mockAccount['access'] = time();
291
292
        $this->setAccount((object)$mockAccount);
293
        $this->mockAccount = (object)$mockAccount;
294
    }
295
296
    /**
297
     * [bucketObj Get the bucket class object]
298
     * @return object
299
     */
300
    private function bucketObj()
301
    {
302
        return $this->bucket;
303
    }
304
305
    /**
306
     * [packerObj Get the token packer class object]
307
     * @return object
308
     */
309
    private function packerObj()
310
    {
311
        return $this->tokenPacker;
312
    }
313
}
314