Passed
Push — master ( 159f4a...679316 )
by
unknown
02:11
created

TieredCache::getMultiple()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 14
c 1
b 0
f 0
dl 0
loc 25
ccs 15
cts 15
cp 1
rs 9.2222
cc 6
nc 7
nop 2
crap 6
1
<?php
2
3
namespace Vectorface\Cache;
4
5
use Vectorface\Cache\Common\PSR16Util;
6
7
/**
8
 * This cache's speed is dependent on underlying caches, usually medium according to basic benchmarks:
9
 *
10
 * Parameters:
11
 *   MCCache + SQLCache
12
 *   9-byte key
13
 *   151-byte value
14
 *   10000-iteration test
15
 *
16
 * Result:
17
 *   11.9338240623 seconds
18
 *
19
 * Conclusion:
20
 *   Capable of approximately 837.96 requests/second
21
 */
22
23
/**
24
 * A cache composed of other caches layered on top of one another.
25
 */
26
class TieredCache implements Cache
27
{
28
    use PSR16Util;
29
30
    /**
31
     * The cache layers.
32
     *
33
     * @var Cache[]
34
     */
35
    private $caches = [];
36
37
    /**
38
     * Create a cache that layers caches on top of each other.
39
     *
40
     * Read requests hit caches in order until they get a hit. The first hit is returned.
41
     * Write operations hit caches in order, performing the write operation on all caches.
42
     *
43
     * @param Cache[] $caches An array of objects implementing the Cache interface.
44
     *
45
     * Note: Order is important. The first element is get/set first, and so on. Usually that means  you want to put the
46
     * fastest caches first.
47
     */
48 2
    public function __construct($caches = null)
49
    {
50 2
        if (!is_array($caches)) {
51 2
            $caches = func_get_args();
52
        }
53 2
        foreach ($caches as $i => $cache) {
54 2
            if (!($cache instanceof Cache)) {
55 1
                throw new \InvalidArgumentException("Argument $i is not of class Cache");
56
            }
57 1
            $this->caches[] = $cache;
58
        }
59 1
    }
60
61
    /**
62
     * @inheritDoc
63
     */
64 1
    public function get($key, $default = null)
65
    {
66 1
        $key = $this->key($key);
67 1
        foreach ($this->caches as $cache) {
68 1
            $value = $cache->get($key, null);
69 1
            if ($value !== null) {
70 1
                return $value;
71
            }
72
        }
73 1
        return $default;
74
    }
75
76
    /**
77
     * @inheritDoc
78
     */
79 1
    public function set($key, $value, $ttl = null)
80
    {
81 1
        return $this->any('set', $this->key($key), $value, $this->ttl($ttl));
82
    }
83
84
    /**
85
     * @inheritDoc
86
     */
87 1
    public function delete($key)
88
    {
89 1
        return $this->all('delete', $this->key($key));
90
    }
91
92
    /**
93
     * @inheritDoc
94
     */
95 1
    public function clean()
96
    {
97 1
        return $this->all('clean');
98
    }
99
100
    /**
101
     * @inheritDoc
102
     */
103 1
    public function flush()
104
    {
105 1
        return $this->all('flush');
106
    }
107
108
    /**
109
     * @inheritDoc
110
     */
111 1
    public function getMultiple($keys, $default = null)
112
    {
113 1
        $neededKeys = $keys;
114 1
        $values = [];
115 1
        foreach ($this->caches as $cache) {
116 1
            $result = $cache->getMultiple($neededKeys, null);
117 1
            $values = array_merge(
118 1
                $values,
119 1
                array_filter(is_array($result) ? $result : iterator_to_array($result, true))
120
            );
121 1
            if (count($values) === count($keys)) {
122 1
                return $values;
123
            }
124
125 1
            $neededKeys = array_diff($keys, $values);
126
        }
127
128
        /* Finally, set defaults */
129 1
        foreach ($keys as $key) {
130 1
            if (!isset($values[$key])) {
131 1
                $values[$key] = $default;
132
            }
133
        }
134
135 1
        return $values;
136
    }
137
138
    /**
139
     * @inheritDoc
140
     */
141 1
    public function setMultiple($values, $ttl = null)
142
    {
143 1
        return $this->any('setMultiple', $this->values($values), $this->ttl($ttl));
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->any('setMu...ues), $this->ttl($ttl)) returns the type boolean which is incompatible with the return type mandated by Vectorface\Cache\Cache::setMultiple() of Traversable|array.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
144
    }
145
146
    /**
147
     * @inheritDoc
148
     */
149 1
    public function deleteMultiple($keys)
150
    {
151 1
        return $this->all('deleteMultiple', $this->keys($keys));
152
    }
153
154
    /**
155
     * @inheritDoc
156
     */
157 1
    public function clear()
158
    {
159 1
        return $this->flush();
160
    }
161
162
    /**
163
     * @inheritDoc
164
     */
165 1
    public function has($key)
166
    {
167 1
        return $this->get($this->key($key)) !== null;
168
    }
169
170
    /**
171
     * Run a method on all caches, expect all caches to success for success
172
     *
173
     * @param string $call The cache interface method to be called
174
     * @param mixed ...$args The method's arguments
175
     * @return bool True if the operation was successful on all caches
176
     */
177 1
    private function all(string $call, ...$args)
178
    {
179 1
        $success = true;
180 1
        foreach ($this->caches as $cache) {
181 1
            $success = $success && ([$cache, $call])(...$args);
182
        }
183 1
        return $success;
184
    }
185
186
    /**
187
     * Run a method on all caches, expect any successful result for success
188
     *
189
     * @param string $call The cache interface method to be called
190
     * @param mixed ...$args The method's arguments
191
     * @return bool True if the operation was successful on any cache
192
     */
193 1
    private function any($call, ...$args)
194
    {
195 1
        $success = false;
196 1
        foreach ($this->caches as $cache) {
197 1
            $success = $success || ([$cache, $call])(...$args);
198
        }
199 1
        return $success;
200
    }
201
}
202