3 namespace Drupal\memcache\Driver;
5 use Drupal\Component\Utility\Timer;
6 use Drupal\memcache\MemcacheSettings;
7 use Drupal\memcache\DrupalMemcacheInterface;
12 abstract class DriverBase implements DrupalMemcacheInterface {
15 * The memcache config object.
17 * @var \Drupal\memcache\MemcacheSettings
22 * The memcache object.
24 * @var \Memcache|\Memcached
25 * E.g. \Memcache|\Memcached
30 * The hash algorithm to pass to hash(). Defaults to 'sha1'.
34 protected $hashAlgorithm;
37 * The prefix memcache key for all keys.
44 * Stats for the entire request.
48 protected static $stats = [
54 * Constructs a DriverBase object.
56 * @param \Drupal\memcache\MemcacheSettings $settings
57 * The memcache config object.
58 * @param \Memcached|\Memcache $memcache
59 * An existing memcache connection object.
61 * The class instance specific cache bin to use.
63 public function __construct(MemcacheSettings $settings, $memcache, $bin = NULL) {
64 $this->settings = $settings;
65 $this->memcache = $memcache;
67 $this->hashAlgorithm = $this->settings->get('key_hash_algorithm', 'sha1');
69 $prefix = $this->settings->get('key_prefix', '');
71 $this->prefix = $prefix . ':';
75 $this->prefix .= $bin . ':';
82 public function get($key) {
83 $collect_stats = $this->statsInit();
85 $full_key = $this->key($key);
86 $result = $this->memcache->get($full_key);
89 $this->statsWrite('get', 'cache', [$full_key => (bool) $result]);
98 public function key($key) {
99 $full_key = urlencode($this->prefix . '-' . $key);
101 // Memcache only supports key lengths up to 250 bytes. If we have generated
102 // a longer key, we shrink it to an acceptable length with a configurable
103 // hashing algorithm. Sha1 was selected as the default as it performs
104 // quickly with minimal collisions.
105 if (strlen($full_key) > 250) {
106 $full_key = urlencode($this->prefix . '-' . hash($this->hashAlgorithm, $key));
107 $full_key .= '-' . substr(urlencode($key), 0, (250 - 1) - strlen($full_key) - 1);
116 public function delete($key) {
117 $collect_stats = $this->statsInit();
119 $full_key = $this->key($key);
120 $result = $this->memcache->delete($full_key, 0);
122 if ($collect_stats) {
123 $this->statsWrite('delete', 'cache', [$full_key => $result]);
132 public function flush() {
133 $collect_stats = $this->statsInit();
135 $result = $this->memcache->flush();
137 if ($collect_stats) {
138 $this->statsWrite('flush', 'cache', ['' => $result]);
143 * Retrieves statistics recorded during memcache operations.
145 * @param string $stats_bin
146 * The bin to retrieve statistics for.
147 * @param string $stats_type
148 * The type of statistics to retrieve when using the Memcache extension.
149 * @param bool $aggregate
150 * Whether to aggregate statistics.
152 public function stats($stats_bin = 'cache', $stats_type = 'default', $aggregate = FALSE) {
154 // The stats_type can be over-loaded with an integer slab id, if doing a
155 // cachedump. We know we're doing a cachedump if $slab is non-zero.
156 $slab = (int) $stats_type;
159 foreach ($this->getBins() as $bin => $target) {
160 if ($stats_bin == $bin) {
161 if (isset($this->memcache)) {
162 if ($this->memcache instanceof \Memcached) {
163 $stats[$bin] = $this->memcache->getStats();
166 // The PHP Memcache extension 3.x version throws an error if the stats
167 // type is NULL or not in {reset, malloc, slabs, cachedump, items,
168 // sizes}. If $stats_type is 'default', then no parameter should be
169 // passed to the Memcache memcache_get_extended_stats() function.
170 elseif ($this->memcache instanceof \Memcache) {
171 if ($stats_type == 'default' || $stats_type == '') {
172 $stats[$bin] = $this->memcache->getExtendedStats();
175 // If $slab isn't zero, then we are dumping the contents of a
176 // specific cache slab.
177 elseif (!empty($slab)) {
178 $stats[$bin] = $this->memcache->getStats('cachedump', $slab);
181 $stats[$bin] = $this->memcache->getExtendedStats($stats_type);
188 // Optionally calculate a sum-total for all servers in the current bin.
191 // Some variables don't logically aggregate.
198 'listen_disabled_num',
201 foreach ($stats as $bin => $servers) {
202 if (is_array($servers)) {
203 foreach ($servers as $server) {
204 if (is_array($server)) {
205 foreach ($server as $key => $value) {
206 if (!in_array($key, $no_aggregate)) {
207 if (isset($stats[$bin]['total'][$key])) {
208 $stats[$bin]['total'][$key] += $value;
211 $stats[$bin]['total'][$key] = $value;
225 * Helper function to get the bins.
227 public function getBins() {
228 $memcache_bins = \Drupal::configFactory()->getEditable('memcache.settings')->get('memcache_bins');
229 if (!isset($memcache_bins)) {
230 $memcache_bins = ['cache' => 'default'];
233 return $memcache_bins;
237 * Helper function to get the servers.
239 public function getServers() {
240 $memcache_servers = \Drupal::configFactory()->getEditable('memcache.settings')->get('memcache_servers');
241 if (!isset($memcache_servers)) {
242 $memcache_servers = ['127.0.0.1:11211' => 'default'];
245 return $memcache_servers;
249 * Helper function to get memcache.
251 public function getMemcache() {
252 return $this->memcache;
256 * Helper function to get request stats.
258 public function requestStats() {
263 * Returns an array of available statistics types.
265 public function statsTypes() {
266 if ($this->memcache instanceof \Memcache) {
267 // TODO: Determine which versions of the PECL memcache extension have
268 // these other stats types: 'malloc', 'maps', optionally detect this
269 // version and expose them. These stats are "subject to change without
270 // warning" unfortunately.
271 return ['default', 'slabs', 'items', 'sizes'];
274 // The Memcached PECL extension only offers the default statistics.
280 * Helper function to initialize the stats for a memcache operation.
282 protected function statsInit() {
283 static $drupal_static_fast;
285 if (!isset($drupal_static_fast)) {
286 $drupal_static_fast = &drupal_static(__FUNCTION__, ['variable_checked' => NULL, 'user_access_checked' => NULL]);
288 $variable_checked = &$drupal_static_fast['variable_checked'];
289 $user_access_checked = &$drupal_static_fast['user_access_checked'];
291 // Confirm DRUPAL_BOOTSTRAP_VARIABLES has been reached. We don't use
292 // drupal_get_bootstrap_phase() as it's buggy. We can use variable_get()
293 // here because _drupal_bootstrap_variables() includes module.inc
294 // immediately after it calls variable_initialize().
295 // @codingStandardsIgnoreStart
296 // if (!isset($variable_checked) && function_exists('module_list')) {
297 // $variable_checked = variable_get('show_memcache_statistics', FALSE);
299 // If statistics are enabled we need to check user access.
300 // if (!empty($variable_checked) && !isset($user_access_checked) && !empty($GLOBALS['user']) && function_exists('user_access')) {
301 // // Statistics are enabled and the $user object has been populated, so check
302 // // that the user has access to view them.
303 // $user_access_checked = user_access('access memcache statistics');
305 // @codingStandardsIgnoreEnd
306 // Return whether or not statistics are enabled and the user can access
308 if ((!isset($variable_checked) || $variable_checked) && (!isset($user_access_checked) || $user_access_checked)) {
309 Timer::start('dmemcache');
318 * Memcache statistics to be displayed at end of page generation.
320 * @param string $action
321 * The action being performed (get, set, etc...).
323 * The memcache bin the action is being performed in.
325 * Keyed array in the form (string)$cid => (bool)$success. The keys the
326 * action is being performed on, and whether or not it was a success.
328 protected function statsWrite($action, $bin, array $keys) {
330 // Determine how much time elapsed to execute this action.
331 $time = Timer::read('dmemcache');
333 // Build the 'all' and 'ops' arrays displayed by memcache_admin.module.
334 foreach ($keys as $key => $success) {
335 self::$stats['all'][] = [
336 number_format($time, 2),
340 $success ? 'hit' : 'miss',
342 if (!isset(self::$stats['ops'][$action])) {
343 self::$stats['ops'][$action] = [$action, 0, 0, 0];
345 self::$stats['ops'][$action][1] += $time;
347 self::$stats['ops'][$action][2]++;
350 self::$stats['ops'][$action][3]++;