RedisCache::doFetchMultiple()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3

Importance

Changes 6
Bugs 1 Features 0
Metric Value
cc 3
eloc 11
c 6
b 1
f 0
nc 2
nop 1
dl 0
loc 20
ccs 12
cts 12
cp 1
crap 3
rs 9.9
1
<?php
2
3
namespace Doctrine\Common\Cache;
4
5
use Redis;
6
use function array_combine;
7
use function array_diff_key;
8
use function array_fill_keys;
9
use function array_filter;
10
use function array_keys;
11
use function count;
12
use function defined;
13
use function extension_loaded;
14
use function is_bool;
15
16
/**
17
 * Redis cache provider.
18
 *
19
 * @link   www.doctrine-project.org
20
 */
21
class RedisCache extends CacheProvider
22
{
23
    /** @var Redis|null */
24
    private $redis;
25
26
    /**
27
     * Sets the redis instance to use.
28
     *
29
     * @return void
30
     */
31 81
    public function setRedis(Redis $redis)
32
    {
33 81
        $redis->setOption(Redis::OPT_SERIALIZER, $this->getSerializerValue());
34 81
        $this->redis = $redis;
35 81
    }
36
37
    /**
38
     * Gets the redis instance used by the cache.
39
     *
40
     * @return Redis|null
41
     */
42 2
    public function getRedis()
43
    {
44 2
        return $this->redis;
45
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50 76
    protected function doFetch($id)
51
    {
52 76
        return $this->redis->get($id);
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

52
        return $this->redis->/** @scrutinizer ignore-call */ get($id);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58 2
    protected function doFetchMultiple(array $keys)
59
    {
60 2
        $fetchedItems = array_combine($keys, $this->redis->mget($keys));
61
62
        // Redis mget returns false for keys that do not exist. So we need to filter those out unless it's the real data.
63
        $keysToFilter = array_keys(array_filter($fetchedItems, static function ($item) : bool {
0 ignored issues
show
Bug introduced by
It seems like $fetchedItems can also be of type false; however, parameter $input of array_filter() does only seem to accept array, 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

63
        $keysToFilter = array_keys(array_filter(/** @scrutinizer ignore-type */ $fetchedItems, static function ($item) : bool {
Loading history...
64 2
            return $item === false;
65 2
        }));
66
67 2
        if ($keysToFilter) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $keysToFilter of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
68 2
            $multi = $this->redis->multi(Redis::PIPELINE);
69 2
            foreach ($keysToFilter as $key) {
70 2
                $multi->exists($key);
71
            }
72 2
            $existItems     = array_filter($multi->exec());
73 2
            $missedItemKeys = array_diff_key($keysToFilter, $existItems);
74 2
            $fetchedItems   = array_diff_key($fetchedItems, array_fill_keys($missedItemKeys, true));
0 ignored issues
show
Bug introduced by
It seems like $fetchedItems can also be of type false; however, parameter $array1 of array_diff_key() does only seem to accept array, 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

74
            $fetchedItems   = array_diff_key(/** @scrutinizer ignore-type */ $fetchedItems, array_fill_keys($missedItemKeys, true));
Loading history...
75
        }
76
77 2
        return $fetchedItems;
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83 1
    protected function doSaveMultiple(array $keysAndValues, $lifetime = 0)
84
    {
85 1
        if ($lifetime) {
86
            // Keys have lifetime, use SETEX for each of them
87
            $multi = $this->redis->multi(Redis::PIPELINE);
88
            foreach ($keysAndValues as $key => $value) {
89
                $multi->setex($key, $lifetime, $value);
90
            }
91
            $succeeded = array_filter($multi->exec());
92
93
            return count($succeeded) == count($keysAndValues);
94
        }
95
96
        // No lifetime, use MSET
97 1
        return (bool) $this->redis->mset($keysAndValues);
98
    }
99
100
    /**
101
     * {@inheritdoc}
102
     */
103 71
    protected function doContains($id)
104
    {
105 71
        $exists = $this->redis->exists($id);
106
107 71
        if (is_bool($exists)) {
0 ignored issues
show
introduced by
The condition is_bool($exists) is always false.
Loading history...
108
            return $exists;
109
        }
110
111 71
        return $exists > 0;
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117 74
    protected function doSave($id, $data, $lifeTime = 0)
118
    {
119 74
        if ($lifeTime > 0) {
120 3
            return $this->redis->setex($id, $lifeTime, $data);
121
        }
122
123 72
        return $this->redis->set($id, $data);
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129 44
    protected function doDelete($id)
130
    {
131 44
        return $this->redis->del($id) >= 0;
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137 1
    protected function doDeleteMultiple(array $keys)
138
    {
139 1
        return $this->redis->del($keys) >= 0;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 2
    protected function doFlush()
146
    {
147 2
        return $this->redis->flushDB();
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 2
    protected function doGetStats()
154
    {
155 2
        $info = $this->redis->info();
156
157
        return [
158 2
            Cache::STATS_HITS   => $info['keyspace_hits'],
159 2
            Cache::STATS_MISSES => $info['keyspace_misses'],
160 2
            Cache::STATS_UPTIME => $info['uptime_in_seconds'],
161 2
            Cache::STATS_MEMORY_USAGE      => $info['used_memory'],
162
            Cache::STATS_MEMORY_AVAILABLE  => false,
163
        ];
164
    }
165
166
    /**
167
     * Returns the serializer constant to use. If Redis is compiled with
168
     * igbinary support, that is used. Otherwise the default PHP serializer is
169
     * used.
170
     *
171
     * @return int One of the Redis::SERIALIZER_* constants
172
     */
173 81
    protected function getSerializerValue()
174
    {
175 81
        if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')) {
176
            return Redis::SERIALIZER_IGBINARY;
177
        }
178
179 81
        return Redis::SERIALIZER_PHP;
180
    }
181
}
182