2013-04-08 15:40:06 +00:00
< ? php
2013-08-09 14:06:45 +00:00
/**
* OPcache GUI
2014-11-23 01:07:38 +00:00
*
2013-08-09 14:06:45 +00:00
* A simple but effective single - file GUI for the OPcache PHP extension .
2014-11-23 01:07:38 +00:00
*
2013-08-09 14:06:45 +00:00
* @ author Andrew Collington , andy @ amnuts . com
2024-06-23 13:01:33 +00:00
* @ version 3.5 . 5
2014-11-26 21:42:23 +00:00
* @ link https :// github . com / amnuts / opcache - gui
2021-06-27 11:47:40 +00:00
* @ license MIT , https :// acollington . mit - license . org /
2013-08-09 14:06:45 +00:00
*/
2015-05-08 16:04:40 +00:00
/*
* User configuration
2020-09-18 23:30:17 +00:00
* These are all the default values ; you only really need to supply the ones
* that you wish to change .
2015-05-08 16:04:40 +00:00
*/
$options = [
2022-07-17 01:43:40 +00:00
'allow_filelist' => true , // show/hide the files tab
'allow_invalidate' => true , // give a link to invalidate files
'allow_reset' => true , // give option to reset the whole cache
'allow_realtime' => true , // give option to enable/disable real-time updates
'refresh_time' => 5 , // how often the data will refresh, in seconds
'size_precision' => 2 , // Digits after decimal point
'size_space' => false , // have '1MB' or '1 MB' when showing sizes
'charts' => true , // show gauge chart or just big numbers
'debounce_rate' => 250 , // milliseconds after key press to send keyup event when filtering
'per_page' => 200 , // How many results per page to show in the file list, false for no pagination
'cookie_name' => 'opcachegui' , // name of cookie
'cookie_ttl' => 365 , // days to store cookie
'datetime_format' => 'D, d M Y H:i:s O' , // Show datetime in this format
2020-09-18 23:30:17 +00:00
'highlight' => [
2022-07-17 01:43:40 +00:00
'memory' => true , // show the memory chart/big number
'hits' => true , // show the hit rate chart/big number
'keys' => true , // show the keys used chart/big number
'jit' => true // show the jit buffer chart/big number
],
// json structure of all text strings used, or null for default
2022-08-02 22:50:35 +00:00
'language_pack' => null
2015-05-08 16:04:40 +00:00
];
/*
* Shouldn ' t need to alter anything else below here
*/
2014-11-23 01:07:38 +00:00
if ( ! extension_loaded ( 'Zend OPcache' )) {
2013-04-26 10:56:54 +00:00
die ( 'The Zend OPcache extension does not appear to be installed' );
2013-04-08 15:40:06 +00:00
}
2017-06-07 10:59:24 +00:00
$ocEnabled = ini_get ( 'opcache.enable' );
if ( empty ( $ocEnabled )) {
2019-07-12 00:41:26 +00:00
die ( 'The Zend OPcache extension is installed but not active' );
2017-01-06 13:40:59 +00:00
}
2019-07-12 00:41:26 +00:00
header ( 'Cache-Control: no-cache, must-revalidate' );
header ( 'Pragma: no-cache' );
2020-09-18 23:30:17 +00:00
class Service
2014-11-23 01:07:38 +00:00
{
2024-06-23 13:01:33 +00:00
public const VERSION = '3.5.5' ;
2020-02-22 18:15:23 +00:00
2022-07-16 17:46:15 +00:00
protected $tz ;
2014-11-25 13:20:33 +00:00
protected $data ;
2015-05-08 16:04:40 +00:00
protected $options ;
2020-02-22 19:51:12 +00:00
protected $optimizationLevels ;
2022-07-17 01:43:40 +00:00
protected $jitModes ;
protected $jitModeMapping = [
'tracing' => 1254 ,
'on' => 1254 ,
'function' => 1205
];
2015-05-08 16:04:40 +00:00
protected $defaults = [
2022-07-16 17:46:15 +00:00
'allow_filelist' => true , // show/hide the files tab
'allow_invalidate' => true , // give a link to invalidate files
'allow_reset' => true , // give option to reset the whole cache
'allow_realtime' => true , // give option to enable/disable real-time updates
'refresh_time' => 5 , // how often the data will refresh, in seconds
'size_precision' => 2 , // Digits after decimal point
'size_space' => false , // have '1MB' or '1 MB' when showing sizes
'charts' => true , // show gauge chart or just big numbers
'debounce_rate' => 250 , // milliseconds after key press to send keyup event when filtering
'per_page' => 200 , // How many results per page to show in the file list, false for no pagination
'cookie_name' => 'opcachegui' , // name of cookie
'cookie_ttl' => 365 , // days to store cookie
'datetime_format' => 'D, d M Y H:i:s O' , // Show datetime in this format
2020-09-18 23:30:17 +00:00
'highlight' => [
2022-07-16 17:46:15 +00:00
'memory' => true , // show the memory chart/big number
'hits' => true , // show the hit rate chart/big number
'keys' => true , // show the keys used chart/big number
'jit' => true // show the jit buffer chart/big number
2021-06-27 11:47:40 +00:00
],
2022-07-17 01:43:40 +00:00
'language_pack' => null // json structure of all text strings used, or null for default
2022-01-09 23:48:23 +00:00
];
2013-04-26 10:56:54 +00:00
2020-09-18 23:30:17 +00:00
/**
* Service constructor .
* @ param array $options
2022-07-16 15:19:55 +00:00
* @ throws Exception
2020-09-18 23:30:17 +00:00
*/
public function __construct ( array $options = [])
2014-11-23 01:07:38 +00:00
{
2015-05-08 16:04:40 +00:00
$this -> options = array_merge ( $this -> defaults , $options );
2022-07-16 17:46:15 +00:00
$this -> tz = new DateTimeZone ( date_default_timezone_get ());
2022-07-17 01:43:40 +00:00
if ( is_string ( $this -> options [ 'language_pack' ])) {
$this -> options [ 'language_pack' ] = json_decode ( $this -> options [ 'language_pack' ], true );
}
$this -> optimizationLevels = [
1 << 0 => $this -> txt ( 'CSE, STRING construction' ),
1 << 1 => $this -> txt ( 'Constant conversion and jumps' ),
1 << 2 => $this -> txt ( '++, +=, series of jumps' ),
1 << 3 => $this -> txt ( 'INIT_FCALL_BY_NAME -> DO_FCALL' ),
1 << 4 => $this -> txt ( 'CFG based optimization' ),
1 << 5 => $this -> txt ( 'DFA based optimization' ),
1 << 6 => $this -> txt ( 'CALL GRAPH optimization' ),
1 << 7 => $this -> txt ( 'SCCP (constant propagation)' ),
1 << 8 => $this -> txt ( 'TMP VAR usage' ),
1 << 9 => $this -> txt ( 'NOP removal' ),
1 << 10 => $this -> txt ( 'Merge equal constants' ),
1 << 11 => $this -> txt ( 'Adjust used stack' ),
1 << 12 => $this -> txt ( 'Remove unused variables' ),
1 << 13 => $this -> txt ( 'DCE (dead code elimination)' ),
1 << 14 => $this -> txt ( '(unsafe) Collect constants' ),
1 << 15 => $this -> txt ( 'Inline functions' ),
];
$this -> jitModes = [
[
'flag' => $this -> txt ( 'CPU-specific optimization' ),
'value' => [
$this -> txt ( 'Disable CPU-specific optimization' ),
$this -> txt ( 'Enable use of AVX, if the CPU supports it' )
]
],
[
'flag' => $this -> txt ( 'Register allocation' ),
'value' => [
$this -> txt ( 'Do not perform register allocation' ),
$this -> txt ( 'Perform block-local register allocation' ),
$this -> txt ( 'Perform global register allocation' )
]
],
[
'flag' => $this -> txt ( 'Trigger' ),
'value' => [
$this -> txt ( 'Compile all functions on script load' ),
$this -> txt ( 'Compile functions on first execution' ),
$this -> txt ( 'Profile functions on first request and compile the hottest functions afterwards' ),
$this -> txt ( 'Profile on the fly and compile hot functions' ),
$this -> txt ( 'Currently unused' ),
$this -> txt ( 'Use tracing JIT. Profile on the fly and compile traces for hot code segments' )
]
],
[
'flag' => $this -> txt ( 'Optimization level' ),
'value' => [
$this -> txt ( 'No JIT' ),
$this -> txt ( 'Minimal JIT (call standard VM handlers)' ),
$this -> txt ( 'Inline VM handlers' ),
$this -> txt ( 'Use type inference' ),
$this -> txt ( 'Use call graph' ),
$this -> txt ( 'Optimize whole script' )
]
]
];
2014-11-23 01:07:38 +00:00
$this -> data = $this -> compileState ();
2013-08-09 14:06:45 +00:00
}
2013-04-08 15:40:06 +00:00
2022-07-17 01:43:40 +00:00
/**
* @ return string
*/
public function txt () : string
{
$args = func_get_args ();
$text = array_shift ( $args );
if ((( $lang = $this -> getOption ( 'language_pack' )) !== null ) && ! empty ( $lang [ $text ])) {
$text = $lang [ $text ];
}
foreach ( $args as $i => $arg ) {
$text = str_replace ( '{' . $i . '}' , $arg , $text );
}
return $text ;
}
2020-09-18 23:30:17 +00:00
/**
* @ return $this
2022-01-09 23:48:23 +00:00
* @ throws Exception
2020-09-18 23:30:17 +00:00
*/
public function handle () : Service
2014-11-23 01:07:38 +00:00
{
2020-09-18 23:30:17 +00:00
$response = function ( $success ) {
if ( $this -> isJsonRequest ()) {
echo '{ "success": "' . ( $success ? 'yes' : 'no' ) . '" }' ;
2014-11-25 17:11:37 +00:00
} else {
2020-09-18 23:30:17 +00:00
header ( 'Location: ?' );
2014-11-25 17:11:37 +00:00
}
2014-11-23 01:07:38 +00:00
exit ;
2020-09-18 23:30:17 +00:00
};
if ( isset ( $_GET [ 'reset' ]) && $this -> getOption ( 'allow_reset' )) {
$response ( $this -> resetCache ());
2022-01-09 23:48:23 +00:00
} elseif ( isset ( $_GET [ 'invalidate' ]) && $this -> getOption ( 'allow_invalidate' )) {
2020-09-18 23:30:17 +00:00
$response ( $this -> resetCache ( $_GET [ 'invalidate' ]));
2022-01-09 23:48:23 +00:00
} elseif ( isset ( $_GET [ 'invalidate_searched' ]) && $this -> getOption ( 'allow_invalidate' )) {
2020-09-18 23:30:17 +00:00
$response ( $this -> resetSearched ( $_GET [ 'invalidate_searched' ]));
2022-01-09 23:48:23 +00:00
} elseif ( $this -> isJsonRequest () && $this -> getOption ( 'allow_realtime' )) {
2020-11-28 16:16:58 +00:00
echo json_encode ( $this -> getData ( $_GET [ 'section' ] ? ? null ));
2016-02-10 00:09:49 +00:00
exit ;
2014-11-23 01:07:38 +00:00
}
2020-09-18 23:30:17 +00:00
return $this ;
2014-11-23 01:07:38 +00:00
}
2013-04-08 15:40:06 +00:00
2020-09-18 23:30:17 +00:00
/**
* @ param string | null $name
* @ return array | mixed | null
*/
public function getOption ( ? string $name = null )
2014-11-25 13:20:33 +00:00
{
if ( $name === null ) {
return $this -> options ;
}
2021-06-27 11:47:40 +00:00
return $this -> options [ $name ] ? ? null ;
2014-11-25 13:20:33 +00:00
}
2020-09-18 23:30:17 +00:00
/**
* @ param string | null $section
* @ param string | null $property
* @ return array | mixed | null
*/
public function getData ( ? string $section = null , ? string $property = null )
2014-11-23 01:07:38 +00:00
{
if ( $section === null ) {
return $this -> data ;
}
$section = strtolower ( $section );
2014-11-26 21:42:23 +00:00
if ( isset ( $this -> data [ $section ])) {
if ( $property === null || ! isset ( $this -> data [ $section ][ $property ])) {
return $this -> data [ $section ];
}
return $this -> data [ $section ][ $property ];
}
return null ;
}
2020-09-18 23:30:17 +00:00
/**
* @ return bool
*/
public function canInvalidate () : bool
2014-11-26 21:42:23 +00:00
{
return ( $this -> getOption ( 'allow_invalidate' ) && function_exists ( 'opcache_invalidate' ));
2013-04-08 15:40:06 +00:00
}
2020-09-18 23:30:17 +00:00
/**
* @ param string | null $file
* @ return bool
2022-01-09 23:48:23 +00:00
* @ throws Exception
2020-09-18 23:30:17 +00:00
*/
public function resetCache ( ? string $file = null ) : bool
2014-11-23 01:07:38 +00:00
{
$success = false ;
if ( $file === null ) {
$success = opcache_reset ();
2022-01-09 23:48:23 +00:00
} elseif ( function_exists ( 'opcache_invalidate' )) {
2014-11-23 01:07:38 +00:00
$success = opcache_invalidate ( urldecode ( $file ), true );
}
if ( $success ) {
$this -> compileState ();
}
return $success ;
2013-04-08 15:40:06 +00:00
}
2020-09-18 23:30:17 +00:00
/**
* @ param string $search
* @ return bool
2022-01-09 23:48:23 +00:00
* @ throws Exception
2020-09-18 23:30:17 +00:00
*/
public function resetSearched ( string $search ) : bool
{
$found = $success = 0 ;
$search = urldecode ( $search );
foreach ( $this -> getData ( 'files' ) as $file ) {
if ( strpos ( $file [ 'full_path' ], $search ) !== false ) {
++ $found ;
$success += ( int ) opcache_invalidate ( $file [ 'full_path' ], true );
}
}
if ( $success ) {
$this -> compileState ();
}
return $found === $success ;
}
/**
* @ param mixed $size
* @ return string
*/
protected function size ( $size ) : string
2015-05-08 16:04:40 +00:00
{
$i = 0 ;
2020-09-18 23:30:17 +00:00
$val = [ 'b' , 'KB' , 'MB' , 'GB' , 'TB' , 'PB' , 'EB' , 'ZB' , 'YB' ];
2015-05-08 16:04:40 +00:00
while (( $size / 1024 ) > 1 ) {
$size /= 1024 ;
++ $i ;
}
2020-09-18 23:30:17 +00:00
return sprintf ( '%.' . $this -> getOption ( 'size_precision' ) . 'f%s%s' ,
2016-02-09 23:51:09 +00:00
$size , ( $this -> getOption ( 'size_space' ) ? ' ' : '' ), $val [ $i ]
2015-05-08 16:04:40 +00:00
);
}
2020-09-18 23:30:17 +00:00
/**
* @ return bool
*/
protected function isJsonRequest () : bool
{
return ! empty ( $_SERVER [ 'HTTP_ACCEPT' ])
&& stripos ( $_SERVER [ 'HTTP_ACCEPT' ], 'application/json' ) !== false ;
}
/**
* @ return array
2022-01-09 23:48:23 +00:00
* @ throws Exception
2020-09-18 23:30:17 +00:00
*/
protected function compileState () : array
2014-11-23 01:07:38 +00:00
{
2014-11-25 13:20:33 +00:00
$status = opcache_get_status ();
2014-11-23 01:07:38 +00:00
$config = opcache_get_configuration ();
2019-07-11 22:55:54 +00:00
$missingConfig = array_diff_key ( ini_get_all ( 'zend opcache' , false ), $config [ 'directives' ]);
if ( ! empty ( $missingConfig )) {
$config [ 'directives' ] = array_merge ( $config [ 'directives' ], $missingConfig );
}
2013-04-22 12:28:29 +00:00
2014-11-25 13:20:33 +00:00
$files = [];
2015-12-30 18:18:03 +00:00
if ( ! empty ( $status [ 'scripts' ]) && $this -> getOption ( 'allow_filelist' )) {
2022-01-09 23:48:23 +00:00
uasort ( $status [ 'scripts' ], static function ( $a , $b ) {
2020-12-16 11:10:48 +00:00
return $a [ 'hits' ] <=> $b [ 'hits' ];
2014-11-23 01:07:38 +00:00
});
2014-11-25 13:20:33 +00:00
foreach ( $status [ 'scripts' ] as & $file ) {
$file [ 'full_path' ] = str_replace ( '\\' , '/' , $file [ 'full_path' ]);
$file [ 'readable' ] = [
2020-09-18 23:30:17 +00:00
'hits' => number_format ( $file [ 'hits' ]),
2015-05-08 16:04:40 +00:00
'memory_consumption' => $this -> size ( $file [ 'memory_consumption' ])
2014-11-25 13:20:33 +00:00
];
2022-07-16 17:46:15 +00:00
$file [ 'last_used' ] = ( new DateTimeImmutable ( " @ { $file [ 'last_used_timestamp' ] } " ))
-> setTimezone ( $this -> tz )
-> format ( $this -> getOption ( 'datetime_format' ));
$file [ 'last_modified' ] = " " ;
if ( ! empty ( $file [ 'timestamp' ])) {
$file [ 'last_modified' ] = ( new DateTimeImmutable ( " @ { $file [ 'timestamp' ] } " ))
-> setTimezone ( $this -> tz )
-> format ( $this -> getOption ( 'datetime_format' ));
}
2014-11-25 13:20:33 +00:00
}
$files = array_values ( $status [ 'scripts' ]);
2014-11-23 01:07:38 +00:00
}
2020-02-22 17:26:43 +00:00
if ( $config [ 'directives' ][ 'opcache.file_cache_only' ] || ! empty ( $status [ 'file_cache_only' ])) {
2019-07-12 00:31:05 +00:00
$overview = false ;
} else {
$overview = array_merge (
$status [ 'memory_usage' ], $status [ 'opcache_statistics' ], [
'used_memory_percentage' => round ( 100 * (
2020-09-18 23:30:17 +00:00
( $status [ 'memory_usage' ][ 'used_memory' ] + $status [ 'memory_usage' ][ 'wasted_memory' ])
/ $config [ 'directives' ][ 'opcache.memory_consumption' ]
)),
2019-07-12 00:31:05 +00:00
'hit_rate_percentage' => round ( $status [ 'opcache_statistics' ][ 'opcache_hit_rate' ]),
2019-07-13 16:17:52 +00:00
'used_key_percentage' => round ( 100 * (
2020-09-18 23:30:17 +00:00
$status [ 'opcache_statistics' ][ 'num_cached_keys' ]
/ $status [ 'opcache_statistics' ][ 'max_cached_keys' ]
)),
2019-07-12 00:31:05 +00:00
'wasted_percentage' => round ( $status [ 'memory_usage' ][ 'current_wasted_percentage' ], 2 ),
'readable' => [
'total_memory' => $this -> size ( $config [ 'directives' ][ 'opcache.memory_consumption' ]),
'used_memory' => $this -> size ( $status [ 'memory_usage' ][ 'used_memory' ]),
'free_memory' => $this -> size ( $status [ 'memory_usage' ][ 'free_memory' ]),
'wasted_memory' => $this -> size ( $status [ 'memory_usage' ][ 'wasted_memory' ]),
'num_cached_scripts' => number_format ( $status [ 'opcache_statistics' ][ 'num_cached_scripts' ]),
'hits' => number_format ( $status [ 'opcache_statistics' ][ 'hits' ]),
'misses' => number_format ( $status [ 'opcache_statistics' ][ 'misses' ]),
'blacklist_miss' => number_format ( $status [ 'opcache_statistics' ][ 'blacklist_misses' ]),
'num_cached_keys' => number_format ( $status [ 'opcache_statistics' ][ 'num_cached_keys' ]),
'max_cached_keys' => number_format ( $status [ 'opcache_statistics' ][ 'max_cached_keys' ]),
'interned' => null ,
2022-01-09 23:48:23 +00:00
'start_time' => ( new DateTimeImmutable ( " @ { $status [ 'opcache_statistics' ][ 'start_time' ] } " ))
2022-07-16 17:46:15 +00:00
-> setTimezone ( $this -> tz )
-> format ( $this -> getOption ( 'datetime_format' )),
2022-07-16 15:19:55 +00:00
'last_restart_time' => ( $status [ 'opcache_statistics' ][ 'last_restart_time' ] === 0
2022-07-18 20:21:27 +00:00
? $this -> txt ( 'never' )
2022-01-09 23:48:23 +00:00
: ( new DateTimeImmutable ( " @ { $status [ 'opcache_statistics' ][ 'last_restart_time' ] } " ))
2022-07-16 17:46:15 +00:00
-> setTimezone ( $this -> tz )
-> format ( $this -> getOption ( 'datetime_format' ))
2014-11-23 01:07:38 +00:00
)
2019-07-12 00:31:05 +00:00
]
2014-11-23 01:07:38 +00:00
]
2019-07-12 00:31:05 +00:00
);
}
2018-02-08 20:23:48 +00:00
2020-09-18 23:30:17 +00:00
$preload = [];
if ( ! empty ( $status [ 'preload_statistics' ][ 'scripts' ]) && $this -> getOption ( 'allow_filelist' )) {
$preload = $status [ 'preload_statistics' ][ 'scripts' ];
sort ( $preload , SORT_STRING );
if ( $overview ) {
$overview [ 'preload_memory' ] = $status [ 'preload_statistics' ][ 'memory_consumption' ];
$overview [ 'readable' ][ 'preload_memory' ] = $this -> size ( $status [ 'preload_statistics' ][ 'memory_consumption' ]);
}
}
2017-07-31 13:25:14 +00:00
if ( ! empty ( $status [ 'interned_strings_usage' ])) {
$overview [ 'readable' ][ 'interned' ] = [
'buffer_size' => $this -> size ( $status [ 'interned_strings_usage' ][ 'buffer_size' ]),
'strings_used_memory' => $this -> size ( $status [ 'interned_strings_usage' ][ 'used_memory' ]),
'strings_free_memory' => $this -> size ( $status [ 'interned_strings_usage' ][ 'free_memory' ]),
'number_of_strings' => number_format ( $status [ 'interned_strings_usage' ][ 'number_of_strings' ])
];
}
2013-04-08 15:40:06 +00:00
2023-10-25 19:03:44 +00:00
if ( $overview && ! empty ( $status [ 'jit' ][ 'enabled' ])) {
2021-06-27 11:47:40 +00:00
$overview [ 'jit_buffer_used_percentage' ] = ( $status [ 'jit' ][ 'buffer_size' ]
? round ( 100 * (( $status [ 'jit' ][ 'buffer_size' ] - $status [ 'jit' ][ 'buffer_free' ]) / $status [ 'jit' ][ 'buffer_size' ]))
: 0
);
$overview [ 'readable' ] = array_merge ( $overview [ 'readable' ], [
'jit_buffer_size' => $this -> size ( $status [ 'jit' ][ 'buffer_size' ]),
'jit_buffer_free' => $this -> size ( $status [ 'jit' ][ 'buffer_free' ])
]);
} else {
$this -> options [ 'highlight' ][ 'jit' ] = false ;
}
2014-11-24 00:09:48 +00:00
$directives = [];
2014-11-23 01:07:38 +00:00
ksort ( $config [ 'directives' ]);
2014-11-24 00:09:48 +00:00
foreach ( $config [ 'directives' ] as $k => $v ) {
2021-06-27 11:47:40 +00:00
if ( in_array ( $k , [ 'opcache.max_file_size' , 'opcache.memory_consumption' , 'opcache.jit_buffer_size' ]) && $v ) {
2019-07-11 18:04:14 +00:00
$v = $this -> size ( $v ) . " ( { $v } ) " ;
2021-06-27 11:47:40 +00:00
} elseif ( $k === 'opcache.optimization_level' ) {
2020-02-22 19:51:12 +00:00
$levels = [];
foreach ( $this -> optimizationLevels as $level => $info ) {
if ( $level & $v ) {
2020-12-16 11:10:48 +00:00
$levels [] = " { $info } [ { $level } ] " ;
2020-02-22 19:51:12 +00:00
}
}
2020-09-18 23:30:17 +00:00
$v = $levels ? : 'none' ;
2021-06-27 11:47:40 +00:00
} elseif ( $k === 'opcache.jit' ) {
2022-01-09 23:48:23 +00:00
if ( $v === '1' ) {
$v = 'on' ;
}
if ( isset ( $this -> jitModeMapping [ $v ]) || is_numeric ( $v )) {
2021-06-27 11:47:40 +00:00
$levels = [];
2022-01-09 23:48:23 +00:00
foreach ( str_split (( string )( $this -> jitModeMapping [ $v ] ? ? $v )) as $type => $level ) {
2021-06-27 11:47:40 +00:00
$levels [] = " { $level } : { $this -> jitModes [ $type ][ 'value' ][ $level ] } ( { $this -> jitModes [ $type ][ 'flag' ] } ) " ;
}
2022-01-09 23:48:23 +00:00
$v = [ $v , $levels ];
} elseif ( empty ( $v ) || strtolower ( $v ) === 'off' ) {
2021-06-27 11:47:40 +00:00
$v = 'Off' ;
}
2019-07-11 18:04:14 +00:00
}
$directives [] = [
'k' => $k ,
'v' => $v
];
2014-11-24 00:09:48 +00:00
}
2014-11-23 01:07:38 +00:00
$version = array_merge (
$config [ 'version' ],
[
2022-07-16 17:46:15 +00:00
'php' => PHP_VERSION ,
2020-09-18 23:30:17 +00:00
'server' => $_SERVER [ 'SERVER_SOFTWARE' ] ? : '' ,
'host' => ( function_exists ( 'gethostname' )
2014-11-23 01:07:38 +00:00
? gethostname ()
: ( php_uname ( 'n' )
? : ( empty ( $_SERVER [ 'SERVER_NAME' ])
? $_SERVER [ 'HOST_NAME' ]
: $_SERVER [ 'SERVER_NAME' ]
)
)
2020-09-18 23:30:17 +00:00
),
'gui' => self :: VERSION
2014-11-23 01:07:38 +00:00
]
);
return [
2020-09-18 23:30:17 +00:00
'version' => $version ,
'overview' => $overview ,
'files' => $files ,
'preload' => $preload ,
2014-11-24 00:09:48 +00:00
'directives' => $directives ,
2020-09-18 23:30:17 +00:00
'blacklist' => $config [ 'blacklist' ],
2023-10-25 19:03:44 +00:00
'functions' => get_extension_funcs ( 'Zend OPcache' ),
'jitState' => $this -> jitState ( $status , $config [ 'directives' ]),
2014-11-23 01:07:38 +00:00
];
}
2023-10-25 19:03:44 +00:00
protected function jitState ( array $status , array $directives ) : array
{
$state = [
'enabled' => $status [ 'jit' ][ 'enabled' ],
'reason' => ''
];
if ( ! $state [ 'enabled' ]) {
if ( empty ( $directives [ 'opcache.jit' ]) || $directives [ 'opcache.jit' ] === 'disable' ) {
$state [ 'reason' ] = $this -> txt ( 'disabled due to <i>opcache.jit</i> setting' );
} elseif ( ! $directives [ 'opcache.jit_buffer_size' ]) {
$state [ 'reason' ] = $this -> txt ( 'the <i>opcache.jit_buffer_size</i> must be set to fully enable JIT' );
} else {
$state [ 'reason' ] = $this -> txt ( 'incompatible with extensions that override <i>zend_execute_ex()</i>, such as <i>xdebug</i>' );
}
}
return $state ;
}
2013-04-08 15:40:06 +00:00
}
2020-09-18 23:30:17 +00:00
$opcache = ( new Service ( $options )) -> handle ();
2013-04-09 15:44:50 +00:00
2013-04-08 15:40:06 +00:00
?>
2017-02-10 15:22:49 +00:00
<! DOCTYPE html >
2022-07-16 21:52:27 +00:00
< html lang = " en " >
2013-04-08 15:40:06 +00:00
< head >
2017-02-10 15:22:49 +00:00
< meta charset = " UTF-8 " >
2013-04-08 15:40:06 +00:00
< meta name = " viewport " content = " width=device-width,initial-scale=1.0 " >
2023-04-23 00:35:00 +00:00
< meta name = " robots " content = " noindex, nofollow " />
2020-09-18 23:30:17 +00:00
< title > OPcache statistics on < ? = $opcache -> getData ( 'version' , 'host' ); ?> </title>
2022-11-02 13:25:22 +00:00
< script src = " //cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js " ></ script >
< script src = " //cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js " ></ script >
2023-04-23 00:35:00 +00:00
< script src = " //cdnjs.cloudflare.com/ajax/libs/axios/1.3.6/axios.min.js " ></ script >
2022-07-16 21:52:27 +00:00
< style >
2023-08-15 16:57:35 +00:00
: 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
2013-04-08 15:40:06 +00:00
</ style >
</ head >
2020-04-07 21:43:20 +00:00
< body style = " padding: 0; margin: 0; " >
2020-09-18 23:30:17 +00:00
< div class = " opcache-gui " id = " interface " />
< script type = " text/javascript " >
2024-06-23 13:01:33 +00:00
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 ); }
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 ; }
function _toPropertyKey ( t ) { var i = _toPrimitive ( t , " string " ); return " symbol " == typeof i ? i : i + " " ; }
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 ); }
2020-09-18 23:30:17 +00:00
class Interface extends React . Component {
constructor ( props ) {
super ( props );
_defineProperty ( this , " startTimer " , () => {
this . setState ({
realtime : true
});
this . polling = setInterval (() => {
this . setState ({
fetching : true ,
resetting : false
2016-02-23 15:46:34 +00:00
});
2020-12-16 11:10:48 +00:00
axios . get ( window . location . pathname , {
2020-09-18 23:30:17 +00:00
time : Date . now ()
}) . then ( response => {
this . setState ({
opstate : response . data
});
});
}, this . props . realtimeRefresh * 1000 );
});
_defineProperty ( this , " stopTimer " , () => {
this . setState ({
realtime : false ,
resetting : false
});
clearInterval ( this . polling );
});
_defineProperty ( this , " realtimeHandler " , () => {
const realtime = ! this . state . realtime ;
if ( ! realtime ) {
this . stopTimer ();
this . removeCookie ();
} else {
this . startTimer ();
this . setCookie ();
}
});
_defineProperty ( this , " resetHandler " , () => {
if ( this . state . realtime ) {
this . setState ({
resetting : true
});
2020-12-16 11:10:48 +00:00
axios . get ( window . location . pathname , {
2020-09-18 23:30:17 +00:00
params : {
reset : 1
}
}) . then ( response => {
console . log ( 'success: ' , response . data );
});
} else {
window . location . href = '?reset=1' ;
}
});
_defineProperty ( this , " setCookie " , () => {
let d = new Date ();
d . setTime ( d . getTime () + this . props . cookie . ttl * 86400000 );
document . cookie = `${this.props.cookie.name}=true;expires=${d.toUTCString()};path=/${this.isSecure ? ';secure' : ''}` ;
});
_defineProperty ( this , " removeCookie " , () => {
document . cookie = `${this.props.cookie.name}=;expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/${this.isSecure ? ';secure' : ''}` ;
});
_defineProperty ( this , " getCookie " , () => {
const v = document . cookie . match ( `(^|;) ?${this.props.cookie.name}=([^;]*)(;|$)` );
return v ? !! v [ 2 ] : false ;
});
2022-07-17 01:43:40 +00:00
_defineProperty ( this , " txt " , ( text , ... args ) => {
if ( this . props . language !== null && this . props . language . hasOwnProperty ( text ) && this . props . language [ text ]) {
text = this . props . language [ text ];
}
args . forEach (( arg , i ) => {
text = text . replaceAll ( `{${i}}` , arg );
});
return text ;
});
2020-09-18 23:30:17 +00:00
this . state = {
realtime : this . getCookie (),
resetting : false ,
opstate : props . opstate
2016-02-23 15:46:34 +00:00
};
2020-09-18 23:30:17 +00:00
this . polling = false ;
this . isSecure = window . location . protocol === 'https:' ;
if ( this . getCookie ()) {
this . startTimer ();
2015-05-08 16:04:40 +00:00
}
2020-09-18 23:30:17 +00:00
}
render () {
const {
opstate ,
realtimeRefresh ,
... otherProps
} = this . props ;
return /*#__PURE__*/ React . createElement ( React . Fragment , null , /*#__PURE__*/ React . createElement ( " header " , null , /*#__PURE__*/ React . createElement ( MainNavigation , _extends ({}, otherProps , {
opstate : this . state . opstate ,
realtime : this . state . realtime ,
resetting : this . state . resetting ,
realtimeHandler : this . realtimeHandler ,
2022-07-17 01:43:40 +00:00
resetHandler : this . resetHandler ,
txt : this . txt
2020-09-18 23:30:17 +00:00
}))), /*#__PURE__*/ React . createElement ( Footer , {
2024-06-23 13:01:33 +00:00
version : this . props . opstate . version . gui ,
txt : this . txt
2020-09-18 23:30:17 +00:00
}));
}
}
function MainNavigation ( props ) {
return /*#__PURE__*/ React . createElement ( " nav " , {
className : " main-nav "
}, /*#__PURE__*/ React . createElement ( Tabs , null , /*#__PURE__*/ React . createElement ( " div " , {
2022-07-17 01:43:40 +00:00
label : props . txt ( " Overview " ),
2020-09-18 23:30:17 +00:00
tabId : " overview " ,
tabIndex : 1
}, /*#__PURE__*/ React . createElement ( OverviewCounts , {
overview : props . opstate . overview ,
highlight : props . highlight ,
2022-07-17 01:43:40 +00:00
useCharts : props . useCharts ,
txt : props . txt
2020-09-18 23:30:17 +00:00
}), /*#__PURE__*/ React . createElement ( " div " , {
id : " info " ,
className : " tab-content-overview-info "
}, /*#__PURE__*/ React . createElement ( GeneralInfo , {
start : props . opstate . overview && props . opstate . overview . readable . start_time || null ,
reset : props . opstate . overview && props . opstate . overview . readable . last_restart_time || null ,
2022-07-17 01:43:40 +00:00
version : props . opstate . version ,
2023-10-25 19:03:44 +00:00
jit : props . opstate . jitState ,
2022-07-17 01:43:40 +00:00
txt : props . txt
2020-09-18 23:30:17 +00:00
}), /*#__PURE__*/ React . createElement ( Directives , {
2022-07-17 01:43:40 +00:00
directives : props . opstate . directives ,
txt : props . txt
2020-09-18 23:30:17 +00:00
}), /*#__PURE__*/ React . createElement ( Functions , {
2022-07-17 01:43:40 +00:00
functions : props . opstate . functions ,
txt : props . txt
2020-09-18 23:30:17 +00:00
}))), props . allow . filelist && /*#__PURE__*/ React . createElement ( " div " , {
2022-07-17 01:43:40 +00:00
label : props . txt ( " Cached " ),
2020-09-18 23:30:17 +00:00
tabId : " cached " ,
tabIndex : 2
}, /*#__PURE__*/ React . createElement ( CachedFiles , {
perPageLimit : props . perPageLimit ,
allFiles : props . opstate . files ,
searchTerm : props . searchTerm ,
debounceRate : props . debounceRate ,
allow : {
fileList : props . allow . filelist ,
invalidate : props . allow . invalidate
},
2022-07-17 01:43:40 +00:00
realtime : props . realtime ,
txt : props . txt
2020-09-18 23:30:17 +00:00
})), props . allow . filelist && props . opstate . blacklist . length && /*#__PURE__*/ React . createElement ( " div " , {
2022-07-17 01:43:40 +00:00
label : props . txt ( " Ignored " ),
2020-09-18 23:30:17 +00:00
tabId : " ignored " ,
tabIndex : 3
}, /*#__PURE__*/ React . createElement ( IgnoredFiles , {
perPageLimit : props . perPageLimit ,
allFiles : props . opstate . blacklist ,
allow : {
fileList : props . allow . filelist
2022-07-17 01:43:40 +00:00
},
txt : props . txt
2020-09-18 23:30:17 +00:00
})), props . allow . filelist && props . opstate . preload . length && /*#__PURE__*/ React . createElement ( " div " , {
2022-07-17 01:43:40 +00:00
label : props . txt ( " Preloaded " ),
2020-09-18 23:30:17 +00:00
tabId : " preloaded " ,
tabIndex : 4
}, /*#__PURE__*/ React . createElement ( PreloadedFiles , {
perPageLimit : props . perPageLimit ,
allFiles : props . opstate . preload ,
allow : {
fileList : props . allow . filelist
2022-07-17 01:43:40 +00:00
},
txt : props . txt
2020-09-18 23:30:17 +00:00
})), props . allow . reset && /*#__PURE__*/ React . createElement ( " div " , {
2022-07-17 01:43:40 +00:00
label : props . txt ( " Reset cache " ),
2020-09-18 23:30:17 +00:00
tabId : " resetCache " ,
className : `nav-tab-link-reset${props.resetting ? ' is-resetting pulse' : ''}` ,
handler : props . resetHandler ,
tabIndex : 5
}), props . allow . realtime && /*#__PURE__*/ React . createElement ( " div " , {
2022-07-17 01:43:40 +00:00
label : props . txt ( `${props.realtime ? 'Disable' : 'Enable'} real-time update` ),
2020-09-18 23:30:17 +00:00
tabId : " toggleRealtime " ,
className : `nav-tab-link-realtime${props.realtime ? ' live-update pulse' : ''}` ,
handler : props . realtimeHandler ,
tabIndex : 6
})));
}
class Tabs extends React . Component {
constructor ( props ) {
super ( props );
_defineProperty ( this , " onClickTabItem " , tab => {
this . setState ({
activeTab : tab
});
});
this . state = {
activeTab : this . props . children [ 0 ] . props . label
};
}
render () {
const {
onClickTabItem ,
state : {
activeTab
}
} = this ;
const children = this . props . children . filter ( Boolean );
return /*#__PURE__*/ React . createElement ( React . Fragment , null , /*#__PURE__*/ React . createElement ( " ul " , {
className : " nav-tab-list "
}, children . map ( child => {
const {
tabId ,
label ,
className ,
handler ,
tabIndex
} = child . props ;
return /*#__PURE__*/ React . createElement ( Tab , {
activeTab : activeTab ,
key : tabId ,
label : label ,
onClick : handler || onClickTabItem ,
className : className ,
tabIndex : tabIndex ,
tabId : tabId
});
})), /*#__PURE__*/ React . createElement ( " div " , {
className : " tab-content "
}, children . map ( child => /*#__PURE__*/ React . createElement ( " div " , {
key : child . props . label ,
style : {
display : child . props . label === activeTab ? 'block' : 'none'
},
id : `${child.props.tabId}-content`
}, child . props . children ))));
}
}
class Tab extends React . Component {
constructor ( ... args ) {
super ( ... args );
_defineProperty ( this , " onClick " , () => {
const {
label ,
onClick
} = this . props ;
onClick ( label );
});
}
render () {
const {
onClick ,
props : {
activeTab ,
label ,
tabIndex ,
tabId
}
} = this ;
let className = 'nav-tab' ;
if ( this . props . className ) {
className += ` ${this.props.className}` ;
}
if ( activeTab === label ) {
className += ' active' ;
}
return /*#__PURE__*/ React . createElement ( " li " , {
className : className ,
onClick : onClick ,
tabIndex : tabIndex ,
role : " tab " ,
" aria-controls " : `${tabId}-content`
}, label );
}
}
function OverviewCounts ( props ) {
if ( props . overview === false ) {
return /*#__PURE__*/ React . createElement ( " p " , {
class : " file-cache-only "
2022-07-17 01:43:40 +00:00
}, 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>.` ));
2020-09-18 23:30:17 +00:00
}
const graphList = [{
id : 'memoryUsageCanvas' ,
2022-07-17 01:43:40 +00:00
title : props . txt ( 'memory' ),
2020-09-18 23:30:17 +00:00
show : props . highlight . memory ,
value : props . overview . used_memory_percentage
}, {
id : 'hitRateCanvas' ,
2022-07-17 01:43:40 +00:00
title : props . txt ( 'hit rate' ),
2020-09-18 23:30:17 +00:00
show : props . highlight . hits ,
value : props . overview . hit_rate_percentage
}, {
id : 'keyUsageCanvas' ,
2022-07-17 01:43:40 +00:00
title : props . txt ( 'keys' ),
2020-09-18 23:30:17 +00:00
show : props . highlight . keys ,
value : props . overview . used_key_percentage
2021-06-27 11:47:40 +00:00
}, {
id : 'jitUsageCanvas' ,
2022-07-17 01:43:40 +00:00
title : props . txt ( 'jit buffer' ),
2021-06-27 11:47:40 +00:00
show : props . highlight . jit ,
value : props . overview . jit_buffer_used_percentage
2020-09-18 23:30:17 +00:00
}];
return /*#__PURE__*/ React . createElement ( " div " , {
id : " counts " ,
className : " tab-content-overview-counts "
}, graphList . map ( graph => {
if ( ! graph . show ) {
return null ;
}
return /*#__PURE__*/ React . createElement ( " div " , {
className : " widget-panel " ,
key : graph . id
}, /*#__PURE__*/ React . createElement ( " h3 " , {
className : " widget-header "
}, graph . title ), /*#__PURE__*/ React . createElement ( UsageGraph , {
charts : props . useCharts ,
value : graph . value ,
gaugeId : graph . id
}));
}), /*#__PURE__*/ React . createElement ( MemoryUsagePanel , {
total : props . overview . readable . total_memory ,
used : props . overview . readable . used_memory ,
free : props . overview . readable . free_memory ,
wasted : props . overview . readable . wasted_memory ,
preload : props . overview . readable . preload_memory || null ,
2021-06-27 11:47:40 +00:00
wastedPercent : props . overview . wasted_percentage ,
jitBuffer : props . overview . readable . jit_buffer_size || null ,
jitBufferFree : props . overview . readable . jit_buffer_free || null ,
2022-07-17 01:43:40 +00:00
jitBufferFreePercentage : props . overview . jit_buffer_used_percentage || null ,
txt : props . txt
2020-09-18 23:30:17 +00:00
}), /*#__PURE__*/ React . createElement ( StatisticsPanel , {
num_cached_scripts : props . overview . readable . num_cached_scripts ,
hits : props . overview . readable . hits ,
misses : props . overview . readable . misses ,
blacklist_miss : props . overview . readable . blacklist_miss ,
num_cached_keys : props . overview . readable . num_cached_keys ,
2022-07-17 01:43:40 +00:00
max_cached_keys : props . overview . readable . max_cached_keys ,
txt : props . txt
2020-09-18 23:30:17 +00:00
}), props . overview . readable . interned && /*#__PURE__*/ React . createElement ( InternedStringsPanel , {
buffer_size : props . overview . readable . interned . buffer_size ,
strings_used_memory : props . overview . readable . interned . strings_used_memory ,
strings_free_memory : props . overview . readable . interned . strings_free_memory ,
2022-07-17 01:43:40 +00:00
number_of_strings : props . overview . readable . interned . number_of_strings ,
txt : props . txt
2020-09-18 23:30:17 +00:00
}));
}
function GeneralInfo ( props ) {
return /*#__PURE__*/ React . createElement ( " table " , {
className : " tables general-info-table "
}, /*#__PURE__*/ React . createElement ( " thead " , null , /*#__PURE__*/ React . createElement ( " tr " , null , /*#__PURE__*/ React . createElement ( " th " , {
colSpan : " 2 "
2023-10-25 19:03:44 +00:00
}, 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 " , {
dangerouslySetInnerHTML : {
__html : ` (${props.jit.reason})`
}
})))));
2020-09-18 23:30:17 +00:00
}
function Directives ( props ) {
2020-12-16 11:10:48 +00:00
let directiveList = directive => {
return /*#__PURE__*/ React . createElement ( " ul " , {
className : " directive-list "
}, directive . v . map (( item , key ) => {
2022-01-09 23:48:23 +00:00
return Array . isArray ( item ) ? /*#__PURE__*/ React . createElement ( " li " , {
key : " sublist_ " + key
}, directiveList ({
v : item
})) : /*#__PURE__*/ React . createElement ( " li " , {
2020-12-16 11:10:48 +00:00
key : key
}, item );
}));
};
2020-09-18 23:30:17 +00:00
let directiveNodes = props . directives . map ( function ( directive ) {
let map = {
'opcache.' : '' ,
'_' : ' '
};
let dShow = directive . k . replace ( / opcache\ .| _ / gi , function ( matched ) {
return map [ matched ];
});
let vShow ;
if ( directive . v === true || directive . v === false ) {
2022-07-17 01:43:40 +00:00
vShow = React . createElement ( 'i' , {}, props . txt ( directive . v . toString ()));
2020-09-18 23:30:17 +00:00
} else if ( directive . v === '' ) {
2022-07-17 01:43:40 +00:00
vShow = React . createElement ( 'i' , {}, props . txt ( 'no value' ));
2020-09-18 23:30:17 +00:00
} else {
if ( Array . isArray ( directive . v )) {
2020-12-16 11:10:48 +00:00
vShow = directiveList ( directive );
2020-09-18 23:30:17 +00:00
} else {
vShow = directive . v ;
}
}
2023-10-19 19:31:59 +00:00
let directiveLink = name => {
if ( name === 'opcache.jit_max_recursive_returns' ) {
return 'opcache.jit-max-recursive-return' ;
}
return [ 'opcache.file_update_protection' , 'opcache.huge_code_pages' , 'opcache.lockfile_path' , 'opcache.opt_debug_level' ] . includes ( name ) ? name : name . replace ( / _ / g , '-' );
};
2020-09-18 23:30:17 +00:00
return /*#__PURE__*/ React . createElement ( " tr " , {
key : directive . k
}, /*#__PURE__*/ React . createElement ( " td " , {
2022-07-17 01:43:40 +00:00
title : props . txt ( 'View {0} manual entry' , directive . k )
2020-09-18 23:30:17 +00:00
}, /*#__PURE__*/ React . createElement ( " a " , {
2023-10-19 19:31:59 +00:00
href : 'https://php.net/manual/en/opcache.configuration.php#ini.' + directiveLink ( directive . k ),
2020-09-18 23:30:17 +00:00
target : " _blank "
}, dShow )), /*#__PURE__*/ React . createElement ( " td " , null , vShow ));
});
return /*#__PURE__*/ React . createElement ( " table " , {
className : " tables directives-table "
}, /*#__PURE__*/ React . createElement ( " thead " , null , /*#__PURE__*/ React . createElement ( " tr " , null , /*#__PURE__*/ React . createElement ( " th " , {
colSpan : " 2 "
2022-07-17 01:43:40 +00:00
}, props . txt ( 'Directives' )))), /*#__PURE__*/ React . createElement ( " tbody " , null , directiveNodes ));
2020-09-18 23:30:17 +00:00
}
function Functions ( props ) {
return /*#__PURE__*/ React . createElement ( " div " , {
id : " functions "
}, /*#__PURE__*/ React . createElement ( " table " , {
className : " tables "
2022-07-17 01:43:40 +00:00
}, /*#__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 " , {
2020-09-18 23:30:17 +00:00
key : f
}, /*#__PURE__*/ React . createElement ( " td " , null , /*#__PURE__*/ React . createElement ( " a " , {
2021-06-27 11:47:40 +00:00
href : " https://php.net/ " + f ,
2022-07-17 01:43:40 +00:00
title : props . txt ( 'View manual page' ),
2020-09-18 23:30:17 +00:00
target : " _blank "
}, f )))))));
}
function UsageGraph ( props ) {
const percentage = Math . round ( 3.6 * props . value / 360 * 100 );
return props . charts ? /*#__PURE__*/ React . createElement ( ReactCustomizableProgressbar , {
progress : percentage ,
radius : 100 ,
strokeWidth : 30 ,
trackStrokeWidth : 30 ,
strokeColor : getComputedStyle ( document . documentElement ) . getPropertyValue ( '--opcache-gui-graph-track-fill-color' ) || " #6CA6EF " ,
trackStrokeColor : getComputedStyle ( document . documentElement ) . getPropertyValue ( '--opcache-gui-graph-track-background-color' ) || " #CCC " ,
gaugeId : props . gaugeId
}) : /*#__PURE__*/ React . createElement ( " p " , {
className : " widget-value "
}, /*#__PURE__*/ React . createElement ( " span " , {
className : " large "
}, percentage ), /*#__PURE__*/ React . createElement ( " span " , null , " % " ));
}
2024-06-23 13:01:33 +00:00
2020-09-18 23:30:17 +00:00
/**
* This component is from < https :// github . com / martyan / react - customizable - progressbar />
* MIT License ( MIT ), Copyright ( c ) 2019 Martin Juzl
*/
class ReactCustomizableProgressbar extends React . Component {
constructor ( props ) {
super ( props );
_defineProperty ( this , " initAnimation " , () => {
this . setState ({
animationInited : true
});
});
_defineProperty ( this , " getProgress " , () => {
const {
initialAnimation ,
progress
} = this . props ;
const {
animationInited
} = this . state ;
return initialAnimation && ! animationInited ? 0 : progress ;
});
_defineProperty ( this , " getStrokeDashoffset " , strokeLength => {
const {
counterClockwise ,
inverse ,
steps
} = this . props ;
const progress = this . getProgress ();
const progressLength = strokeLength / steps * ( steps - progress );
if ( inverse ) return counterClockwise ? 0 : progressLength - strokeLength ;
return counterClockwise ? - 1 * progressLength : progressLength ;
});
_defineProperty ( this , " getStrokeDashArray " , ( strokeLength , circumference ) => {
const {
counterClockwise ,
inverse ,
steps
} = this . props ;
const progress = this . getProgress ();
const progressLength = strokeLength / steps * ( steps - progress );
if ( inverse ) return `${progressLength}, ${circumference}` ;
return counterClockwise ? `${strokeLength * (progress / 100)}, ${circumference}` : `${strokeLength}, ${circumference}` ;
});
_defineProperty ( this , " getTrackStrokeDashArray " , ( strokeLength , circumference ) => {
const {
initialAnimation
} = this . props ;
const {
animationInited
} = this . state ;
if ( initialAnimation && ! animationInited ) return `0, ${circumference}` ;
return `${strokeLength}, ${circumference}` ;
});
_defineProperty ( this , " getExtendedWidth " , () => {
const {
strokeWidth ,
pointerRadius ,
pointerStrokeWidth ,
trackStrokeWidth
} = this . props ;
const pointerWidth = pointerRadius + pointerStrokeWidth ;
if ( pointerWidth > strokeWidth && pointerWidth > trackStrokeWidth ) return pointerWidth * 2 ; else if ( strokeWidth > trackStrokeWidth ) return strokeWidth * 2 ; else return trackStrokeWidth * 2 ;
});
_defineProperty ( this , " getPointerAngle " , () => {
const {
cut ,
counterClockwise ,
steps
} = this . props ;
const progress = this . getProgress ();
return counterClockwise ? ( 360 - cut ) / steps * ( steps - progress ) : ( 360 - cut ) / steps * progress ;
});
this . state = {
animationInited : false
};
}
componentDidMount () {
const {
initialAnimation ,
initialAnimationDelay
} = this . props ;
if ( initialAnimation ) setTimeout ( this . initAnimation , initialAnimationDelay );
}
render () {
const {
radius ,
pointerRadius ,
pointerStrokeWidth ,
pointerFillColor ,
pointerStrokeColor ,
fillColor ,
trackStrokeWidth ,
trackStrokeColor ,
trackStrokeLinecap ,
strokeColor ,
strokeWidth ,
strokeLinecap ,
rotate ,
cut ,
trackTransition ,
transition ,
progress
} = this . props ;
const d = 2 * radius ;
const width = d + this . getExtendedWidth ();
const circumference = 2 * Math . PI * radius ;
const strokeLength = circumference / 360 * ( 360 - cut );
return /*#__PURE__*/ React . createElement ( " figure " , {
className : `graph-widget` ,
style : {
width : `${width || 250}px`
},
" data-value " : progress ,
id : this . props . guageId
}, /*#__PURE__*/ React . createElement ( " svg " , {
width : width ,
height : width ,
viewBox : `0 0 ${width} ${width}` ,
style : {
transform : `rotate(${rotate}deg)`
}
}, trackStrokeWidth > 0 && /*#__PURE__*/ React . createElement ( " circle " , {
cx : width / 2 ,
cy : width / 2 ,
r : radius ,
fill : " none " ,
stroke : trackStrokeColor ,
strokeWidth : trackStrokeWidth ,
strokeDasharray : this . getTrackStrokeDashArray ( strokeLength , circumference ),
strokeLinecap : trackStrokeLinecap ,
style : {
transition : trackTransition
}
}), strokeWidth > 0 && /*#__PURE__*/ React . createElement ( " circle " , {
cx : width / 2 ,
cy : width / 2 ,
r : radius ,
fill : fillColor ,
stroke : strokeColor ,
strokeWidth : strokeWidth ,
strokeDasharray : this . getStrokeDashArray ( strokeLength , circumference ),
strokeDashoffset : this . getStrokeDashoffset ( strokeLength ),
strokeLinecap : strokeLinecap ,
style : {
transition
}
}), pointerRadius > 0 && /*#__PURE__*/ React . createElement ( " circle " , {
cx : d ,
cy : " 50% " ,
r : pointerRadius ,
fill : pointerFillColor ,
stroke : pointerStrokeColor ,
strokeWidth : pointerStrokeWidth ,
style : {
transformOrigin : '50% 50%' ,
transform : `rotate(${this.getPointerAngle()}deg) translate(${this.getExtendedWidth() / 2}px)` ,
transition
}
})), /*#__PURE__*/ React . createElement ( " figcaption " , {
className : `widget-value`
}, progress , " % " ));
}
}
ReactCustomizableProgressbar . defaultProps = {
radius : 100 ,
progress : 0 ,
steps : 100 ,
cut : 0 ,
rotate : - 90 ,
strokeWidth : 20 ,
strokeColor : 'indianred' ,
fillColor : 'none' ,
strokeLinecap : 'round' ,
transition : '.3s ease' ,
pointerRadius : 0 ,
pointerStrokeWidth : 20 ,
pointerStrokeColor : 'indianred' ,
pointerFillColor : 'white' ,
trackStrokeColor : '#e6e6e6' ,
trackStrokeWidth : 20 ,
trackStrokeLinecap : 'round' ,
trackTransition : '.3s ease' ,
counterClockwise : false ,
inverse : false ,
initialAnimation : false ,
initialAnimationDelay : 0
};
function MemoryUsagePanel ( props ) {
return /*#__PURE__*/ React . createElement ( " div " , {
className : " widget-panel "
}, /*#__PURE__*/ React . createElement ( " h3 " , {
className : " widget-header "
}, " memory usage " ), /*#__PURE__*/ React . createElement ( " div " , {
className : " widget-value widget-info "
2022-07-17 01:43:40 +00:00
}, /*#__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 , " %) " )));
2020-09-18 23:30:17 +00:00
}
function StatisticsPanel ( props ) {
return /*#__PURE__*/ React . createElement ( " div " , {
className : " widget-panel "
}, /*#__PURE__*/ React . createElement ( " h3 " , {
className : " widget-header "
2022-07-17 01:43:40 +00:00
}, props . txt ( 'opcache statistics' )), /*#__PURE__*/ React . createElement ( " div " , {
2020-09-18 23:30:17 +00:00
className : " widget-value widget-info "
2022-07-17 01:43:40 +00:00
}, /*#__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 )));
2020-09-18 23:30:17 +00:00
}
function InternedStringsPanel ( props ) {
return /*#__PURE__*/ React . createElement ( " div " , {
className : " widget-panel "
}, /*#__PURE__*/ React . createElement ( " h3 " , {
className : " widget-header "
2022-07-17 01:43:40 +00:00
}, props . txt ( 'interned strings usage' )), /*#__PURE__*/ React . createElement ( " div " , {
2020-09-18 23:30:17 +00:00
className : " widget-value widget-info "
2022-07-17 01:43:40 +00:00
}, /*#__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 )));
2020-09-18 23:30:17 +00:00
}
class CachedFiles extends React . Component {
constructor ( props ) {
super ( props );
_defineProperty ( this , " setSearchTerm " , debounce ( searchTerm => {
this . setState ({
searchTerm ,
refreshPagination : ! this . state . refreshPagination
});
}, this . props . debounceRate ));
_defineProperty ( this , " onPageChanged " , currentPage => {
this . setState ({
currentPage
});
});
_defineProperty ( this , " handleInvalidate " , e => {
e . preventDefault ();
if ( this . props . realtime ) {
2020-12-16 11:10:48 +00:00
axios . get ( window . location . pathname , {
2020-09-18 23:30:17 +00:00
params : {
invalidate_searched : this . state . searchTerm
}
}) . then ( response => {
console . log ( 'success: ' , response . data );
2014-11-25 17:11:37 +00:00
});
2020-09-18 23:30:17 +00:00
} else {
window . location . href = e . currentTarget . href ;
}
});
2020-11-28 16:16:58 +00:00
_defineProperty ( this , " changeSort " , e => {
this . setState ({
[ e . target . name ] : e . target . value
});
});
_defineProperty ( this , " compareValues " , ( key , order = 'asc' ) => {
return function innerSort ( a , b ) {
if ( ! a . hasOwnProperty ( key ) || ! b . hasOwnProperty ( key )) {
return 0 ;
}
const varA = typeof a [ key ] === 'string' ? a [ key ] . toUpperCase () : a [ key ];
const varB = typeof b [ key ] === 'string' ? b [ key ] . toUpperCase () : b [ key ];
let comparison = 0 ;
if ( varA > varB ) {
comparison = 1 ;
} else if ( varA < varB ) {
comparison = - 1 ;
}
return order === 'desc' ? comparison * - 1 : comparison ;
};
});
2020-09-18 23:30:17 +00:00
this . doPagination = typeof props . perPageLimit === " number " && props . perPageLimit > 0 ;
this . state = {
currentPage : 1 ,
searchTerm : props . searchTerm ,
2020-11-28 16:16:58 +00:00
refreshPagination : 0 ,
sortBy : `last_used_timestamp` ,
sortDir : `desc`
2020-09-18 23:30:17 +00:00
};
}
render () {
if ( ! this . props . allow . fileList ) {
return null ;
}
if ( this . props . allFiles . length === 0 ) {
2022-07-17 01:43:40 +00:00
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' ));
2020-09-18 23:30:17 +00:00
}
const {
searchTerm ,
currentPage
} = this . state ;
const offset = ( currentPage - 1 ) * this . props . perPageLimit ;
const filesInSearch = searchTerm ? this . props . allFiles . filter ( file => {
2020-12-08 21:59:47 +00:00
return ! ( file . full_path . indexOf ( searchTerm ) === - 1 );
2020-09-18 23:30:17 +00:00
}) : this . props . allFiles ;
2020-11-28 16:16:58 +00:00
filesInSearch . sort ( this . compareValues ( this . state . sortBy , this . state . sortDir ));
2020-09-18 23:30:17 +00:00
const filesInPage = this . doPagination ? filesInSearch . slice ( offset , offset + this . props . perPageLimit ) : filesInSearch ;
const allFilesTotal = this . props . allFiles . length ;
const showingTotal = filesInSearch . length ;
2022-07-17 01:43:40 +00:00
const showing = showingTotal !== allFilesTotal ? " , { 1} showing due to filter ' { 2}' " : " " ;
2020-09-18 23:30:17 +00:00
return /*#__PURE__*/ React . createElement ( " div " , null , /*#__PURE__*/ React . createElement ( " form " , {
action : " # "
}, /*#__PURE__*/ React . createElement ( " label " , {
htmlFor : " frmFilter "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Start typing to filter on script path' )), /*#__PURE__*/ React . createElement ( " br " , null ), /*#__PURE__*/ React . createElement ( " input " , {
2020-09-18 23:30:17 +00:00
type : " text " ,
name : " filter " ,
id : " frmFilter " ,
className : " file-filter " ,
onChange : e => {
this . setSearchTerm ( e . target . value );
}
2022-07-17 01:43:40 +00:00
})), /*#__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 " , {
2020-09-18 23:30:17 +00:00
href : `?invalidate_searched=${encodeURIComponent(this.state.searchTerm)}` ,
onClick : this . handleInvalidate
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Invalidate all matching files' ))), /*#__PURE__*/ React . createElement ( " div " , {
2020-11-28 16:16:58 +00:00
className : " paginate-filter "
}, this . doPagination && /*#__PURE__*/ React . createElement ( Pagination , {
2020-09-18 23:30:17 +00:00
totalRecords : filesInSearch . length ,
pageLimit : this . props . perPageLimit ,
pageNeighbours : 2 ,
onPageChanged : this . onPageChanged ,
2022-07-17 01:43:40 +00:00
refresh : this . state . refreshPagination ,
txt : this . props . txt
2020-11-28 16:16:58 +00:00
}), /*#__PURE__*/ React . createElement ( " nav " , {
className : " filter " ,
2022-07-17 01:43:40 +00:00
" aria-label " : this . props . txt ( 'Sort order' )
2020-11-28 16:16:58 +00:00
}, /*#__PURE__*/ React . createElement ( " select " , {
name : " sortBy " ,
onChange : this . changeSort ,
value : this . state . sortBy
}, /*#__PURE__*/ React . createElement ( " option " , {
value : " last_used_timestamp "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Last used' )), /*#__PURE__*/ React . createElement ( " option " , {
2022-07-16 17:46:15 +00:00
value : " last_modified "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Last modified' )), /*#__PURE__*/ React . createElement ( " option " , {
2020-11-28 16:16:58 +00:00
value : " full_path "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Path' )), /*#__PURE__*/ React . createElement ( " option " , {
2020-11-28 16:16:58 +00:00
value : " hits "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Number of hits' )), /*#__PURE__*/ React . createElement ( " option " , {
2020-11-28 16:16:58 +00:00
value : " memory_consumption "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Memory consumption' ))), /*#__PURE__*/ React . createElement ( " select " , {
2020-11-28 16:16:58 +00:00
name : " sortDir " ,
onChange : this . changeSort ,
value : this . state . sortDir
}, /*#__PURE__*/ React . createElement ( " option " , {
value : " desc "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Descending' )), /*#__PURE__*/ React . createElement ( " option " , {
2020-11-28 16:16:58 +00:00
value : " asc "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Ascending' ))))), /*#__PURE__*/ React . createElement ( " table " , {
2020-09-18 23:30:17 +00:00
className : " tables cached-list-table "
2022-07-17 01:43:40 +00:00
}, /*#__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 ) => {
2020-09-18 23:30:17 +00:00
return /*#__PURE__*/ React . createElement ( CachedFile , _extends ({
key : file . full_path ,
canInvalidate : this . props . allow . invalidate ,
2022-07-17 01:43:40 +00:00
realtime : this . props . realtime ,
txt : this . props . txt
2020-09-18 23:30:17 +00:00
}, file ));
}))));
}
}
class CachedFile extends React . Component {
constructor ( ... args ) {
super ( ... args );
_defineProperty ( this , " handleInvalidate " , e => {
e . preventDefault ();
if ( this . props . realtime ) {
2020-12-16 11:10:48 +00:00
axios . get ( window . location . pathname , {
2020-09-18 23:30:17 +00:00
params : {
invalidate : e . currentTarget . getAttribute ( 'data-file' )
}
}) . then ( response => {
console . log ( 'success: ' , response . data );
2014-11-25 17:11:37 +00:00
});
2020-09-18 23:30:17 +00:00
} else {
window . location . href = e . currentTarget . href ;
}
2014-11-25 17:11:37 +00:00
});
2020-09-18 23:30:17 +00:00
}
render () {
return /*#__PURE__*/ React . createElement ( " tr " , {
" data-path " : this . props . full_path . toLowerCase ()
}, /*#__PURE__*/ React . createElement ( " td " , null , /*#__PURE__*/ React . createElement ( " span " , {
className : " file-pathname "
}, this . props . full_path ), /*#__PURE__*/ React . createElement ( " span " , {
className : " file-metainfo "
2022-07-17 01:43:40 +00:00
}, /*#__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 " , {
2020-09-18 23:30:17 +00:00
className : " invalid file-metainfo "
2022-07-17 01:43:40 +00:00
}, " - " , this . props . txt ( 'has been invalidated' )), this . props . canInvalidate && /*#__PURE__*/ React . createElement ( " span " , null , " , \xA0 " , /*#__PURE__*/ React . createElement ( " a " , {
2020-09-18 23:30:17 +00:00
className : " file-metainfo " ,
href : '?invalidate=' + this . props . full_path ,
" data-file " : this . props . full_path ,
onClick : this . handleInvalidate
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'force file invalidation' )))));
2020-09-18 23:30:17 +00:00
}
}
class IgnoredFiles extends React . Component {
constructor ( props ) {
super ( props );
_defineProperty ( this , " onPageChanged " , currentPage => {
this . setState ({
currentPage
});
2017-07-31 13:25:14 +00:00
});
2020-09-18 23:30:17 +00:00
this . doPagination = typeof props . perPageLimit === " number " && props . perPageLimit > 0 ;
this . state = {
currentPage : 1 ,
refreshPagination : 0
};
}
render () {
if ( ! this . props . allow . fileList ) {
return null ;
}
if ( this . props . allFiles . length === 0 ) {
2022-07-17 01:43:40 +00:00
return /*#__PURE__*/ React . createElement ( " p " , null , this . props . txt ( 'No files have been ignored via <i>opcache.blacklist_filename</i>' ));
2020-09-18 23:30:17 +00:00
}
const {
currentPage
} = this . state ;
const offset = ( currentPage - 1 ) * this . props . perPageLimit ;
const filesInPage = this . doPagination ? this . props . allFiles . slice ( offset , offset + this . props . perPageLimit ) : this . props . allFiles ;
const allFilesTotal = this . props . allFiles . length ;
2022-07-17 01:43:40 +00:00
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 , {
2020-09-18 23:30:17 +00:00
totalRecords : allFilesTotal ,
pageLimit : this . props . perPageLimit ,
pageNeighbours : 2 ,
onPageChanged : this . onPageChanged ,
2022-07-17 01:43:40 +00:00
refresh : this . state . refreshPagination ,
txt : this . props . txt
2020-09-18 23:30:17 +00:00
}), /*#__PURE__*/ React . createElement ( " table " , {
className : " tables ignored-list-table "
2022-07-17 01:43:40 +00:00
}, /*#__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 ) => {
2020-09-18 23:30:17 +00:00
return /*#__PURE__*/ React . createElement ( " tr " , {
key : file
}, /*#__PURE__*/ React . createElement ( " td " , null , file ));
}))));
}
}
class PreloadedFiles extends React . Component {
constructor ( props ) {
super ( props );
_defineProperty ( this , " onPageChanged " , currentPage => {
this . setState ({
currentPage
});
2015-05-08 16:04:40 +00:00
});
2020-09-18 23:30:17 +00:00
this . doPagination = typeof props . perPageLimit === " number " && props . perPageLimit > 0 ;
this . state = {
currentPage : 1 ,
refreshPagination : 0
};
}
render () {
if ( ! this . props . allow . fileList ) {
return null ;
}
if ( this . props . allFiles . length === 0 ) {
2022-07-17 01:43:40 +00:00
return /*#__PURE__*/ React . createElement ( " p " , null , this . props . txt ( 'No files have been preloaded <i>opcache.preload</i>' ));
2020-09-18 23:30:17 +00:00
}
const {
currentPage
} = this . state ;
const offset = ( currentPage - 1 ) * this . props . perPageLimit ;
const filesInPage = this . doPagination ? this . props . allFiles . slice ( offset , offset + this . props . perPageLimit ) : this . props . allFiles ;
const allFilesTotal = this . props . allFiles . length ;
2022-07-17 01:43:40 +00:00
return /*#__PURE__*/ React . createElement ( " div " , null , /*#__PURE__*/ React . createElement ( " h3 " , null , this . props . txt ( '{0} preloaded files' , allFilesTotal )), this . doPagination && /*#__PURE__*/ React . createElement ( Pagination , {
2020-09-18 23:30:17 +00:00
totalRecords : allFilesTotal ,
pageLimit : this . props . perPageLimit ,
pageNeighbours : 2 ,
onPageChanged : this . onPageChanged ,
2022-07-17 01:43:40 +00:00
refresh : this . state . refreshPagination ,
txt : this . props . txt
2020-09-18 23:30:17 +00:00
}), /*#__PURE__*/ React . createElement ( " table " , {
className : " tables preload-list-table "
2022-07-17 01:43:40 +00:00
}, /*#__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 ) => {
2020-09-18 23:30:17 +00:00
return /*#__PURE__*/ React . createElement ( " tr " , {
key : file
}, /*#__PURE__*/ React . createElement ( " td " , null , file ));
}))));
}
}
class Pagination extends React . Component {
constructor ( props ) {
super ( props );
_defineProperty ( this , " gotoPage " , page => {
const {
onPageChanged = f => f
} = this . props ;
const currentPage = Math . max ( 0 , Math . min ( page , this . totalPages ()));
this . setState ({
currentPage
}, () => onPageChanged ( currentPage ));
2017-07-31 13:25:14 +00:00
});
2020-09-18 23:30:17 +00:00
_defineProperty ( this , " totalPages " , () => {
return Math . ceil ( this . props . totalRecords / this . props . pageLimit );
2017-07-31 13:25:14 +00:00
});
2020-09-18 23:30:17 +00:00
_defineProperty ( this , " handleClick " , ( page , evt ) => {
evt . preventDefault ();
this . gotoPage ( page );
2014-11-24 00:09:48 +00:00
});
2020-09-18 23:30:17 +00:00
_defineProperty ( this , " handleJumpLeft " , evt => {
evt . preventDefault ();
this . gotoPage ( this . state . currentPage - this . pageNeighbours * 2 - 1 );
2014-11-24 00:09:48 +00:00
});
2020-09-18 23:30:17 +00:00
_defineProperty ( this , " handleJumpRight " , evt => {
evt . preventDefault ();
this . gotoPage ( this . state . currentPage + this . pageNeighbours * 2 + 1 );
2014-11-24 00:09:48 +00:00
});
2020-09-18 23:30:17 +00:00
_defineProperty ( this , " handleMoveLeft " , evt => {
evt . preventDefault ();
this . gotoPage ( this . state . currentPage - 1 );
2014-11-25 13:20:33 +00:00
});
2020-09-18 23:30:17 +00:00
_defineProperty ( this , " handleMoveRight " , evt => {
evt . preventDefault ();
this . gotoPage ( this . state . currentPage + 1 );
2014-11-26 11:13:39 +00:00
});
2020-09-18 23:30:17 +00:00
_defineProperty ( this , " range " , ( from , to , step = 1 ) => {
let i = from ;
const range = [];
while ( i <= to ) {
range . push ( i );
i += step ;
}
return range ;
});
_defineProperty ( this , " fetchPageNumbers " , () => {
const totalPages = this . totalPages ();
const pageNeighbours = this . pageNeighbours ;
const totalNumbers = this . pageNeighbours * 2 + 3 ;
const totalBlocks = totalNumbers + 2 ;
if ( totalPages > totalBlocks ) {
let pages = [];
const leftBound = this . state . currentPage - pageNeighbours ;
const rightBound = this . state . currentPage + pageNeighbours ;
const beforeLastPage = totalPages - 1 ;
const startPage = leftBound > 2 ? leftBound : 2 ;
const endPage = rightBound < beforeLastPage ? rightBound : beforeLastPage ;
pages = this . range ( startPage , endPage );
const pagesCount = pages . length ;
const singleSpillOffset = totalNumbers - pagesCount - 1 ;
const leftSpill = startPage > 2 ;
const rightSpill = endPage < beforeLastPage ;
const leftSpillPage = " LEFT " ;
const rightSpillPage = " RIGHT " ;
if ( leftSpill && ! rightSpill ) {
const extraPages = this . range ( startPage - singleSpillOffset , startPage - 1 );
pages = [ leftSpillPage , ... extraPages , ... pages ];
} else if ( ! leftSpill && rightSpill ) {
const extraPages = this . range ( endPage + 1 , endPage + singleSpillOffset );
pages = [ ... pages , ... extraPages , rightSpillPage ];
} else if ( leftSpill && rightSpill ) {
pages = [ leftSpillPage , ... pages , rightSpillPage ];
2014-11-25 17:11:37 +00:00
}
2020-09-18 23:30:17 +00:00
return [ 1 , ... pages , totalPages ];
}
return this . range ( 1 , totalPages );
2014-11-26 11:13:39 +00:00
});
2020-09-18 23:30:17 +00:00
this . state = {
currentPage : 1
};
this . pageNeighbours = typeof props . pageNeighbours === " number " ? Math . max ( 0 , Math . min ( props . pageNeighbours , 2 )) : 0 ;
}
componentDidMount () {
this . gotoPage ( 1 );
}
componentDidUpdate ( props ) {
const {
refresh
} = this . props ;
if ( props . refresh !== refresh ) {
this . gotoPage ( 1 );
}
}
render () {
if ( ! this . props . totalRecords || this . totalPages () === 1 ) {
return null ;
}
const {
currentPage
} = this . state ;
const pages = this . fetchPageNumbers ();
return /*#__PURE__*/ React . createElement ( " nav " , {
" aria-label " : " File list pagination "
}, /*#__PURE__*/ React . createElement ( " ul " , {
className : " pagination "
}, pages . map (( page , index ) => {
if ( page === " LEFT " ) {
return /*#__PURE__*/ React . createElement ( React . Fragment , {
key : index
}, /*#__PURE__*/ React . createElement ( " li " , {
className : " page-item arrow "
}, /*#__PURE__*/ React . createElement ( " a " , {
className : " page-link " ,
href : " # " ,
2022-07-17 01:43:40 +00:00
" aria-label " : this . props . txt ( 'Previous' ),
2020-09-18 23:30:17 +00:00
onClick : this . handleJumpLeft
}, /*#__PURE__*/ React . createElement ( " span " , {
" aria-hidden " : " true "
}, " \ u219E " ), /*#__PURE__*/ React . createElement ( " span " , {
className : " sr-only "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Jump back' )))), /*#__PURE__*/ React . createElement ( " li " , {
2020-09-18 23:30:17 +00:00
className : " page-item arrow "
}, /*#__PURE__*/ React . createElement ( " a " , {
className : " page-link " ,
href : " # " ,
2022-07-17 01:43:40 +00:00
" aria-label " : this . props . txt ( 'Previous' ),
2020-09-18 23:30:17 +00:00
onClick : this . handleMoveLeft
}, /*#__PURE__*/ React . createElement ( " span " , {
" aria-hidden " : " true "
}, " \ u21E0 " ), /*#__PURE__*/ React . createElement ( " span " , {
className : " sr-only "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Previous page' )))));
2020-09-18 23:30:17 +00:00
}
if ( page === " RIGHT " ) {
return /*#__PURE__*/ React . createElement ( React . Fragment , {
key : index
}, /*#__PURE__*/ React . createElement ( " li " , {
className : " page-item arrow "
}, /*#__PURE__*/ React . createElement ( " a " , {
className : " page-link " ,
href : " # " ,
2022-07-17 01:43:40 +00:00
" aria-label " : this . props . txt ( 'Next' ),
2020-09-18 23:30:17 +00:00
onClick : this . handleMoveRight
}, /*#__PURE__*/ React . createElement ( " span " , {
" aria-hidden " : " true "
}, " \ u21E2 " ), /*#__PURE__*/ React . createElement ( " span " , {
className : " sr-only "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Next page' )))), /*#__PURE__*/ React . createElement ( " li " , {
2020-09-18 23:30:17 +00:00
className : " page-item arrow "
}, /*#__PURE__*/ React . createElement ( " a " , {
className : " page-link " ,
href : " # " ,
2022-07-17 01:43:40 +00:00
" aria-label " : this . props . txt ( 'Next' ),
2020-09-18 23:30:17 +00:00
onClick : this . handleJumpRight
}, /*#__PURE__*/ React . createElement ( " span " , {
" aria-hidden " : " true "
}, " \ u21A0 " ), /*#__PURE__*/ React . createElement ( " span " , {
className : " sr-only "
2022-07-17 01:43:40 +00:00
}, this . props . txt ( 'Jump forward' )))));
2020-09-18 23:30:17 +00:00
}
return /*#__PURE__*/ React . createElement ( " li " , {
key : index ,
className : " page-item "
}, /*#__PURE__*/ React . createElement ( " a " , {
className : `page-link${currentPage === page ? " active" : ""}` ,
href : " # " ,
onClick : e => this . handleClick ( page , e )
}, page ));
})));
}
}
function Footer ( props ) {
return /*#__PURE__*/ React . createElement ( " footer " , {
className : " main-footer "
}, /*#__PURE__*/ React . createElement ( " a " , {
className : " github-link " ,
href : " https://github.com/amnuts/opcache-gui " ,
target : " _blank " ,
2024-06-23 13:01:33 +00:00
title : props . txt ( " opcache-gui (currently version { 0}) on GitHub " , props . version )
}, " https://github.com/amnuts/opcache-gui - " , props . txt ( " version { 0} " , props . version )), /*#__PURE__*/ React . createElement ( " a " , {
2023-08-15 16:57:35 +00:00
className : " sponsor-link " ,
href : " https://github.com/sponsors/amnuts " ,
target : " _blank " ,
2024-06-23 13:01:33 +00:00
title : props . txt ( " Sponsor this project and author on GitHub " )
}, props . txt ( " Sponsor this project " )));
2020-09-18 23:30:17 +00:00
}
function debounce ( func , wait , immediate ) {
let timeout ;
wait = wait || 250 ;
return function () {
let context = this ,
2024-06-23 13:01:33 +00:00
args = arguments ;
2020-09-18 23:30:17 +00:00
let later = function () {
timeout = null ;
if ( ! immediate ) {
func . apply ( context , args );
}
};
let callNow = immediate && ! timeout ;
clearTimeout ( timeout );
timeout = setTimeout ( later , wait );
if ( callNow ) {
func . apply ( context , args );
}
};
}
ReactDOM . render ( React . createElement ( Interface , {
allow : {
filelist : < ? = $opcache -> getOption ( 'allow_filelist' ) ? 'true' : 'false' ; ?> ,
invalidate : < ? = $opcache -> getOption ( 'allow_invalidate' ) ? 'true' : 'false' ; ?> ,
reset : < ? = $opcache -> getOption ( 'allow_reset' ) ? 'true' : 'false' ; ?> ,
realtime : < ? = $opcache -> getOption ( 'allow_realtime' ) ? 'true' : 'false' ; ?>
},
cookie : {
name : '<?= $opcache->getOption(' cookie_name '); ?>' ,
ttl : < ? = $opcache -> getOption ( 'cookie_ttl' ); ?>
},
opstate : < ? = json_encode ( $opcache -> getData ()); ?> ,
useCharts : < ? = json_encode ( $opcache -> getOption ( 'charts' )); ?> ,
highlight : < ? = json_encode ( $opcache -> getOption ( 'highlight' )); ?> ,
debounceRate : < ? = $opcache -> getOption ( 'debounce_rate' ); ?> ,
perPageLimit : < ? = json_encode ( $opcache -> getOption ( 'per_page' )); ?> ,
2022-07-17 01:43:40 +00:00
realtimeRefresh : < ? = json_encode ( $opcache -> getOption ( 'refresh_time' )); ?> ,
language : < ? = json_encode ( $opcache -> getOption ( 'language_pack' )); ?> ,
2020-09-18 23:30:17 +00:00
}), document . getElementById ( 'interface' ));
</ script >
2013-04-08 15:40:06 +00:00
</ body >
2023-04-23 00:35:00 +00:00
</ html >