Skip to content

Commit aebaa0e

Browse files
committed
NewMemcachedStorage -> MemcachedStorage
1 parent f1a3d53 commit aebaa0e

File tree

8 files changed

+237
-219
lines changed

8 files changed

+237
-219
lines changed
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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+
15+
16+
/**
17+
* Memcached storage using memcached extension.
18+
*/
19+
class MemcachedStorage implements Nette\Caching\IStorage, Nette\Caching\IBulkReader
20+
{
21+
use Nette\SmartObject;
22+
23+
/** @internal cache structure */
24+
private const
25+
META_CALLBACKS = 'callbacks',
26+
META_DATA = 'data',
27+
META_DELTA = 'delta';
28+
29+
/** @var \Memcached */
30+
private $memcached;
31+
32+
/** @var string */
33+
private $prefix;
34+
35+
/** @var IJournal */
36+
private $journal;
37+
38+
39+
/**
40+
* Checks if Memcached extension is available.
41+
*/
42+
public static function isAvailable(): bool
43+
{
44+
return extension_loaded('memcached');
45+
}
46+
47+
48+
public function __construct(string $host = 'localhost', int $port = 11211, string $prefix = '', IJournal $journal = null)
49+
{
50+
if (!static::isAvailable()) {
51+
throw new Nette\NotSupportedException("PHP extension 'memcached' is not loaded.");
52+
}
53+
54+
$this->prefix = $prefix;
55+
$this->journal = $journal;
56+
$this->memcached = new \Memcached;
57+
if ($host) {
58+
$this->addServer($host, $port);
59+
}
60+
}
61+
62+
63+
public function addServer(string $host = 'localhost', int $port = 11211): void
64+
{
65+
if ($this->memcached->addServer($host, $port, 1) === false) {
66+
$error = error_get_last();
67+
throw new Nette\InvalidStateException("Memcached::addServer(): $error[message].");
68+
}
69+
}
70+
71+
72+
public function getConnection(): \Memcached
73+
{
74+
return $this->memcached;
75+
}
76+
77+
78+
/**
79+
* Read from cache.
80+
* @return mixed
81+
*/
82+
public function read(string $key)
83+
{
84+
$key = urlencode($this->prefix . $key);
85+
$meta = $this->memcached->get($key);
86+
if (!$meta) {
87+
return null;
88+
}
89+
90+
// meta structure:
91+
// array(
92+
// data => stored data
93+
// delta => relative (sliding) expiration
94+
// callbacks => array of callbacks (function, args)
95+
// )
96+
97+
// verify dependencies
98+
if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) {
99+
$this->memcached->delete($key, 0);
100+
return null;
101+
}
102+
103+
if (!empty($meta[self::META_DELTA])) {
104+
$this->memcached->replace($key, $meta, $meta[self::META_DELTA] + time());
105+
}
106+
107+
return $meta[self::META_DATA];
108+
}
109+
110+
111+
/**
112+
* Reads from cache in bulk.
113+
* @return array key => value pairs, missing items are omitted
114+
*/
115+
public function bulkRead(array $keys): array
116+
{
117+
$prefixedKeys = array_map(function ($key) {
118+
return urlencode($this->prefix . $key);
119+
}, $keys);
120+
$keys = array_combine($prefixedKeys, $keys);
121+
$metas = $this->memcached->getMulti($prefixedKeys);
122+
$result = [];
123+
$deleteKeys = [];
124+
foreach ($metas as $prefixedKey => $meta) {
125+
if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) {
126+
$deleteKeys[] = $prefixedKey;
127+
} else {
128+
$result[$keys[$prefixedKey]] = $meta[self::META_DATA];
129+
}
130+
131+
if (!empty($meta[self::META_DELTA])) {
132+
$this->memcached->replace($prefixedKey, $meta, $meta[self::META_DELTA] + time());
133+
}
134+
}
135+
if (!empty($deleteKeys)) {
136+
$this->memcached->deleteMulti($deleteKeys, 0);
137+
}
138+
139+
return $result;
140+
}
141+
142+
143+
/**
144+
* Prevents item reading and writing. Lock is released by write() or remove().
145+
*/
146+
public function lock(string $key): void
147+
{
148+
}
149+
150+
151+
/**
152+
* Writes item into the cache.
153+
*/
154+
public function write(string $key, $data, array $dp): void
155+
{
156+
if (isset($dp[Cache::ITEMS])) {
157+
throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.');
158+
}
159+
160+
$key = urlencode($this->prefix . $key);
161+
$meta = [
162+
self::META_DATA => $data,
163+
];
164+
165+
$expire = 0;
166+
if (isset($dp[Cache::EXPIRATION])) {
167+
$expire = (int) $dp[Cache::EXPIRATION];
168+
if (!empty($dp[Cache::SLIDING])) {
169+
$meta[self::META_DELTA] = $expire; // sliding time
170+
}
171+
}
172+
173+
if (isset($dp[Cache::CALLBACKS])) {
174+
$meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS];
175+
}
176+
177+
if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) {
178+
if (!$this->journal) {
179+
throw new Nette\InvalidStateException('CacheJournal has not been provided.');
180+
}
181+
$this->journal->write($key, $dp);
182+
}
183+
184+
$this->memcached->set($key, $meta, $expire);
185+
}
186+
187+
188+
/**
189+
* Removes item from the cache.
190+
*/
191+
public function remove(string $key): void
192+
{
193+
$this->memcached->delete(urlencode($this->prefix . $key), 0);
194+
}
195+
196+
197+
/**
198+
* Removes items from the cache by conditions & garbage collector.
199+
*/
200+
public function clean(array $conditions): void
201+
{
202+
if (!empty($conditions[Cache::ALL])) {
203+
$this->memcached->flush();
204+
205+
} elseif ($this->journal) {
206+
foreach ($this->journal->clean($conditions) as $entry) {
207+
$this->memcached->delete($entry, 0);
208+
}
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)