Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / memcache / memcache_admin / src / Controller / MemcacheStatisticsController.php
1 <?php
2
3 namespace Drupal\memcache_admin\Controller;
4
5 use Drupal\Core\Controller\ControllerBase;
6 use Drupal\Core\Datetime\DateFormatter;
7 use Drupal\Core\Link;
8 use Drupal\Core\Url;
9 use Drupal\Component\Render\HtmlEscapedText;
10
11 /**
12  * Memcache Statistics.
13  */
14 class MemcacheStatisticsController extends ControllerBase {
15
16   /**
17    * Callback for the Memcache Stats page.
18    *
19    * @param string $bin
20    *   The bin name.
21    *
22    * @return string
23    *   The page output.
24    */
25   public function statsTable($bin = 'default') {
26     $output = [];
27     $servers = [];
28
29     // Get the statistics.
30     $bin      = $this->binMapping($bin);
31     /** @var $memcache \Drupal\memcache\DrupalMemcacheInterface */
32     $memcache = \Drupal::service('memcache.factory')->get($bin, TRUE);
33     $stats    = $memcache->stats($bin, 'default', TRUE);
34
35     if (empty($stats[$bin])) {
36
37       // Break this out to make drupal_set_message easier to read.
38       $additional_message = $this->t(
39         '@enable the memcache module',
40         [
41           '@enable' => Link::fromTextAndUrl(t('enable'), Url::fromUri('base:/admin/modules', ['fragment' => 'edit-modules-performance-and-scalability'])),
42         ]
43       );
44       if (\Drupal::moduleHandler()->moduleExists('memcache')) {
45         $additional_message = $this->t(
46           'visit the Drupal admin @status page',
47           [
48             '@status' => Link::fromTextAndUrl(t('status report'), Url::fromUri('base:/admin/reports/status')),
49           ]
50         );
51       }
52
53       // Failed to load statistics. Provide a useful error about where to get
54       // more information and help.
55       drupal_set_message(
56         t(
57           'There may be a problem with your Memcache configuration. Please review @readme and :more for more information.',
58           [
59             '@readme' => 'README.txt',
60             ':more'   => $additional_message,
61           ]
62         ),
63         'error'
64       );
65     }
66     else {
67       if (count($stats[$bin])) {
68         $stats     = $stats[$bin];
69         $aggregate = array_pop($stats);
70
71         if ($memcache->getMemcache() instanceof \Memcached) {
72           $version = t('Memcached v@version', ['@version' => phpversion('Memcached')]);
73         }
74         elseif ($memcache->getMemcache() instanceof \Memcache) {
75           $version = t('Memcache v@version', ['@version' => phpversion('Memcache')]);
76         }
77         else {
78           $version = t('Unknown');
79           drupal_set_message(t('Failed to detect the memcache PECL extension.'), 'error');
80         }
81
82         foreach ($stats as $server => $statistics) {
83           if (empty($statistics['uptime'])) {
84             drupal_set_message(t('Failed to connect to server at :address.', [':address' => $server]), 'error');
85           }
86           else {
87             $servers[] = $server;
88
89             $data['server_overview'][$server]    = t('v@version running @uptime', ['@version' => $statistics['version'], '@uptime' => \Drupal::service('date.formatter')->formatInterval($statistics['uptime'])]);
90             $data['server_pecl'][$server]        = t('n/a');
91             $data['server_time'][$server]        = \Drupal::service('date.formatter')->format($statistics['time']);
92             $data['server_connections'][$server] = $this->statsConnections($statistics);
93             $data['cache_sets'][$server]         = $this->statsSets($statistics);
94             $data['cache_gets'][$server]         = $this->statsGets($statistics);
95             $data['cache_counters'][$server]     = $this->statsCounters($statistics);
96             $data['cache_transfer'][$server]     = $this->statsTransfer($statistics);
97             $data['cache_average'][$server]      = $this->statsAverage($statistics);
98             $data['memory_available'][$server]   = $this->statsMemory($statistics);
99             $data['memory_evictions'][$server]   = number_format($statistics['evictions']);
100           }
101         }
102       }
103
104       // Build a custom report array.
105       $report = [
106         'uptime' => [
107           'uptime' => [
108             'label'   => t('Uptime'),
109             'servers' => $data['server_overview'],
110           ],
111           'extension' => [
112             'label'   => t('PECL extension'),
113             'servers' => [$servers[0] => $version],
114           ],
115           'time' => [
116             'label'   => t('Time'),
117             'servers' => $data['server_time'],
118           ],
119           'connections' => [
120             'label'   => t('Connections'),
121             'servers' => $data['server_connections'],
122           ],
123         ],
124         'stats' => [],
125         'memory' => [
126           'memory' => [
127             'label'   => t('Available memory'),
128             'servers' => $data['memory_available'],
129           ],
130           'evictions' => [
131             'label'   => t('Evictions'),
132             'servers' => $data['memory_evictions'],
133           ],
134         ],
135       ];
136
137       // Don't display aggregate totals if there's only one server.
138       if (count($servers) > 1) {
139         $report['uptime']['uptime']['total']      = t('n/a');
140         $report['uptime']['extension']['servers'] = $data['server_pecl'];
141         $report['uptime']['extension']['total']   = $version;
142         $report['uptime']['time']['total']        = t('n/a');
143         $report['uptime']['connections']['total'] = $this->statsConnections($aggregate);
144         $report['memory']['memory']['total']      = $this->statsMemory($aggregate);
145         $report['memory']['evictions']['total']   = number_format($aggregate['evictions']);
146       }
147
148       // Report on stats.
149       $stats = [
150         'sets'     => t('Sets'),
151         'gets'     => t('Gets'),
152         'counters' => t('Counters'),
153         'transfer' => t('Transferred'),
154         'average'  => t('Per-connection average'),
155       ];
156
157       foreach ($stats as $type => $label) {
158         $report['stats'][$type] = [
159           'label'   => $label,
160           'servers' => $data["cache_{$type}"],
161         ];
162
163         if (count($servers) > 1) {
164           $func = 'stats' . ucfirst($type);
165           $report['stats'][$type]['total'] = $this->{$func}($aggregate);
166         }
167       }
168
169       $output = $this->statsTablesOutput($bin, $servers, $report);
170     }
171
172     return $output;
173   }
174
175   /**
176    * Callback for the Memcache Stats page.
177    *
178    * @param string $cluster
179    *   The Memcache cluster name.
180    * @param string $server
181    *   The Memcache server name.
182    * @param string $type
183    *   The type of statistics to retrieve when using the Memcache extension.
184    *
185    * @return string
186    *   The page output.
187    */
188   public function statsTableRaw($cluster, $server, $type = 'default') {
189     $cluster = $this->binMapping($cluster);
190     $server = str_replace('!', '/', $server);
191
192     $slab = \Drupal::routeMatch()->getParameter('slab');
193     $memcache = \Drupal::service('memcache.factory')->get($cluster, TRUE);
194     if ($type == 'slabs' && !empty($slab)) {
195       $stats = $memcache->stats($cluster, $slab, FALSE);
196     }
197     else {
198       $stats = $memcache->stats($cluster, $type, FALSE);
199     }
200
201     // @codingStandardsIgnoreStart
202     // @todo - breadcrumb
203     // $breadcrumbs = [
204     //   l(t('Home'), NULL),
205     //   l(t('Administer'), 'admin'),
206     //   l(t('Reports'), 'admin/reports'),
207     //   l(t('Memcache'), 'admin/reports/memcache'),
208     //   l(t($bin), "admin/reports/memcache/$bin"),
209     // ];
210     // if ($type == 'slabs' && arg(6) == 'cachedump' && user_access('access slab cachedump')) {
211     //   $breadcrumbs[] = l($server, "admin/reports/memcache/$bin/$server");
212     //   $breadcrumbs[] = l(t('slabs'), "admin/reports/memcache/$bin/$server/$type");
213     // }
214     // drupal_set_breadcrumb($breadcrumbs);
215     // @codingStandardsIgnoreEnd
216     if (isset($stats[$cluster][$server]) && is_array($stats[$cluster][$server]) && count($stats[$cluster][$server])) {
217       $output = $this->statsTablesRawOutput($cluster, $server, $stats[$cluster][$server], $type);
218     }
219     elseif ($type == 'slabs' && is_array($stats[$cluster]) && count($stats[$cluster])) {
220       $output = $this->statsTablesRawOutput($cluster, $server, $stats[$cluster], $type);
221     }
222     else {
223       $output = $this->statsTablesRawOutput($cluster, $server, [], $type);
224       drupal_set_message(t('No @type statistics for this bin.', ['@type' => $type]));
225     }
226
227     return $output;
228   }
229
230   /**
231    * Helper function, reverse map the memcache_bins variable.
232    */
233   private function binMapping($bin = 'cache') {
234     $memcache      = \Drupal::service('memcache.factory')->get(NULL, TRUE);
235     $memcache_bins = $memcache->getBins();
236
237     $bins = array_flip($memcache_bins);
238     if (isset($bins[$bin])) {
239       return $bins[$bin];
240     }
241     else {
242       return $this->defaultBin($bin);
243     }
244   }
245
246   /**
247    * Helper function. Returns the bin name.
248    */
249   private function defaultBin($bin) {
250     if ($bin == 'default') {
251       return 'cache';
252     }
253
254     return $bin;
255   }
256
257   /**
258    * Statistics report: format total and open connections.
259    */
260   private function statsConnections($stats) {
261     return $this->t(
262       '@current open of @total total',
263       [
264         '@current' => number_format($stats['curr_connections']),
265         '@total'   => number_format($stats['total_connections']),
266       ]
267     );
268   }
269
270   /**
271    * Statistics report: calculate # of set cmds and total cmds.
272    */
273   private function statsSets($stats) {
274     if (($stats['cmd_set'] + $stats['cmd_get']) == 0) {
275       $sets = 0;
276     }
277     else {
278       $sets = $stats['cmd_set'] / ($stats['cmd_set'] + $stats['cmd_get']) * 100;
279     }
280     if (empty($stats['uptime'])) {
281       $average = 0;
282     }
283     else {
284       $average = $sets / $stats['uptime'];
285     }
286     return $this->t(
287       '@average/s; @set sets (@sets%) of @total commands',
288       [
289         '@average' => number_format($average, 2),
290         '@sets'    => number_format($sets, 2),
291         '@set'     => number_format($stats['cmd_set']),
292         '@total'   => number_format($stats['cmd_set'] + $stats['cmd_get']),
293       ]
294     );
295   }
296
297   /**
298    * Statistics report: calculate # of get cmds, broken down by hits and misses.
299    */
300   private function statsGets($stats) {
301     if (($stats['cmd_set'] + $stats['cmd_get']) == 0) {
302       $gets = 0;
303     }
304     else {
305       $gets = $stats['cmd_get'] / ($stats['cmd_set'] + $stats['cmd_get']) * 100;
306     }
307     if (empty($stats['uptime'])) {
308       $average = 0;
309     }
310     else {
311       $average = $stats['cmd_get'] / $stats['uptime'];
312     }
313     return $this->t(
314       '@average/s; @total gets (@gets%); @hit hits (@percent_hit%) @miss misses (@percent_miss%)',
315       [
316         '@average'      => number_format($average, 2),
317         '@gets'         => number_format($gets, 2),
318         '@hit'          => number_format($stats['get_hits']),
319         '@percent_hit'  => ($stats['cmd_get'] > 0 ? number_format($stats['get_hits'] / $stats['cmd_get'] * 100, 2) : '0.00'),
320         '@miss'         => number_format($stats['get_misses']),
321         '@percent_miss' => ($stats['cmd_get'] > 0 ? number_format($stats['get_misses'] / $stats['cmd_get'] * 100, 2) : '0.00'),
322         '@total'        => number_format($stats['cmd_get']),
323       ]
324     );
325   }
326
327   /**
328    * Statistics report: calculate # of increments and decrements.
329    */
330   private function statsCounters($stats) {
331     if (!is_array($stats)) {
332       $stats = [];
333     }
334
335     $stats += [
336       'incr_hits'   => 0,
337       'incr_misses' => 0,
338       'decr_hits'   => 0,
339       'decr_misses' => 0,
340     ];
341
342     return $this->t(
343       '@incr increments, @decr decrements',
344       [
345         '@incr' => number_format($stats['incr_hits'] + $stats['incr_misses']),
346         '@decr' => number_format($stats['decr_hits'] + $stats['decr_misses']),
347       ]
348     );
349   }
350
351   /**
352    * Statistics report: calculate bytes transferred.
353    */
354   private function statsTransfer($stats) {
355     if ($stats['bytes_written'] == 0) {
356       $written = 0;
357     }
358     else {
359       $written = $stats['bytes_read'] / $stats['bytes_written'] * 100;
360     }
361     return $this->t(
362       '@to:@from (@written% to cache)',
363       [
364         '@to'      => format_size((int) $stats['bytes_read']),
365         '@from'    => format_size((int) $stats['bytes_written']),
366         '@written' => number_format($written, 2),
367       ]
368     );
369   }
370
371   /**
372    * Statistics report: calculate per-connection averages.
373    */
374   private function statsAverage($stats) {
375     if ($stats['total_connections'] == 0) {
376       $get   = 0;
377       $set   = 0;
378       $read  = 0;
379       $write = 0;
380     }
381     else {
382       $get   = $stats['cmd_get'] / $stats['total_connections'];
383       $set   = $stats['cmd_set'] / $stats['total_connections'];
384       $read  = $stats['bytes_written'] / $stats['total_connections'];
385       $write = $stats['bytes_read'] / $stats['total_connections'];
386     }
387     return $this->t(
388       '@read in @get gets; @write in @set sets',
389       [
390         '@get'   => number_format($get, 2),
391         '@set'   => number_format($set, 2),
392         '@read'  => format_size(number_format($read, 2)),
393         '@write' => format_size(number_format($write, 2)),
394       ]
395     );
396   }
397
398   /**
399    * Statistics report: calculate available memory.
400    */
401   private function statsMemory($stats) {
402     if ($stats['limit_maxbytes'] == 0) {
403       $percent = 0;
404     }
405     else {
406       $percent = 100 - $stats['bytes'] / $stats['limit_maxbytes'] * 100;
407     }
408     return $this->t(
409       '@available (@percent%) of @total',
410       [
411         '@available' => format_size($stats['limit_maxbytes'] - $stats['bytes']),
412         '@percent'   => number_format($percent, 2),
413         '@total'     => format_size($stats['limit_maxbytes']),
414       ]
415     );
416   }
417
418   /**
419    * Generates render array for output.
420    */
421   private function statsTablesOutput($bin, $servers, $stats) {
422     $memcache      = \Drupal::service('memcache.factory')->get(NULL, TRUE);
423     $memcache_bins = $memcache->getBins();
424
425     $links = [];
426     foreach ($servers as $server) {
427
428       // Convert socket file path so it works with an argument, this should
429       // have no impact on non-socket configurations. Convert / to !.
430       $links[] = Link::fromTextandUrl($server, Url::fromUri('base:/admin/reports/memcache/' . $memcache_bins[$bin] . '/' . str_replace('/', '!', $server)))->toString();
431     }
432
433     if (count($servers) > 1) {
434       $headers = array_merge(['', t('Totals')], $links);
435     }
436     else {
437       $headers = array_merge([''], $links);
438     }
439
440     $output = [];
441     foreach ($stats as $table => $data) {
442       $rows = [];
443       foreach ($data as $data_row) {
444         $row = [];
445         $row[] = $data_row['label'];
446         if (isset($data_row['total'])) {
447           $row[] = $data_row['total'];
448         }
449         foreach ($data_row['servers'] as $server) {
450           $row[] = $server;
451         }
452         $rows[] = $row;
453       }
454       $output[$table] = [
455         '#theme'  => 'table',
456         '#header' => $headers,
457         '#rows'   => $rows,
458
459       ];
460     }
461
462     return $output;
463   }
464
465   /**
466    * Generates render array for output.
467    */
468   private function statsTablesRawOutput($cluster, $server, $stats, $type) {
469     $user          = \Drupal::currentUser();
470     $current_type  = isset($type) ? $type : 'default';
471     $memcache      = \Drupal::service('memcache.factory')->get(NULL, TRUE);
472     $memcache_bins = $memcache->getBins();
473     $bin           = isset($memcache_bins[$cluster]) ? $memcache_bins[$cluster] : 'default';
474     $slab = \Drupal::routeMatch()->getParameter('slab');
475
476     // Provide navigation for the various memcache stats types.
477     $links = [];
478     if (count($memcache->statsTypes())) {
479       foreach ($memcache->statsTypes() as $type) {
480         // @todo render array
481         $link = Link::fromTextandUrl($type, Url::fromUri('base:/admin/reports/memcache/' . $bin . '/' . str_replace('/', '!', $server) . '/' . ($type == 'default' ? '' : $type)))->toString();
482         if ($current_type == $type) {
483           $links[] = '<strong>' . $link . '</strong>';
484         }
485         else {
486           $links[] = $link;
487         }
488       }
489     }
490     $build = [
491       'links' => [
492         '#markup' => !empty($links) ? implode($links, ' | ') : '',
493       ],
494     ];
495
496     $build['table'] = [
497       '#type'  => 'table',
498       '#header' => [
499         $this->t('Property'),
500         $this->t('Value'),
501       ],
502     ];
503
504     $row = 0;
505
506     // Items are returned as an array within an array within an array.  We step
507     // in one level to properly display the contained statistics.
508     if ($current_type == 'items' && isset($stats['items'])) {
509       $stats = $stats['items'];
510     }
511
512     foreach ($stats as $key => $value) {
513
514       // Add navigation for getting a cachedump of individual slabs.
515       if (($current_type == 'slabs' || $current_type == 'items') && is_int($key) && $user->hasPermission('access slab cachedump')) {
516         $build['table'][$row]['key'] = [
517           '#type' => 'link',
518           '#title' => $this->t('Slab @slab', ['@slab' => $key]),
519           '#url' => Url::fromUri('base:/admin/reports/memcache/' . $bin . '/' . str_replace('/', '!', $server) . '/slabs/cachedump/' . $key),
520         ];
521       }
522       else {
523         $build['table'][$row]['key'] = ['#plain_text' => $key];
524       }
525
526       if (is_array($value)) {
527         $subrow = 0;
528         $build['table'][$row]['value'] = ['#type' => 'table'];
529         foreach ($value as $k => $v) {
530
531           // Format timestamp when viewing cachedump of individual slabs.
532           if ($current_type == 'slabs' && $user->hasPermission('access slab cachedump') && !empty($slab) && $k == 0) {
533             $k = $this->t('Size');
534             $v = format_size($v);
535           }
536           elseif ($current_type == 'slabs' && $user->hasPermission('access slab cachedump') && !empty($slab) && $k == 1) {
537             $k          = $this->t('Expire');
538             $full_stats = $memcache->stats($cluster);
539             $infinite   = $full_stats[$cluster][$server]['time'] - $full_stats[$cluster][$server]['uptime'];
540             if ($v == $infinite) {
541               $v = $this->t('infinite');
542             }
543             else {
544               $v = $this->t('in @time', ['@time' => \Drupal::service('date.formatter')->formatInterval($v - \Drupal::time()->getRequestTime())]);
545             }
546           }
547           $build['table'][$row]['value'][$subrow] = [
548             'key' => ['#plain_text' => $k],
549             'value' => ['#plain_text' => $v],
550           ];
551           $subrow++;
552         }
553       }
554       else {
555         $build['table'][$row]['value'] = ['#plain_text' => $value];
556       }
557       $row++;
558     }
559
560     return $build;
561   }
562
563 }