Passed
Push — master ( 89971b...1a5c63 )
by Vince
01:47
created

limiter::getAccount()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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