Test Setup Failed
Pull Request — master (#1)
by
unknown
06:50
created

BandwidthQuota::enforce()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 2
cp 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
3
/**
4
 * This file is part of laravel-quota
5
 *
6
 * (c) David Faith <[email protected]>
7
 *
8
 * Full copyright and license information is available
9
 * in the LICENSE file distributed with this source code.
10
 */
11
12
namespace Projectmentor\Quota;
13
14
use \bandwidthThrottle\tokenBucket\storage\FileStorage;
15
use \bandwidthThrottle\tokenBucket\Rate;
16
use \bandwidthThrottle\tokenBucket\TokenBucket;
17
use \bandwidthThrottle\tokenBucket\BlockingConsumer;
18
19
20
/**
21
 * This is the BandwidthQuota class.
22
 * It provides rate-of-flow quota restriction
23
 * e.g. limit api requests to 60/second.
24
 *
25
 * Currently only support FileStorage type
26
 * of persistance. 
27
 */
28
29
class BandwidthQuota extends Quota 
30
{
31
    /**
32
     * The driver shortcut name as known to the IoC.
33
     * 
34
     * @var string
35
     */
36
    protected $driver;
37
38
    /**
39
     * The file path, if driver resolves to FileStorage
40
     *
41
     * @var string
42
     */
43
    protected $path;
44
45
    /**
46
     * The persistant storage for the bucket.
47
     *
48
     * @var mixed
49
     */
50
    protected $storage;
51
52
    /**
53
     * The rate
54
     *
55
     * @var \bandwidthThrottle\tokenBucket\Rate
56
     */
57
    protected $rate;
58
59
    /**
60
     * The bucket
61
     *
62
     * @var \bandwidthThrottle\tokenBucket\TokenBucket
63
     */
64
    protected $bucket;
65
66
    /**
67
     * The blocking consumer
68
     *
69
     * @var \bandwidthThrottle\tokenBucket\BlockingConsumer
70
     */
71
    protected $blocker;
72
73
    /**
74
     *  Construct instance.
75
     *
76
     *  NOTE: Laravel 5.4 breaking change:
77
     *      App::make(...) ~> App::makeWith(...)
78
     *
79
     *  TODO: REFACTOR Currently only supports FileStorage.
80
     *  Also, resolving from container with parameters is not
81
     *  best practice.
82
     *  @See https://github.com/laravel/internals/issues/391
83
     *
84
     *  @param string $connection
85
     *  @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
86
     */
87 2
    public function __construct($connection)
88
    {
89 2
        parent::__construct($connection);
90
91 2
        $this->driver = config($this->index . '.driver');
92
93
        //TODO: REFACTOR for multiple storage types.
94 2
        if($this->driver != 'quota.storage.file')
95 2
            throw new \Exception('Driver: ' . $this->driver . ' not supported.');
96
97 2
        $this->path = config($this->index . '.path');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
98 2
        $this->storage = app($this->driver, ['path' =>  $this->path]);
99
        //END REFACTOR
100
101
        //Resolve rate via IoC container.
102 2
        $this->rate = app('quota.rate',
103 2
            ['limit' => $this->limit, 'period' => $this->period]);
104
105
        //Resolve bucket via IoC container.
106 2
        $capacity = config($this->index . '.capacity');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
107 2
        $this->bucket = app('quota.bucket', [
108 2
            'capacity' => $capacity,
109 2
            'rate' => $this->rate,
110 2
            'storage' => $this->storage
111 2
        ]);
112
113
        //Bootstrap storage if not already initialized.
114 2
        $this->bucket->bootstrap($capacity);
115
116
        //Optionally enclose bucket within a blocking consumer.
117 2
        if(config($this->index . '.block') == TRUE)
118 2
        {
119 2
            $this->blocker = app('quota.blocker', ['bucket' => $this->bucket]);
120 2
        }
121 2
    }
122
123
    /**
124
     * Enforce the quota.
125
     * Future public interface
126
     *
127
     * @return boolean true on success
128
     *
129
     * @throws ErrorException
130
     * Overquota exceeds bandwidth rate.
131
     *
132
     * @throws LengthException
133
     *  The token amount is larger than the bucket's capacity.
134
     *
135
     * @throws StorageException
136
     * The stored microtime could not be accessed.
137
     */
138
    public function enforce()
139
    {
140
        return $this->consume();
141
    }
142
143
    /**
144
     * Consume tokens.
145
     *
146
     * If  $this->bucket does not have sufficient tokens
147
     * and $this->block is true, then block until available.
148
     *
149
     * If this->bucket does not have sufficient tokens
150
     * and $this->block is false, the throw exception.
151
     *
152
     * If this->bucket has sufficent tokens, then:
153
     *
154
     *    If $this is logging to database then:
155
     *      If  hits exceed periodic limit then:
156
     *          increment misses in the database
157
     *          throw exception.
158
     *      Else
159
     *          increment hits in database
160
     *
161
     *    reduce the amount of tokens in the bucket
162
     *    return  true.
163
     *
164
     * @param int $tokens default  === 1
165
     * @return boolean true on success
166
     *
167
     * @throws ErrorException
168
     * Overquota exceeds bandwidth rate.
169
     *
170
     * @throws LengthException
171
     *  The token amount is larger than the bucket's capacity.
172
     *
173
     * @throws StorageException
174
     * The stored microtime could not be accessed.
175
     */
176
    public function consume($tokens = 1)
0 ignored issues
show
Coding Style introduced by
function consume() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
177
    {
178
        if(isset($this->bucket))
179
        {
180
            if (! isset($this->blocker))
181
            {
182
                $result = $this->bucket->consume($tokens, $seconds);
183
                if( $result === false)
184
                    throw new \ErrorException(
185
                        __CLASS__ . '::' . __FUNCTION__ .
186
                        ' Overquota. Exceeded bandwidth. Wait ' . $seconds . ' seconds.');
187
            }
188
            else
189
            {
190
                $result = $this->blocker->consume($tokens);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $result is correct as $this->blocker->consume($tokens) (which targets bandwidthThrottle\tokenB...kingConsumer::consume()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
191
            }
192
        }
193
        return (isset($result)) ? $result : false;
194
    }
195
196
    /**
197
     * Clean up before removing storage for gc.
198
     * Releases $blocker, $bucket, $rate
199
     * Does not release $storage.
200
     * Does not unlink file storage file.
201
     *
202
     * @return void
203
     */
204 2
    public function release()
205
    {
206 2
        if (isset($this->blocker))
207 2
            unset($this->blocker);
208
209 2
        if (isset($this->bucket))
210 2
            unset($this->bucket);
211
212 2
        if (isset($this->rate))
213 2
            unset($this->rate);
214 2
    }
215
216
    /**
217
     * Unlink the handle to storage then release storage.
218
     * e.g: if file storage, then unlink (delete) file on disk.
219
     *
220
     * @return void
221
     */
222 2
    public function remove()
223
    {
224 2
        if(isset($this->storage))
225 2
        {
226 2
            $this->storage->remove();
227 2
            unset($this->storage);
228 2
        }
229 2
    }
230
231
    /**
232
     * Orderly clean up.
233
     *
234
     * @internal
235
     */
236 2
    public function __destruct()
237
    {
238 2
        $this->release();
239 2
        $this->remove();
240 2
    }
241
242
    /**
243
     * Get the driver IoC shortcut name
244
     *
245
     * @return string
246
     */
247
    public function getDriver()
248
    {
249
        return $this->driver;
250
    }
251
252
    /**
253
     * Get the path to FileStorage
254
     *
255
     * @return string
256
     */
257
    public function getPath()
258
    {
259
        return $this->path;
260
    }
261
262
    /**
263
     * Get the persistant storage backing instance.
264
     *
265
     * @return mixed
266
     */
267 1
    public function getStorage()
268
    {
269 1
        return $this->storage;
270
    }
271
272
    /**
273
     * Get the rate instance.
274
     *
275
     * @return \bandwidthThrottle\tokenBucket\Rate
276
     */
277 1
    public function getRate()
278
    {
279 1
        return $this->rate;
280
    }
281
282
    /**
283
     * Get the TokenBucket instance.
284
     *
285
     * @return \bandwidthThrottle\tokenBucket\TokenBucket
286
     */
287 1
    public function getBucket()
288
    {
289 1
        return $this->bucket;
290
    }
291
292
    /**
293
     * Get the (optional) BlockingConsumer instance.
294
     *
295
     * @return \bandwidthThrottle\tokenBucket\BlockingConsumer
296
     */
297 2
    public function getBlocker()
298
    {
299 2
        return $this->blocker;
300
    }
301
302
    /**
303
     * Set the driver IoC shortcut name.
304
     *
305
     * @param  string $driver
306
     * @return void
307
     */
308
    public function setDriver($driver)
309
    {
310
        $this->driver = $driver;
311
    }
312
313
    /**
314
     * Set the path for FileStorage
315
     *
316
     * @param string $path
317
     * @return void
318
     */
319
    public function setPath($path)
320
    {
321
        //TODO: validate path
322
        $this->path = $path;
323
    }
324
    /**
325
     * Set the persistant storage backing instance.
326
     *
327
     * @param  mixed $storage
328
     * @return void
329
     */
330
    public function setStorage($storage)
331
    {
332
        $this->storage = $storage;
333
    }
334
335
    /**
336
     * Set the rate instance.
337
     *
338
     * @param \bandwidthThrottle\tokenBucket\Rate $rate
339
     * @return void
340
     */
341
    public function setRate($rate)
342
    {
343
        //TODO: validate rate
344
        $this->rate = $rate;
345
    }
346
347
    /**
348
     * Set the TokenBucket instance.
349
     *
350
     * @param \bandwidthThrottle\tokenBucket\TokenBucket $bucket
351
     * @return void
352
     */
353
    public function setBucket($bucket)
354
    {
355
        $this->bucket = $bucket;
356
    }
357
358
    /**
359
     * Set the (optional) BlockingConsumer instance.
360
     *
361
     * @param \bandwidthThrottle\tokenBucket\BlockingConsumer $blocker
362
     * @return void
363
     */
364
    public function setBlocker($blocker)
365
    {
366
        $this->blocker = $blocker;
367
    }
368
}
0 ignored issues
show
Coding Style introduced by
As per coding style, files should not end with a newline character.

This check marks files that end in a newline character, i.e. an empy line.

Loading history...
369