Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.75% covered (success)
93.75%
30 / 32
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
MemcachedDriver
93.75% covered (success)
93.75%
30 / 32
77.78% covered (warning)
77.78%
7 / 9
23.13
0.00% covered (danger)
0.00%
0 / 1
 __construct
n/a
0 / 0
n/a
0 / 0
4
 __destruct
n/a
0 / 0
n/a
0 / 0
1
 exists
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMultiple
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 set
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 setMultiple
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
3.04
 delete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deleteMultiple
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 flush
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 expiresAt
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php declare(strict_types=1);
2/**
3 * Banker
4 *
5 * A Caching library implementing psr/cache (PSR 6) and psr/simple-cache (PSR 16)
6 *
7 * PHP version 8+
8 *
9 * @package     Banker
10 * @author      Timothy J. Warren <tim@timshomepage.net>
11 * @copyright   2016 - 2023  Timothy J. Warren
12 * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
13 * @version     4.1.0
14 * @link        https://git.timshomepage.net/timw4mail/banker
15 */
16namespace Aviat\Banker\Driver;
17
18use Aviat\Banker\Exception\CacheException;
19
20use Aviat\Banker\Exception\InvalidArgumentException;
21use DateInterval;
22use Memcached;
23use MemcachedException;
24
25/**
26 * Memcached cache backend
27 */
28class MemcachedDriver extends AbstractDriver {
29
30    /**
31     * The Memcached connection
32     */
33    private Memcached $conn;
34
35    /**
36     * Driver for PHP Memcache extension
37     *
38     * @codeCoverageIgnore
39     * @param array $config
40     * @param array $options
41     * @throws CacheException
42     */
43    public function __construct(
44        array $config = ['host' => '127.0.0.1', 'port' => '11211'],
45        array $options = []
46    )
47    {
48        if ( ! class_exists('Memcached'))
49        {
50            throw new CacheException('Memcached driver requires memcached extension');
51        }
52
53        try
54        {
55            $this->conn = new Memcached();
56            $this->conn->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
57            $this->conn->addServer($config['host'], (int) $config['port']);
58
59            if ( ! empty($options))
60            {
61                $this->conn->setOptions($options);
62            }
63        }
64        catch (MemcachedException $e)
65        {
66            // Rewrite MemcachedException as a CacheException to
67            // match the requirements of the interface
68            throw new CacheException($e->getMessage(), $e->getCode(), $e);
69        }
70    }
71
72    /**
73     * Disconnect from memcached server
74     * @codeCoverageIgnore
75     */
76    public function __destruct()
77    {
78        $this->conn->quit();
79    }
80
81    /**
82     * See if a key currently exists in the cache
83     *
84     * @param string $key
85     * @return bool
86     */
87    public function exists(string $key): bool
88    {
89        $this->conn->get($key);
90        $resultFlag = $this->conn->getResultCode();
91
92        return ($resultFlag !== Memcached::RES_NOTFOUND);
93    }
94
95    /**
96     * Get the value for the selected cache key
97     *
98     * @param string $key
99     * @return mixed
100     */
101    public function get(string $key): mixed
102    {
103        return $this->conn->get($key);
104    }
105
106    /**
107     * Retrieve a set of values by their cache key
108     *
109     * @param string[] $keys
110     * @return array
111     */
112    public function getMultiple(array $keys = []): array
113    {
114        $this->validateKeys($keys);
115
116        $response = $this->conn->getMulti($keys);
117        return (is_array($response)) ? $response : [];
118    }
119
120    /**
121     * Set a cached value
122     *
123     * @param string $key
124     * @param mixed $value
125     * @param int|DateInterval|null $expires
126     * @return bool
127     * @throws InvalidArgumentException
128     */
129    public function set(string $key, mixed $value, int|DateInterval|null $expires = NULL): bool
130    {
131        $this->validateKey($key);
132
133        if ($expires instanceof DateInterval)
134        {
135            $expires = time() + $expires->s;
136        }
137
138        return ($expires === NULL)
139            ? $this->conn->set($key, $value)
140            : $this->conn->set($key, $value, $expires);
141    }
142
143    /**
144     * Set multiple cache values
145     *
146     * @param array $items
147     * @param DateInterval|int|null $expires
148     * @return bool
149     */
150    public function setMultiple(array $items, DateInterval|int|null $expires = NULL): bool
151    {
152        $this->validateKeys($items, TRUE);
153
154        if ($expires instanceof DateInterval)
155        {
156            $expires = $expires->s;
157        }
158
159        return ($expires === NULL)
160            ? $this->conn->setMulti($items)
161            : $this->conn->setMulti($items, $expires);
162    }
163
164    /**
165     * Remove an item from the cache
166     *
167     * @param string $key
168     * @return boolean
169     */
170    public function delete(string $key): bool
171    {
172        return $this->conn->delete($key);
173    }
174
175    /**
176     * Remove multiple items from the cache
177     *
178     * @param string[] $keys
179     * @return boolean
180     */
181    public function deleteMultiple(array $keys = []): bool
182    {
183        $this->validateKeys($keys);
184
185        $deleted = $this->conn->deleteMulti($keys);
186
187        if (is_array($deleted))
188        {
189            foreach ($deleted as $key => $status)
190            {
191                if ($status !== TRUE)
192                {
193                    return FALSE;
194                }
195            }
196
197            return TRUE;
198        }
199    }
200
201    /**
202     * Empty the cache
203     *
204     * @return boolean
205     */
206    public function flush(): bool
207    {
208        return $this->conn->flush();
209    }
210
211    /**
212     * Set the specified key to expire at the given time
213     *
214     * @param string $key
215     * @param int $expires
216     * @return boolean
217     */
218    public function expiresAt(string $key, int $expires): bool
219    {
220        if ($this->exists($key))
221        {
222            return $this->conn->touch($key, $expires);
223        }
224
225        $this->getLogger()->log('warning','Tried to set expiration on a key that does not exist');
226
227        return FALSE;
228    }
229}