Passed
Push — master ( ebb577...c43191 )
by Alexander
03:05
created

ArrayCache::deleteMultiple()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 5
nc 2
nop 1
dl 0
loc 8
c 0
b 0
f 0
cc 2
rs 10
ccs 6
cts 6
cp 1
crap 2
1
<?php declare(strict_types=1);
2
3
namespace Yiisoft\Cache;
4
5
use DateInterval;
6
use DateTime;
7
use Psr\SimpleCache\CacheInterface;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Yiisoft\Cache\CacheInterface. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use Yiisoft\Cache\Exception\InvalidArgumentException;
9
10
/**
11
 * ArrayCache provides caching for the current request only by storing the values in an array.
12
 *
13
 * See {@see \Psr\SimpleCache\CacheInterface} for common cache operations that ArrayCache supports.
14
 */
15
final class ArrayCache implements CacheInterface
16
{
17
    private const EXPIRATION_INFINITY = 0;
18
    private const EXPIRATION_EXPIRED = -1;
19
20
    private $cache = [];
21
22 162
    public function get($key, $default = null)
23
    {
24 162
        $this->validateKey($key);
25 161
        if (isset($this->cache[$key]) && !$this->isExpired($key)) {
26 134
            $value = $this->cache[$key][0];
27 134
            if (is_object($value)) {
28 20
                $value = clone $value;
29
            }
30
31 134
            return $value;
32
        }
33
34 68
        return $default;
35
    }
36
37 186
    public function set($key, $value, $ttl = null): bool
38
    {
39 186
        $this->validateKey($key);
40 185
        $expiration = $this->ttlToExpiration($ttl);
41 185
        if ($expiration < 0) {
42 2
            return $this->delete($key);
43
        }
44 185
        if (is_object($value)) {
45 50
            $value = clone $value;
46
        }
47 185
        $this->cache[$key] = [$value, $expiration];
48 185
        return true;
49
    }
50
51 29
    public function delete($key): bool
52
    {
53 29
        $this->validateKey($key);
54 28
        unset($this->cache[$key]);
55 28
        return true;
56
    }
57
58 182
    public function clear(): bool
59
    {
60 182
        $this->cache = [];
61 182
        return true;
62
    }
63
64 26
    public function getMultiple($keys, $default = null): iterable
65
    {
66 26
        $keys = $this->iterableToArray($keys);
67 25
        $this->validateKeys($keys);
68 24
        $results = [];
69 24
        foreach ($keys as $key) {
70 24
            $value = $this->get($key, $default);
71 24
            $results[$key] = $value;
72
        }
73 24
        return $results;
74
    }
75
76 31
    public function setMultiple($values, $ttl = null): bool
77
    {
78 31
        $values = $this->iterableToArray($values);
79 30
        $this->validateKeysOfValues($values);
80 30
        foreach ($values as $key => $value) {
81 30
            $this->set((string)$key, $value, $ttl);
82
        }
83 30
        return true;
84
    }
85
86 4
    public function deleteMultiple($keys): bool
87
    {
88 4
        $keys = $this->iterableToArray($keys);
89 3
        $this->validateKeys($keys);
90 2
        foreach ($keys as $key) {
91 2
            $this->delete($key);
92
        }
93 2
        return true;
94
    }
95
96 38
    public function has($key): bool
97
    {
98 38
        $this->validateKey($key);
99 37
        return isset($this->cache[$key]) && !$this->isExpired($key);
100
    }
101
102
    /**
103
     * Checks whether item is expired or not
104
     * @param string $key
105
     * @return bool
106
     */
107 136
    private function isExpired(string $key): bool
108
    {
109 136
        return $this->cache[$key][1] !== 0 && $this->cache[$key][1] <= time();
110
    }
111
112
    /**
113
     * Converts TTL to expiration
114
     * @param int|DateInterval|null $ttl
115
     * @return int
116
     */
117 191
    private function ttlToExpiration($ttl): int
118
    {
119 191
        $ttl = $this->normalizeTtl($ttl);
120
121 191
        if ($ttl === null) {
122 183
            $expiration = static::EXPIRATION_INFINITY;
123 12
        } elseif ($ttl <= 0) {
124 4
            $expiration = static::EXPIRATION_EXPIRED;
125
        } else {
126 8
            $expiration = $ttl + time();
127
        }
128
129 191
        return $expiration;
130
    }
131
132
    /**
133
     * @noinspection PhpDocMissingThrowsInspection DateTime won't throw exception because constant string is passed as time
134
     *
135
     * Normalizes cache TTL handling strings and {@see DateInterval} objects.
136
     * @param int|string|DateInterval|null $ttl raw TTL.
137
     * @return int|null TTL value as UNIX timestamp or null meaning infinity
138
     */
139 203
    private function normalizeTtl($ttl): ?int
140
    {
141 203
        if ($ttl instanceof DateInterval) {
142 5
            return (new DateTime('@0'))->add($ttl)->getTimestamp();
143
        }
144
145 199
        if (is_string($ttl)) {
146 2
            return (int)$ttl;
147
        }
148
149 197
        return $ttl;
150
    }
151
152
    /**
153
     * Converts iterable to array. If provided value is not iterable it throws an InvalidArgumentException
154
     * @param $iterable
155
     * @return array
156
     */
157 35
    private function iterableToArray($iterable): array
158
    {
159 35
        if (!is_iterable($iterable)) {
160 3
            throw new InvalidArgumentException('Iterable is expected, got ' . gettype($iterable));
161
        }
162
163 32
        return $iterable instanceof \Traversable ? iterator_to_array($iterable) : (array)$iterable;
164
    }
165
166
    /**
167
     * @param $key
168
     */
169 193
    private function validateKey($key): void
170
    {
171 193
        if (!\is_string($key)) {
172 6
            throw new InvalidArgumentException('Invalid key value.');
173
        }
174
    }
175
176
    /**
177
     * @param array $keys
178
     */
179 32
    private function validateKeys(array $keys): void
180
    {
181 32
        foreach ($keys as $key) {
182 32
            $this->validateKey($key);
183
        }
184
    }
185
186
    /**
187
     * @param array $values
188
     */
189 30
    private function validateKeysOfValues(array $values): void
190
    {
191 30
        $keys = array_map('strval', array_keys($values));
192 30
        $this->validateKeys($keys);
193
    }
194
}
195