Issues (27)

src/AbstractCache.php (3 issues)

Severity
1
<?php
2
3
/*
4
 * This file is part of the Cache package.
5
 *
6
 * Copyright (c) Daniel González
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @author Daniel González <[email protected]>
12
 * @author Arnold Daniels <[email protected]>
13
 */
14
15
declare(strict_types=1);
16
17
namespace Desarrolla2\Cache;
18
19
use Desarrolla2\Cache\Option\PrefixTrait as PrefixOption;
20
use Desarrolla2\Cache\Option\TtlTrait as TtlOption;
21
use Desarrolla2\Cache\Packer\PackingTrait as Packing;
22
use Desarrolla2\Cache\Exception\InvalidArgumentException;
23
use DateTimeImmutable;
24
use DateInterval;
25
use Traversable;
26
27
/**
28
 * AbstractAdapter
29
 */
30
abstract class AbstractCache implements CacheInterface
31
{
32
    use PrefixOption;
33
    use TtlOption;
34
    use Packing;
35
36
    /**
37
     * Make a clone of this object.
38
     *
39
     * @return static
40
     */
41 820
    protected function cloneSelf(): self
42
    {
43 820
        return clone $this;
44
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49 819
    public function withOption(string $key, $value): self
50
    {
51 819
        return $this->withOptions([$key => $value]);
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57 826
    public function withOptions(array $options): self
58
    {
59 826
        $cache = $this->cloneSelf();
60
61 826
        foreach ($options as $key => $value) {
62 826
            $method = "set" . str_replace('-', '', $key) . "Option";
63
64 826
            if (empty($key) || !method_exists($cache, $method)) {
65 11
                throw new InvalidArgumentException("unknown option '$key'");
66
            }
67
68 819
            $cache->$method($value);
69
        }
70
71 812
        return $cache;
72
    }
73
    
74
    /**
75
     * {@inheritdoc}
76
     */
77 34
    public function getOption($key)
78
    {
79 34
        $method = "get" . str_replace('-', '', $key) . "Option";
80
        
81 34
        if (empty($key) || !method_exists($this, $method)) {
82
            throw new InvalidArgumentException("unknown option '$key'");
83
        }
84
        
85 34
        return $this->$method();
86
    }
87
88
89
    /**
90
     * Validate the key
91
     *
92
     * @param string $key
93
     * @return void
94
     * @throws InvalidArgumentException
95
     */
96 2047
    protected function assertKey($key): void
97
    {
98 2047
        if (!is_string($key)) {
0 ignored issues
show
The condition is_string($key) is always true.
Loading history...
99 528
            $type = (is_object($key) ? get_class($key) . ' ' : '') . gettype($key);
100 528
            throw new InvalidArgumentException("Expected key to be a string, not $type");
101
        }
102
103 1739
        if ($key === '' || preg_match('~[{}()/\\\\@:]~', $key)) {
104 847
            throw new InvalidArgumentException("Invalid key '$key'");
105
        }
106
    }
107
108
    /**
109
     * Assert that the keys are an array or traversable
110
     * 
111
     * @param iterable $subject
112
     * @param string   $msg
113
     * @return void
114
     * @throws InvalidArgumentException if subject are not iterable
115
     */
116 926
    protected function assertIterable($subject, $msg): void
117
    {
118 926
        $iterable = function_exists('is_iterable')
119 926
            ? is_iterable($subject)
120 926
            : is_array($subject) || $subject instanceof Traversable;
121
        
122 926
        if (!$iterable) {
123 33
            throw new InvalidArgumentException($msg);
124
        }
125
    }
126
127
    /**
128
     * Turn the key into a cache identifier
129
     *
130
     * @param string $key
131
     * @return string
132
     * @throws InvalidArgumentException
133
     */
134 1878
    protected function keyToId($key): string
135
    {
136 1878
        $this->assertKey($key);
137
138 1140
        return sprintf('%s%s', $this->prefix, $key);
139
    }
140
141
    /**
142
     * Create a map with keys and ids
143
     *
144
     * @param iterable $keys
145
     * @return array
146
     * @throws InvalidArgumentException
147
     */
148 153
    protected function mapKeysToIds($keys): array
149
    {
150 153
        $this->assertIterable($keys, 'keys not iterable');
151
152 147
        $map = [];
153
154 147
        foreach ($keys as $key) {
155 147
            $id = $this->keyToId($key);
156 147
            $map[$id] = $key;
157
        }
158
159 39
        return $map;
160
    }
161
162
163
    /**
164
     * Pack all values and turn keys into ids
165
     *
166
     * @param iterable $values
167
     * @return array
168
     */
169
    protected function packValues(iterable $values): array
170
    {
171
        $packed = [];
172
173
        foreach ($values as $key => $value) {
174
            $id = $this->keyToId(is_int($key) ? (string)$key : $key);
175
            $packed[$id] = $this->pack($value);
176
        }
177
178
        return $packed;
179
    }
180
181
182
183
    /**
184
     * {@inheritdoc}
185
     */
186 191
    public function getMultiple($keys, $default = null)
187
    {
188 191
        $this->assertIterable($keys, 'keys not iterable');
189
        
190 185
        $result = [];
191
        
192 185
        foreach ($keys as $key) {
193 185
            $result[$key] = $this->get($key, $default);
194
        }
195
        
196 77
        return $result;
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 294
    public function setMultiple($values, $ttl = null)
203
    {
204 294
        $this->assertIterable($values, 'values not iterable');
205
206 287
        $success = true;
207
        
208 287
        foreach ($values as $key => $value) {
209 287
            $success = $this->set(is_int($key) ? (string)$key : $key, $value, $ttl) && $success;
210
        }
211
        
212 98
        return $success;
213
    }
214
215
    /**
216
     * {@inheritdoc}
217
     */
218 168
    public function deleteMultiple($keys)
219
    {
220 168
        $this->assertIterable($keys, 'keys not iterable');
221
222 160
        $success = true;
223
224 160
        foreach ($keys as $key) {
225 160
            $success = $this->delete($key) && $success;
226
        }
227
228 16
        return $success;
229
    }
230
231
232
    /**
233
     * Get the current time.
234
     *
235
     * @return int
236
     */
237 89
    protected function currentTimestamp(): int
238
    {
239 89
        return time();
240
    }
241
242
    /**
243
     * Convert TTL to seconds from now
244
     *
245
     * @param null|int|DateInterval $ttl
246
     * @return int|null
247
     * @throws InvalidArgumentException
248
     */
249 226
    protected function ttlToSeconds($ttl): ?int
250
    {
251 226
        if (!isset($ttl)) {
252 157
            return $this->ttl;
253
        }
254
255 72
        if ($ttl instanceof DateInterval) {
256 6
            $reference = new DateTimeImmutable();
257 6
            $endTime = $reference->add($ttl);
258
259 6
            $ttl = $endTime->getTimestamp() - $reference->getTimestamp();
260
        }
261
262 72
        if (!is_int($ttl)) {
0 ignored issues
show
The condition is_int($ttl) is always true.
Loading history...
263 60
            $type = (is_object($ttl) ? get_class($ttl) . ' ' : '') . gettype($ttl);
264 60
            throw new InvalidArgumentException("ttl should be of type int or DateInterval, not $type");
265
        }
266
267 12
        return isset($this->ttl) ? min($ttl, $this->ttl) : $ttl;
268
    }
269
270
    /**
271
     * Convert TTL to epoch timestamp
272
     *
273
     * @param null|int|DateInterval $ttl
274
     * @return int|null
275
     * @throws InvalidArgumentException
276
     */
277 587
    protected function ttlToTimestamp($ttl): ?int
278
    {
279 587
        if (!isset($ttl)) {
280 402
            return isset($this->ttl) ? time() + $this->ttl : null;
281
        }
282
283 192
        if (is_int($ttl)) {
284 32
            return time() + (isset($this->ttl) ? min($ttl, $this->ttl) : $ttl);
285
        }
286
287 176
        if ($ttl instanceof DateInterval) {
0 ignored issues
show
$ttl is always a sub-type of DateInterval.
Loading history...
288 16
            $timestamp = (new DateTimeImmutable())->add($ttl)->getTimestamp();
289
290 16
            return isset($this->ttl) ? min($timestamp, time() + $this->ttl) : $timestamp;
291
        }
292
293 160
        $type = (is_object($ttl) ? get_class($ttl) . ' ' : '') . gettype($ttl);
294 160
        throw new InvalidArgumentException("ttl should be of type int or DateInterval, not $type");
295
    }
296
}
297