Skip to content

Commit 2679261

Browse files
authored
Merge pull request #18 from JorgenEvens/feature/redis-support
[redis] Add Redis support
2 parents 7049eaa + 76cd260 commit 2679261

File tree

5 files changed

+270
-10
lines changed

5 files changed

+270
-10
lines changed

README.md

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
11
PHP Cache Dashboard
22
===================
33

4-
A dashboard for multiple caches in PHP
4+
A dashboard for multiple caches in PHP with support for
55
[PHP Opcache](http://php.net/manual/en/intro.opcache.php),
6-
[APCu](http://php.net/manual/en/intro.apcu.php) and
7-
[realpath](http://php.net/manual/en/function.realpath-cache-get.php)
6+
[APCu](http://php.net/manual/en/intro.apcu.php),
7+
[realpath](http://php.net/manual/en/function.realpath-cache-get.php) and
8+
[Redis](https://pecl.php.net/package/redis)
89

910
Try it out at the [demo page](https://je-php-cache-dashboard-demo.herokuapp.com/).
1011

1112
## Prerequisites
1213

13-
- PHP
14+
- PHP 5.3+
1415

1516
and one or more of the supported caches
1617

1718
- PHP OpCache (opcache extension for php5, included by default in php5.5+)
1819
- APC or APCu extension
1920
- Realpath cache ( available since PHP 5.3.2+ )
2021
- Memcache (partially) and Memcached extension
22+
- Redis
2123

2224
## Supported operations
2325

@@ -28,7 +30,7 @@ and one or more of the supported caches
2830
- Selecting all keys
2931
- Deleting keys without regular expressions
3032
- Sort on any data column
31-
- View APCu entry contents
33+
- View entry contents
3234

3335
## Usage
3436

@@ -37,7 +39,7 @@ Navigate to the page using your browser and you will receive cache information.
3739

3840
![Screenshot of php-cache-dashboard](http://jorgen.evens.eu/github/php-cache-dashboard.png)
3941

40-
## Disabling caches
42+
## Configuring caches
4143

4244
Information about specific caches can be disabled by setting the `ENABLE_<cache>` key to false.
4345
The default code tests whether the specific cache is available and supported before enabling it.
@@ -75,6 +77,27 @@ define('ENABLE_REALPATH', true);
7577
define('ENABLE_REALPATH', false);
7678
```
7779

80+
### Redis
81+
82+
```php
83+
<?php
84+
// Enable Redis
85+
define('ENABLE_REDIS', true);
86+
87+
// Disable Redis
88+
define('ENABLE_REDIS', false);
89+
```
90+
91+
Redis configuration can be done by either changing the `REDIS_` constants or by setting the environment variables with the same name.
92+
93+
| Environment Variable | Default | Description |
94+
| --- | --- | --- |
95+
| REDIS\_HOST | `127.0.0.1` | The hostname of the redis instance to connect to |
96+
| REDIS\_PORT | `6379` | The TCP port number on which Redis is listening for connections |
97+
| REDIS\_PASSWORD | `null` | The password used to connect |
98+
| REDIS\_DATABASE | `null` | Set this to the database number if you want to lock the database number |
99+
| REDIS\_SIZE | `null` | The size of your Redis database in bytes if total size is detected incorrectly |
100+
78101
## Contributing
79102

80103
I really appreciate any contribution you would like to make, so don't hesitate to report an issue or submit pull requests.

cache.php

Lines changed: 214 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,21 @@
88
define('ENABLE_OPCACHE', extension_loaded('Zend OPcache'));
99
define('ENABLE_REALPATH', function_exists('realpath_cache_size'));
1010
define('ENABLE_MEMCACHE', extension_loaded('memcache') || extension_loaded('memcached'));
11+
define('ENABLE_REDIS', extension_loaded('redis'));
1112

1213
// Memcache configuration
1314
define('MEMCACHE_HOST', getenv('MEMCACHE_HOST') ?: '127.0.0.1');
1415
define('MEMCACHE_PORT', getenv('MEMCACHE_PORT') ?: 11211);
1516
define('MEMCACHE_USER', getenv('MEMCACHE_USER') ?: null);
1617
define('MEMCACHE_PASSWORD', getenv('MEMCACHE_PASSWORD') ?: null);
1718

19+
// Redis configuration
20+
define('REDIS_HOST', getenv('REDIS_HOST') ?: '127.0.0.1');
21+
define('REDIS_PORT', getenv('REDIS_PORT') ?: 6379);
22+
define('REDIS_PASSWORD', getenv('REDIS_PASSWORD') ?: null);
23+
define('REDIS_DATABASE', getenv('REDIS_DATABASE') ?: null);
24+
define('REDIS_SIZE', getenv('REDIS_SIZE') ?: null);
25+
1826
if (ENABLE_APC) {
1927
if (!extension_loaded('apcu')) {
2028
function apcu_cache_info($limited = false) { return apc_cache_info('user', $limited); }
@@ -88,6 +96,45 @@ function __construct($search = null) { parent::__construct('user', $search); }
8896
}
8997
}
9098

99+
if (ENABLE_REDIS) {
100+
$redis = new Redis();
101+
102+
try {
103+
$redis->connect(REDIS_HOST, REDIS_PORT);
104+
105+
if (!empty(REDIS_PASSWORD))
106+
$redis->auth(REDIS_PASSWORD);
107+
108+
$redis_db = 0;
109+
$redis_dbs = $redis->config('GET', 'databases');
110+
$redis_db_select = !is_numeric(REDIS_DATABASE) && !empty($redis_dbs);
111+
$redis_memory = $redis->info('memory');
112+
113+
if (!$redis_db_select)
114+
$redis_db = REDIS_DATABASE;
115+
else if (!empty($_COOKIE['redis_db']))
116+
$redis_db = (int)$_COOKIE['redis_db'];
117+
118+
$redis->select($redis_db);
119+
} catch(RedisException $ex) {
120+
// Failed to connect
121+
}
122+
123+
if( $redis->isConnected() && is_action('redis_clear') ) {
124+
$redis->flushDb();
125+
redirect('?');
126+
}
127+
128+
if( $redis->isConnected() && is_action('redis_delete') ) {
129+
$list = redis_keys(get_selector());
130+
131+
foreach ($list as $key => $item)
132+
$redis->del($key);
133+
134+
redirect( '?action=redis_select&selector=' . $_GET['selector'] );
135+
}
136+
}
137+
91138
function val_to_str($value) {
92139
return htmlentities(var_export($value, true));
93140
}
@@ -223,6 +270,104 @@ function memcache_ref() {
223270
return $keys;
224271
}
225272

273+
function redis_mem( $key ) {
274+
global $redis_memory;
275+
276+
if( $key == 'free' )
277+
return max(redis_mem('total') - redis_mem('used'), 0);
278+
279+
if( $key == 'total' && !empty($redis_memory['maxmemory']) )
280+
return $redis_memory['maxmemory'];
281+
282+
if( $key == 'total' && !empty($redis_memory['total_system_memory']) )
283+
return $redis_memory['total_system_memory'];
284+
285+
if( $key == 'total' && !empty(REDIS_SIZE))
286+
return (int)REDIS_SIZE;
287+
288+
if( $key == 'total')
289+
return redis_mem('used');
290+
291+
if ($key == 'used')
292+
return $redis_memory['used_memory'];
293+
294+
if ($key == 'overhead' && !empty($redis_memory['used_memory_overhead']))
295+
return $redis_memory['used_memory_overhead'];
296+
297+
return 0;
298+
}
299+
300+
function redis_get_key($key, &$found = false) {
301+
global $redis;
302+
303+
$type = $redis->type($key);
304+
$found = $redis->exists($key);
305+
$value = null;
306+
307+
if ($type == Redis::REDIS_STRING || $type == Redis::REDIS_NOT_FOUND)
308+
$value = $redis->get($key);
309+
else if ($type == Redis::REDIS_HASH)
310+
$value == $redis->hgetall($key);
311+
else if ($type == Redis::REDIS_LIST)
312+
$value = $redis->lrange(0, -1);
313+
else if ($type == Redis::REDIS_SET || $type == Redis::REDIS_ZSET) {
314+
$it = null;
315+
$value = array();
316+
317+
do {
318+
$res = $redis->sscan($key, $it);
319+
320+
if (!is_array($res))
321+
continue;
322+
323+
$value = array_merge($value, $res);
324+
} while ($it > 0);
325+
}
326+
else if ($type == Redis::REDIS_ZSET) {
327+
$len = $redis->zcard($key);
328+
$start = 0;
329+
$value = array();
330+
331+
while($start < $len) {
332+
$stop = $start + 99;
333+
334+
$res = $redis->zrange($key, $start, $stop, true);
335+
336+
$start += 100;
337+
}
338+
}
339+
340+
return $value;
341+
}
342+
343+
function redis_keys($selector) {
344+
global $redis;
345+
$keys = array();
346+
347+
$it = null;
348+
do {
349+
$res = $redis->scan($it);
350+
351+
if (!is_array($res))
352+
continue;
353+
354+
foreach ($res as $key) {
355+
if (!preg_match($selector, $key))
356+
continue;
357+
358+
$keys[$key] = array(
359+
'key' => $key,
360+
'ttl' => $redis->ttl($key),
361+
'type' => $redis->type($key),
362+
'size' => $redis->rawCommand('memory', 'usage', $key)
363+
);
364+
}
365+
366+
} while($it > 0);
367+
368+
return $keys;
369+
}
370+
226371
function human_size( $s ) {
227372
$size = 'B';
228373
$sizes = array( 'KB', 'MB', 'GB' );
@@ -285,7 +430,7 @@ function sort_url($on) {
285430
return '?' . $query;
286431
}
287432

288-
function sort_list(&$list) {
433+
function sort_list($list) {
289434
if( !isset( $_GET['sort'] ) )
290435
return $list;
291436

@@ -676,6 +821,74 @@ function sort_list(&$list) {
676821
<?php endif; ?>
677822
</div>
678823
<?php endif; ?>
824+
825+
<?php if(ENABLE_REDIS && $redis->isConnected()): ?>
826+
<h2 id="redis">Redis</h2>
827+
<div>
828+
<h3>Memory <?=human_size(redis_mem('used') + redis_mem('hash'))?> of <?=human_size(redis_mem('total'))?></h3>
829+
<div class="full bar green">
830+
<div class="orange" style="width: <?=percentage(redis_mem('used'), redis_mem('total'))?>%"></div>
831+
<div class="red" style="width: <?=percentage(redis_mem('overhead'), redis_mem('total'))?>%"></div>
832+
</div>
833+
<div>
834+
<h3>Actions</h3>
835+
<form action="?" method="GET">
836+
<label>Cache:
837+
<button name="action" value="redis_clear">Restart</button>
838+
</label>
839+
</form>
840+
<form action="?" method="GET">
841+
<label>Key(s):
842+
<input name="selector" type="text" value="" placeholder=".*" />
843+
</label>
844+
<button type="submit" name="action" value="redis_select">Select</button>
845+
<button type="submit" name="action" value="redis_delete">Delete</button>
846+
</form>
847+
</div>
848+
849+
<?php if( is_action('redis_view') ): ?>
850+
<?php $value = redis_get_key(urldecode($_GET['selector']), $found); ?>
851+
<div>
852+
<h3>Value for <?=htmlentities('"'.urldecode($_GET['selector']).'"')?></h3>
853+
<?php if ($found): ?>
854+
<pre><?=val_to_str($value); ?></pre>
855+
<?php else: ?>
856+
<p>Key not found</p>
857+
<?php endif ?>
858+
</div>
859+
<?php endif ?>
860+
861+
<?php if( is_action('redis_select') ): ?>
862+
<div>
863+
<table>
864+
<thead>
865+
<tr>
866+
<th><a href="<?=sort_url('key')?>">Key</a></th>
867+
<th><a href="<?=sort_url('size')?>">Size</a></th>
868+
<th><a href="<?=sort_url('ttl')?>">TTL</a></th>
869+
<th>Expires</th>
870+
<th>Action</th>
871+
</tr>
872+
</thead>
873+
<tbody>
874+
<?php foreach( sort_list(redis_keys(get_selector())) as $key => $item ):?>
875+
<tr>
876+
<td><?=$item['key']?></td>
877+
<td><?=$item['size']?></td>
878+
<td><?=$item['ttl']?></td>
879+
<td><?=($item['ttl'] < 0 ? 'indefinite' : date('Y-m-d H:i', time() + $item['ttl'] ))?></td>
880+
<td>
881+
<a href="?action=redis_delete&selector=<?=urlencode('^'.$key.'$')?>">Delete</a>
882+
<a href="?action=redis_view&selector=<?=urlencode($key)?>">View</a>
883+
</td>
884+
</tr>
885+
<?php endforeach; ?>
886+
</tbody>
887+
</table>
888+
</div>
889+
<?php endif; ?>
890+
</div>
891+
<?php endif; ?>
679892
</div>
680893
</body>
681894
</html>

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"ext-apcu": "^5.1",
77
"ext-Zend-OPcache": "^7.1",
88
"php": "^7.1",
9-
"ext-memcached": "^3.0"
9+
"ext-memcached": "^3.0",
10+
"ext-redis": "*"
1011
},
1112
"license": "MIT",
1213
"authors": [

composer.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo/index.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,28 @@ class CacheDemoClass {
4545
$memcache->add('type.array', ['abc', 'def']);
4646
$memcache->add('type.string', 'hello-world');
4747
$memcache->add('type.ttl.string', 'hello-world', time() + 3600);
48+
49+
// Redis configuration
50+
$redis_host = getenv('REDIS_HOST') ?: '127.0.0.1';
51+
$redis_port = getenv('REDIS_PORT') ?: 6379;
52+
$redis_password = getenv('REDIS_PASSWORD') ?: null;
53+
$redis_database = getenv('REDIS_DATABASE') ?: null;
54+
55+
$redis = new Redis();
56+
try {
57+
$redis->connect($redis_host, $redis_port);
58+
59+
if (!empty($redis_password))
60+
$redis->auth($redis_password);
61+
62+
if (!empty($redis_database))
63+
$redis->select($redis_database);
64+
65+
$redis->sAdd('type.set', 'abc', 'def');
66+
$redis->set('type.string', 'hello-world');
67+
$redis->setEx('type.ttl.string', 3600, 'hello-world');
68+
} catch(Exception $ex) {}
69+
4870
}
4971

5072
require_once('../cache.php');

0 commit comments

Comments
 (0)