Passed
Push — develop ( 304111...cd86fb )
by Jonathan
07:42 queued 06:18
created

LogDecorator::decrement()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 14
ccs 0
cts 0
cp 0
rs 9.9666
cc 3
nc 1
nop 3
crap 12
1
<?php
2
3
namespace Vectorface\Cache;
4
5
use InvalidArgumentException;
6
use Psr\Log\LoggerInterface;
7
use Vectorface\Cache\Exception\CacheException;
8
9
/**
10
 * Decorates (Wraps) a Cache implementation with logging
11
 *
12
 * Note:
13
 *   This logs an estimated serialized object size. Cache serialization may
14
 *   use a different serialization mechanism, so the size should be used to
15
 *   give an idea of actual cached size rather than an exact value.
16
 */
17
class LogDecorator implements Cache, AtomicCounter
18
{
19
    /**
20
     * The wrapped cache class
21
     * @var Cache|AtomicCounter
22
     */
23
    private $cache;
24
25
    /**
26
     * The logger instance to which operations will be logged
27
     *
28
     * @var LoggerInterface
29
     */
30
    private $log;
31
32
    /**
33
     * The log level, which corresponds to a PSR-3 log level function call
34
     *
35
     * @var string
36
     */
37
    private $level;
38
39
    /**
40
     * @param Cache|AtomicCounter $cache
41
     * @param LoggerInterface $log
42 4
     * @param string $level
43
     */
44 4
    public function __construct(Cache $cache, LoggerInterface $log = null, $level = 'debug')
45 4
    {
46 1
        $levels = ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug'];
47
        if (!in_array($level, $levels)) {
48
            throw new InvalidArgumentException("Incompatible log level: $level");
49 3
        }
50 3
51 3
        $this->cache = $cache;
52 3
        $this->log = $log;
53
        $this->level = $level;
54
    }
55
56
    /**
57 2
     * @inheritDoc
58
     * @throws CacheException
59 2
     */
60 2
    public function get($key, $default = null)
61 2
    {
62 2
        $this->throwIfNotInstanceof(Cache::class);
63
64
        /** @scrutinizer ignore-call */
65 1
        $result = $this->cache->get($key);
66 1
        if ($result === null) {
67
            $this->log(sprintf("get %s MISS", $key));
68 1
            return $default;
69
        }
70 1
71
        $this->log(sprintf(
72
            "get %s HIT size=%d",
73
            $key,
74
            $this->getSize($result)
75
        ));
76 3
        return $result;
77
    }
78 3
79 3
    /**
80 3
     * @inheritDoc
81
     */
82 3
    public function set($key, $value, $ttl = false)
83 3
    {
84 3
        $this->throwIfNotInstanceof(Cache::class);
85 3
86
        /** @scrutinizer ignore-call */
87 3
        $result = $this->cache->set($key, $value, $ttl);
88
        $this->log(sprintf(
89
            "set %s %s ttl=%s, type=%s, size=%d",
90
            $key,
91
            $result ? 'SUCCESS' : 'FAILURE',
92
            is_numeric($ttl) ? $ttl : "false",
93 2
            gettype($value),
94
            $this->getSize($value)
95 2
        ));
96 2
        return $result;
97 2
    }
98
99 2
    /**
100
     * @inheritDoc
101 2
     * @throws CacheException
102
     */
103
    public function delete($key)
104
    {
105
        $this->throwIfNotInstanceof(Cache::class);
106
107 2
        /** @scrutinizer ignore-call */
108
        $result = $this->cache->delete($key);
109 2
        $this->log(sprintf(
110 2
            "delete %s %s",
111 2
            $key,
112
            $result ? 'SUCCESS' : 'FAILURE'
113
        ));
114
        return $result;
115
    }
116
117 2
    /**
118
     * @inheritDoc
119 2
     * @throws CacheException
120 2
     */
121 2
    public function flush()
122
    {
123
        $this->throwIfNotInstanceof(Cache::class);
124
125
        /** @scrutinizer ignore-call */
126
        $result = $this->cache->flush();
127 1
        $this->log(sprintf("flush %s", $result ? 'SUCCESS' : 'FAILURE'));
128
        return $result;
129 1
    }
130
131
    /**
132
     * @inheritDoc
133
     * @throws CacheException
134
     */
135 1
    public function clean()
136
    {
137 1
        $this->throwIfNotInstanceof(Cache::class);
138 1
139 1
        /** @scrutinizer ignore-call */
140 1
        $result = $this->cache->clean();
141 1
        $this->log(sprintf("clean %s", $result ? 'SUCCESS' : 'FAILURE'));
142
        return $result;
143 1
    }
144
145
    /**
146
     * @inheritDoc
147
     * @throws CacheException
148
     */
149 1
    public function clear()
150
    {
151 1
        $this->throwIfNotInstanceof(Cache::class);
152 1
153 1
        return $this->flush();
154 1
    }
155 1
156 1
    /**
157
     * @inheritDoc
158 1
     * @throws CacheException
159
     */
160
    public function getMultiple($keys, $default = null)
161
    {
162
        $this->throwIfNotInstanceof(Cache::class);
163
164 1
        /** @scrutinizer ignore-call */
165
        $values = $this->cache->getMultiple($keys, $default);
166 1
        $this->log(sprintf(
167 1
            "getMultiple [%s] count=%s",
168 1
            implode(', ', $keys),
169 1
            is_array($values) ? count($values) : ('[' . gettype($values) . ']')
170 1
        ));
171
        return $values;
172 1
    }
173
174
    /**
175
     * @inheritDoc
176
     */
177
    public function setMultiple($values, $ttl = null)
178 1
    {
179
        $this->throwIfNotInstanceof(Cache::class);
180 1
181 1
        /** @scrutinizer ignore-call */
182 1
        $result = $this->cache->setMultiple($values, $ttl);
183
        $this->log(sprintf(
184 1
            "setMultiple [%s] %s ttl=%s",
185
            implode(', ', array_keys($values)),
186 1
            $result ? 'SUCCESS' : 'FAILURE',
187
            is_numeric($ttl) ? $ttl : "null"
188
        ));
189
        return $result;
190
    }
191
192
    /**
193
     * @inheritDoc
194 3
     * @throws CacheException
195
     */
196 3
    public function deleteMultiple($keys)
197 1
    {
198
        $this->throwIfNotInstanceof(Cache::class);
199
200 2
        /** @scrutinizer ignore-call */
201 2
        $result = $this->cache->deleteMultiple($keys);
202
        $this->log(sprintf(
203
            "deleteMultiple [%s] %s",
204
            implode(', ', $keys),
205
            $result ? 'SUCCESS' : 'FAILURE'
206
        ));
207
        return $result;
208
    }
209 3
210
    /**
211 3
     * @inheritDoc
212
     * @throws CacheException
213
     */
214
    public function has($key)
215
    {
216
        $this->throwIfNotInstanceof(Cache::class);
217
218
        /** @scrutinizer ignore-call */
219
        $result = $this->cache->has($key);
220
        $this->log(sprintf(
221
            "has %s %s",
222
            $key,
223
            $result ? 'true' : 'false'
224
        ));
225
        return $result;
226
    }
227
228
    /**
229
     * @inheritDoc
230
     * @throws CacheException
231
     */
232
    public function increment($key, $step = 1, $ttl = null)
233
    {
234
        $this->throwIfNotInstanceof(AtomicCounter::class);
235
236
        /** @scrutinizer ignore-call */
237
        $result = $this->cache->increment($key, $step, $ttl);
238
        $this->log(sprintf(
239
            "increment %s by %d %s, value=%d",
240
            $key,
241
            $step,
242
            ($result !== false ? 'SUCCESS' : 'FAILURE'),
243
            ($result !== false ? $result : 0)
244
        ));
245
        return $result;
246
    }
247
248
    /**
249
     * @inheritDoc
250
     * @throws CacheException
251
     */
252
    public function decrement($key, $step = 1, $ttl = null)
253
    {
254
        $this->throwIfNotInstanceof(AtomicCounter::class);
255
256
        /** @scrutinizer ignore-call */
257
        $result = $this->cache->decrement($key, $step, $ttl);
258
        $this->log(sprintf(
259
            "decrement %s by %d %s, value=%d",
260
            $key,
261
            $step,
262
            ($result !== false ? 'SUCCESS' : 'FAILURE'),
263
            ($result !== false ? $result : 0)
264
        ));
265
        return $result;
266
    }
267
268
    /**
269
     * Log a message to the configured logger
270
     *
271
     * @param $message
272
     */
273
    private function log(string $message)
274
    {
275
        if (!$this->log) {
276
            return;
277
        }
278
279
        ([$this->log, $this->level])($message);
280
    }
281
282
    /**
283
     * Guards against calls on a decorated instance that does not support the underlying method
284
     *
285
     * @param string $class
286
     * @throws CacheException
287
     */
288
    private function throwIfNotInstanceof($class)
289
    {
290
        if (! $this->cache instanceof $class) {
291
            throw new CacheException("This decorated instance does not implement $class");
292
        }
293
    }
294
295
    /**
296
     * Get a reasonable estimation for the serialized size of a cacheable value
297
     *
298
     * @param mixed $val The cacheable value
299
     * @return int An estimate of the cached size of the value
300
     */
301
    private function getSize($val)
302
    {
303
        return strlen(is_scalar($val) ? (string)$val : serialize($val));
304
    }
305
}
306