Test Failed
Pull Request — master (#43)
by
unknown
02:26
created

ApcuStorage::saveIfNotExists()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 2
Metric Value
eloc 4
c 2
b 0
f 2
dl 0
loc 9
rs 10
cc 3
nc 2
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\RateLimiter\Storage;
6
7
use InvalidArgumentException;
8
use Yiisoft\Yii\RateLimiter\Exception\CannotUseException;
9
10
final class ApcuStorage implements StorageInterface
11
{
12
    private const DEFAULT_FIX_PRECISION_RATE = 1000;
13
14
    /**
15
     * @param int $fixPrecisionRate 
16
     * Apcu_cas of ACPu does not support float,  and yet supports int.
17
     * APCu's stored value multiply by $fixPrecisionRate converts to int,
18
     * AND the getter's value divide by $fixPrecisionRate converts to float.
19
     * So use it to improve precision.
20
     */
21
    public function __construct(
22
        private int $fixPrecisionRate = self::DEFAULT_FIX_PRECISION_RATE
23
    ) {
24
        /**
25
         * To use this implementation class, the [APCu PHP extension](http://www.php.net/apcu) must be loaded,
26
         * And you should add "apc.enabled = 1" to your php.ini.
27
         * In order to enable APCu for CLI you should add "apc.enable_cli = 1" to your php.ini.
28
         */
29
        if (!extension_loaded('apcu') || !ini_get('apc.enabled')) {
30
            throw new CannotUseException('APCu extension is not loaded or not enabled.');
31
        }
32
    }
33
34
    public function saveIfNotExists(string $key, mixed $value, int $ttl): bool
35
    {
36
        if ((!is_int($value)) && !is_float($value)) {
37
            throw new InvalidArgumentException('The value is not supported by ApcuStorage,it must be int or float');
38
        }
39
40
        $value = (int) ($value * $this->fixPrecisionRate);
41
42
        return apcu_add($key, $value, $ttl);
0 ignored issues
show
Bug Best Practice introduced by
The expression return apcu_add($key, $value, $ttl) could return the type array which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
43
    }
44
45
    public function saveCompareAndSwap(string $key, mixed $oldValue, mixed $newValue, int $ttl): bool
46
    {
47
        if ((!is_int($oldValue)) && !is_float($oldValue)) {
48
            throw new InvalidArgumentException('The oldValue is not supported by ApcuStorage,it must be int or float');
49
        }
50
51
        if ((!is_int($newValue)) && !is_float($newValue)) {
52
            throw new InvalidArgumentException('The newValue is not supported by ApcuStorage,it must be int or float');
53
        }
54
55
        $oldValue = (int) ($oldValue * $this->fixPrecisionRate);
56
        $newValue = (int) ($newValue * $this->fixPrecisionRate);
57
58
        return apcu_cas($key, $oldValue, $newValue);
59
    }
60
61
    public function get(string $key): mixed
62
    {
63
        $value = apcu_fetch($key);
64
65
        if ((!is_int($value)) && !is_float($value) && !is_bool($value)) {
66
            throw new InvalidArgumentException('The value is not supported by ApcuStorage,it must be bool, int or float');
67
        }
68
69
        if ($value != false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $value of type boolean|double|integer against false; this is ambiguous if the integer can be zero. Consider using a strict comparison !== instead.
Loading history...
70
            $value = (float)$value / $this->fixPrecisionRate;
71
        }
72
        return $value;
73
    }
74
}
75