Skip to content

Commit 32b9f86

Browse files
authored
Merge pull request #16 from JorgenEvens/feature/memcache
[memcache] Add memcache support
2 parents 7098093 + 4195a6d commit 32b9f86

File tree

3 files changed

+217
-3
lines changed

3 files changed

+217
-3
lines changed

cache.php

Lines changed: 192 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
<?php
2+
3+
// Hides error when no default timezone has been set
4+
$TZ = @date_default_timezone_get();
5+
date_default_timezone_set($TZ);
6+
27
define('ENABLE_APC', extension_loaded('apcu') || extension_loaded('apc'));
38
define('ENABLE_OPCACHE', extension_loaded('Zend OPcache'));
49
define('ENABLE_REALPATH', function_exists('realpath_cache_size'));
10+
define('ENABLE_MEMCACHE', extension_loaded('memcache') || extension_loaded('memcached'));
11+
12+
// Memcache configuration
13+
define('MEMCACHE_HOST', getenv('MEMCACHE_HOST') ?: '127.0.0.1');
14+
define('MEMCACHE_PORT', getenv('MEMCACHE_PORT') ?: 11211);
15+
define('MEMCACHE_USER', getenv('MEMCACHE_USER') ?: null);
16+
define('MEMCACHE_PASSWORD', getenv('MEMCACHE_PASSWORD') ?: null);
517

618
if (ENABLE_APC) {
719
if (!extension_loaded('apcu')) {
@@ -34,6 +46,49 @@ function __construct($search = null) { parent::__construct('user', $search); }
3446
$realpathCacheTotal = machine_size(ini_get('realpath_cache_size'));
3547
}
3648

49+
if (ENABLE_MEMCACHE) {
50+
if (extension_loaded('memcached')) {
51+
$memcache = new \Memcached();
52+
$memcacheVersion = 'memcached';
53+
$memcache->addServer(MEMCACHE_HOST, MEMCACHE_PORT);
54+
if (!empty(MEMCACHE_USER) && !empty(MEMCACHE_PASSWORD))
55+
$memcache->setSaslAuthData(MEMCACHE_USER, MEMCACHE_PASSWORD);
56+
$memcache_stats = $memcache->getStats();
57+
} else if (extension_loaded('memcache')) {
58+
// This extension does not support SASL authentication
59+
$memcache = new \Memcache();
60+
$memcacheVersion = 'memcache';
61+
$memcache->addServer(MEMCACHE_HOST, MEMCACHE_PORT);
62+
$memcache_stats = $memcache->getExtendedStats();
63+
}
64+
65+
if( is_action('memcache_clear') ) {
66+
$memcache->flush();
67+
redirect('?');
68+
}
69+
70+
if( is_action('memcache_delete') && $memcacheVersion == 'memcached' ) {
71+
$list = memcache_ref();
72+
$selector = get_selector();
73+
74+
foreach ($list as $key => $item)
75+
if (preg_match($selector, $key))
76+
$memcache->delete($key);
77+
78+
redirect( '?action=memcache_select&selector=' . $_GET['selector'] );
79+
}
80+
81+
if( is_action('memcache_delete') && $memcacheVersion != 'memcached' ) {
82+
$memcache->delete($_GET['selector']);
83+
84+
redirect( '?action=memcache_view&selector=' . $_GET['selector'] );
85+
}
86+
}
87+
88+
function val_to_str($value) {
89+
return htmlentities(var_export($value, true));
90+
}
91+
3792
function is_action($action) {
3893
return isset( $_GET['action'] ) && $_GET['action'] == $action;
3994
}
@@ -104,6 +159,66 @@ function apcu_ref() {
104159
return array();
105160
}
106161

162+
function memcache_mem( $key ) {
163+
global $memcache_stats;
164+
165+
if( $key == 'free' )
166+
return memcache_mem('total') - memcache_mem('used');
167+
168+
if( $key == 'total')
169+
$key = 'limit_maxbytes';
170+
171+
if( $key == 'used' )
172+
$key = 'bytes';
173+
174+
if( $key == 'hash' )
175+
$key = 'hash_bytes';
176+
177+
$result = 0;
178+
foreach( $memcache_stats as $server )
179+
$result += empty($server[$key]) ? 0 : $server[$key];
180+
181+
return $result;
182+
}
183+
184+
function memcache_get_key($key, &$found = false) {
185+
global $memcache;
186+
global $memcacheVersion;
187+
188+
if (empty($key)) {
189+
$found = false;
190+
return false;
191+
}
192+
193+
if ($memcacheVersion == 'memcache') {
194+
$val = $memcache->get(array($key));
195+
$found = count($val) > 0;
196+
return $found ? array_pop($val) : false;
197+
}
198+
199+
$val = $memcache->get($key, null, Memcached::GET_EXTENDED);
200+
$found = $val !== false;
201+
return $val['value'];
202+
}
203+
204+
function memcache_ref() {
205+
global $memcache;
206+
207+
// Listing keys is not supported using the legacy Memcache module
208+
// PHP 7 and newer do not support this extension anymore
209+
if (!extension_loaded('memcached'))
210+
return array();
211+
212+
$items = $memcache->getAllKeys();
213+
214+
$keys = array();
215+
foreach( $items as $item ) {
216+
$keys[$item] = memcache_get_key($item);
217+
}
218+
219+
return $keys;
220+
}
221+
107222
function human_size( $s ) {
108223
$size = 'B';
109224
$sizes = array( 'KB', 'MB', 'GB' );
@@ -383,7 +498,7 @@ function sort_list(&$list) {
383498
<?php if( is_action('apcu_view') ): ?>
384499
<div>
385500
<h3>Value for <?=htmlentities('"'.$_GET['selector'].'"')?></h3>
386-
<pre><?=htmlentities(var_export(apcu_fetch(urldecode($_GET['selector'])), true)); ?></pre>
501+
<pre><?=val_to_str(apcu_fetch(urldecode($_GET['selector']))); ?></pre>
387502
</div>
388503
<?php endif; ?>
389504
<?php if( is_action('apcu_select') ): ?>
@@ -477,6 +592,82 @@ function sort_list(&$list) {
477592
<?php endif; ?>
478593
</div>
479594
<?php endif; ?>
595+
596+
<?php if(ENABLE_MEMCACHE): ?>
597+
<?php $list = memcache_ref(); ?>
598+
<h2 id="memcached">Memcached</h2>
599+
<div>
600+
<h3>Memory <?=human_size(memcache_mem('used') + memcache_mem('hash'))?> of <?=human_size(memcache_mem('total'))?></h3>
601+
<div class="full bar green">
602+
<div class="orange" style="width: <?=percentage(memcache_mem('used'), memcache_mem('total'))?>%"></div>
603+
<div class="red" style="width: <?=percentage(memcache_mem('hash'), memcache_mem('total'))?>%"></div>
604+
</div>
605+
<div>
606+
<h3>Actions</h3>
607+
<form action="?" method="GET">
608+
<label>Cache:
609+
<button name="action" value="memcache_clear">Restart</button>
610+
</label>
611+
</form>
612+
<form action="?" method="GET">
613+
<label>Key(s):
614+
<input name="selector" type="text" value="" placeholder=".*" />
615+
</label>
616+
<?php if ($memcacheVersion == 'memcached'): ?>
617+
<button type="submit" name="action" value="memcache_select">Select</button>
618+
<?php else: ?>
619+
<button type="submit" name="action" value="memcache_view">View</button>
620+
<?php endif ?>
621+
<button type="submit" name="action" value="memcache_delete">Delete</button>
622+
</form>
623+
</div>
624+
625+
<?php if( is_action('memcache_view') ): ?>
626+
<?php $value = memcache_get_key($_GET['selector'], $found); ?>
627+
<div>
628+
<h3>Value for <?=htmlentities('"'.$_GET['selector'].'"')?></h3>
629+
<?php if ($found): ?>
630+
<pre><?=val_to_str($value); ?></pre>
631+
<?php else: ?>
632+
<p>Key not found</p>
633+
<?php endif ?>
634+
</div>
635+
<?php endif ?>
636+
637+
<?php if ($memcacheVersion == 'memcached'): ?>
638+
<?php if( is_action('memcache_select') ): ?>
639+
<div>
640+
<table>
641+
<thead>
642+
<tr>
643+
<th><a href="<?=sort_url('key')?>">Key</a></th>
644+
<th>Action</th>
645+
</tr>
646+
</thead>
647+
<tbody>
648+
<?php foreach( sort_list($list) as $key => $value ):
649+
if( !preg_match(get_selector(), $key) ) continue;?>
650+
<tr>
651+
<td><?=$key?></td>
652+
<td>
653+
<a href="?action=memcache_delete&selector=<?=urlencode('^'.$key.'$')?>">Delete</a>
654+
<a href="?action=memcache_view&selector=<?=urlencode($key)?>">View</a>
655+
</td>
656+
</tr>
657+
<?php endforeach; ?>
658+
</tbody>
659+
</table>
660+
</div>
661+
<?php endif; ?>
662+
<?php else: ?>
663+
<p style="text-align: center">
664+
Legacy <a href="https://pecl.php.net/package/memcache">memcache extension</a> does not support listing keys
665+
<br />
666+
Please install the newer <a href="https://pecl.php.net/package/memcached">memcached extension</a>
667+
</p>
668+
<?php endif; ?>
669+
</div>
670+
<?php endif; ?>
480671
</div>
481672
</body>
482673
</html>

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,8 @@
1313
"name": "Jorgen Evens",
1414
"email": "jorgen@evens.eu"
1515
}
16-
]
16+
],
17+
"scripts": {
18+
"dev": "php -S 127.0.0.1:8181"
19+
}
1720
}

demo/index.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
* DEMO PAGE
44
*
55
* This page is only use to load the demo on Heroku!
6+
* This page requires PHP >= 7
67
*
78
* Please use the `cache.php` file directly for your own deployments.
89
*/
910
class CacheDemoClass {
1011
public $hello = 'world';
1112
}
1213

13-
if (!preg_match('#herokuapp.com#', $_SERVER['HTTP_REFERER'] ?? '')) {
14+
define('INIT', !preg_match('#herokuapp.com|localhost|127.0.0.1#', $_SERVER['HTTP_REFERER'] ?? ''));
15+
16+
if (INIT) {
1417
apcu_store('type.bool.true', true);
1518
apcu_store('type.bool.false', true);
1619
apcu_store('type.numeric.10', 10);
@@ -25,4 +28,21 @@ class CacheDemoClass {
2528
apcu_store('type.class-instance', new CacheDemoClass(), 3600);
2629
}
2730

31+
if (INIT) {
32+
// Memcache configuration
33+
$memcache_host = getenv('MEMCACHE_HOST') ?: '127.0.0.1';
34+
$memcache_port = getenv('MEMCACHE_PORT') ?: 11211;
35+
$memcache_user = getenv('MEMCACHE_USER') ?: null;
36+
$memcache_password = getenv('MEMCACHE_PASSWORD') ?: null;
37+
38+
$memcache = new \Memcached();
39+
$memcache->addServer($memcache_host, $memcache_port);
40+
if (!empty($memcache_user) && !empty($memcache_password))
41+
$memcache->setSaslAuthData($memcache_user, $memcache_password);
42+
43+
$memcache->add('type.array', ['abc', 'def']);
44+
$memcache->add('type.string', 'hello-world');
45+
$memcache->add('type.ttl.string', 'hello-world', time() + 3600);
46+
}
47+
2848
require_once('../cache.php');

0 commit comments

Comments
 (0)