Index: memcache.db.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/memcache/memcache.db.inc,v
retrieving revision 1.2.2.11
diff -u -p -r1.2.2.11 memcache.db.inc
--- memcache.db.inc	12 Feb 2008 21:58:06 -0000	1.2.2.11
+++ memcache.db.inc	19 Oct 2008 19:39:22 -0000
@@ -6,32 +6,34 @@ require_once 'dmemcache.inc';
 /** Implementation of cache.inc with memcache logic included **/
 
 /**
- * Return data from the persistent cache.
+ * Return data from the persistent cache. Data may be stored as either plain text or as serialized data.
+ * cache_get will automatically return unserialized objects and arrays.
  *
- * @param $key
+ * @param $cid
  *   The cache ID of the data to retrieve.
  * @param $table
  *   The table $table to store the data in. Valid core values are 'cache_filter',
  *   'cache_menu', 'cache_page', or 'cache' for the default cache.
  */
-function cache_get($key, $table = 'cache') {
+function cache_get($cid, $table = 'cache') {
   global $user;
 
   // Garbage collection necessary when enforcing a minimum cache lifetime
   $cache_flush = variable_get('cache_flush', 0);
   if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= time())) {
+    // Reset the variable immediately to prevent a meltdown in heavy load situations.
+    variable_set('cache_flush', 0);
     // Time to flush old cache data
     db_query("DELETE FROM {". $table ."} WHERE expire != %d AND expire <= %d", CACHE_PERMANENT, $cache_flush);
-    variable_set('cache_flush', 0);
   }
 
   // If we have a memcache hit for this, return it.
-  if ($cache = dmemcache_get($key, $table)) {
+  if ($cache = dmemcache_get($cid, $table)) {
     return $cache;
   }
 
   // Look for a database cache hit.
-  if ($cache = db_fetch_object(db_query("SELECT data, created, headers, expire, serialized FROM {". $table ."} WHERE cid = '%s'", $key))) {
+  if ($cache = db_fetch_object(db_query("SELECT data, created, headers, expire, serialized FROM {". $table ."} WHERE cid = '%s'", $cid))) {
     if (isset($cache->data)) {
       // If the data is permanent or we're not enforcing a minimum cache lifetime
       // always return the cached data.
@@ -62,7 +64,7 @@ function cache_get($key, $table = 'cache
 
     // By calling cache_set with an extra paramater to signify no db storage,
     // we can lazy instantiate memcache that just comes online.
-    cache_set($key, $table, $cache->data, $cache->expire, $cache->headers, FALSE);
+    cache_set($cid, $cache->data, $table, $cache->expire, $cache->headers, FALSE);
     return $cache;
   }
   return 0;
@@ -95,11 +97,13 @@ function cache_get($key, $table = 'cache
  *
  * @param $cid
  *   The cache ID of the data to store.
+ * @param $data
+ *   The data to store in the cache. Complex data types will be automatically 
+ *   serialized before insertion. Strings will be stored as plain text and 
+ *   not serialized.
  * @param $table
  *   The table $table to store the data in. Valid core values are 'cache_filter',
  *   'cache_menu', 'cache_page', or 'cache'.
- * @param $data
- *   The data to store in the cache. Complex data types must be serialized first.
  * @param $expire
  *   One of the following values:
  *   - CACHE_PERMANENT: Indicates that the item should never be removed unless
@@ -115,14 +119,14 @@ function cache_get($key, $table = 'cache
  *   It allows us to do a cache_set and not write to the database, but only
  *   to memcache.
  */
-function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $headers = NULL, $db_storage = TRUE) {
-  $time = time();
+function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL, $db_storage = TRUE) {
+  $created = time();
 
   // Create new cache object.
   $cache = new stdClass;
   $cache->cid = $cid;
   $cache->data = is_object($data) ? memcache_clone($data) : $data;
-  $cache->created = $time;
+  $cache->created = $created;
   $cache->expire = $expire;
   $cache->headers = $headers;
 
@@ -135,11 +139,12 @@ function cache_set($cid, $table = 'cache
 
     // Save to the database
     db_query("DELETE FROM {%s} WHERE cid = '%s'", $table, $cid);
-    db_query("INSERT INTO {%s} (data, created, expire, headers, serialized, cid) VALUES (%b, %d, %d, '%s', %d, '%s')", $table, $data, time(), $expire, $headers, $serialized, $cid);
+    db_query("INSERT INTO {%s} (cid, data, expire, created, headers, serialized) VALUES ('%s', %b, %d, %d, '%s', '%s')", $table, $cid, $data, $expire, $created, $headers, $serialized);
   }
 
   // Save to memcache
   if ($expire == CACHE_TEMPORARY) {
+    // 2591199 seconds = about 1 month
     $expire = variable_get('cache_lifetime', 2591999);
   }
   dmemcache_set($cid, $cache, $expire, $table);
@@ -166,28 +171,14 @@ function cache_set($cid, $table = 'cache
 function cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) {
   global $user;
 
-  $bin = empty($table) ? 'cache_page' : $table;
-  // Memcache logic is simpler because memcache doesn't have a minimum cache
-  // lifetime consideration (it handles it internally), and doesn't support
-  // wildcards.
-  if (empty($cid) || ($cid == '*' && $wildcard !== TRUE)) {
-    # don't do anything if cid is unset. this matches the default drupal behavior...
-    if ($wildcard && $cid != '*') {
-      if (variable_get('memcache_debug', FALSE)) {
-        // call watchdog, since you probably didn't want to flush the entire bin.
-        watchdog('memcache', "illegal wildcard in cache_clear_all - not flushing entire bin. table: $table. cid: $cid", WATCHDOG_WARNING);
-      }
-    }
-  }
-  else if ($cid == '*' || $wildcard === TRUE) {
-    dmemcache_flush($table);
-  }
-  else {
-    dmemcache_delete($cid, $table);
-  }
-
+  // Default behavior for when cache_clear_all() is called without parameters
+  // is to clear all of the expirable entries in the block and page caches.
   if (!isset($cid) && !isset($table)) {
-    cache_clear_all('*', 'cache_page', TRUE);
+    // Clear the block cache first, so stale data will
+    // not end up in the page cache.
+    cache_clear_all(NULL, 'cache_block');
+    cache_clear_all(NULL, 'cache_page');
+
     return;
   }
 
@@ -209,16 +200,25 @@ function cache_clear_all($cid = NULL, $t
         // passed since the first request to clear the cache.
         db_query("DELETE FROM {". $table ."} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time());
         variable_set('cache_flush', 0);
+        // Note there is no memcache flush here.
+        // Items are cached in memcache with an expiry equal to cache_lifetime
+        // and memcache will expire these automatically.
       }
     }
     else {
       // No minimum cache lifetime, flush all temporary cache entries now.
+      // Note, however, that memcache does not have the ability to discern
+      // between CACHE_PERMANENT and CACHE_TEMPORARY items. So having no
+      // minimum cache lifetime means that all CACHE_PERMANENT items are
+      // lost here, too. To avoid that, set a minimum cache lifetime.
+      dmemcache_flush($table);
       db_query("DELETE FROM {". $table ."} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time());
     }
   }
   else {
     if ($wildcard) {
       if ($cid == '*') {
+        dmemcache_flush($table);
         db_query("DELETE FROM {". $table ."}");
       }
       else {
@@ -226,6 +226,7 @@ function cache_clear_all($cid = NULL, $t
       }
     }
     else {
+      dmemcache_delete($cid, $table);
       db_query("DELETE FROM {". $table ."} WHERE cid = '%s'", $cid);
     }
   }
Index: memcache.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/memcache/memcache.inc,v
retrieving revision 1.15.2.8
diff -u -p -r1.15.2.8 memcache.inc
--- memcache.inc	12 Feb 2008 21:58:06 -0000	1.15.2.8
+++ memcache.inc	19 Oct 2008 19:39:22 -0000
@@ -7,15 +7,18 @@ require_once 'dmemcache.inc';
 
 /**
  * Return data from the persistent cache.
+ * 
+ * Data may be stored as either plain text or as serialized data.
+ * cache_get() will automatically return unserialized objects and arrays.
  *
- * @param $key
+ * @param $cid
  *   The cache ID of the data to retrieve.
  * @param $table
  *   The table $table to store the data in. Valid core values are 'cache_filter',
  *   'cache_menu', 'cache_page', or 'cache' for the default cache.
  */
-function cache_get($key, $table = 'cache') {
-  return dmemcache_get($key, $table);
+function cache_get($cid, $table = 'cache') {
+  return dmemcache_get($cid, $table);
 }
 
 /**
@@ -23,11 +26,13 @@ function cache_get($key, $table = 'cache
  *
  * @param $cid
  *   The cache ID of the data to store.
+ * @param $data
+ *   The data to store in the cache. Complex data types will be automatically
+ *   serialized before insertion. Strings will be stored as plain text and 
+ *   not serialized.
  * @param $table
  *   The table $table to store the data in. Valid core values are 'cache_filter',
  *   'cache_menu', 'cache_page', or 'cache'.
- * @param $data
- *   The data to store in the cache. Complex data types must be serialized first.
  * @param $expire
  *   One of the following values:
  *   - CACHE_PERMANENT: Indicates that the item should never be removed unless
@@ -38,19 +43,15 @@ function cache_get($key, $table = 'cache
  *     the given time, after which it behaves like CACHE_TEMPORARY.
  * @param $headers
  *   A string containing HTTP header information for cached pages.
- * @param $db_storage
- *   This boolean is unique to the memcache.inc implementation of cache set.
- *   It allows us to do a cache_set and not write to the database, but only
- *   to memcache.
  */
-function cache_set($cid, $table = 'cache', $data, $expire = CACHE_PERMANENT, $headers = NULL) {
-  $time = time();
+function cache_set($cid, $data, $table = 'cache', $expire = CACHE_PERMANENT, $headers = NULL) {
+  $created = time();
 
   // Create new cache object.
   $cache = new stdClass;
   $cache->cid = $cid;
   $cache->data = is_object($data) ? memcache_clone($data) : $data;
-  $cache->created = $time;
+  $cache->created = $created;
   $cache->expire = $expire;
   $cache->headers = $headers;
 
@@ -64,7 +65,7 @@ function cache_set($cid, $table = 'cache
 /**
  *
  * Expire data from the cache. If called without arguments, expirable
- * entries will be cleared from the cache_page table.
+ * entries will be cleared from the cache_page and cache_block tables.
  *
  * @param $cid
  *   If set, the cache ID to delete. Otherwise, all cache entries that can
Index: memcache_admin/memcache_admin.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/memcache/memcache_admin/memcache_admin.info,v
retrieving revision 1.1.2.5
diff -u -p -r1.1.2.5 memcache_admin.info
--- memcache_admin/memcache_admin.info	31 Jul 2007 14:51:36 -0000	1.1.2.5
+++ memcache_admin/memcache_admin.info	19 Oct 2008 19:39:22 -0000
@@ -1,5 +1,5 @@
-; $Id: memcache_admin.info,v 1.1.2.5 2007/07/31 14:51:36 robertDouglass Exp $

-name = Memcache Admin

-description = Adds a User Interface to monitor the Memcache for this site.

-package = Caching

-dependencies = memcache

+; $Id: memcache_admin.info,v 1.1.2.5 2007/07/31 14:51:36 robertDouglass Exp $
+name = Memcache Admin
+description = Adds a User Interface to monitor the Memcache for this site.
+package = Caching
+core = 6.x
Index: memcache_admin/memcache_admin.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/memcache/memcache_admin/memcache_admin.module,v
retrieving revision 1.3.2.10
diff -u -p -r1.3.2.10 memcache_admin.module
--- memcache_admin/memcache_admin.module	1 Aug 2008 10:24:33 -0000	1.3.2.10
+++ memcache_admin/memcache_admin.module	19 Oct 2008 19:39:22 -0000
@@ -1,251 +1,249 @@
-<?php

-// $Id: memcache_admin.module,v 1.3.2.10 2008/08/01 10:24:33 robertDouglass Exp $

-

-/**

- * For the collection of memcache stats. This small .js file makes sure that the

- * HTML displaying the stats is inside of the <body> part of the HTML

- * document.

- */

-function memcache_admin_init() {

-  global $user;

-  if (($user->uid == 0) || strstr($_SERVER['PHP_SELF'], 'update.php') || strstr($_GET['q'], 'autocomplete')) {

-    // update.php relies on standard error handler

-  }

-  else {

-    if ($user->uid) {

-      drupal_add_js(drupal_get_path('module', 'memcache_admin'). '/memcache.js');

-    }

-    register_shutdown_function('memcache_admin_shutdown');

-  }

-}

-

-/**

- * Implementation of hook_perm

- */

-function memcache_admin_perm() {

-  return array('access memcache statistics');

-}

-

-/**

- * Implementation of hook_menu

- */

-function memcache_admin_menu($may_cache) {

-  $items = array();

-

-  if ($may_cache) {

-    $items[] = array(

-      'path' => 'admin/settings/memcache',

-      'title' => t('Memcache'),

-      'callback' => 'drupal_get_form',

-      'callback arguments' => array('memcache_admin_admin_settings'),

-      'access' => user_access('administer site configuration'),

-      'type' => MENU_NORMAL_ITEM

-    );

-    $items[] = array(

-      'path' => 'admin/logs/memcache',

-      'callback' => 'memcache_admin_stats',

-      'title' => t('Memcache status'),

-      'access' => user_access('access memcache statistics'),

-      'description' => t("View the statistics for this site's memcache and generate new settings."),

-      'weight' => 1,

-    );

-  }

-

-  else if (arg(0) == 'admin' && arg(1) == 'logs' && arg(2) == 'memcache') {

-    $memache_servers = variable_get('memcache_servers', array());

-    $clusters = array();

-    foreach($memache_servers as $server => $cluster) {

-      $clusters[$cluster]['servers'][] = $server;

-      $clusters[$cluster]['bin'] = _memcache_admin_get_bin_for_cluster($cluster);

-    }

-

-    $count = 0;

-    foreach ($clusters as $cluster => $cluster_info) {

-      if ($cluster_info['bin']) {

-        if (empty($current_cluster)) {

-          $current_cluster = arg(3);

-          if (empty($current_cluster)) {

-            $current_cluster = $cluster;

-          }

-        }

-

-        $items[] = array(

-          'path' => 'admin/logs/memcache/' . $cluster,

-          'type' =>  $count == 0 ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,

-          'callback' => 'memcache_admin_stats',

-          'callback arguments' => array($cluster),

-          'title' => $cluster,

-          'access' => user_access('access memcache statistics'),

-          'weight' => $count,

-        );

-

-        $count++;

-

-        if ($cluster == $current_cluster) {

-          $sub_count = 0;

-          foreach(array('default', 'reset', 'malloc', 'maps', 'slabs', 'items', 'sizes') as $type) {

-            $items[] = array(

-              'path' => 'admin/logs/memcache/' . $cluster . '/' . $type,

-              'type' => $type == 'default' ?  MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,

-              'callback' => 'memcache_admin_stats',

-              'callback arguments' => array($cluster, $type),

-              'title' => $type,

-              'access' => user_access('access memcache statistics'),

-              'weight' => $sub_count,

-            );

-            $sub_count++;

-          }

-        }

-      }

-    }

-  }

-

-  return $items;

-}

-

-/**

- * Settings form

- */

-function memcache_admin_admin_settings() {

-  $form['show_memcache_statistics'] = array('#type' => 'checkbox',

-    '#title' => t('Show memcache statistics at the bottom of each page'),

-    '#default_value' => variable_get('show_memcache_statistics', 1),

-    '#description' => t("These statistics will be visible to users with the 'access memcache statistics' permission."),

-  );

-  return system_settings_form($form);

-}

-

-/**

- * Memcahe Stats page

- *

- * @param string $cluster - which cluster to view?

- * @param string $type - which type of stat, eg: default, reset, malloc, maps, cachedump, slabs, items or sizes

- * @return string

- */

-function memcache_admin_stats($cluster = 'default', $type = 'default') {

-  $bin = _memcache_admin_get_bin_for_cluster($cluster);

-

-  if ($bin) {

-    $stats = dmemcache_stats($bin, $type);

-

-    if (is_array($stats) && count($stats)) {

-      $output = "";

-

-      foreach ($stats as $server => $values) {

-        if (is_array($values)) {

-          //Do some custome value tweaks for specific stat page types.

-          switch ($type) {

-            case 'default' :

-              $values['uptime'] = format_interval($values['uptime']);

-              $values['time'] = format_date($values['time']);

-              $values['bytes'] = format_size($values['bytes']);

-              $values['bytes_read'] = format_size($values['bytes_read']);

-              $values['bytes_written'] = format_size($values['bytes_written']);

-              $values['limit_maxbytes'] = format_size($values['limit_maxbytes']);

-

-              //Custom Entries

-              $values['hit_percentage'] = ($values['cmd_get'] > 0) 

-                ? number_format(100.0 * $values['get_hits'] / $values['cmd_get'], 2) . '%' 

-                : '0';

-

-              $mem_used = intval($values['bytes']) / (intval($values['limit_maxbytes']) * 1024);

-              $values['mem_used'] = number_format(100.0 * $mem_used, 2) . '%';

-              break;

-          }

-

-          $output .= theme('memcache_admin_stats_table', $server, $values);

-        }

-        else {

-          drupal_set_message(t('Unable to connect to server: %server', array('%server' => $server)));

-        }

-      }

-    }

-

-    else {

-      $output = '';

-      drupal_set_message(t('No available statistics for this bin.'));

-    }

-  }

-

-  return $output;

-}

-

-

-/**

- * Theme function for rendering the output from memcache_admin_stats

- *

- * @param string $server - Server name:port for caption for the table

- * @param array $stats - array of key/value string pairs for the table results

- * @return string

- */

-function theme_memcache_admin_stats_table($server, $stats) {

-  $rows = array();

-

-  foreach ($stats as $key => $value) {

-    if (is_array($value)) {

-      $rs = array();

-      foreach ($value as $k => $v) {

-        $rs[] = array($k, $v);

-      }

-      $rows[] = array($key, theme('table', array(), $rs));

-    }

-    else {

-      $rows[] = array($key, $value);

-    }

-  }

-

-  return theme('table', array(t('Property'), t('Value')), $rows, array(), $server);

-}

-

-

-/**

- * Retrieve the cluster for any given bin

- *

- * @param string $cluster - Cluster ID

- * @return string

- */

-function _memcache_admin_get_bin_for_cluster($cluster) {

-  static $cluster_map = array();

-

-  if (!isset($cluster_map[$cluster])) {

-    $memache_bins = variable_get('memcache_bins', array());

-    if ($mapping = array_search($cluster, $memache_bins)) {

-      $cluster_map[$cluster] = $mapping;

-    }

-    else {

-      $cluster_map[$cluster] = 'default';

-    }

-  }

-

-  return $cluster_map[$cluster];

-}

-

-/**

- * See memcache_admin_init() which registers this function as a shutdown function.

- * Displays memcache stats in the footer.

- */

-function memcache_admin_shutdown() {

-  global $_memcache_statistics;

-

-  // Try not to break non html pages.

-  if (function_exists('drupal_get_headers')) {

-    $headers = drupal_get_headers();

-    if(strstr($headers, 'xml') || strstr($headers, 'javascript') || strstr($headers, 'plain')) {

-      return;

-    }

-  }

-

-  if (variable_get('show_memcache_statistics', TRUE) && function_exists('user_access') && user_access('access memcache statistics')) {

-    $stats = array();

-

-    foreach ($_memcache_statistics as $stat => $val) {

-      $stats[] = "<strong>$stat:</strong> ". theme('item_list', $val);

-    }

-    if (!empty($stats)) {

-      $output = theme('item_list', $stats);

-

-      // this makes sure all of the HTML is within the <body> even though this <script> is outside it

-      print '<div id="memcache-devel"><h2>'. t('Memcache statistics'). '</h2>'. $output. '</div>';

-    }

-  }

-}

+<?php
+// $Id: memcache_admin.module,v 1.3.2.10 2008/08/01 10:24:33 robertDouglass Exp $
+
+/**
+ * For the collection of memcache stats. This small .js file makes sure that the
+ * HTML displaying the stats is inside of the <body> part of the HTML
+ * document.
+ */
+function memcache_admin_init() {
+  global $user;
+  if (($user->uid == 0) || strstr($_SERVER['PHP_SELF'], 'update.php') || strstr($_GET['q'], 'autocomplete')) {
+    // update.php relies on standard error handler
+  }
+  else {
+    if ($user->uid) {
+      drupal_add_js(drupal_get_path('module', 'memcache_admin'). '/memcache.js');
+    }
+    register_shutdown_function('memcache_admin_shutdown');
+  }
+}
+
+/**
+ * Implementation of hook_perm().
+ */
+function memcache_admin_perm() {
+  return array('access memcache statistics');
+}
+
+/**
+ * Implementation of hook_menu().
+ */
+function memcache_admin_menu() {
+  $items['admin/settings/memcache'] = array(
+    'title' => 'Memcache',
+    'description' => 'Show or hide memcache statistics at the bottom of each page.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('memcache_admin_admin_settings'),
+    'access arguments' => array('administer site configuration'),
+  );
+  $items['admin/reports/memcache'] = array(
+    'title' => 'Memcache status',
+    'description' => "View the statistics for this site's memcache.",
+    'page callback' => 'memcache_admin_stats',
+    'access arguments' => array('access memcache statistics'),
+    'weight' => 1,
+  );
+  $memache_servers = variable_get('memcache_servers', array());
+  $clusters = array();
+  foreach($memache_servers as $server => $cluster) {
+    $clusters[$cluster]['servers'][] = $server;
+    $clusters[$cluster]['bin'] = _memcache_admin_get_bin_for_cluster($cluster);
+  }
+
+  $count = 0;
+  foreach ($clusters as $cluster => $cluster_info) {
+    if ($cluster_info['bin']) {
+      if (empty($current_cluster)) {
+        $current_cluster = arg(3);
+        if (empty($current_cluster)) {
+          $current_cluster = $cluster;
+        }
+      }
+
+      $items['admin/reports/memcache/'. $cluster] = array(
+        'title' => $cluster,
+        'type' =>  $count == 0 ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
+        'page callback' => 'memcache_admin_stats',
+        'page arguments' => array($cluster),
+        'access arguments' => array('access memcache statistics'),
+        'weight' => $count,
+      );
+      $count++;
+
+      $sub_count = 0;
+      foreach (array('default', 'reset', 'malloc', 'maps', 'slabs', 'items', 'sizes') as $type) {
+        $items['admin/reports/memcache/'. $cluster .'/'. $type] = array(
+          'type' => $type == 'default' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
+          'page callback' => 'memcache_admin_stats',
+          'page arguments' => array($cluster, $type),
+          'title' => $type,
+          'access arguments' => array('access memcache statistics'),
+          'weight' => $sub_count,
+        );
+        $sub_count++;
+      }
+
+    }
+  }
+  
+
+  return $items;
+}
+
+/**
+ * Settings form.
+ */
+function memcache_admin_admin_settings() {
+  $form['show_memcache_statistics'] = array('#type' => 'checkbox',
+    '#title' => t('Show memcache statistics at the bottom of each page'),
+    '#default_value' => variable_get('show_memcache_statistics', 1),
+    '#description' => t("These statistics will be visible to users with the 'access memcache statistics' permission."),
+  );
+  return system_settings_form($form);
+}
+
+/**
+ * Memcahe Stats page
+ *
+ * @param string $cluster - which cluster to view?
+ * @param string $type - which type of stat, eg: default, reset, malloc, maps, cachedump, slabs, items or sizes
+ * @return string
+ */
+function memcache_admin_stats($cluster = 'default', $type = 'default') {
+  $bin = _memcache_admin_get_bin_for_cluster($cluster);
+
+  if ($bin) {
+    $stats = dmemcache_stats($bin, $type);
+
+    if (is_array($stats) && count($stats)) {
+      $output = '';
+
+      foreach ($stats as $server => $values) {
+        if (is_array($values)) {
+          // Do some custome value tweaks for specific stat page types.
+          switch ($type) {
+            case 'default' :
+              $values['uptime'] = format_interval($values['uptime']);
+              $values['time'] = format_date($values['time']);
+              $values['bytes'] = format_size($values['bytes']);
+              $values['bytes_read'] = format_size($values['bytes_read']);
+              $values['bytes_written'] = format_size($values['bytes_written']);
+              $values['limit_maxbytes'] = format_size($values['limit_maxbytes']);
+
+              //Custom Entries
+              $values['hit_percentage'] = ($values['cmd_get'] > 0) 
+                ? number_format(100.0 * $values['get_hits'] / $values['cmd_get'], 2) . '%' 
+                : '0';
+
+              $mem_used = intval($values['bytes']) / (intval($values['limit_maxbytes']) * 1024);
+              $values['mem_used'] = number_format(100.0 * $mem_used, 2) . '%';
+              break;
+          }
+
+          $output .= theme('memcache_admin_stats_table', $server, $values);
+        }
+        else {
+          drupal_set_message(t('Unable to get statistic from server %server', array('%server' => $server)));
+        }
+      }
+    }
+
+    else {
+      $output = '';
+      drupal_set_message(t('No available statistics for this bin.'));
+    }
+  }
+
+  return $output;
+}
+
+/**
+ * Implementation of hook_theme().
+ */
+function memcache_admin_theme() {
+  return array(
+    'memcache_admin_stats_table' => array(
+      'arguments' => array('server' => NULL, 'stats' => NULL),
+    )
+  );
+}
+
+/**
+ * Theme function for rendering the output from memcache_admin_stats
+ *
+ * @param string $server - Server name:port for caption for the table
+ * @param array $stats - array of key/value string pairs for the table results
+ * @return string
+ */
+function theme_memcache_admin_stats_table($server, $stats) {
+  $rows = array();
+
+  foreach ($stats as $key => $value) {
+    if (is_array($value)) {
+      $rs = array();
+      foreach ($value as $k => $v) {
+        $rs[] = array($k, $v);
+      }
+      $rows[] = array($key, theme('table', array(), $rs));
+    }
+    else {
+      $rows[] = array($key, $value);
+    }
+  }
+
+  return theme('table', array(t('Property'), t('Value')), $rows, array(), $server);
+}
+
+
+/**
+ * Retrieve the cluster for any given bin
+ *
+ * @param string $cluster - Cluster ID
+ * @return string
+ */
+function _memcache_admin_get_bin_for_cluster($cluster) {
+  static $cluster_map = array();
+
+  if (!isset($cluster_map[$cluster])) {
+    $memache_bins = variable_get('memcache_bins', array());
+    if ($mapping = array_search($cluster, $memache_bins)) {
+      $cluster_map[$cluster] = $mapping;
+    }
+    else {
+      $cluster_map[$cluster] = 'default';
+    }
+  }
+
+  return $cluster_map[$cluster];
+}
+
+/**
+ * See memcache_admin_init() which registers this function as a shutdown function.
+ * Displays memcache stats in the footer.
+ */
+function memcache_admin_shutdown() {
+  global $_memcache_statistics;
+
+  // Try not to break non-HTML pages.
+  if (function_exists('drupal_get_headers')) {
+    $headers = drupal_get_headers();
+    if(strstr($headers, 'xml') || strstr($headers, 'javascript') || strstr($headers, 'plain')) {
+      return;
+    }
+  }
+
+  if (variable_get('show_memcache_statistics', TRUE) && function_exists('user_access') && user_access('access memcache statistics')) {
+    $stats = array();
+
+    foreach ($_memcache_statistics as $stat => $value) {
+      $stats[] = "<strong>$stat:</strong> ". theme('item_list', $value);
+    }
+    if (!empty($stats)) {
+      $output = theme('item_list', $stats);
+
+      // this makes sure all of the HTML is within the <body> even though this <script> is outside it
+      print '<div id="memcache-devel"><h2>'. t('Memcache statistics'). '</h2>'. $output. '</div>';
+    }
+  }
+}
