index.php 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686
  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 3.5.5
  9. * @link https://github.com/amnuts/opcache-gui
  10. * @license MIT, https://acollington.mit-license.org/
  11. */
  12. /*
  13. * User configuration
  14. * These are all the default values; you only really need to supply the ones
  15. * that you wish to change.
  16. */
  17. $options = [
  18. 'allow_filelist' => true, // show/hide the files tab
  19. 'allow_invalidate' => true, // give a link to invalidate files
  20. 'allow_reset' => true, // give option to reset the whole cache
  21. 'allow_realtime' => true, // give option to enable/disable real-time updates
  22. 'refresh_time' => 5, // how often the data will refresh, in seconds
  23. 'size_precision' => 2, // Digits after decimal point
  24. 'size_space' => false, // have '1MB' or '1 MB' when showing sizes
  25. 'charts' => true, // show gauge chart or just big numbers
  26. 'debounce_rate' => 250, // milliseconds after key press to send keyup event when filtering
  27. 'per_page' => 200, // How many results per page to show in the file list, false for no pagination
  28. 'cookie_name' => 'opcachegui', // name of cookie
  29. 'cookie_ttl' => 365, // days to store cookie
  30. 'datetime_format' => 'D, d M Y H:i:s O', // Show datetime in this format
  31. 'highlight' => [
  32. 'memory' => true, // show the memory chart/big number
  33. 'hits' => true, // show the hit rate chart/big number
  34. 'keys' => true, // show the keys used chart/big number
  35. 'jit' => true // show the jit buffer chart/big number
  36. ],
  37. // json structure of all text strings used, or null for default
  38. 'language_pack' => null
  39. ];
  40. /*
  41. * Shouldn't need to alter anything else below here
  42. */
  43. if (!extension_loaded('Zend OPcache')) {
  44. die('The Zend OPcache extension does not appear to be installed');
  45. }
  46. $ocEnabled = ini_get('opcache.enable');
  47. if (empty($ocEnabled)) {
  48. die('The Zend OPcache extension is installed but not active');
  49. }
  50. header('Cache-Control: no-cache, must-revalidate');
  51. header('Pragma: no-cache');
  52. class Service
  53. {
  54. public const VERSION = '3.5.5';
  55. protected $tz;
  56. protected $data;
  57. protected $options;
  58. protected $optimizationLevels;
  59. protected $jitModes;
  60. protected $jitModeMapping = [
  61. 'tracing' => 1254,
  62. 'on' => 1254,
  63. 'function' => 1205
  64. ];
  65. protected $defaults = [
  66. 'allow_filelist' => true, // show/hide the files tab
  67. 'allow_invalidate' => true, // give a link to invalidate files
  68. 'allow_reset' => true, // give option to reset the whole cache
  69. 'allow_realtime' => true, // give option to enable/disable real-time updates
  70. 'refresh_time' => 5, // how often the data will refresh, in seconds
  71. 'size_precision' => 2, // Digits after decimal point
  72. 'size_space' => false, // have '1MB' or '1 MB' when showing sizes
  73. 'charts' => true, // show gauge chart or just big numbers
  74. 'debounce_rate' => 250, // milliseconds after key press to send keyup event when filtering
  75. 'per_page' => 200, // How many results per page to show in the file list, false for no pagination
  76. 'cookie_name' => 'opcachegui', // name of cookie
  77. 'cookie_ttl' => 365, // days to store cookie
  78. 'datetime_format' => 'D, d M Y H:i:s O', // Show datetime in this format
  79. 'highlight' => [
  80. 'memory' => true, // show the memory chart/big number
  81. 'hits' => true, // show the hit rate chart/big number
  82. 'keys' => true, // show the keys used chart/big number
  83. 'jit' => true // show the jit buffer chart/big number
  84. ],
  85. 'language_pack' => null // json structure of all text strings used, or null for default
  86. ];
  87. /**
  88. * Service constructor.
  89. * @param array $options
  90. * @throws Exception
  91. */
  92. public function __construct(array $options = [])
  93. {
  94. $this->options = array_merge($this->defaults, $options);
  95. $this->tz = new DateTimeZone(date_default_timezone_get());
  96. if (is_string($this->options['language_pack'])) {
  97. $this->options['language_pack'] = json_decode($this->options['language_pack'], true);
  98. }
  99. $this->optimizationLevels = [
  100. 1 << 0 => $this->txt('CSE, STRING construction'),
  101. 1 << 1 => $this->txt('Constant conversion and jumps'),
  102. 1 << 2 => $this->txt('++, +=, series of jumps'),
  103. 1 << 3 => $this->txt('INIT_FCALL_BY_NAME -> DO_FCALL'),
  104. 1 << 4 => $this->txt('CFG based optimization'),
  105. 1 << 5 => $this->txt('DFA based optimization'),
  106. 1 << 6 => $this->txt('CALL GRAPH optimization'),
  107. 1 << 7 => $this->txt('SCCP (constant propagation)'),
  108. 1 << 8 => $this->txt('TMP VAR usage'),
  109. 1 << 9 => $this->txt('NOP removal'),
  110. 1 << 10 => $this->txt('Merge equal constants'),
  111. 1 << 11 => $this->txt('Adjust used stack'),
  112. 1 << 12 => $this->txt('Remove unused variables'),
  113. 1 << 13 => $this->txt('DCE (dead code elimination)'),
  114. 1 << 14 => $this->txt('(unsafe) Collect constants'),
  115. 1 << 15 => $this->txt('Inline functions'),
  116. ];
  117. $this->jitModes = [
  118. [
  119. 'flag' => $this->txt('CPU-specific optimization'),
  120. 'value' => [
  121. $this->txt('Disable CPU-specific optimization'),
  122. $this->txt('Enable use of AVX, if the CPU supports it')
  123. ]
  124. ],
  125. [
  126. 'flag' => $this->txt('Register allocation'),
  127. 'value' => [
  128. $this->txt('Do not perform register allocation'),
  129. $this->txt('Perform block-local register allocation'),
  130. $this->txt('Perform global register allocation')
  131. ]
  132. ],
  133. [
  134. 'flag' => $this->txt('Trigger'),
  135. 'value' => [
  136. $this->txt('Compile all functions on script load'),
  137. $this->txt('Compile functions on first execution'),
  138. $this->txt('Profile functions on first request and compile the hottest functions afterwards'),
  139. $this->txt('Profile on the fly and compile hot functions'),
  140. $this->txt('Currently unused'),
  141. $this->txt('Use tracing JIT. Profile on the fly and compile traces for hot code segments')
  142. ]
  143. ],
  144. [
  145. 'flag' => $this->txt('Optimization level'),
  146. 'value' => [
  147. $this->txt('No JIT'),
  148. $this->txt('Minimal JIT (call standard VM handlers)'),
  149. $this->txt('Inline VM handlers'),
  150. $this->txt('Use type inference'),
  151. $this->txt('Use call graph'),
  152. $this->txt('Optimize whole script')
  153. ]
  154. ]
  155. ];
  156. $this->data = $this->compileState();
  157. }
  158. /**
  159. * @return string
  160. */
  161. public function txt(): string
  162. {
  163. $args = func_get_args();
  164. $text = array_shift($args);
  165. if ((($lang = $this->getOption('language_pack')) !== null) && !empty($lang[$text])) {
  166. $text = $lang[$text];
  167. }
  168. foreach ($args as $i => $arg) {
  169. $text = str_replace('{' . $i . '}', $arg, $text);
  170. }
  171. return $text;
  172. }
  173. /**
  174. * @return $this
  175. * @throws Exception
  176. */
  177. public function handle(): Service
  178. {
  179. $response = function($success) {
  180. if ($this->isJsonRequest()) {
  181. echo '{ "success": "' . ($success ? 'yes' : 'no') . '" }';
  182. } else {
  183. header('Location: ?');
  184. }
  185. exit;
  186. };
  187. if (isset($_GET['reset']) && $this->getOption('allow_reset')) {
  188. $response($this->resetCache());
  189. } elseif (isset($_GET['invalidate']) && $this->getOption('allow_invalidate')) {
  190. $response($this->resetCache($_GET['invalidate']));
  191. } elseif (isset($_GET['invalidate_searched']) && $this->getOption('allow_invalidate')) {
  192. $response($this->resetSearched($_GET['invalidate_searched']));
  193. } elseif ($this->isJsonRequest() && $this->getOption('allow_realtime')) {
  194. echo json_encode($this->getData($_GET['section'] ?? null));
  195. exit;
  196. }
  197. return $this;
  198. }
  199. /**
  200. * @param string|null $name
  201. * @return array|mixed|null
  202. */
  203. public function getOption(?string $name = null)
  204. {
  205. if ($name === null) {
  206. return $this->options;
  207. }
  208. return $this->options[$name] ?? null;
  209. }
  210. /**
  211. * @param string|null $section
  212. * @param string|null $property
  213. * @return array|mixed|null
  214. */
  215. public function getData(?string $section = null, ?string $property = null)
  216. {
  217. if ($section === null) {
  218. return $this->data;
  219. }
  220. $section = strtolower($section);
  221. if (isset($this->data[$section])) {
  222. if ($property === null || !isset($this->data[$section][$property])) {
  223. return $this->data[$section];
  224. }
  225. return $this->data[$section][$property];
  226. }
  227. return null;
  228. }
  229. /**
  230. * @return bool
  231. */
  232. public function canInvalidate(): bool
  233. {
  234. return ($this->getOption('allow_invalidate') && function_exists('opcache_invalidate'));
  235. }
  236. /**
  237. * @param string|null $file
  238. * @return bool
  239. * @throws Exception
  240. */
  241. public function resetCache(?string $file = null): bool
  242. {
  243. $success = false;
  244. if ($file === null) {
  245. $success = opcache_reset();
  246. } elseif (function_exists('opcache_invalidate')) {
  247. $success = opcache_invalidate(urldecode($file), true);
  248. }
  249. if ($success) {
  250. $this->compileState();
  251. }
  252. return $success;
  253. }
  254. /**
  255. * @param string $search
  256. * @return bool
  257. * @throws Exception
  258. */
  259. public function resetSearched(string $search): bool
  260. {
  261. $found = $success = 0;
  262. $search = urldecode($search);
  263. foreach ($this->getData('files') as $file) {
  264. if (strpos($file['full_path'], $search) !== false) {
  265. ++$found;
  266. $success += (int)opcache_invalidate($file['full_path'], true);
  267. }
  268. }
  269. if ($success) {
  270. $this->compileState();
  271. }
  272. return $found === $success;
  273. }
  274. /**
  275. * @param mixed $size
  276. * @return string
  277. */
  278. protected function size($size): string
  279. {
  280. $i = 0;
  281. $val = ['b', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  282. while (($size / 1024) > 1) {
  283. $size /= 1024;
  284. ++$i;
  285. }
  286. return sprintf('%.' . $this->getOption('size_precision') . 'f%s%s',
  287. $size, ($this->getOption('size_space') ? ' ' : ''), $val[$i]
  288. );
  289. }
  290. /**
  291. * @return bool
  292. */
  293. protected function isJsonRequest(): bool
  294. {
  295. return !empty($_SERVER['HTTP_ACCEPT'])
  296. && stripos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false;
  297. }
  298. /**
  299. * @return array
  300. * @throws Exception
  301. */
  302. protected function compileState(): array
  303. {
  304. $status = opcache_get_status();
  305. $config = opcache_get_configuration();
  306. $missingConfig = array_diff_key(ini_get_all('zend opcache', false), $config['directives']);
  307. if (!empty($missingConfig)) {
  308. $config['directives'] = array_merge($config['directives'], $missingConfig);
  309. }
  310. $files = [];
  311. if (!empty($status['scripts']) && $this->getOption('allow_filelist')) {
  312. uasort($status['scripts'], static function ($a, $b) {
  313. return $a['hits'] <=> $b['hits'];
  314. });
  315. foreach ($status['scripts'] as &$file) {
  316. $file['full_path'] = str_replace('\\', '/', $file['full_path']);
  317. $file['readable'] = [
  318. 'hits' => number_format($file['hits']),
  319. 'memory_consumption' => $this->size($file['memory_consumption'])
  320. ];
  321. $file['last_used'] = (new DateTimeImmutable("@{$file['last_used_timestamp']}"))
  322. ->setTimezone($this->tz)
  323. ->format($this->getOption('datetime_format'));
  324. $file['last_modified'] = "";
  325. if (!empty($file['timestamp'])) {
  326. $file['last_modified'] = (new DateTimeImmutable("@{$file['timestamp']}"))
  327. ->setTimezone($this->tz)
  328. ->format($this->getOption('datetime_format'));
  329. }
  330. }
  331. $files = array_values($status['scripts']);
  332. }
  333. if ($config['directives']['opcache.file_cache_only'] || !empty($status['file_cache_only'])) {
  334. $overview = false;
  335. } else {
  336. $overview = array_merge(
  337. $status['memory_usage'], $status['opcache_statistics'], [
  338. 'used_memory_percentage' => round(100 * (
  339. ($status['memory_usage']['used_memory'] + $status['memory_usage']['wasted_memory'])
  340. / $config['directives']['opcache.memory_consumption']
  341. )),
  342. 'hit_rate_percentage' => round($status['opcache_statistics']['opcache_hit_rate']),
  343. 'used_key_percentage' => round(100 * (
  344. $status['opcache_statistics']['num_cached_keys']
  345. / $status['opcache_statistics']['max_cached_keys']
  346. )),
  347. 'wasted_percentage' => round($status['memory_usage']['current_wasted_percentage'], 2),
  348. 'readable' => [
  349. 'total_memory' => $this->size($config['directives']['opcache.memory_consumption']),
  350. 'used_memory' => $this->size($status['memory_usage']['used_memory']),
  351. 'free_memory' => $this->size($status['memory_usage']['free_memory']),
  352. 'wasted_memory' => $this->size($status['memory_usage']['wasted_memory']),
  353. 'num_cached_scripts' => number_format($status['opcache_statistics']['num_cached_scripts']),
  354. 'hits' => number_format($status['opcache_statistics']['hits']),
  355. 'misses' => number_format($status['opcache_statistics']['misses']),
  356. 'blacklist_miss' => number_format($status['opcache_statistics']['blacklist_misses']),
  357. 'num_cached_keys' => number_format($status['opcache_statistics']['num_cached_keys']),
  358. 'max_cached_keys' => number_format($status['opcache_statistics']['max_cached_keys']),
  359. 'interned' => null,
  360. 'start_time' => (new DateTimeImmutable("@{$status['opcache_statistics']['start_time']}"))
  361. ->setTimezone($this->tz)
  362. ->format($this->getOption('datetime_format')),
  363. 'last_restart_time' => ($status['opcache_statistics']['last_restart_time'] === 0
  364. ? $this->txt('never')
  365. : (new DateTimeImmutable("@{$status['opcache_statistics']['last_restart_time']}"))
  366. ->setTimezone($this->tz)
  367. ->format($this->getOption('datetime_format'))
  368. )
  369. ]
  370. ]
  371. );
  372. }
  373. $preload = [];
  374. if (!empty($status['preload_statistics']['scripts']) && $this->getOption('allow_filelist')) {
  375. $preload = $status['preload_statistics']['scripts'];
  376. sort($preload, SORT_STRING);
  377. if ($overview) {
  378. $overview['preload_memory'] = $status['preload_statistics']['memory_consumption'];
  379. $overview['readable']['preload_memory'] = $this->size($status['preload_statistics']['memory_consumption']);
  380. }
  381. }
  382. if (!empty($status['interned_strings_usage'])) {
  383. $overview['readable']['interned'] = [
  384. 'buffer_size' => $this->size($status['interned_strings_usage']['buffer_size']),
  385. 'strings_used_memory' => $this->size($status['interned_strings_usage']['used_memory']),
  386. 'strings_free_memory' => $this->size($status['interned_strings_usage']['free_memory']),
  387. 'number_of_strings' => number_format($status['interned_strings_usage']['number_of_strings'])
  388. ];
  389. }
  390. if ($overview && !empty($status['jit']['enabled'])) {
  391. $overview['jit_buffer_used_percentage'] = ($status['jit']['buffer_size']
  392. ? round(100 * (($status['jit']['buffer_size'] - $status['jit']['buffer_free']) / $status['jit']['buffer_size']))
  393. : 0
  394. );
  395. $overview['readable'] = array_merge($overview['readable'], [
  396. 'jit_buffer_size' => $this->size($status['jit']['buffer_size']),
  397. 'jit_buffer_free' => $this->size($status['jit']['buffer_free'])
  398. ]);
  399. } else {
  400. $this->options['highlight']['jit'] = false;
  401. }
  402. $directives = [];
  403. ksort($config['directives']);
  404. foreach ($config['directives'] as $k => $v) {
  405. if (in_array($k, ['opcache.max_file_size', 'opcache.memory_consumption', 'opcache.jit_buffer_size']) && $v) {
  406. $v = $this->size($v) . " ({$v})";
  407. } elseif ($k === 'opcache.optimization_level') {
  408. $levels = [];
  409. foreach ($this->optimizationLevels as $level => $info) {
  410. if ($level & $v) {
  411. $levels[] = "{$info} [{$level}]";
  412. }
  413. }
  414. $v = $levels ?: 'none';
  415. } elseif ($k === 'opcache.jit') {
  416. if ($v === '1') {
  417. $v = 'on';
  418. }
  419. if (isset($this->jitModeMapping[$v]) || is_numeric($v)) {
  420. $levels = [];
  421. foreach (str_split((string)($this->jitModeMapping[$v] ?? $v)) as $type => $level) {
  422. $levels[] = "{$level}: {$this->jitModes[$type]['value'][$level]} ({$this->jitModes[$type]['flag']})";
  423. }
  424. $v = [$v, $levels];
  425. } elseif (empty($v) || strtolower($v) === 'off') {
  426. $v = 'Off';
  427. }
  428. }
  429. $directives[] = [
  430. 'k' => $k,
  431. 'v' => $v
  432. ];
  433. }
  434. $version = array_merge(
  435. $config['version'],
  436. [
  437. 'php' => PHP_VERSION,
  438. 'server' => $_SERVER['SERVER_SOFTWARE'] ?: '',
  439. 'host' => (function_exists('gethostname')
  440. ? gethostname()
  441. : (php_uname('n')
  442. ?: (empty($_SERVER['SERVER_NAME'])
  443. ? $_SERVER['HOST_NAME']
  444. : $_SERVER['SERVER_NAME']
  445. )
  446. )
  447. ),
  448. 'gui' => self::VERSION
  449. ]
  450. );
  451. return [
  452. 'version' => $version,
  453. 'overview' => $overview,
  454. 'files' => $files,
  455. 'preload' => $preload,
  456. 'directives' => $directives,
  457. 'blacklist' => $config['blacklist'],
  458. 'functions' => get_extension_funcs('Zend OPcache'),
  459. 'jitState' => $this->jitState($status, $config['directives']),
  460. ];
  461. }
  462. protected function jitState(array $status, array $directives): array
  463. {
  464. $state = [
  465. 'enabled' => $status['jit']['enabled'],
  466. 'reason' => ''
  467. ];
  468. if (!$state['enabled']) {
  469. if (empty($directives['opcache.jit']) || $directives['opcache.jit'] === 'disable') {
  470. $state['reason'] = $this->txt('disabled due to <i>opcache.jit</i> setting');
  471. } elseif (!$directives['opcache.jit_buffer_size']) {
  472. $state['reason'] = $this->txt('the <i>opcache.jit_buffer_size</i> must be set to fully enable JIT');
  473. } else {
  474. $state['reason'] = $this->txt('incompatible with extensions that override <i>zend_execute_ex()</i>, such as <i>xdebug</i>');
  475. }
  476. }
  477. return $state;
  478. }
  479. }
  480. $opcache = (new Service($options))->handle();
  481. ?>
  482. <!DOCTYPE html>
  483. <html lang="en">
  484. <head>
  485. <meta charset="UTF-8">
  486. <meta name="viewport" content="width=device-width,initial-scale=1.0">
  487. <meta name="robots" content="noindex, nofollow" />
  488. <title>OPcache statistics on <?= $opcache->getData('version', 'host'); ?></title>
  489. <script src="//cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
  490. <script src="//cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
  491. <script src="//cdnjs.cloudflare.com/ajax/libs/axios/1.3.6/axios.min.js"></script>
  492. <style>
  493. :root{--opcache-gui-graph-track-fill-color: #6CA6EF;--opcache-gui-graph-track-background-color: rgba(229,231,231,0.905882)}.opcache-gui{font-family:sans-serif;font-size:90%;padding:0;margin:0}.opcache-gui .hide{display:none}.opcache-gui .sr-only{border:0 !important;clip:rect(1px, 1px, 1px, 1px) !important;-webkit-clip-path:inset(50%) !important;clip-path:inset(50%) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important;white-space:nowrap !important}.opcache-gui .main-nav{padding-top:20px}.opcache-gui .nav-tab-list{list-style-type:none;padding-left:8px;margin:0;border-bottom:1px solid #CCC}.opcache-gui .nav-tab{display:inline-block;margin:0 0 -1px 0;padding:15px 30px;border:1px solid transparent;border-bottom-color:#CCC;text-decoration:none;background-color:#fff;cursor:pointer;user-select:none}.opcache-gui .nav-tab:hover{background-color:#F4F4F4;text-decoration:underline}.opcache-gui .nav-tab.active{border:1px solid #CCC;border-bottom-color:#fff;border-top:3px solid #6CA6EF}.opcache-gui .nav-tab.active:hover{background-color:initial}.opcache-gui .nav-tab:focus{outline:0;text-decoration:underline}.opcache-gui .nav-tab-link-reset{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1.5em" height="1.5em" viewBox="0 0 24 24"><path d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" fill="rgb(98, 98, 98)"/></svg>')}.opcache-gui .nav-tab-link-reset.is-resetting{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1.5em" height="1.5em" viewBox="0 0 24 24"><path d="M17.65 6.35A7.958 7.958 0 0 0 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08A5.99 5.99 0 0 1 12 18c-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" fill="rgb(0, 186, 0)"/></svg>')}.opcache-gui .nav-tab-link-realtime{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1.5em" height="1.5em" viewBox="0 0 24 24"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8s8 3.58 8 8s-3.58 8-8 8z" fill="rgb(98, 98, 98)"/><path d="M12.5 7H11v6l5.25 3.15l.75-1.23l-4.5-2.67z" fill="rgb(98, 98, 98)"/></svg>')}.opcache-gui .nav-tab-link-realtime.live-update{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1.5em" height="1.5em" viewBox="0 0 24 24"><path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8s8 3.58 8 8s-3.58 8-8 8z" fill="rgb(0, 186, 0)"/><path d="M12.5 7H11v6l5.25 3.15l.75-1.23l-4.5-2.67z" fill="rgb(0, 186, 0)"/></svg>')}.opcache-gui .nav-tab-link-reset,.opcache-gui .nav-tab-link-realtime{position:relative;padding-left:50px}.opcache-gui .nav-tab-link-reset.pulse::before,.opcache-gui .nav-tab-link-realtime.pulse::before{content:"";position:absolute;top:12px;left:25px;width:18px;height:18px;z-index:10;opacity:0;background-color:transparent;border:2px solid #00ba00;border-radius:100%;animation:pulse 2s linear infinite}.opcache-gui .tab-content{padding:2em}.opcache-gui .tab-content-overview-counts{width:270px;float:right}.opcache-gui .tab-content-overview-info{margin-right:280px}.opcache-gui .graph-widget{max-width:100%;height:auto;margin:0 auto;display:flex;position:relative}.opcache-gui .graph-widget .widget-value{display:flex;align-items:center;justify-content:center;text-align:center;position:absolute;top:0;width:100%;height:100%;margin:0 auto;font-size:3.2em;font-weight:100;color:#6CA6EF;user-select:none}.opcache-gui .widget-panel{background-color:#EDEDED;margin-bottom:10px}.opcache-gui .widget-header{background-color:#CDCDCD;padding:4px 6px;margin:0;text-align:center;font-size:1rem;font-weight:bold}.opcache-gui .widget-value{margin:0;text-align:center}.opcache-gui .widget-value span.large{color:#6CA6EF;font-size:80pt;margin:0;padding:0;text-align:center}.opcache-gui .widget-value span.large+span{font-size:20pt;margin:0;color:#6CA6EF}.opcache-gui .widget-info{margin:0;padding:10px}.opcache-gui .widget-info *{margin:0;line-height:1.75em;text-align:left}.opcache-gui .tables{margin:0 0 1em 0;border-collapse:collapse;width:100%;table-layout:fixed}.opcache-gui .tables tr:nth-child(odd){background-color:#EFFEFF}.opcache-gui .tables tr:nth-child(even){background-color:#E0ECEF}.opcache-gui .tables th{text-align:left;padding:6px;background-color:#6CA6EF;color:#fff;border-color:#fff;font-weight:normal}.opcache-gui .tables td{padding:4px 6px;line-height:1.4em;vertical-align:top;border-color:#fff;overflow:hidden;overflow-wrap:break-word;text-overflow:ellipsis}.opcache-gui .directive-list{list-style-type:none;padding:0;margin:0}.opcache-gui .directive-list li{margin-bottom:0.5em}.opcache-gui .directive-list li:last-child{margin-bottom:0}.opcache-gui .directive-list li ul{margin-top:1.5em}.opcache-gui .file-filter{width:520px}.opcache-gui .file-metainfo{font-size:80%}.opcache-gui .file-metainfo.invalid{font-style:italic}.opcache-gui .file-pathname{width:70%;display:block}.opcache-gui .nav-tab-link-reset,.opcache-gui .nav-tab-link-realtime,.opcache-gui .github-link,.opcache-gui .sponsor-link{background-repeat:no-repeat;background-color:transparent}.opcache-gui .nav-tab-link-reset,.opcache-gui .nav-tab-link-realtime{background-position:24px 50%}.opcache-gui .main-footer{border-top:1px solid #CCC;padding:1em 2em}.opcache-gui .github-link,.opcache-gui .sponsor-link{background-position:0 50%;padding:2em 0 2em 2.3em;text-decoration:none;opacity:0.7;font-size:80%}.opcache-gui .github-link:hover,.opcache-gui .sponsor-link:hover{opacity:1}.opcache-gui .github-link{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="1.19em" height="1em" viewBox="0 0 1664 1408"><path d="M640 960q0 40-12.5 82t-43 76t-72.5 34t-72.5-34t-43-76t-12.5-82t12.5-82t43-76t72.5-34t72.5 34t43 76t12.5 82zm640 0q0 40-12.5 82t-43 76t-72.5 34t-72.5-34t-43-76t-12.5-82t12.5-82t43-76t72.5-34t72.5 34t43 76t12.5 82zm160 0q0-120-69-204t-187-84q-41 0-195 21q-71 11-157 11t-157-11q-152-21-195-21q-118 0-187 84t-69 204q0 88 32 153.5t81 103t122 60t140 29.5t149 7h168q82 0 149-7t140-29.5t122-60t81-103t32-153.5zm224-176q0 207-61 331q-38 77-105.5 133t-141 86t-170 47.5t-171.5 22t-167 4.5q-78 0-142-3t-147.5-12.5t-152.5-30t-137-51.5t-121-81t-86-115Q0 992 0 784q0-237 136-396q-27-82-27-170q0-116 51-218q108 0 190 39.5T539 163q147-35 309-35q148 0 280 32q105-82 187-121t189-39q51 102 51 218q0 87-27 168q136 160 136 398z" fill="rgb(98, 98, 98)"/></svg>')}.opcache-gui .sponsor-link{background-image:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewbox="0 0 24 24"><path fill="crimson" d="M12 21.35l-1.45-1.32c-5.15-4.67-8.55-7.75-8.55-11.53 0-3.08 2.42-5.5 5.5-5.5 1.74 0 3.41.81 4.5 2.09 1.09-1.28 2.76-2.09 4.5-2.09 3.08 0 5.5 2.42 5.5 5.5 0 3.78-3.4 6.86-8.55 11.54l-1.45 1.31z"/></svg>');margin-left:2em}.opcache-gui .file-cache-only{margin-top:0}.opcache-gui .paginate-filter{display:flex;align-items:baseline;justify-content:space-between;flex-wrap:wrap}.opcache-gui .paginate-filter .filter>*{padding:3px;margin:3px 3px 10px 0}.opcache-gui .pagination{margin:10px 0;padding:0}.opcache-gui .pagination li{display:inline-block}.opcache-gui .pagination li a{display:inline-flex;align-items:center;white-space:nowrap;line-height:1;padding:0.5rem 0.75rem;border-radius:3px;text-decoration:none;height:100%}.opcache-gui .pagination li a.arrow{font-size:1.1rem}.opcache-gui .pagination li a:active{transform:translateY(2px)}.opcache-gui .pagination li a.active{background-color:#4d75af;color:#fff}.opcache-gui .pagination li a:hover:not(.active){background-color:#FF7400;color:#fff}@media screen and (max-width: 750px){.opcache-gui .nav-tab-list{border-bottom:0}.opcache-gui .nav-tab{display:block;margin:0}.opcache-gui .nav-tab-link{display:block;margin:0 10px;padding:10px 0 10px 30px;border:0}.opcache-gui .nav-tab-link[data-for].active{border-bottom-color:#CCC}.opcache-gui .tab-content-overview-info{margin-right:auto;clear:both}.opcache-gui .tab-content-overview-counts{position:relative;display:block;width:100%}}@media screen and (max-width: 550px){.opcache-gui .file-filter{width:100%}}@keyframes pulse{0%{transform:scale(1);opacity:1}50%,100%{transform:scale(2);opacity:0}}
  494. </style>
  495. </head>
  496. <body style="padding: 0; margin: 0;">
  497. <div class="opcache-gui" id="interface" />
  498. <script type="text/javascript">
  499. function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
  500. function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
  501. function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
  502. function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
  503. class Interface extends React.Component {
  504. constructor(props) {
  505. super(props);
  506. _defineProperty(this, "startTimer", () => {
  507. this.setState({
  508. realtime: true
  509. });
  510. this.polling = setInterval(() => {
  511. this.setState({
  512. fetching: true,
  513. resetting: false
  514. });
  515. axios.get(window.location.pathname, {
  516. time: Date.now()
  517. }).then(response => {
  518. this.setState({
  519. opstate: response.data
  520. });
  521. });
  522. }, this.props.realtimeRefresh * 1000);
  523. });
  524. _defineProperty(this, "stopTimer", () => {
  525. this.setState({
  526. realtime: false,
  527. resetting: false
  528. });
  529. clearInterval(this.polling);
  530. });
  531. _defineProperty(this, "realtimeHandler", () => {
  532. const realtime = !this.state.realtime;
  533. if (!realtime) {
  534. this.stopTimer();
  535. this.removeCookie();
  536. } else {
  537. this.startTimer();
  538. this.setCookie();
  539. }
  540. });
  541. _defineProperty(this, "resetHandler", () => {
  542. if (this.state.realtime) {
  543. this.setState({
  544. resetting: true
  545. });
  546. axios.get(window.location.pathname, {
  547. params: {
  548. reset: 1
  549. }
  550. }).then(response => {
  551. console.log('success: ', response.data);
  552. });
  553. } else {
  554. window.location.href = '?reset=1';
  555. }
  556. });
  557. _defineProperty(this, "setCookie", () => {
  558. let d = new Date();
  559. d.setTime(d.getTime() + this.props.cookie.ttl * 86400000);
  560. document.cookie = `${this.props.cookie.name}=true;expires=${d.toUTCString()};path=/${this.isSecure ? ';secure' : ''}`;
  561. });
  562. _defineProperty(this, "removeCookie", () => {
  563. document.cookie = `${this.props.cookie.name}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/${this.isSecure ? ';secure' : ''}`;
  564. });
  565. _defineProperty(this, "getCookie", () => {
  566. const v = document.cookie.match(`(^|;) ?${this.props.cookie.name}=([^;]*)(;|$)`);
  567. return v ? !!v[2] : false;
  568. });
  569. _defineProperty(this, "txt", (text, ...args) => {
  570. if (this.props.language !== null && this.props.language.hasOwnProperty(text) && this.props.language[text]) {
  571. text = this.props.language[text];
  572. }
  573. args.forEach((arg, i) => {
  574. text = text.replaceAll(`{${i}}`, arg);
  575. });
  576. return text;
  577. });
  578. this.state = {
  579. realtime: this.getCookie(),
  580. resetting: false,
  581. opstate: props.opstate
  582. };
  583. this.polling = false;
  584. this.isSecure = window.location.protocol === 'https:';
  585. if (this.getCookie()) {
  586. this.startTimer();
  587. }
  588. }
  589. render() {
  590. const {
  591. opstate,
  592. realtimeRefresh,
  593. ...otherProps
  594. } = this.props;
  595. return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("header", null, /*#__PURE__*/React.createElement(MainNavigation, _extends({}, otherProps, {
  596. opstate: this.state.opstate,
  597. realtime: this.state.realtime,
  598. resetting: this.state.resetting,
  599. realtimeHandler: this.realtimeHandler,
  600. resetHandler: this.resetHandler,
  601. txt: this.txt
  602. }))), /*#__PURE__*/React.createElement(Footer, {
  603. version: this.props.opstate.version.gui,
  604. txt: this.txt
  605. }));
  606. }
  607. }
  608. function MainNavigation(props) {
  609. return /*#__PURE__*/React.createElement("nav", {
  610. className: "main-nav"
  611. }, /*#__PURE__*/React.createElement(Tabs, null, /*#__PURE__*/React.createElement("div", {
  612. label: props.txt("Overview"),
  613. tabId: "overview",
  614. tabIndex: 1
  615. }, /*#__PURE__*/React.createElement(OverviewCounts, {
  616. overview: props.opstate.overview,
  617. highlight: props.highlight,
  618. useCharts: props.useCharts,
  619. txt: props.txt
  620. }), /*#__PURE__*/React.createElement("div", {
  621. id: "info",
  622. className: "tab-content-overview-info"
  623. }, /*#__PURE__*/React.createElement(GeneralInfo, {
  624. start: props.opstate.overview && props.opstate.overview.readable.start_time || null,
  625. reset: props.opstate.overview && props.opstate.overview.readable.last_restart_time || null,
  626. version: props.opstate.version,
  627. jit: props.opstate.jitState,
  628. txt: props.txt
  629. }), /*#__PURE__*/React.createElement(Directives, {
  630. directives: props.opstate.directives,
  631. txt: props.txt
  632. }), /*#__PURE__*/React.createElement(Functions, {
  633. functions: props.opstate.functions,
  634. txt: props.txt
  635. }))), props.allow.filelist && /*#__PURE__*/React.createElement("div", {
  636. label: props.txt("Cached"),
  637. tabId: "cached",
  638. tabIndex: 2
  639. }, /*#__PURE__*/React.createElement(CachedFiles, {
  640. perPageLimit: props.perPageLimit,
  641. allFiles: props.opstate.files,
  642. searchTerm: props.searchTerm,
  643. debounceRate: props.debounceRate,
  644. allow: {
  645. fileList: props.allow.filelist,
  646. invalidate: props.allow.invalidate
  647. },
  648. realtime: props.realtime,
  649. txt: props.txt
  650. })), props.allow.filelist && props.opstate.blacklist.length && /*#__PURE__*/React.createElement("div", {
  651. label: props.txt("Ignored"),
  652. tabId: "ignored",
  653. tabIndex: 3
  654. }, /*#__PURE__*/React.createElement(IgnoredFiles, {
  655. perPageLimit: props.perPageLimit,
  656. allFiles: props.opstate.blacklist,
  657. allow: {
  658. fileList: props.allow.filelist
  659. },
  660. txt: props.txt
  661. })), props.allow.filelist && props.opstate.preload.length && /*#__PURE__*/React.createElement("div", {
  662. label: props.txt("Preloaded"),
  663. tabId: "preloaded",
  664. tabIndex: 4
  665. }, /*#__PURE__*/React.createElement(PreloadedFiles, {
  666. perPageLimit: props.perPageLimit,
  667. allFiles: props.opstate.preload,
  668. allow: {
  669. fileList: props.allow.filelist
  670. },
  671. txt: props.txt
  672. })), props.allow.reset && /*#__PURE__*/React.createElement("div", {
  673. label: props.txt("Reset cache"),
  674. tabId: "resetCache",
  675. className: `nav-tab-link-reset${props.resetting ? ' is-resetting pulse' : ''}`,
  676. handler: props.resetHandler,
  677. tabIndex: 5
  678. }), props.allow.realtime && /*#__PURE__*/React.createElement("div", {
  679. label: props.txt(`${props.realtime ? 'Disable' : 'Enable'} real-time update`),
  680. tabId: "toggleRealtime",
  681. className: `nav-tab-link-realtime${props.realtime ? ' live-update pulse' : ''}`,
  682. handler: props.realtimeHandler,
  683. tabIndex: 6
  684. })));
  685. }
  686. class Tabs extends React.Component {
  687. constructor(props) {
  688. super(props);
  689. _defineProperty(this, "onClickTabItem", tab => {
  690. this.setState({
  691. activeTab: tab
  692. });
  693. });
  694. this.state = {
  695. activeTab: this.props.children[0].props.label
  696. };
  697. }
  698. render() {
  699. const {
  700. onClickTabItem,
  701. state: {
  702. activeTab
  703. }
  704. } = this;
  705. const children = this.props.children.filter(Boolean);
  706. return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("ul", {
  707. className: "nav-tab-list"
  708. }, children.map(child => {
  709. const {
  710. tabId,
  711. label,
  712. className,
  713. handler,
  714. tabIndex
  715. } = child.props;
  716. return /*#__PURE__*/React.createElement(Tab, {
  717. activeTab: activeTab,
  718. key: tabId,
  719. label: label,
  720. onClick: handler || onClickTabItem,
  721. className: className,
  722. tabIndex: tabIndex,
  723. tabId: tabId
  724. });
  725. })), /*#__PURE__*/React.createElement("div", {
  726. className: "tab-content"
  727. }, children.map(child => /*#__PURE__*/React.createElement("div", {
  728. key: child.props.label,
  729. style: {
  730. display: child.props.label === activeTab ? 'block' : 'none'
  731. },
  732. id: `${child.props.tabId}-content`
  733. }, child.props.children))));
  734. }
  735. }
  736. class Tab extends React.Component {
  737. constructor(...args) {
  738. super(...args);
  739. _defineProperty(this, "onClick", () => {
  740. const {
  741. label,
  742. onClick
  743. } = this.props;
  744. onClick(label);
  745. });
  746. }
  747. render() {
  748. const {
  749. onClick,
  750. props: {
  751. activeTab,
  752. label,
  753. tabIndex,
  754. tabId
  755. }
  756. } = this;
  757. let className = 'nav-tab';
  758. if (this.props.className) {
  759. className += ` ${this.props.className}`;
  760. }
  761. if (activeTab === label) {
  762. className += ' active';
  763. }
  764. return /*#__PURE__*/React.createElement("li", {
  765. className: className,
  766. onClick: onClick,
  767. tabIndex: tabIndex,
  768. role: "tab",
  769. "aria-controls": `${tabId}-content`
  770. }, label);
  771. }
  772. }
  773. function OverviewCounts(props) {
  774. if (props.overview === false) {
  775. return /*#__PURE__*/React.createElement("p", {
  776. class: "file-cache-only"
  777. }, props.txt(`You have <i>opcache.file_cache_only</i> turned on. As a result, the memory information is not available. Statistics and file list may also not be returned by <i>opcache_get_statistics()</i>.`));
  778. }
  779. const graphList = [{
  780. id: 'memoryUsageCanvas',
  781. title: props.txt('memory'),
  782. show: props.highlight.memory,
  783. value: props.overview.used_memory_percentage
  784. }, {
  785. id: 'hitRateCanvas',
  786. title: props.txt('hit rate'),
  787. show: props.highlight.hits,
  788. value: props.overview.hit_rate_percentage
  789. }, {
  790. id: 'keyUsageCanvas',
  791. title: props.txt('keys'),
  792. show: props.highlight.keys,
  793. value: props.overview.used_key_percentage
  794. }, {
  795. id: 'jitUsageCanvas',
  796. title: props.txt('jit buffer'),
  797. show: props.highlight.jit,
  798. value: props.overview.jit_buffer_used_percentage
  799. }];
  800. return /*#__PURE__*/React.createElement("div", {
  801. id: "counts",
  802. className: "tab-content-overview-counts"
  803. }, graphList.map(graph => {
  804. if (!graph.show) {
  805. return null;
  806. }
  807. return /*#__PURE__*/React.createElement("div", {
  808. className: "widget-panel",
  809. key: graph.id
  810. }, /*#__PURE__*/React.createElement("h3", {
  811. className: "widget-header"
  812. }, graph.title), /*#__PURE__*/React.createElement(UsageGraph, {
  813. charts: props.useCharts,
  814. value: graph.value,
  815. gaugeId: graph.id
  816. }));
  817. }), /*#__PURE__*/React.createElement(MemoryUsagePanel, {
  818. total: props.overview.readable.total_memory,
  819. used: props.overview.readable.used_memory,
  820. free: props.overview.readable.free_memory,
  821. wasted: props.overview.readable.wasted_memory,
  822. preload: props.overview.readable.preload_memory || null,
  823. wastedPercent: props.overview.wasted_percentage,
  824. jitBuffer: props.overview.readable.jit_buffer_size || null,
  825. jitBufferFree: props.overview.readable.jit_buffer_free || null,
  826. jitBufferFreePercentage: props.overview.jit_buffer_used_percentage || null,
  827. txt: props.txt
  828. }), /*#__PURE__*/React.createElement(StatisticsPanel, {
  829. num_cached_scripts: props.overview.readable.num_cached_scripts,
  830. hits: props.overview.readable.hits,
  831. misses: props.overview.readable.misses,
  832. blacklist_miss: props.overview.readable.blacklist_miss,
  833. num_cached_keys: props.overview.readable.num_cached_keys,
  834. max_cached_keys: props.overview.readable.max_cached_keys,
  835. txt: props.txt
  836. }), props.overview.readable.interned && /*#__PURE__*/React.createElement(InternedStringsPanel, {
  837. buffer_size: props.overview.readable.interned.buffer_size,
  838. strings_used_memory: props.overview.readable.interned.strings_used_memory,
  839. strings_free_memory: props.overview.readable.interned.strings_free_memory,
  840. number_of_strings: props.overview.readable.interned.number_of_strings,
  841. txt: props.txt
  842. }));
  843. }
  844. function GeneralInfo(props) {
  845. return /*#__PURE__*/React.createElement("table", {
  846. className: "tables general-info-table"
  847. }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", {
  848. colSpan: "2"
  849. }, props.txt('General info')))), /*#__PURE__*/React.createElement("tbody", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, "Zend OPcache"), /*#__PURE__*/React.createElement("td", null, props.version.version)), /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, "PHP"), /*#__PURE__*/React.createElement("td", null, props.version.php)), /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, props.txt('Host')), /*#__PURE__*/React.createElement("td", null, props.version.host)), /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, props.txt('Server Software')), /*#__PURE__*/React.createElement("td", null, props.version.server)), props.start ? /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, props.txt('Start time')), /*#__PURE__*/React.createElement("td", null, props.start)) : null, props.reset ? /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, props.txt('Last reset')), /*#__PURE__*/React.createElement("td", null, props.reset)) : null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, props.txt('JIT enabled')), /*#__PURE__*/React.createElement("td", null, props.txt(props.jit.enabled ? "Yes" : "No"), props.jit.reason && /*#__PURE__*/React.createElement("span", {
  850. dangerouslySetInnerHTML: {
  851. __html: ` (${props.jit.reason})`
  852. }
  853. })))));
  854. }
  855. function Directives(props) {
  856. let directiveList = directive => {
  857. return /*#__PURE__*/React.createElement("ul", {
  858. className: "directive-list"
  859. }, directive.v.map((item, key) => {
  860. return Array.isArray(item) ? /*#__PURE__*/React.createElement("li", {
  861. key: "sublist_" + key
  862. }, directiveList({
  863. v: item
  864. })) : /*#__PURE__*/React.createElement("li", {
  865. key: key
  866. }, item);
  867. }));
  868. };
  869. let directiveNodes = props.directives.map(function (directive) {
  870. let map = {
  871. 'opcache.': '',
  872. '_': ' '
  873. };
  874. let dShow = directive.k.replace(/opcache\.|_/gi, function (matched) {
  875. return map[matched];
  876. });
  877. let vShow;
  878. if (directive.v === true || directive.v === false) {
  879. vShow = React.createElement('i', {}, props.txt(directive.v.toString()));
  880. } else if (directive.v === '') {
  881. vShow = React.createElement('i', {}, props.txt('no value'));
  882. } else {
  883. if (Array.isArray(directive.v)) {
  884. vShow = directiveList(directive);
  885. } else {
  886. vShow = directive.v;
  887. }
  888. }
  889. let directiveLink = name => {
  890. if (name === 'opcache.jit_max_recursive_returns') {
  891. return 'opcache.jit-max-recursive-return';
  892. }
  893. return ['opcache.file_update_protection', 'opcache.huge_code_pages', 'opcache.lockfile_path', 'opcache.opt_debug_level'].includes(name) ? name : name.replace(/_/g, '-');
  894. };
  895. return /*#__PURE__*/React.createElement("tr", {
  896. key: directive.k
  897. }, /*#__PURE__*/React.createElement("td", {
  898. title: props.txt('View {0} manual entry', directive.k)
  899. }, /*#__PURE__*/React.createElement("a", {
  900. href: 'https://php.net/manual/en/opcache.configuration.php#ini.' + directiveLink(directive.k),
  901. target: "_blank"
  902. }, dShow)), /*#__PURE__*/React.createElement("td", null, vShow));
  903. });
  904. return /*#__PURE__*/React.createElement("table", {
  905. className: "tables directives-table"
  906. }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", {
  907. colSpan: "2"
  908. }, props.txt('Directives')))), /*#__PURE__*/React.createElement("tbody", null, directiveNodes));
  909. }
  910. function Functions(props) {
  911. return /*#__PURE__*/React.createElement("div", {
  912. id: "functions"
  913. }, /*#__PURE__*/React.createElement("table", {
  914. className: "tables"
  915. }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", null, props.txt('Available functions')))), /*#__PURE__*/React.createElement("tbody", null, props.functions.map(f => /*#__PURE__*/React.createElement("tr", {
  916. key: f
  917. }, /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement("a", {
  918. href: "https://php.net/" + f,
  919. title: props.txt('View manual page'),
  920. target: "_blank"
  921. }, f)))))));
  922. }
  923. function UsageGraph(props) {
  924. const percentage = Math.round(3.6 * props.value / 360 * 100);
  925. return props.charts ? /*#__PURE__*/React.createElement(ReactCustomizableProgressbar, {
  926. progress: percentage,
  927. radius: 100,
  928. strokeWidth: 30,
  929. trackStrokeWidth: 30,
  930. strokeColor: getComputedStyle(document.documentElement).getPropertyValue('--opcache-gui-graph-track-fill-color') || "#6CA6EF",
  931. trackStrokeColor: getComputedStyle(document.documentElement).getPropertyValue('--opcache-gui-graph-track-background-color') || "#CCC",
  932. gaugeId: props.gaugeId
  933. }) : /*#__PURE__*/React.createElement("p", {
  934. className: "widget-value"
  935. }, /*#__PURE__*/React.createElement("span", {
  936. className: "large"
  937. }, percentage), /*#__PURE__*/React.createElement("span", null, "%"));
  938. }
  939. /**
  940. * This component is from <https://github.com/martyan/react-customizable-progressbar/>
  941. * MIT License (MIT), Copyright (c) 2019 Martin Juzl
  942. */
  943. class ReactCustomizableProgressbar extends React.Component {
  944. constructor(props) {
  945. super(props);
  946. _defineProperty(this, "initAnimation", () => {
  947. this.setState({
  948. animationInited: true
  949. });
  950. });
  951. _defineProperty(this, "getProgress", () => {
  952. const {
  953. initialAnimation,
  954. progress
  955. } = this.props;
  956. const {
  957. animationInited
  958. } = this.state;
  959. return initialAnimation && !animationInited ? 0 : progress;
  960. });
  961. _defineProperty(this, "getStrokeDashoffset", strokeLength => {
  962. const {
  963. counterClockwise,
  964. inverse,
  965. steps
  966. } = this.props;
  967. const progress = this.getProgress();
  968. const progressLength = strokeLength / steps * (steps - progress);
  969. if (inverse) return counterClockwise ? 0 : progressLength - strokeLength;
  970. return counterClockwise ? -1 * progressLength : progressLength;
  971. });
  972. _defineProperty(this, "getStrokeDashArray", (strokeLength, circumference) => {
  973. const {
  974. counterClockwise,
  975. inverse,
  976. steps
  977. } = this.props;
  978. const progress = this.getProgress();
  979. const progressLength = strokeLength / steps * (steps - progress);
  980. if (inverse) return `${progressLength}, ${circumference}`;
  981. return counterClockwise ? `${strokeLength * (progress / 100)}, ${circumference}` : `${strokeLength}, ${circumference}`;
  982. });
  983. _defineProperty(this, "getTrackStrokeDashArray", (strokeLength, circumference) => {
  984. const {
  985. initialAnimation
  986. } = this.props;
  987. const {
  988. animationInited
  989. } = this.state;
  990. if (initialAnimation && !animationInited) return `0, ${circumference}`;
  991. return `${strokeLength}, ${circumference}`;
  992. });
  993. _defineProperty(this, "getExtendedWidth", () => {
  994. const {
  995. strokeWidth,
  996. pointerRadius,
  997. pointerStrokeWidth,
  998. trackStrokeWidth
  999. } = this.props;
  1000. const pointerWidth = pointerRadius + pointerStrokeWidth;
  1001. if (pointerWidth > strokeWidth && pointerWidth > trackStrokeWidth) return pointerWidth * 2;else if (strokeWidth > trackStrokeWidth) return strokeWidth * 2;else return trackStrokeWidth * 2;
  1002. });
  1003. _defineProperty(this, "getPointerAngle", () => {
  1004. const {
  1005. cut,
  1006. counterClockwise,
  1007. steps
  1008. } = this.props;
  1009. const progress = this.getProgress();
  1010. return counterClockwise ? (360 - cut) / steps * (steps - progress) : (360 - cut) / steps * progress;
  1011. });
  1012. this.state = {
  1013. animationInited: false
  1014. };
  1015. }
  1016. componentDidMount() {
  1017. const {
  1018. initialAnimation,
  1019. initialAnimationDelay
  1020. } = this.props;
  1021. if (initialAnimation) setTimeout(this.initAnimation, initialAnimationDelay);
  1022. }
  1023. render() {
  1024. const {
  1025. radius,
  1026. pointerRadius,
  1027. pointerStrokeWidth,
  1028. pointerFillColor,
  1029. pointerStrokeColor,
  1030. fillColor,
  1031. trackStrokeWidth,
  1032. trackStrokeColor,
  1033. trackStrokeLinecap,
  1034. strokeColor,
  1035. strokeWidth,
  1036. strokeLinecap,
  1037. rotate,
  1038. cut,
  1039. trackTransition,
  1040. transition,
  1041. progress
  1042. } = this.props;
  1043. const d = 2 * radius;
  1044. const width = d + this.getExtendedWidth();
  1045. const circumference = 2 * Math.PI * radius;
  1046. const strokeLength = circumference / 360 * (360 - cut);
  1047. return /*#__PURE__*/React.createElement("figure", {
  1048. className: `graph-widget`,
  1049. style: {
  1050. width: `${width || 250}px`
  1051. },
  1052. "data-value": progress,
  1053. id: this.props.guageId
  1054. }, /*#__PURE__*/React.createElement("svg", {
  1055. width: width,
  1056. height: width,
  1057. viewBox: `0 0 ${width} ${width}`,
  1058. style: {
  1059. transform: `rotate(${rotate}deg)`
  1060. }
  1061. }, trackStrokeWidth > 0 && /*#__PURE__*/React.createElement("circle", {
  1062. cx: width / 2,
  1063. cy: width / 2,
  1064. r: radius,
  1065. fill: "none",
  1066. stroke: trackStrokeColor,
  1067. strokeWidth: trackStrokeWidth,
  1068. strokeDasharray: this.getTrackStrokeDashArray(strokeLength, circumference),
  1069. strokeLinecap: trackStrokeLinecap,
  1070. style: {
  1071. transition: trackTransition
  1072. }
  1073. }), strokeWidth > 0 && /*#__PURE__*/React.createElement("circle", {
  1074. cx: width / 2,
  1075. cy: width / 2,
  1076. r: radius,
  1077. fill: fillColor,
  1078. stroke: strokeColor,
  1079. strokeWidth: strokeWidth,
  1080. strokeDasharray: this.getStrokeDashArray(strokeLength, circumference),
  1081. strokeDashoffset: this.getStrokeDashoffset(strokeLength),
  1082. strokeLinecap: strokeLinecap,
  1083. style: {
  1084. transition
  1085. }
  1086. }), pointerRadius > 0 && /*#__PURE__*/React.createElement("circle", {
  1087. cx: d,
  1088. cy: "50%",
  1089. r: pointerRadius,
  1090. fill: pointerFillColor,
  1091. stroke: pointerStrokeColor,
  1092. strokeWidth: pointerStrokeWidth,
  1093. style: {
  1094. transformOrigin: '50% 50%',
  1095. transform: `rotate(${this.getPointerAngle()}deg) translate(${this.getExtendedWidth() / 2}px)`,
  1096. transition
  1097. }
  1098. })), /*#__PURE__*/React.createElement("figcaption", {
  1099. className: `widget-value`
  1100. }, progress, "%"));
  1101. }
  1102. }
  1103. ReactCustomizableProgressbar.defaultProps = {
  1104. radius: 100,
  1105. progress: 0,
  1106. steps: 100,
  1107. cut: 0,
  1108. rotate: -90,
  1109. strokeWidth: 20,
  1110. strokeColor: 'indianred',
  1111. fillColor: 'none',
  1112. strokeLinecap: 'round',
  1113. transition: '.3s ease',
  1114. pointerRadius: 0,
  1115. pointerStrokeWidth: 20,
  1116. pointerStrokeColor: 'indianred',
  1117. pointerFillColor: 'white',
  1118. trackStrokeColor: '#e6e6e6',
  1119. trackStrokeWidth: 20,
  1120. trackStrokeLinecap: 'round',
  1121. trackTransition: '.3s ease',
  1122. counterClockwise: false,
  1123. inverse: false,
  1124. initialAnimation: false,
  1125. initialAnimationDelay: 0
  1126. };
  1127. function MemoryUsagePanel(props) {
  1128. return /*#__PURE__*/React.createElement("div", {
  1129. className: "widget-panel"
  1130. }, /*#__PURE__*/React.createElement("h3", {
  1131. className: "widget-header"
  1132. }, "memory usage"), /*#__PURE__*/React.createElement("div", {
  1133. className: "widget-value widget-info"
  1134. }, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('total memory'), ":"), " ", props.total), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('used memory'), ":"), " ", props.used), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('free memory'), ":"), " ", props.free), props.preload && /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('preload memory'), ":"), " ", props.preload), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('wasted memory'), ":"), " ", props.wasted, " (", props.wastedPercent, "%)"), props.jitBuffer && /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('jit buffer'), ":"), " ", props.jitBuffer), props.jitBufferFree && /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('jit buffer free'), ":"), " ", props.jitBufferFree, " (", 100 - props.jitBufferFreePercentage, "%)")));
  1135. }
  1136. function StatisticsPanel(props) {
  1137. return /*#__PURE__*/React.createElement("div", {
  1138. className: "widget-panel"
  1139. }, /*#__PURE__*/React.createElement("h3", {
  1140. className: "widget-header"
  1141. }, props.txt('opcache statistics')), /*#__PURE__*/React.createElement("div", {
  1142. className: "widget-value widget-info"
  1143. }, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('number of cached'), " files:"), " ", props.num_cached_scripts), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('number of hits'), ":"), " ", props.hits), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('number of misses'), ":"), " ", props.misses), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('blacklist misses'), ":"), " ", props.blacklist_miss), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('number of cached keys'), ":"), " ", props.num_cached_keys), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('max cached keys'), ":"), " ", props.max_cached_keys)));
  1144. }
  1145. function InternedStringsPanel(props) {
  1146. return /*#__PURE__*/React.createElement("div", {
  1147. className: "widget-panel"
  1148. }, /*#__PURE__*/React.createElement("h3", {
  1149. className: "widget-header"
  1150. }, props.txt('interned strings usage')), /*#__PURE__*/React.createElement("div", {
  1151. className: "widget-value widget-info"
  1152. }, /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('buffer size'), ":"), " ", props.buffer_size), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('used memory'), ":"), " ", props.strings_used_memory), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('free memory'), ":"), " ", props.strings_free_memory), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("b", null, props.txt('number of strings'), ":"), " ", props.number_of_strings)));
  1153. }
  1154. class CachedFiles extends React.Component {
  1155. constructor(props) {
  1156. super(props);
  1157. _defineProperty(this, "setSearchTerm", debounce(searchTerm => {
  1158. this.setState({
  1159. searchTerm,
  1160. refreshPagination: !this.state.refreshPagination
  1161. });
  1162. }, this.props.debounceRate));
  1163. _defineProperty(this, "onPageChanged", currentPage => {
  1164. this.setState({
  1165. currentPage
  1166. });
  1167. });
  1168. _defineProperty(this, "handleInvalidate", e => {
  1169. e.preventDefault();
  1170. if (this.props.realtime) {
  1171. axios.get(window.location.pathname, {
  1172. params: {
  1173. invalidate_searched: this.state.searchTerm
  1174. }
  1175. }).then(response => {
  1176. console.log('success: ', response.data);
  1177. });
  1178. } else {
  1179. window.location.href = e.currentTarget.href;
  1180. }
  1181. });
  1182. _defineProperty(this, "changeSort", e => {
  1183. this.setState({
  1184. [e.target.name]: e.target.value
  1185. });
  1186. });
  1187. _defineProperty(this, "compareValues", (key, order = 'asc') => {
  1188. return function innerSort(a, b) {
  1189. if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
  1190. return 0;
  1191. }
  1192. const varA = typeof a[key] === 'string' ? a[key].toUpperCase() : a[key];
  1193. const varB = typeof b[key] === 'string' ? b[key].toUpperCase() : b[key];
  1194. let comparison = 0;
  1195. if (varA > varB) {
  1196. comparison = 1;
  1197. } else if (varA < varB) {
  1198. comparison = -1;
  1199. }
  1200. return order === 'desc' ? comparison * -1 : comparison;
  1201. };
  1202. });
  1203. this.doPagination = typeof props.perPageLimit === "number" && props.perPageLimit > 0;
  1204. this.state = {
  1205. currentPage: 1,
  1206. searchTerm: props.searchTerm,
  1207. refreshPagination: 0,
  1208. sortBy: `last_used_timestamp`,
  1209. sortDir: `desc`
  1210. };
  1211. }
  1212. render() {
  1213. if (!this.props.allow.fileList) {
  1214. return null;
  1215. }
  1216. if (this.props.allFiles.length === 0) {
  1217. return /*#__PURE__*/React.createElement("p", null, this.props.txt('No files have been cached or you have <i>opcache.file_cache_only</i> turned on'));
  1218. }
  1219. const {
  1220. searchTerm,
  1221. currentPage
  1222. } = this.state;
  1223. const offset = (currentPage - 1) * this.props.perPageLimit;
  1224. const filesInSearch = searchTerm ? this.props.allFiles.filter(file => {
  1225. return !(file.full_path.indexOf(searchTerm) === -1);
  1226. }) : this.props.allFiles;
  1227. filesInSearch.sort(this.compareValues(this.state.sortBy, this.state.sortDir));
  1228. const filesInPage = this.doPagination ? filesInSearch.slice(offset, offset + this.props.perPageLimit) : filesInSearch;
  1229. const allFilesTotal = this.props.allFiles.length;
  1230. const showingTotal = filesInSearch.length;
  1231. const showing = showingTotal !== allFilesTotal ? ", {1} showing due to filter '{2}'" : "";
  1232. return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("form", {
  1233. action: "#"
  1234. }, /*#__PURE__*/React.createElement("label", {
  1235. htmlFor: "frmFilter"
  1236. }, this.props.txt('Start typing to filter on script path')), /*#__PURE__*/React.createElement("br", null), /*#__PURE__*/React.createElement("input", {
  1237. type: "text",
  1238. name: "filter",
  1239. id: "frmFilter",
  1240. className: "file-filter",
  1241. onChange: e => {
  1242. this.setSearchTerm(e.target.value);
  1243. }
  1244. })), /*#__PURE__*/React.createElement("h3", null, this.props.txt(`{0} files cached${showing}`, allFilesTotal, showingTotal, this.state.searchTerm)), this.props.allow.invalidate && this.state.searchTerm && showingTotal !== allFilesTotal && /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("a", {
  1245. href: `?invalidate_searched=${encodeURIComponent(this.state.searchTerm)}`,
  1246. onClick: this.handleInvalidate
  1247. }, this.props.txt('Invalidate all matching files'))), /*#__PURE__*/React.createElement("div", {
  1248. className: "paginate-filter"
  1249. }, this.doPagination && /*#__PURE__*/React.createElement(Pagination, {
  1250. totalRecords: filesInSearch.length,
  1251. pageLimit: this.props.perPageLimit,
  1252. pageNeighbours: 2,
  1253. onPageChanged: this.onPageChanged,
  1254. refresh: this.state.refreshPagination,
  1255. txt: this.props.txt
  1256. }), /*#__PURE__*/React.createElement("nav", {
  1257. className: "filter",
  1258. "aria-label": this.props.txt('Sort order')
  1259. }, /*#__PURE__*/React.createElement("select", {
  1260. name: "sortBy",
  1261. onChange: this.changeSort,
  1262. value: this.state.sortBy
  1263. }, /*#__PURE__*/React.createElement("option", {
  1264. value: "last_used_timestamp"
  1265. }, this.props.txt('Last used')), /*#__PURE__*/React.createElement("option", {
  1266. value: "last_modified"
  1267. }, this.props.txt('Last modified')), /*#__PURE__*/React.createElement("option", {
  1268. value: "full_path"
  1269. }, this.props.txt('Path')), /*#__PURE__*/React.createElement("option", {
  1270. value: "hits"
  1271. }, this.props.txt('Number of hits')), /*#__PURE__*/React.createElement("option", {
  1272. value: "memory_consumption"
  1273. }, this.props.txt('Memory consumption'))), /*#__PURE__*/React.createElement("select", {
  1274. name: "sortDir",
  1275. onChange: this.changeSort,
  1276. value: this.state.sortDir
  1277. }, /*#__PURE__*/React.createElement("option", {
  1278. value: "desc"
  1279. }, this.props.txt('Descending')), /*#__PURE__*/React.createElement("option", {
  1280. value: "asc"
  1281. }, this.props.txt('Ascending'))))), /*#__PURE__*/React.createElement("table", {
  1282. className: "tables cached-list-table"
  1283. }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", null, this.props.txt('Script')))), /*#__PURE__*/React.createElement("tbody", null, filesInPage.map((file, index) => {
  1284. return /*#__PURE__*/React.createElement(CachedFile, _extends({
  1285. key: file.full_path,
  1286. canInvalidate: this.props.allow.invalidate,
  1287. realtime: this.props.realtime,
  1288. txt: this.props.txt
  1289. }, file));
  1290. }))));
  1291. }
  1292. }
  1293. class CachedFile extends React.Component {
  1294. constructor(...args) {
  1295. super(...args);
  1296. _defineProperty(this, "handleInvalidate", e => {
  1297. e.preventDefault();
  1298. if (this.props.realtime) {
  1299. axios.get(window.location.pathname, {
  1300. params: {
  1301. invalidate: e.currentTarget.getAttribute('data-file')
  1302. }
  1303. }).then(response => {
  1304. console.log('success: ', response.data);
  1305. });
  1306. } else {
  1307. window.location.href = e.currentTarget.href;
  1308. }
  1309. });
  1310. }
  1311. render() {
  1312. return /*#__PURE__*/React.createElement("tr", {
  1313. "data-path": this.props.full_path.toLowerCase()
  1314. }, /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement("span", {
  1315. className: "file-pathname"
  1316. }, this.props.full_path), /*#__PURE__*/React.createElement("span", {
  1317. className: "file-metainfo"
  1318. }, /*#__PURE__*/React.createElement("b", null, this.props.txt('hits'), ": "), /*#__PURE__*/React.createElement("span", null, this.props.readable.hits, ", "), /*#__PURE__*/React.createElement("b", null, this.props.txt('memory'), ": "), /*#__PURE__*/React.createElement("span", null, this.props.readable.memory_consumption, ", "), this.props.last_modified && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("b", null, this.props.txt('last modified'), ": "), /*#__PURE__*/React.createElement("span", null, this.props.last_modified, ", ")), /*#__PURE__*/React.createElement("b", null, this.props.txt('last used'), ": "), /*#__PURE__*/React.createElement("span", null, this.props.last_used)), !this.props.timestamp && /*#__PURE__*/React.createElement("span", {
  1319. className: "invalid file-metainfo"
  1320. }, " - ", this.props.txt('has been invalidated')), this.props.canInvalidate && /*#__PURE__*/React.createElement("span", null, ",\xA0", /*#__PURE__*/React.createElement("a", {
  1321. className: "file-metainfo",
  1322. href: '?invalidate=' + this.props.full_path,
  1323. "data-file": this.props.full_path,
  1324. onClick: this.handleInvalidate
  1325. }, this.props.txt('force file invalidation')))));
  1326. }
  1327. }
  1328. class IgnoredFiles extends React.Component {
  1329. constructor(props) {
  1330. super(props);
  1331. _defineProperty(this, "onPageChanged", currentPage => {
  1332. this.setState({
  1333. currentPage
  1334. });
  1335. });
  1336. this.doPagination = typeof props.perPageLimit === "number" && props.perPageLimit > 0;
  1337. this.state = {
  1338. currentPage: 1,
  1339. refreshPagination: 0
  1340. };
  1341. }
  1342. render() {
  1343. if (!this.props.allow.fileList) {
  1344. return null;
  1345. }
  1346. if (this.props.allFiles.length === 0) {
  1347. return /*#__PURE__*/React.createElement("p", null, this.props.txt('No files have been ignored via <i>opcache.blacklist_filename</i>'));
  1348. }
  1349. const {
  1350. currentPage
  1351. } = this.state;
  1352. const offset = (currentPage - 1) * this.props.perPageLimit;
  1353. const filesInPage = this.doPagination ? this.props.allFiles.slice(offset, offset + this.props.perPageLimit) : this.props.allFiles;
  1354. const allFilesTotal = this.props.allFiles.length;
  1355. return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", null, this.props.txt('{0} ignore file locations', allFilesTotal)), this.doPagination && /*#__PURE__*/React.createElement(Pagination, {
  1356. totalRecords: allFilesTotal,
  1357. pageLimit: this.props.perPageLimit,
  1358. pageNeighbours: 2,
  1359. onPageChanged: this.onPageChanged,
  1360. refresh: this.state.refreshPagination,
  1361. txt: this.props.txt
  1362. }), /*#__PURE__*/React.createElement("table", {
  1363. className: "tables ignored-list-table"
  1364. }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", null, this.props.txt('Path')))), /*#__PURE__*/React.createElement("tbody", null, filesInPage.map((file, index) => {
  1365. return /*#__PURE__*/React.createElement("tr", {
  1366. key: file
  1367. }, /*#__PURE__*/React.createElement("td", null, file));
  1368. }))));
  1369. }
  1370. }
  1371. class PreloadedFiles extends React.Component {
  1372. constructor(props) {
  1373. super(props);
  1374. _defineProperty(this, "onPageChanged", currentPage => {
  1375. this.setState({
  1376. currentPage
  1377. });
  1378. });
  1379. this.doPagination = typeof props.perPageLimit === "number" && props.perPageLimit > 0;
  1380. this.state = {
  1381. currentPage: 1,
  1382. refreshPagination: 0
  1383. };
  1384. }
  1385. render() {
  1386. if (!this.props.allow.fileList) {
  1387. return null;
  1388. }
  1389. if (this.props.allFiles.length === 0) {
  1390. return /*#__PURE__*/React.createElement("p", null, this.props.txt('No files have been preloaded <i>opcache.preload</i>'));
  1391. }
  1392. const {
  1393. currentPage
  1394. } = this.state;
  1395. const offset = (currentPage - 1) * this.props.perPageLimit;
  1396. const filesInPage = this.doPagination ? this.props.allFiles.slice(offset, offset + this.props.perPageLimit) : this.props.allFiles;
  1397. const allFilesTotal = this.props.allFiles.length;
  1398. return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h3", null, this.props.txt('{0} preloaded files', allFilesTotal)), this.doPagination && /*#__PURE__*/React.createElement(Pagination, {
  1399. totalRecords: allFilesTotal,
  1400. pageLimit: this.props.perPageLimit,
  1401. pageNeighbours: 2,
  1402. onPageChanged: this.onPageChanged,
  1403. refresh: this.state.refreshPagination,
  1404. txt: this.props.txt
  1405. }), /*#__PURE__*/React.createElement("table", {
  1406. className: "tables preload-list-table"
  1407. }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", null, this.props.txt('Path')))), /*#__PURE__*/React.createElement("tbody", null, filesInPage.map((file, index) => {
  1408. return /*#__PURE__*/React.createElement("tr", {
  1409. key: file
  1410. }, /*#__PURE__*/React.createElement("td", null, file));
  1411. }))));
  1412. }
  1413. }
  1414. class Pagination extends React.Component {
  1415. constructor(props) {
  1416. super(props);
  1417. _defineProperty(this, "gotoPage", page => {
  1418. const {
  1419. onPageChanged = f => f
  1420. } = this.props;
  1421. const currentPage = Math.max(0, Math.min(page, this.totalPages()));
  1422. this.setState({
  1423. currentPage
  1424. }, () => onPageChanged(currentPage));
  1425. });
  1426. _defineProperty(this, "totalPages", () => {
  1427. return Math.ceil(this.props.totalRecords / this.props.pageLimit);
  1428. });
  1429. _defineProperty(this, "handleClick", (page, evt) => {
  1430. evt.preventDefault();
  1431. this.gotoPage(page);
  1432. });
  1433. _defineProperty(this, "handleJumpLeft", evt => {
  1434. evt.preventDefault();
  1435. this.gotoPage(this.state.currentPage - this.pageNeighbours * 2 - 1);
  1436. });
  1437. _defineProperty(this, "handleJumpRight", evt => {
  1438. evt.preventDefault();
  1439. this.gotoPage(this.state.currentPage + this.pageNeighbours * 2 + 1);
  1440. });
  1441. _defineProperty(this, "handleMoveLeft", evt => {
  1442. evt.preventDefault();
  1443. this.gotoPage(this.state.currentPage - 1);
  1444. });
  1445. _defineProperty(this, "handleMoveRight", evt => {
  1446. evt.preventDefault();
  1447. this.gotoPage(this.state.currentPage + 1);
  1448. });
  1449. _defineProperty(this, "range", (from, to, step = 1) => {
  1450. let i = from;
  1451. const range = [];
  1452. while (i <= to) {
  1453. range.push(i);
  1454. i += step;
  1455. }
  1456. return range;
  1457. });
  1458. _defineProperty(this, "fetchPageNumbers", () => {
  1459. const totalPages = this.totalPages();
  1460. const pageNeighbours = this.pageNeighbours;
  1461. const totalNumbers = this.pageNeighbours * 2 + 3;
  1462. const totalBlocks = totalNumbers + 2;
  1463. if (totalPages > totalBlocks) {
  1464. let pages = [];
  1465. const leftBound = this.state.currentPage - pageNeighbours;
  1466. const rightBound = this.state.currentPage + pageNeighbours;
  1467. const beforeLastPage = totalPages - 1;
  1468. const startPage = leftBound > 2 ? leftBound : 2;
  1469. const endPage = rightBound < beforeLastPage ? rightBound : beforeLastPage;
  1470. pages = this.range(startPage, endPage);
  1471. const pagesCount = pages.length;
  1472. const singleSpillOffset = totalNumbers - pagesCount - 1;
  1473. const leftSpill = startPage > 2;
  1474. const rightSpill = endPage < beforeLastPage;
  1475. const leftSpillPage = "LEFT";
  1476. const rightSpillPage = "RIGHT";
  1477. if (leftSpill && !rightSpill) {
  1478. const extraPages = this.range(startPage - singleSpillOffset, startPage - 1);
  1479. pages = [leftSpillPage, ...extraPages, ...pages];
  1480. } else if (!leftSpill && rightSpill) {
  1481. const extraPages = this.range(endPage + 1, endPage + singleSpillOffset);
  1482. pages = [...pages, ...extraPages, rightSpillPage];
  1483. } else if (leftSpill && rightSpill) {
  1484. pages = [leftSpillPage, ...pages, rightSpillPage];
  1485. }
  1486. return [1, ...pages, totalPages];
  1487. }
  1488. return this.range(1, totalPages);
  1489. });
  1490. this.state = {
  1491. currentPage: 1
  1492. };
  1493. this.pageNeighbours = typeof props.pageNeighbours === "number" ? Math.max(0, Math.min(props.pageNeighbours, 2)) : 0;
  1494. }
  1495. componentDidMount() {
  1496. this.gotoPage(1);
  1497. }
  1498. componentDidUpdate(props) {
  1499. const {
  1500. refresh
  1501. } = this.props;
  1502. if (props.refresh !== refresh) {
  1503. this.gotoPage(1);
  1504. }
  1505. }
  1506. render() {
  1507. if (!this.props.totalRecords || this.totalPages() === 1) {
  1508. return null;
  1509. }
  1510. const {
  1511. currentPage
  1512. } = this.state;
  1513. const pages = this.fetchPageNumbers();
  1514. return /*#__PURE__*/React.createElement("nav", {
  1515. "aria-label": "File list pagination"
  1516. }, /*#__PURE__*/React.createElement("ul", {
  1517. className: "pagination"
  1518. }, pages.map((page, index) => {
  1519. if (page === "LEFT") {
  1520. return /*#__PURE__*/React.createElement(React.Fragment, {
  1521. key: index
  1522. }, /*#__PURE__*/React.createElement("li", {
  1523. className: "page-item arrow"
  1524. }, /*#__PURE__*/React.createElement("a", {
  1525. className: "page-link",
  1526. href: "#",
  1527. "aria-label": this.props.txt('Previous'),
  1528. onClick: this.handleJumpLeft
  1529. }, /*#__PURE__*/React.createElement("span", {
  1530. "aria-hidden": "true"
  1531. }, "\u219E"), /*#__PURE__*/React.createElement("span", {
  1532. className: "sr-only"
  1533. }, this.props.txt('Jump back')))), /*#__PURE__*/React.createElement("li", {
  1534. className: "page-item arrow"
  1535. }, /*#__PURE__*/React.createElement("a", {
  1536. className: "page-link",
  1537. href: "#",
  1538. "aria-label": this.props.txt('Previous'),
  1539. onClick: this.handleMoveLeft
  1540. }, /*#__PURE__*/React.createElement("span", {
  1541. "aria-hidden": "true"
  1542. }, "\u21E0"), /*#__PURE__*/React.createElement("span", {
  1543. className: "sr-only"
  1544. }, this.props.txt('Previous page')))));
  1545. }
  1546. if (page === "RIGHT") {
  1547. return /*#__PURE__*/React.createElement(React.Fragment, {
  1548. key: index
  1549. }, /*#__PURE__*/React.createElement("li", {
  1550. className: "page-item arrow"
  1551. }, /*#__PURE__*/React.createElement("a", {
  1552. className: "page-link",
  1553. href: "#",
  1554. "aria-label": this.props.txt('Next'),
  1555. onClick: this.handleMoveRight
  1556. }, /*#__PURE__*/React.createElement("span", {
  1557. "aria-hidden": "true"
  1558. }, "\u21E2"), /*#__PURE__*/React.createElement("span", {
  1559. className: "sr-only"
  1560. }, this.props.txt('Next page')))), /*#__PURE__*/React.createElement("li", {
  1561. className: "page-item arrow"
  1562. }, /*#__PURE__*/React.createElement("a", {
  1563. className: "page-link",
  1564. href: "#",
  1565. "aria-label": this.props.txt('Next'),
  1566. onClick: this.handleJumpRight
  1567. }, /*#__PURE__*/React.createElement("span", {
  1568. "aria-hidden": "true"
  1569. }, "\u21A0"), /*#__PURE__*/React.createElement("span", {
  1570. className: "sr-only"
  1571. }, this.props.txt('Jump forward')))));
  1572. }
  1573. return /*#__PURE__*/React.createElement("li", {
  1574. key: index,
  1575. className: "page-item"
  1576. }, /*#__PURE__*/React.createElement("a", {
  1577. className: `page-link${currentPage === page ? " active" : ""}`,
  1578. href: "#",
  1579. onClick: e => this.handleClick(page, e)
  1580. }, page));
  1581. })));
  1582. }
  1583. }
  1584. function Footer(props) {
  1585. return /*#__PURE__*/React.createElement("footer", {
  1586. className: "main-footer"
  1587. }, /*#__PURE__*/React.createElement("a", {
  1588. className: "github-link",
  1589. href: "https://github.com/amnuts/opcache-gui",
  1590. target: "_blank",
  1591. title: props.txt("opcache-gui (currently version {0}) on GitHub", props.version)
  1592. }, "https://github.com/amnuts/opcache-gui - ", props.txt("version {0}", props.version)), /*#__PURE__*/React.createElement("a", {
  1593. className: "sponsor-link",
  1594. href: "https://github.com/sponsors/amnuts",
  1595. target: "_blank",
  1596. title: props.txt("Sponsor this project and author on GitHub")
  1597. }, props.txt("Sponsor this project")));
  1598. }
  1599. function debounce(func, wait, immediate) {
  1600. let timeout;
  1601. wait = wait || 250;
  1602. return function () {
  1603. let context = this,
  1604. args = arguments;
  1605. let later = function () {
  1606. timeout = null;
  1607. if (!immediate) {
  1608. func.apply(context, args);
  1609. }
  1610. };
  1611. let callNow = immediate && !timeout;
  1612. clearTimeout(timeout);
  1613. timeout = setTimeout(later, wait);
  1614. if (callNow) {
  1615. func.apply(context, args);
  1616. }
  1617. };
  1618. }
  1619. ReactDOM.render(React.createElement(Interface, {
  1620. allow: {
  1621. filelist: <?= $opcache->getOption('allow_filelist') ? 'true' : 'false'; ?>,
  1622. invalidate: <?= $opcache->getOption('allow_invalidate') ? 'true' : 'false'; ?>,
  1623. reset: <?= $opcache->getOption('allow_reset') ? 'true' : 'false'; ?>,
  1624. realtime: <?= $opcache->getOption('allow_realtime') ? 'true' : 'false'; ?>
  1625. },
  1626. cookie: {
  1627. name: '<?= $opcache->getOption('cookie_name'); ?>',
  1628. ttl: <?= $opcache->getOption('cookie_ttl'); ?>
  1629. },
  1630. opstate: <?= json_encode($opcache->getData()); ?>,
  1631. useCharts: <?= json_encode($opcache->getOption('charts')); ?>,
  1632. highlight: <?= json_encode($opcache->getOption('highlight')); ?>,
  1633. debounceRate: <?= $opcache->getOption('debounce_rate'); ?>,
  1634. perPageLimit: <?= json_encode($opcache->getOption('per_page')); ?>,
  1635. realtimeRefresh: <?= json_encode($opcache->getOption('refresh_time')); ?>,
  1636. language: <?= json_encode($opcache->getOption('language_pack')); ?>,
  1637. }), document.getElementById('interface'));
  1638. </script>
  1639. </body>
  1640. </html>