index.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. <?php
  2. /**
  3. * OPcache GUI
  4. *
  5. * A simple but effective single-file GUI for the OPcache PHP extension.
  6. *
  7. * @author Andrew Collington, andy@amnuts.com
  8. * @version 2.0.0
  9. * @link https://github.com/amnuts/opcache-gui
  10. * @license MIT, http://acollington.mit-license.org/
  11. */
  12. if (!extension_loaded('Zend OPcache')) {
  13. die('The Zend OPcache extension does not appear to be installed');
  14. }
  15. class OpCacheService
  16. {
  17. protected $data;
  18. protected $options = [
  19. 'allow_invalidate' => true
  20. ];
  21. private function __construct($options = [])
  22. {
  23. $this->data = $this->compileState();
  24. $this->options = array_merge($this->options, $options);
  25. }
  26. public static function init($options = [])
  27. {
  28. $self = new self($options);
  29. if (!empty($_SERVER['HTTP_X_REQUESTED_WITH'])
  30. && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'
  31. ) {
  32. if ((isset($_GET['reset']))) {
  33. echo '{ "success": "' . ($self->resetCache() ? 'yes' : 'no') . '" }';
  34. } else if ((isset($_GET['invalidate']))) {
  35. echo '{ "success": "' . ($self->resetCache($_GET['invalidate']) ? 'yes' : 'no') . '" }';
  36. } else {
  37. echo json_encode($self->getData(@$_GET['section'] ?: null));
  38. }
  39. exit;
  40. } else if ((isset($_GET['reset']))) {
  41. $self->resetCache();
  42. } else if ((isset($_GET['invalidate']))) {
  43. $self->resetCache($_GET['invalidate']);
  44. }
  45. return $self;
  46. }
  47. public function getOption($name = null)
  48. {
  49. if ($name === null) {
  50. return $this->options;
  51. }
  52. return (isset($this->options[$name])
  53. ? $this->options[$name]
  54. : null
  55. );
  56. }
  57. public function getData($section = null, $property = null)
  58. {
  59. if ($section === null) {
  60. return $this->data;
  61. }
  62. $section = strtolower($section);
  63. if (isset($this->data[$section])) {
  64. if ($property === null || !isset($this->data[$section][$property])) {
  65. return $this->data[$section];
  66. }
  67. return $this->data[$section][$property];
  68. }
  69. return null;
  70. }
  71. public function canInvalidate()
  72. {
  73. return ($this->getOption('allow_invalidate') && function_exists('opcache_invalidate'));
  74. }
  75. public function resetCache($file = null)
  76. {
  77. $success = false;
  78. if ($file === null) {
  79. $success = opcache_reset();
  80. } else if (function_exists('opcache_invalidate')) {
  81. $success = opcache_invalidate(urldecode($file), true);
  82. }
  83. if ($success) {
  84. $this->compileState();
  85. }
  86. return $success;
  87. }
  88. protected function compileState()
  89. {
  90. $status = opcache_get_status();
  91. $config = opcache_get_configuration();
  92. $memsize = function($size, $precision = 3, $space = false)
  93. {
  94. $i = 0;
  95. $val = array(' bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
  96. while (($size / 1024) > 1) {
  97. $size /= 1024;
  98. ++$i;
  99. }
  100. return sprintf("%.{$precision}f%s%s", $size, (($space && $i) ? ' ' : ''), $val[$i]);
  101. };
  102. $files = [];
  103. if (!empty($status['scripts'])) {
  104. uasort($status['scripts'], function($a, $b) {
  105. return $a['hits'] < $b['hits'];
  106. });
  107. foreach ($status['scripts'] as &$file) {
  108. $file['full_path'] = str_replace('\\', '/', $file['full_path']);
  109. $file['readable'] = [
  110. 'hits' => number_format($file['hits']),
  111. 'memory_consumption' => $memsize($file['memory_consumption'])
  112. ];
  113. }
  114. $files = array_values($status['scripts']);
  115. }
  116. $overview = array_merge(
  117. $status['memory_usage'], $status['opcache_statistics'], [
  118. 'used_memory_percentage' => round(100 * (
  119. ($status['memory_usage']['used_memory'] + $status['memory_usage']['wasted_memory'])
  120. / $config['directives']['opcache.memory_consumption'])),
  121. 'hit_rate_percentage' => round($status['opcache_statistics']['opcache_hit_rate']),
  122. 'wasted_percentage' => round($status['memory_usage']['current_wasted_percentage'], 2),
  123. 'readable' => [
  124. 'total_memory' => $memsize($config['directives']['opcache.memory_consumption']),
  125. 'used_memory' => $memsize($status['memory_usage']['used_memory']),
  126. 'free_memory' => $memsize($status['memory_usage']['free_memory']),
  127. 'wasted_memory' => $memsize($status['memory_usage']['wasted_memory']),
  128. 'num_cached_scripts' => number_format($status['opcache_statistics']['num_cached_scripts']),
  129. 'hits' => number_format($status['opcache_statistics']['hits']),
  130. 'misses' => number_format($status['opcache_statistics']['misses']),
  131. 'blacklist_miss' => number_format($status['opcache_statistics']['blacklist_misses']),
  132. 'num_cached_keys' => number_format($status['opcache_statistics']['num_cached_keys']),
  133. 'max_cached_keys' => number_format($status['opcache_statistics']['max_cached_keys']),
  134. 'start_time' => date_format(date_create("@{$status['opcache_statistics']['start_time']}"), 'Y-m-d H:i:s'),
  135. 'last_restart_time' => ($status['opcache_statistics']['last_restart_time'] == 0
  136. ? 'never'
  137. : date_format(date_create("@{$status['opcache_statistics']['last_restart_time']}"), 'Y-m-d H:i:s')
  138. )
  139. ]
  140. ]
  141. );
  142. $directives = [];
  143. ksort($config['directives']);
  144. foreach ($config['directives'] as $k => $v) {
  145. $directives[] = ['k' => $k, 'v' => $v];
  146. }
  147. $version = array_merge(
  148. $config['version'],
  149. [
  150. 'php' => phpversion(),
  151. 'server' => $_SERVER['SERVER_SOFTWARE'],
  152. 'host' => (function_exists('gethostname')
  153. ? gethostname()
  154. : (php_uname('n')
  155. ?: (empty($_SERVER['SERVER_NAME'])
  156. ? $_SERVER['HOST_NAME']
  157. : $_SERVER['SERVER_NAME']
  158. )
  159. )
  160. )
  161. ]
  162. );
  163. return [
  164. 'version' => $version,
  165. 'overview' => $overview,
  166. 'files' => $files,
  167. 'directives' => $directives,
  168. 'blacklist' => $config['blacklist'],
  169. 'functions' => get_extension_funcs('Zend OPcache')
  170. ];
  171. }
  172. }
  173. $opcache = OpCacheService::init();
  174. ?>
  175. <!doctype html>
  176. <html>
  177. <head>
  178. <meta charset="UTF-8"/>
  179. <meta name="viewport" content="width=device-width,initial-scale=1.0">
  180. <title>OPcache statistics on <?php echo $opcache->getData('version', 'host'); ?></title>
  181. <script src="http://fb.me/react-0.12.2.min.js"></script>
  182. <script src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
  183. <style type="text/css">
  184. body { font-family:sans-serif; font-size:90%; padding: 0; margin: 0 }
  185. nav { padding-top: 20px; }
  186. nav > ul { list-style-type: none; padding-left: 8px; margin: 0; border-bottom: 1px solid #ccc; }
  187. nav > ul > li { display: inline-block; padding: 0; margin: 0 0 -1px 0; }
  188. nav > ul > li > a { display: block; margin: 0 10px; padding: 15px 30px; border: 1px solid transparent; border-bottom-color: #ccc; text-decoration: none; }
  189. nav > ul > li > a:hover { background-color: #f4f4f4; text-decoration: underline; }
  190. nav > ul > li > a.active:hover { background-color: initial; }
  191. nav > ul > li > a[data-for].active { border: 1px solid #ccc; border-bottom-color: #ffffff; border-top: 3px solid #6ca6ef; }
  192. table { margin: 0 0 1em 0; border-collapse: collapse; border-color: #fff; width: 100%; }
  193. table caption { text-align: left; font-size: 1.5em; }
  194. table tr { background-color: #99D0DF; border-color: #fff; }
  195. table th { text-align: left; padding: 6px; background-color: #6ca6ef; color: #fff; border-color: #fff; font-weight: normal; }
  196. table td { padding: 4px 6px; line-height: 1.4em; vertical-align: top; border-color: #fff; }
  197. table tr:nth-child(odd) { background-color: #EFFEFF; }
  198. table tr:nth-child(even) { background-color: #E0ECEF; }
  199. td.pathname { width: 70%; }
  200. footer { border-top: 1px solid #ccc; padding: 1em 2em; }
  201. footer a { padding: 2em; text-decoration: none; opacity: 0.7; }
  202. footer a:hover { opacity: 1; }
  203. #tabs { padding: 2em; }
  204. #tabs > div { display: none; }
  205. #tabs > div#overview { display:block; }
  206. #resetCache, #toggleRealtime, footer > a { background-position: 5px 50%; background-repeat: no-repeat; background-color: transparent; }
  207. footer > a { background-position: 0 50%; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAQCAYAAAAbBi9cAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjE2MENCRkExNzVBQjExRTQ5NDBGRTUzMzQyMDVDNzFFIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjE2MENCRkEyNzVBQjExRTQ5NDBGRTUzMzQyMDVDNzFFIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MTYwQ0JGOUY3NUFCMTFFNDk0MEZFNTMzNDIwNUM3MUUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MTYwQ0JGQTA3NUFCMTFFNDk0MEZFNTMzNDIwNUM3MUUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7HtUU1AAABN0lEQVR42qyUvWoCQRSF77hCLLKC+FOlCKTyIbYQUuhbWPkSFnZ2NpabUvANLGyz5CkkYGMlFtFAUmiSM8lZOVkWsgm58K079+fMnTusZl92BXbgDrTtZ2szd8fas/XBOzmBKaiCEFyTkL4pc9L8vgpNJJDyWtDna61EoXpO+xcFfXUVqtrf7Vx7m9Pub/EatvgHoYXD4ylztC14BBVwydvydgDPHPgNaErN3jLKIxAUmEvAXK21I18SJpXBGAxyBAaMlblOWOs1bMXFkMGeBFsi0pJNe/QNuV7563+gs8LfhrRfE6GaHLuRqfnUiKi6lJ034B44EXL0baTTJWujNGkG3kBX5uRyZuRkPl3WzDTBtzjnxxiDDq83yNxUk7GYuXM53jeLuMNavvAXkv4zrJkTaeGHAAMAIal3icPMsyQAAAAASUVORK5CYII='); font-size: 80%; }
  208. #resetCache { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NjBFMUMyMjI3NDlGMTFFNEE3QzNGNjQ0OEFDQzQ1MkMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NjBFMUMyMjM3NDlGMTFFNEE3QzNGNjQ0OEFDQzQ1MkMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo2MEUxQzIyMDc0OUYxMUU0QTdDM0Y2NDQ4QUNDNDUyQyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo2MEUxQzIyMTc0OUYxMUU0QTdDM0Y2NDQ4QUNDNDUyQyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PplZ+ZkAAAD1SURBVHjazFPtDYJADIUJZAMZ4UbACWQENjBO4Ao6AW5AnODcADZQJwAnwJ55NbWhB/6zycsdpX39uDZNpsURtjgzwkDoCBecs5ITPGGMwCNAkIrQw+8ri36GhBHsavFdpILEo4wEpZxRigy009EhG760gr0VhFoyZfvJKPwsheIWIeGejBZRIxRVhMRFevbuUXBew/iE/lhlBduV0j8Jx+TvJEWPphq8n5li9utgaw6cW/h6NSt/JcnVBhQxotIgKTBrbNvIHo2G0x1rwlKqTDusxiAz6hHNL1zayTVqVKRKpa/LPljPH1sJh6l/oNSrZfwSYABtq3tFdZA5BAAAAABJRU5ErkJggg=='); }
  209. #toggleRealtime { position: relative; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAUCAYAAACAl21KAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6ODE5RUU4NUE3NDlGMTFFNDkyMzA4QzY1RjRBQkIzQjUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ODE5RUU4NUI3NDlGMTFFNDkyMzA4QzY1RjRBQkIzQjUiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4MTlFRTg1ODc0OUYxMUU0OTIzMDhDNjVGNEFCQjNCNSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4MTlFRTg1OTc0OUYxMUU0OTIzMDhDNjVGNEFCQjNCNSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpXjpvMAAAD2SURBVHjarFQBEcMgDKR3E1AJldA5wMEqAQmTgINqmILdFCChdUAdMAeMcukuSwnQbbnLlZLwJPkQIcrSiT/IGNQHNb8CGQDyRw+2QWUBqC+luzo4OKQZIAVrB+ssyKp3Bkijf0+ijzIh4wQppoBauMSjyDZfMSCDxYZMsfHF120T36AqWZMkgyguQ3GOfottJ5TKnHC+wfeRsC2oDVayPgr3bbN2tHBH3tWuJCPa0JUgKtFzMQrcZH3FNHAc0yOp1cCASALyngoN6lhDopkJWxdifwY9A3u7l29ImpxOFSWIOVsGwHKENIWxss2eBVKdOeeXAAMAk/Z9h4QhXmUAAAAASUVORK5CYII='); }
  210. #counts { width: 270px; float: right; }
  211. #counts > div > div { background-color: #ededed; margin-bottom: 10px; }
  212. #counts > div > div > h3 { background-color: #cdcdcd; padding: 4px 6px; margin: 0; }
  213. #counts > div > div > p { margin: 0; text-align: center; }
  214. #counts > div > div > p > span.large + span { font-size: 20pt; margin: 0; }
  215. #counts > div > div > p > span.large { font-size: 80pt; margin: 0; padding: 0; text-align: center; }
  216. #info { margin-right: 280px; }
  217. #frmFilter { width: 520px; }
  218. #moreinfo { padding: 10px; }
  219. #moreinfo > p { text-align: left !important; line-height: 180%; }
  220. .metainfo { font-size: 80%; }
  221. .hide { display: none; }
  222. #toggleRealtime.pulse::before {
  223. content: ""; position: absolute;
  224. top: 13px; left: 3px; width: 18px; height: 18px;
  225. z-index: 10; opacity: 0; background-color: transparent;
  226. border: 2px solid rgb(255, 116, 0); border-radius: 100%;
  227. -webkit-animation: pulse 1s linear 2;
  228. -moz-animation: pulse 1s linear 2;
  229. animation: pulse 1s linear 2;
  230. }
  231. @media screen and (max-width: 750px) {
  232. #info { margin-right:auto; clear:both; }
  233. nav > ul { border-bottom: 0; }
  234. nav > ul > li { display: block; margin: 0; }
  235. nav > ul > li > a { display: block; margin: 0 10px; padding: 10px 0 10px 30px; border: 0; }
  236. nav > ul > li > a[data-for].active { border-bottom-color: #ccc; }
  237. #counts { position:relative; display:block; width:100%; }
  238. #toggleRealtime.pulse::before { top: 8px; }
  239. }
  240. @media screen and (max-width: 550px) {
  241. #frmFilter { width: 100%; }
  242. }
  243. @keyframes pulse {
  244. 0% {transform: scale(1); opacity: 0;}
  245. 50% {transform: scale(1.3); opacity: 0.7;}
  246. 100% {transform: scale(1.6); opacity: 1;}
  247. }
  248. @-webkit-keyframes pulse {
  249. 0% {-webkit-transform: scale(1); opacity: 0;}
  250. 50% {-webkit-transform: scale(1.3); opacity: 0.7;}
  251. 100% {-webkit-transform: scale(1.6); opacity: 0;}
  252. }
  253. @-moz-keyframes pulse {
  254. 0% {-moz-transform: scale(1); opacity: 0;}
  255. 50% {-moz-transform: scale(1.3); opacity: 0.7;}
  256. 100% {-moz-transform: scale(1.6); opacity: 0;}
  257. }
  258. </style>
  259. </head>
  260. <body>
  261. <header>
  262. <nav>
  263. <ul>
  264. <li><a data-for="overview" href="#overview" class="active">Overview</a></li>
  265. <li><a data-for="files" href="#files">File usage</a></li>
  266. <li><a href="?reset=1" id="resetCache" onclick="return confirm('Are you sure you want to reset the cache?');">Reset cache</a></li>
  267. <li><a href="#" id="toggleRealtime">Enable real-time update</a></li>
  268. </ul>
  269. </nav>
  270. </header>
  271. <div id="tabs">
  272. <div id="overview">
  273. <div class="container">
  274. <div id="counts"></div>
  275. <div id="info">
  276. <div id="generalInfo"></div>
  277. <div id="directives"></div>
  278. <div id="functions">
  279. <table>
  280. <thead>
  281. <tr><th>Available functions</th></tr>
  282. </thead>
  283. <tbody>
  284. <?php foreach ($opcache->getData('functions') as $func): ?>
  285. <tr><td><a href="http://php.net/<?php echo $func; ?>" title="View manual page" target="_blank"><?php echo $func; ?></a></td></tr>
  286. <?php endforeach; ?>
  287. </tbody>
  288. </table>
  289. </div>
  290. <br style="clear:both;" />
  291. </div>
  292. </div>
  293. </div>
  294. <div id="files">
  295. <p><label>Start typing to filter on script path<br/><input type="text" name="filter" id="frmFilter" /><label></p>
  296. <div class="container" id="filelist"></div>
  297. </div>
  298. </div>
  299. <footer>
  300. <a href="https://github.com/amnuts/opcache-gui" target="_blank">https://github.com/amnuts/opcache-gui</a>
  301. </footer>
  302. <script type="text/javascript">
  303. var realtime = false;
  304. var opstate = <?php echo json_encode($opcache->getData()); ?>;
  305. var canInvalidate = <?php echo ($opcache->canInvalidate() ? 'true' : 'false'); ?>;
  306. $(function(){
  307. function updateStatus() {
  308. $('#toggleRealtime').removeClass('pulse');
  309. $.ajax({
  310. url: "#",
  311. dataType: "json",
  312. cache: false,
  313. success: function(data) {
  314. $('#toggleRealtime').addClass('pulse');
  315. opstate = data;
  316. overviewCountsObj.setState({
  317. data : opstate.overview
  318. });
  319. generalInfoObj.setState({
  320. version : opstate.version,
  321. start : opstate.overview.readable.start_time,
  322. reset : opstate.overview.readable.last_restart_time
  323. });
  324. filesObj.setState({
  325. data : opstate.files,
  326. count_formatted : opstate.overview.readable.num_cached_scripts,
  327. count : opstate.overview.num_cached_scripts
  328. });
  329. $('#frmFilter').trigger('keyup');
  330. }
  331. });
  332. }
  333. $('#toggleRealtime').click(function(){
  334. if (realtime === false) {
  335. realtime = setInterval(function(){updateStatus()}, 5000);
  336. $(this).text('Disable real-time update');
  337. } else {
  338. clearInterval(realtime);
  339. realtime = false;
  340. $(this).text('Enable real-time update').removeClass('pulse');
  341. }
  342. });
  343. $('nav a[data-for]').click(function(){
  344. $('#tabs > div').hide();
  345. $('#' + $(this).data('for')).show();
  346. $('nav a[data-for]').removeClass('active');
  347. $(this).addClass('active');
  348. return false;
  349. });
  350. $(document).on('paint', '#filelist table tbody', function(event, params) {
  351. var trs = $('tr', $(this)).not('.hide');
  352. trs.filter(':odd').css({backgroundColor:'#E0ECEF'})
  353. .end().filter(':even').css({backgroundColor:'#EFFEFF'});
  354. filesObj.setState({showing: trs.length});
  355. });
  356. $('#frmFilter').bind('keyup', function(event){
  357. $('span.pathname').each(function(index){
  358. if ($(this).text().toLowerCase().indexOf($('#frmFilter').val().toLowerCase()) == -1) {
  359. $(this).closest('tr').addClass('hide');
  360. } else {
  361. $(this).closest('tr').removeClass('hide');
  362. }
  363. });
  364. $('#filelist table tbody').trigger('paint');
  365. });
  366. });
  367. var OverviewCounts = React.createClass({displayName: 'OverviewCounts',
  368. getInitialState: function() {
  369. return { data : opstate.overview };
  370. },
  371. render: function() {
  372. return (
  373. React.createElement("div", null,
  374. React.createElement("div", null,
  375. React.createElement("h3", null, "memory usage"),
  376. React.createElement("p", null, React.createElement("span", {className: "large"}, this.state.data.used_memory_percentage), React.createElement("span", null, "%"))
  377. ),
  378. React.createElement("div", null,
  379. React.createElement("h3", null, "hit rate"),
  380. React.createElement("p", null, React.createElement("span", {className: "large"}, this.state.data.hit_rate_percentage), React.createElement("span", null, "%"))
  381. ),
  382. React.createElement("div", {id: "moreinfo"},
  383. React.createElement("p", null, React.createElement("b", null, "total memory:"), this.state.data.readable.total_memory),
  384. React.createElement("p", null, React.createElement("b", null, "used memory:"), this.state.data.readable.used_memory),
  385. React.createElement("p", null, React.createElement("b", null, "free memory:"), this.state.data.readable.free_memory),
  386. React.createElement("p", null, React.createElement("b", null, "wasted memory:"), this.state.data.readable.wasted_memory, " (", this.state.data.wasted_percentage, "%)"),
  387. React.createElement("p", null, React.createElement("b", null, "number of cached files:"), this.state.data.readable.num_cached_scripts),
  388. React.createElement("p", null, React.createElement("b", null, "number of hits:"), this.state.data.readable.hits),
  389. React.createElement("p", null, React.createElement("b", null, "number of misses:"), this.state.data.readable.misses),
  390. React.createElement("p", null, React.createElement("b", null, "blacklist misses:"), this.state.data.readable.blacklist_miss),
  391. React.createElement("p", null, React.createElement("b", null, "number of cached keys:"), this.state.data.readable.num_cached_keys),
  392. React.createElement("p", null, React.createElement("b", null, "max cached keys:"), this.state.data.readable.max_cached_keys)
  393. )
  394. )
  395. );
  396. }
  397. });
  398. var GeneralInfo = React.createClass({displayName: 'GeneralInfo',
  399. getInitialState: function() {
  400. return {
  401. version : opstate.version,
  402. start : opstate.overview.readable.start_time,
  403. reset : opstate.overview.readable.last_restart_time
  404. };
  405. },
  406. render: function() {
  407. return (
  408. React.createElement("table", null,
  409. React.createElement("thead", null,
  410. React.createElement("tr", null, React.createElement("th", {colSpan: "2"}, "General info"))
  411. ),
  412. React.createElement("tbody", null,
  413. React.createElement("tr", null, React.createElement("td", null, "Zend OPcache"), React.createElement("td", null, this.state.version.version)),
  414. React.createElement("tr", null, React.createElement("td", null, "PHP"), React.createElement("td", null, this.state.version.php)),
  415. React.createElement("tr", null, React.createElement("td", null, "Host"), React.createElement("td", null, this.state.version.host)),
  416. React.createElement("tr", null, React.createElement("td", null, "Server Software"), React.createElement("td", null, this.state.version.server)),
  417. React.createElement("tr", null, React.createElement("td", null, "Start time"), React.createElement("td", null, this.state.start)),
  418. React.createElement("tr", null, React.createElement("td", null, "Last reset"), React.createElement("td", null, this.state.reset))
  419. )
  420. )
  421. );
  422. }
  423. });
  424. var Directives = React.createClass({displayName: 'Directives',
  425. getInitialState: function() {
  426. return { data : opstate.directives };
  427. },
  428. render: function() {
  429. var directiveNodes = this.state.data.map(function(directive) {
  430. var map = { 'opcache.':'', '_':' ' };
  431. var dShow = directive.k.replace(/opcache\.|_/gi, function(matched){
  432. return map[matched];
  433. });
  434. var vShow;
  435. if (directive.v === true || directive.v === false) {
  436. vShow = React.createElement('i', {}, directive.v.toString());
  437. } else if (directive.v == '') {
  438. vShow = React.createElement('i', {}, 'no value');
  439. } else {
  440. vShow = directive.v;
  441. }
  442. return (
  443. React.createElement("tr", {key: directive.k},
  444. React.createElement("td", {title: directive.k}, dShow),
  445. React.createElement("td", null, vShow)
  446. )
  447. );
  448. });
  449. return (
  450. React.createElement("table", null,
  451. React.createElement("thead", null,
  452. React.createElement("tr", null, React.createElement("th", {colSpan: "2"}, "Directives"))
  453. ),
  454. React.createElement("tbody", null, directiveNodes)
  455. )
  456. );
  457. }
  458. });
  459. var Files = React.createClass({displayName: 'Files',
  460. getInitialState: function() {
  461. return {
  462. data : opstate.files,
  463. showing: null
  464. };
  465. },
  466. handleInvalidate: function(e) {
  467. e.preventDefault();
  468. if (realtime) {
  469. $.get('#', { invalidate: e.currentTarget.getAttribute('data-file') }, function(data) {
  470. console.log('success: ' + data.success);
  471. }, 'json');
  472. } else {
  473. window.location.href = e.currentTarget.href;
  474. }
  475. },
  476. render: function() {
  477. var fileNodes = this.state.data.map(function(file) {
  478. var invalidate, invalidated;
  479. if (file.timestamp == 0) {
  480. invalidated = React.createElement("span", null, React.createElement("i", {className: "invalid metainfo"}, "has been invalidated"));
  481. }
  482. if (canInvalidate) {
  483. invalidate = React.createElement("span", null, ", ", React.createElement("a", {className: "metainfo", href: '?invalidate='
  484. + file.full_path, 'data-file': file.full_path, onClick: this.handleInvalidate}, "force file invalidation"));
  485. }
  486. return (
  487. React.createElement("tr", {key: file.full_path},
  488. React.createElement("td", null,
  489. React.createElement("div", null,
  490. React.createElement("span", {className: "pathname"}, file.full_path), React.createElement("br", null),
  491. React.createElement(FilesMeta, {data: [file.readable.hits, file.readable.memory_consumption, file.last_used]}),
  492. invalidate,
  493. invalidated
  494. )
  495. )
  496. )
  497. );
  498. }.bind(this));
  499. return (
  500. React.createElement("div", null,
  501. React.createElement(FilesListed, {showing: this.state.showing}),
  502. React.createElement("table", null,
  503. React.createElement("thead", null, React.createElement("tr", null, React.createElement("th", null, "Script"))),
  504. React.createElement("tbody", null, fileNodes)
  505. )
  506. )
  507. );
  508. }
  509. });
  510. var FilesMeta = React.createClass({displayName: 'FilesMeta',
  511. render: function() {
  512. return (
  513. React.createElement("span", {className: "metainfo"},
  514. React.createElement("b", null, "hits: "), React.createElement("span", null, this.props.data[0], ", "),
  515. React.createElement("b", null, "memory: "), React.createElement("span", null, this.props.data[1], ", "),
  516. React.createElement("b", null, "last used: "), React.createElement("span", null, this.props.data[2])
  517. )
  518. );
  519. }
  520. });
  521. var FilesListed = React.createClass({displayName: 'FilesListed',
  522. getInitialState: function() {
  523. return {
  524. formatted : opstate.overview.readable.num_cached_scripts,
  525. total : opstate.overview.num_cached_scripts
  526. };
  527. },
  528. render: function() {
  529. var display = this.state.formatted + ' file' + (this.state.total == 1 ? '' : 's') + ' cached';
  530. if (this.props.showing !== null && this.props.showing != this.state.total) {
  531. display += ', ' + this.props.showing + ' showing due to filter';
  532. }
  533. return (React.createElement("h3", null, display));
  534. }
  535. });
  536. var overviewCountsObj = React.render(React.createElement(OverviewCounts, null), document.getElementById('counts'));
  537. var generalInfoObj = React.render(React.createElement(GeneralInfo, null), document.getElementById('generalInfo'));
  538. var filesObj = React.render(React.createElement(Files, null), document.getElementById('filelist'));
  539. React.render(React.createElement(Directives, null), document.getElementById('directives'));
  540. </script>
  541. </body>
  542. </html>