• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / caching / 22226743054

20 Feb 2026 01:53PM UTC coverage: 88.227% (+1.0%) from 87.202%
22226743054

Pull #85

github

web-flow
Merge c3f6b576c into f6698ea58
Pull Request #85: Extend MemoryStorage with Tags, Priority, Expire and Sliding modifiers

35 of 36 new or added lines in 1 file covered. (97.22%)

57 existing lines in 7 files now uncovered.

622 of 705 relevant lines covered (88.23%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.68
/src/Caching/Storages/MemcachedStorage.php
1
<?php
2

3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
declare(strict_types=1);
9

10
namespace Nette\Caching\Storages;
11

12
use Nette;
13
use Nette\Caching\Cache;
14
use function array_combine, array_map, extension_loaded, time, urlencode;
15

16

17
/**
18
 * Memcached storage using memcached extension.
19
 */
20
class MemcachedStorage implements Nette\Caching\Storage, Nette\Caching\BulkReader, Nette\Caching\BulkWriter
21
{
22
        /** @internal cache structure */
23
        private const
24
                MetaCallbacks = 'callbacks',
25
                MetaData = 'data',
26
                MetaDelta = 'delta';
27

28
        private readonly \Memcached $memcached;
29

30

31
        /**
32
         * Checks if Memcached extension is available.
33
         */
34
        public static function isAvailable(): bool
35
        {
36
                return extension_loaded('memcached');
1✔
37
        }
38

39

40
        public function __construct(
1✔
41
                string $host = 'localhost',
42
                int $port = 11211,
43
                private readonly string $prefix = '',
44
                private readonly ?Journal $journal = null,
45
        ) {
46
                if (!static::isAvailable()) {
1✔
UNCOV
47
                        throw new Nette\NotSupportedException("PHP extension 'memcached' is not loaded.");
×
48
                }
49
                $this->memcached = new \Memcached;
1✔
50
                if ($host) {
1✔
51
                        $this->addServer($host, $port);
1✔
52
                }
53
        }
1✔
54

55

56
        public function addServer(string $host = 'localhost', int $port = 11211): void
1✔
57
        {
58
                if (@$this->memcached->addServer($host, $port, 1) === false) { // @ is escalated to exception
1✔
UNCOV
59
                        $error = error_get_last();
×
UNCOV
60
                        throw new Nette\InvalidStateException("Memcached::addServer(): $error[message].");
×
61
                }
62
        }
1✔
63

64

65
        public function getConnection(): \Memcached
66
        {
UNCOV
67
                return $this->memcached;
×
68
        }
69

70

71
        public function read(string $key): mixed
1✔
72
        {
73
                $key = urlencode($this->prefix . $key);
1✔
74
                $meta = $this->memcached->get($key);
1✔
75
                if (!$meta) {
1✔
76
                        return null;
1✔
77
                }
78

79
                // meta structure:
80
                // array(
81
                //     data => stored data
82
                //     delta => relative (sliding) expiration
83
                //     callbacks => array of callbacks (function, args)
84
                // )
85

86
                // verify dependencies
87
                if (!empty($meta[self::MetaCallbacks]) && !Cache::checkCallbacks($meta[self::MetaCallbacks])) {
1✔
88
                        $this->memcached->delete($key, 0);
1✔
89
                        return null;
1✔
90
                }
91

92
                if (!empty($meta[self::MetaDelta])) {
1✔
93
                        $this->memcached->replace($key, $meta, $meta[self::MetaDelta] + time());
1✔
94
                }
95

96
                return $meta[self::MetaData];
1✔
97
        }
98

99

100
        public function bulkRead(array $keys): array
1✔
101
        {
102
                $prefixedKeys = array_map(fn($key) => urlencode($this->prefix . $key), $keys);
1✔
103
                $keys = array_combine($prefixedKeys, $keys);
1✔
104
                $metas = $this->memcached->getMulti($prefixedKeys) ?: [];
1✔
105
                $result = [];
1✔
106
                $deleteKeys = [];
1✔
107
                foreach ($metas as $prefixedKey => $meta) {
1✔
108
                        if (!empty($meta[self::MetaCallbacks]) && !Cache::checkCallbacks($meta[self::MetaCallbacks])) {
1✔
UNCOV
109
                                $deleteKeys[] = $prefixedKey;
×
110
                        } else {
111
                                $result[$keys[$prefixedKey]] = $meta[self::MetaData];
1✔
112
                        }
113

114
                        if (!empty($meta[self::MetaDelta])) {
1✔
115
                                $this->memcached->replace($prefixedKey, $meta, $meta[self::MetaDelta] + time());
1✔
116
                        }
117
                }
118

119
                if (!empty($deleteKeys)) {
1✔
UNCOV
120
                        $this->memcached->deleteMulti($deleteKeys, 0);
×
121
                }
122

123
                return $result;
1✔
124
        }
125

126

127
        public function lock(string $key): void
128
        {
129
        }
130

131

132
        public function write(string $key, $data, array $dp): void
1✔
133
        {
134
                if (isset($dp[Cache::Items])) {
1✔
UNCOV
135
                        throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.');
×
136
                }
137

138
                $key = urlencode($this->prefix . $key);
1✔
139
                $meta = [
1✔
140
                        self::MetaData => $data,
1✔
141
                ];
142

143
                $expire = 0;
1✔
144
                if (isset($dp[Cache::Expire])) {
1✔
145
                        $expire = (int) $dp[Cache::Expire];
1✔
146
                        if (!empty($dp[Cache::Sliding])) {
1✔
147
                                $meta[self::MetaDelta] = $expire; // sliding time
1✔
148
                        }
149
                }
150

151
                if (isset($dp[Cache::Callbacks])) {
1✔
152
                        $meta[self::MetaCallbacks] = $dp[Cache::Callbacks];
1✔
153
                }
154

155
                if (isset($dp[Cache::Tags]) || isset($dp[Cache::Priority])) {
1✔
156
                        if (!$this->journal) {
1✔
UNCOV
157
                                throw new Nette\InvalidStateException('CacheJournal has not been provided.');
×
158
                        }
159

160
                        $this->journal->write($key, $dp);
1✔
161
                }
162

163
                $this->memcached->set($key, $meta, $expire);
1✔
164
        }
1✔
165

166

167
        public function bulkWrite(array $items, array $dp): void
1✔
168
        {
169
                if (isset($dp[Cache::Items])) {
1✔
UNCOV
170
                        throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.');
×
171
                }
172

173
                $meta = $records = [];
1✔
174
                $expire = 0;
1✔
175
                if (isset($dp[Cache::Expire])) {
1✔
UNCOV
176
                        $expire = (int) $dp[Cache::Expire];
×
UNCOV
177
                        if (!empty($dp[Cache::Sliding])) {
×
178
                                $meta[self::MetaDelta] = $expire; // sliding time
×
179
                        }
180
                }
181

182
                if (isset($dp[Cache::Callbacks])) {
1✔
UNCOV
183
                        $meta[self::MetaCallbacks] = $dp[Cache::Callbacks];
×
184
                }
185

186
                foreach ($items as $key => $meta[self::MetaData]) {
1✔
187
                        $key = urlencode($this->prefix . $key);
1✔
188
                        $records[$key] = $meta;
1✔
189

190
                        if (isset($dp[Cache::Tags]) || isset($dp[Cache::Priority])) {
1✔
191
                                if (!$this->journal) {
1✔
UNCOV
192
                                        throw new Nette\InvalidStateException('CacheJournal has not been provided.');
×
193
                                }
194

195
                                $this->journal->write($key, $dp);
1✔
196
                        }
197
                }
198

199
                $this->memcached->setMulti($records, $expire);
1✔
200
        }
1✔
201

202

203
        public function remove(string $key): void
204
        {
UNCOV
205
                $this->memcached->delete(urlencode($this->prefix . $key), 0);
×
206
        }
207

208

209
        public function bulkRemove(array $keys): void
210
        {
UNCOV
211
                $this->memcached->deleteMulti(array_map(fn($key) => urlencode($this->prefix . $key), $keys), 0);
×
212
        }
213

214

215
        public function clean(array $conditions): void
1✔
216
        {
217
                if (!empty($conditions[Cache::All])) {
1✔
UNCOV
218
                        $this->memcached->flush();
×
219

220
                } elseif ($this->journal) {
1✔
221
                        foreach ($this->journal->clean($conditions) as $entry) {
1✔
222
                                $this->memcached->delete($entry, 0);
1✔
223
                        }
224
                }
225
        }
1✔
226
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc