Skip to content

Commit f663e49

Browse files
committed
Refactor Memcached keys loading
1 parent d90a91b commit f663e49

8 files changed

Lines changed: 168 additions & 43 deletions

File tree

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
/**
3+
* This file is part of phpCacheAdmin.
4+
*
5+
* Copyright (c) Róbert Kelčák (https://kelcak.com/)
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
declare(strict_types=1);
12+
13+
namespace RobiNN\Pca\Dashboards\Memcached\MemcacheCompatibility;
14+
15+
trait GetKeysTrait {
16+
/**
17+
* Run command.
18+
*
19+
* @param string $command
20+
*
21+
* @return array<mixed, mixed>
22+
*/
23+
private function runCommand(string $command): array {
24+
static $data = [];
25+
26+
$fp = fsockopen($this->server['host'], (int) $this->server['port']);
27+
28+
fwrite($fp, $command."\n");
29+
30+
$part = '';
31+
32+
while (true) {
33+
$part .= fgets($fp, 1024);
34+
35+
$lines = explode("\n", $part);
36+
$part = array_pop($lines);
37+
38+
foreach ($lines as $line) {
39+
$line = trim($line);
40+
41+
if ($line === 'END' || $line === 'ERROR' || $line === '') {
42+
break 2;
43+
}
44+
45+
$data[] = $line;
46+
}
47+
}
48+
49+
fclose($fp);
50+
51+
return $data;
52+
}
53+
54+
/**
55+
* Get data from line.
56+
*
57+
* @param string $line
58+
*
59+
* @return array<string, string|int>
60+
*/
61+
private function keyData(string $line): array {
62+
static $data = [];
63+
64+
foreach (explode(' ', $line) as $part) {
65+
[$key, $val] = explode('=', $part);
66+
67+
if ($key === 'exp') {
68+
if ($val !== '-1') {
69+
$val = (int) $val - time();
70+
} else {
71+
$val = (int) $val;
72+
}
73+
}
74+
75+
$data[$key] = $val;
76+
}
77+
78+
return $data;
79+
}
80+
81+
/**
82+
* Get all keys.
83+
*
84+
* @return array<int, mixed>
85+
*/
86+
public function getKeys(): array {
87+
static $keys = [];
88+
89+
foreach ($this->runCommand('lru_crawler metadump all') as $line) {
90+
$keys[] = $this->keyData($line);
91+
}
92+
93+
return $keys;
94+
}
95+
}

src/Dashboards/Memcached/MemcacheCompatibility/Memcache.php

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
namespace RobiNN\Pca\Dashboards\Memcached\MemcacheCompatibility;
1414

1515
class Memcache extends \Memcache implements MemcacheInterface {
16+
use GetKeysTrait;
17+
1618
/**
1719
* @var array<string, mixed>
1820
*/
19-
private array $server;
21+
protected array $server;
2022

2123
/**
2224
* @param array<string, mixed> $server
@@ -44,28 +46,15 @@ public function getServerStats(): array {
4446
}
4547

4648
/**
47-
* Get all keys.
49+
* Store item.
50+
*
51+
* @param string $key
52+
* @param mixed $value
53+
* @param int $expiration
4854
*
49-
* @return array<int, string>
55+
* @return bool
5056
*/
51-
public function getKeys(): array {
52-
$list = [];
53-
54-
foreach (@$this->getExtendedStats('slabs') as $slabs) {
55-
$slabs = (array) $slabs;
56-
unset($slabs['active_slabs'], $slabs['total_malloced']);
57-
58-
foreach (array_keys($slabs) as $slab_id) {
59-
foreach ($this->getExtendedStats('cachedump', (int) $slab_id) as $entries) {
60-
if (!empty($entries)) {
61-
foreach ($entries as $name => $data) {
62-
$list[] = $name;
63-
}
64-
}
65-
}
66-
}
67-
}
68-
69-
return $list;
57+
public function store(string $key, $value, int $expiration = 0): bool {
58+
return $this->set($key, $value, 0, $expiration);
7059
}
7160
}

src/Dashboards/Memcached/MemcacheCompatibility/MemcacheInterface.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,21 @@ public function isConnected(): bool;
2727
*/
2828
public function getServerStats(): array;
2929

30+
/**
31+
* Store item.
32+
*
33+
* @param string $key
34+
* @param mixed $value
35+
* @param int $expiration
36+
*
37+
* @return bool
38+
*/
39+
public function store(string $key, $value, int $expiration = 0): bool;
40+
3041
/**
3142
* Get all keys.
3243
*
33-
* @return array<int, string>
44+
* @return array<int, mixed>
3445
*/
3546
public function getKeys(): array;
3647
}

src/Dashboards/Memcached/MemcacheCompatibility/Memcached.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,22 @@
1313
namespace RobiNN\Pca\Dashboards\Memcached\MemcacheCompatibility;
1414

1515
class Memcached extends \Memcached implements MemcacheInterface {
16+
use GetKeysTrait;
17+
18+
/**
19+
* @var array<string, mixed>
20+
*/
21+
protected array $server;
22+
23+
/**
24+
* @param array<string, mixed> $server
25+
*/
26+
public function __construct(array $server = []) {
27+
parent::__construct();
28+
29+
$this->server = $server;
30+
}
31+
1632
/**
1733
* Check connection.
1834
*
@@ -32,11 +48,15 @@ public function getServerStats(): array {
3248
}
3349

3450
/**
35-
* Get all keys.
51+
* Store item.
3652
*
37-
* @return array<int, string>
53+
* @param string $key
54+
* @param mixed $value
55+
* @param int $expiration
56+
*
57+
* @return bool
3858
*/
39-
public function getKeys(): array {
40-
return (array) $this->getAllKeys();
59+
public function store(string $key, $value, int $expiration = 0): bool {
60+
return $this->set($key, $value, $expiration);
4161
}
4262
}

src/Dashboards/Memcached/MemcachedDashboard.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public function getDashboardInfo(): array {
6464
*/
6565
private function connect(array $server) {
6666
if (extension_loaded('memcached')) {
67-
$memcache = new MemcacheCompatibility\Memcached();
67+
$memcache = new MemcacheCompatibility\Memcached($server);
6868
} elseif (extension_loaded('memcache')) {
6969
$memcache = new MemcacheCompatibility\Memcache($server);
7070
} else {

src/Dashboards/Memcached/MemcachedTrait.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,9 +127,10 @@ private function moreInfo(array $servers): string {
127127
private function getAllKeys($memcached): array {
128128
$keys = [];
129129

130-
foreach ($memcached->getKeys() as $key) {
130+
foreach ($memcached->getKeys() as $key_data) {
131131
$keys[] = [
132-
'key' => $key,
132+
'key' => $key_data['key'],
133+
'ttl' => $key_data['exp'],
133134
'type' => 'string', // In Memcache(d) everything is stored as string.
134135
];
135136
}
@@ -153,8 +154,8 @@ private function mainDashboard($memcached): string {
153154
'keys' => $paginator->getPaginated(),
154155
'all_keys' => count($keys),
155156
'new_key_url' => Http::queryString([], ['form' => 'new']),
156-
'edit_url' => Http::queryString([], ['form' => 'edit', 'key' => '']),
157-
'view_url' => Http::queryString([], ['view' => 'key', 'key' => '']),
157+
'edit_url' => Http::queryString([], ['form' => 'edit', 'ttl' => 'ttl_value', 'key' => '']),
158+
'view_url' => Http::queryString([], ['view' => 'key', 'ttl' => 'ttl_value', 'key' => '']),
158159
'paginator' => $paginator->render(),
159160
]);
160161
}
@@ -177,12 +178,15 @@ private function viewKey($memcached): string {
177178

178179
[$value, $encode_fn, $is_formatted] = Helpers::decodeAndFormatValue($value);
179180

181+
$ttl = Http::get('ttl', 'int');
182+
180183
return $this->template->render('partials/view_key', [
181184
'value' => $value,
182185
'type' => 'string', // In Memcache(d) everything is stored as string.
186+
'ttl' => !empty($ttl) ? Helpers::formatSeconds($ttl) : null,
183187
'encode_fn' => $encode_fn,
184188
'formatted' => $is_formatted,
185-
'edit_url' => Http::queryString(['db'], ['form' => 'edit', 'key' => $key]),
189+
'edit_url' => Http::queryString(['ttl'], ['form' => 'edit', 'key' => $key]),
186190
]);
187191
}
188192

@@ -196,17 +200,21 @@ private function viewKey($memcached): string {
196200
private function form($memcached): string {
197201
$key = Http::get('key');
198202
$value = '';
203+
$expire = 0;
199204

200205
$encoder = Http::get('encoder');
201206
$encoder = !empty($encoder) ? $encoder : 'none';
202207

203208
if (isset($_GET['key']) && $memcached->get($key)) {
204209
$value = $memcached->get($key);
210+
$expire = Http::get('ttl', 'int');
211+
$expire = $expire === -1 ? 0 : $expire;
205212
}
206213

207214
if (isset($_POST['submit'])) {
208215
$key = Http::post('key');
209216
$value = Http::post('value');
217+
$expire = Http::post('expire', 'int');
210218
$old_key = Http::post('old_key');
211219
$encoder = Http::post('encoder');
212220

@@ -218,9 +226,9 @@ private function form($memcached): string {
218226
$memcached->delete($old_key);
219227
}
220228

221-
$memcached->set($key, $value);
229+
$memcached->store($key, $value, $expire);
222230

223-
Http::redirect([], ['view' => 'key', 'key' => $key]);
231+
Http::redirect([], ['view' => 'key', 'ttl' => $expire, 'key' => $key]);
224232
}
225233

226234
if ($encoder !== 'none') {
@@ -230,6 +238,7 @@ private function form($memcached): string {
230238
return $this->template->render('dashboards/memcached/form', [
231239
'key' => $key,
232240
'value' => $value,
241+
'expire' => $expire,
233242
'encoders' => Helpers::getEncoders(),
234243
'encoder' => $encoder,
235244
]);

templates/dashboards/memcached/form.twig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88
required: true,
99
}) }}
1010

11+
{{ include('components/input.twig', {
12+
id: 'expire',
13+
label: 'Expire (in seconds)',
14+
type: 'number',
15+
help: '0 removes expiration (default).',
16+
value: expire,
17+
attr: ' min="0" max="2592000"',
18+
}) }}
19+
1120
{% if encoders %}
1221
<div class="mb-3">
1322
<label for="encoder" class="block text-sm font-semibold mb-2">Encode function</label>

templates/dashboards/memcached/memcached.twig

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
{% if all_keys != 0 %}
2-
{{ include('components/alert.twig', {
3-
message: svg('info') ~ 'Due to the limitations of the Memcache(d) PHP extension, it is not possible to return all keys seamlessly.',
4-
hide_close: true,
5-
alert_color: 'bg-emerald-500',
6-
}) }}
7-
{% endif %}
8-
91
<div class="flex md:justify-end gap-1 mb-5">
102
{{ include('partials/keys_buttons.twig') }}
113
</div>

0 commit comments

Comments
 (0)