Completed
Push — drivers ( dadabd...b6b007 )
by Joe
02:05
created

CacheProvider::removeExpired()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
c 0
b 0
f 0
dl 0
loc 9
rs 10
cc 2
nc 2
nop 2
1
<?php
2
3
namespace PhpWinTools\WmiScripting\Support\Cache;
4
5
use Closure;
6
use Psr\SimpleCache\CacheInterface;
7
use Psr\SimpleCache\InvalidArgumentException;
8
use PhpWinTools\WmiScripting\Configuration\Config;
9
use PhpWinTools\WmiScripting\Support\Events\Event;
10
use PhpWinTools\WmiScripting\Support\Events\EventProvider;
11
use function PhpWinTools\WmiScripting\Support\reduce_value;
12
use PhpWinTools\WmiScripting\Support\Cache\Events\CacheHit;
13
use PhpWinTools\WmiScripting\Support\Cache\Events\CacheMissed;
14
use PhpWinTools\WmiScripting\Support\Cache\Events\CacheCleared;
15
use PhpWinTools\WmiScripting\Support\Cache\Events\CacheKeyStored;
16
use PhpWinTools\WmiScripting\Support\Cache\Events\CacheKeyDeleted;
17
use PhpWinTools\WmiScripting\Support\Cache\Events\CacheKeyExpired;
18
use PhpWinTools\WmiScripting\Exceptions\CacheInvalidArgumentException;
19
20
class CacheProvider implements CacheInterface
21
{
22
    /** @var Config */
23
    protected $config;
24
25
    /** @var CacheDriver */
26
    protected $driver;
27
28
    /** @var EventProvider */
29
    protected $events;
30
31
    /** @var array|Event[] */
32
    protected $delayedEvents = [];
33
34
    public function __construct(Config $config = null, CacheDriver $driver = null, EventProvider $events = null)
35
    {
36
        $this->config = $config ?? Config::instance();
37
        $this->driver = $driver ?? $this->config->getCacheDriver();
38
        $this->events = $events ?? $this->config->eventProvider();
39
    }
40
41
    /**
42
     * @param string|array       $key
43
     * @param Closure|null|mixed $default
44
     *
45
     * @throws CacheInvalidArgumentException|InvalidArgumentException
46
     *
47
     * @return iterable|mixed|null
48
     */
49
    public function get($key, $default = null)
50
    {
51
        if (is_array($key)) {
52
            return $this->getMultiple($key, $default);
0 ignored issues
show
Bug introduced by
It seems like $default can also be of type Closure; however, parameter $default of PhpWinTools\WmiScripting...Provider::getMultiple() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

52
            return $this->getMultiple($key, /** @scrutinizer ignore-type */ $default);
Loading history...
53
        }
54
55
        $value = $this->driver()->get($key, null);
56
57
        if ($this->driver()->expired($key)) {
58
            $this->removeExpired($key, $value);
59
            $value = null;
60
        }
61
62
        if (is_null($value)) {
63
            $value = reduce_value($default);
64
            $this->fire(new CacheMissed($this->driver(), $key));
65
        } else {
66
            $this->fire(new CacheHit($this->driver(), $key, $value));
67
        }
68
69
        return $value;
70
    }
71
72
    /**
73
     * Accepts either a string or a key-value pair array. If given an array then the second value is used as the
74
     * ttl, unless $ttl is explicitly set.
75
     *
76
     * @param string|array $key
77
     * @param mixed        $value
78
     * @param null|int     $ttl
79
     *
80
     * @throws CacheInvalidArgumentException|InvalidArgumentException
81
     *
82
     * @return bool
83
     */
84
    public function set($key, $value = null, $ttl = null)
85
    {
86
        if (is_array($key)) {
87
            return $this->setMultiple($key, $ttl ?? $value);
0 ignored issues
show
Bug introduced by
It seems like $ttl ?? $value can also be of type integer; however, parameter $ttl of PhpWinTools\WmiScripting...Provider::setMultiple() does only seem to accept null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

87
            return $this->setMultiple($key, /** @scrutinizer ignore-type */ $ttl ?? $value);
Loading history...
88
        }
89
90
        $result = $this->driver()->set($key, $value, $ttl);
91
92
        if ($result) {
93
            $this->fire(new CacheKeyStored($this->driver(), $key, $value, $ttl));
94
        }
95
96
        return $result;
97
    }
98
99
    /**
100
     * @param string|array $key
101
     *
102
     * @throws CacheInvalidArgumentException|InvalidArgumentException
103
     *
104
     * @return bool
105
     */
106
    public function delete($key)
107
    {
108
        if (is_array($key)) {
109
            return $this->deleteMultiple($key);
110
        }
111
112
        $result = $this->driver()->delete($key);
113
114
        if ($result) {
115
            $this->fire(new CacheKeyDeleted($this->driver(), $key));
116
        }
117
118
        return $result;
119
    }
120
121
    /**
122
     * @return bool
123
     */
124
    public function clear()
125
    {
126
        $result = $this->driver->clear();
127
128
        if ($result) {
129
            $this->fire(new CacheCleared($this->driver));
130
        }
131
132
        return $result;
133
    }
134
135
    /**
136
     * @param iterable $keys
137
     * @param null     $default
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $default is correct as it would always require null to be passed?
Loading history...
138
     *
139
     * @throws InvalidArgumentException|CacheInvalidArgumentException
140
     *
141
     * @return iterable
142
     */
143
    public function getMultiple($keys, $default = null)
144
    {
145
        $hit = [];
146
147
        foreach ($keys as $key) {
148
            if ($this->driver()->expired($key)) {
149
                $this->removeExpired($key);
150
            }
151
152
            if ($this->driver()->canGet($key)) {
153
                $hit[$key] = $key;
154
            }
155
        }
156
157
        $results = $this->driver()->getMultiple($keys, $default);
158
159
        foreach ($results as $key => $value) {
160
            if (array_key_exists($key, $hit)) {
161
                $this->fire(new CacheHit($this->driver(), $key, $value));
162
            } else {
163
                $this->fire(new CacheMissed($this->driver(), $key));
164
            }
165
        }
166
167
        return $results;
168
    }
169
170
    /**
171
     * @param iterable $values
172
     * @param null     $ttl
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $ttl is correct as it would always require null to be passed?
Loading history...
173
     *
174
     * @return bool
175
     */
176
    public function setMultiple($values, $ttl = null)
177
    {
178
        return $this->multiple('set', $values, $ttl) ? $this->driver()->setMultiple($values, $ttl) : false;
179
    }
180
181
    /**
182
     * @param iterable $keys
183
     *
184
     * @return bool
185
     */
186
    public function deleteMultiple($keys)
187
    {
188
        return $this->multiple('delete', $keys) ? $this->driver()->deleteMultiple($keys) : false;
189
    }
190
191
    /**
192
     * @param string $key
193
     *
194
     * @return bool
195
     */
196
    public function has($key)
197
    {
198
        return $this->driver()->has($key);
199
    }
200
201
    /**
202
     * @return bool
203
     */
204
    public function empty(): bool
205
    {
206
        return $this->driver()->empty();
207
    }
208
209
    /**
210
     * @return bool
211
     */
212
    public function notEmpty(): bool
213
    {
214
        return $this->driver()->notEmpty();
215
    }
216
217
    /**
218
     * @return CacheDriver
219
     */
220
    public function driver()
221
    {
222
        return $this->driver;
223
    }
224
225
    /**
226
     * @param string         $method
227
     * @param iterable       $keys
228
     * @param null|int|mixed $ttl
229
     *
230
     * @return bool
231
     */
232
    protected function multiple(string $method, $keys, $ttl = null): bool
233
    {
234
        foreach ($keys as $key => $value) {
235
            $result = false;
236
237
            if ($method === 'set' && $result = $this->driver()->canSet($key, $value)) {
238
                $this->delayedFire(new CacheKeyStored($this->driver, $key, $value, $ttl));
239
            }
240
241
            if ($method === 'delete' && $result = $this->driver()->canDelete($value)) {
242
                $this->delayedFire(new CacheKeyDeleted($this->driver, $value));
243
            }
244
245
            if ($result === false) {
246
                $this->flushDelayedEvents(false);
247
                return false;
248
            }
249
        }
250
251
        $this->flushDelayedEvents();
252
253
        return true;
254
    }
255
256
    /**
257
     * @param      $key
258
     * @param null $value
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $value is correct as it would always require null to be passed?
Loading history...
259
     *
260
     * @throws CacheInvalidArgumentException|InvalidArgumentException
261
     *
262
     * @return self
263
     */
264
    protected function removeExpired($key, $value = null): self
265
    {
266
        if ($this->driver()->expired($key)) {
267
            $this->fire(new CacheKeyExpired($this->driver(), $key, $value));
268
            $this->delete($key);
269
        }
270
271
272
        return $this;
273
    }
274
275
    /**
276
     * @param Event $event
277
     *
278
     * @return self
279
     */
280
    protected function fire(Event $event): self
281
    {
282
        $this->events->fire($event);
283
284
        return $this;
285
    }
286
287
    /**
288
     * @param bool $fire
289
     *
290
     * @return self
291
     */
292
    protected function flushDelayedEvents(bool $fire = true): self
293
    {
294
        if ($fire) {
295
            array_map(function (Event $event) {
296
                $this->fire($event);
297
            }, $this->delayedEvents);
298
        }
299
300
        $this->delayedEvents = [];
301
302
        return $this;
303
    }
304
305
    /**
306
     * @param Event $event
307
     *
308
     * @return $this
309
     */
310
    protected function delayedFire(Event $event): self
311
    {
312
        $this->delayedEvents[] = $event;
313
314
        return $this;
315
    }
316
}
317