.
* @since 1.0.0
*/
namespace OPcacheManager\Plugin\Feature;
use OPcacheManager\System\Conversion;
use OPcacheManager\System\Logger;
use OPcacheManager\System\Date;
use OPcacheManager\System\Timezone;
use OPcacheManager\System\OPcache;
use OPcacheManager\System\Environment;
if ( ! class_exists( 'WP_List_Table' ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
}
/**
* Define the scripts list functionality.
*
* Lists all available scripts.
*
* @package Features
* @author Pierre Lannoy .
* @since 1.0.0
*/
class Scripts extends \WP_List_Table {
/**
* The scripts handler.
*
* @since 1.0.0
* @var array $scripts The scripts list.
*/
private $scripts = [];
/**
* The number of lines to display.
*
* @since 1.0.0
* @var integer $limit The number of lines to display.
*/
private $limit = 0;
/**
* The page to display.
*
* @since 1.0.0
* @var integer $limit The page to display.
*/
private $paged = 1;
/**
* The order by of the list.
*
* @since 1.0.0
* @var string $orderby The order by of the list.
*/
private $orderby = 'script';
/**
* The order of the list.
*
* @since 1.0.0
* @var string $order The order of the list.
*/
private $order = 'desc';
/**
* The current url.
*
* @since 1.0.0
* @var string $url The current url.
*/
private $url = '';
/**
* The form nonce.
*
* @since 1.0.0
* @var string $nonce The form nonce.
*/
private $nonce = '';
/**
* The action to perform.
*
* @since 1.0.0
* @var string $action The action to perform.
*/
private $action = '';
/**
* The bulk args.
*
* @since 1.0.0
* @var array $bulk The bulk args.
*/
private $bulk = [];
/**
* Initialize the class and set its properties.
*
* @since 1.0.0
*/
public function __construct() {
parent::__construct(
[
'singular' => 'script',
'plural' => 'scripts',
'ajax' => true,
]
);
global $wp_version;
if ( version_compare( $wp_version, '4.2-z', '>=' ) && $this->compat_fields && is_array( $this->compat_fields ) ) {
array_push( $this->compat_fields, 'all_items' );
}
$this->process_args();
$this->process_action();
$this->scripts = [];
if ( function_exists( 'opcache_get_status' ) ) {
try {
$raw = opcache_get_status( true );
if ( array_key_exists( 'scripts', $raw ) ) {
foreach ( $raw['scripts'] as $script ) {
if ( false === strpos( $script['full_path'], OPCM_ABSPATH ) ) {
continue;
}
$item = [];
$item['script'] = str_replace( OPCM_ABSPATH, './', $script['full_path'] );
$item['hit'] = $script['hits'];
$item['memory'] = $script['memory_consumption'];
$item['timestamp'] = $script['timestamp'];
$item['used'] = $script['last_used_timestamp'];
$this->scripts[] = $item;
}
}
} catch ( \Throwable $e ) {
\DecaLog\Engine::eventsLogger( OPCM_SLUG )->error( sprintf( 'Unable to query OPcache status: %s.', $e->getMessage() ), [ 'code' => $e->getCode() ] );
}
}
}
/**
* Default column formatter.
*
* @param array $item The current item.
* @param string $column_name The current column name.
* @return string The cell formatted, ready to print.
* @since 1.0.0
*/
protected function column_default( $item, $column_name ) {
return $item[ $column_name ];
}
/**
* Check box column formatter.
*
* @param array $item The current item.
* @return string The cell formatted, ready to print.
* @since 1.0.0
*/
public function column_cb( $item ) {
return sprintf(
'',
$item['script']
);
}
/**
* "hit" column formatter.
*
* @param array $item The current item.
* @return string The cell formatted, ready to print.
* @since 1.0.0
*/
protected function column_hit( $item ) {
return Conversion::number_shorten( $item['hit'] );
}
/**
* "memory" column formatter.
*
* @param array $item The current item.
* @return string The cell formatted, ready to print.
* @since 1.0.0
*/
protected function column_memory( $item ) {
return Conversion::data_shorten( $item['memory'] );
}
/**
* "used" column formatter.
*
* @param array $item The current item.
* @return string The cell formatted, ready to print.
* @since 1.0.0
*/
protected function column_used( $item ) {
$time = new \DateTime();
$time->setTimestamp( $item['used'] );
return ucfirst( Date::get_positive_time_diff_from_mysql_utc( $time->format( 'Y-m-d H:i:s' ) ) );
}
/**
* "timestamp" column formatter.
*
* @param array $item The current item.
* @return string The cell formatted, ready to print.
* @since 1.0.0
*/
protected function column_timestamp( $item ) {
$time = new \DateTime();
$time->setTimestamp( $item['timestamp'] );
return Date::get_date_from_mysql_utc( $time->format( 'Y-m-d H:i:s' ), Timezone::network_get()->getName(), 'Y-m-d H:i:s' );
}
/**
* Enumerates columns.
*
* @return array The columns.
* @since 1.0.0
*/
public function get_columns() {
$columns = [
'cb' => '',
'script' => esc_html__( 'File', 'opcache-manager' ),
'timestamp' => esc_html__( 'Timestamp', 'opcache-manager' ),
'hit' => esc_html__( 'Hits', 'opcache-manager' ),
'memory' => esc_html__( 'Memory size', 'opcache-manager' ),
'used' => esc_html__( 'Used', 'opcache-manager' ),
];
return $columns;
}
/**
* Enumerates hidden columns.
*
* @return array The hidden columns.
* @since 1.0.0
*/
protected function get_hidden_columns() {
return [];
}
/**
* Enumerates sortable columns.
*
* @return array The sortable columns.
* @since 1.0.0
*/
protected function get_sortable_columns() {
$sortable_columns = [
'script' => [ 'script', true ],
'hit' => [ 'hit', false ],
'memory' => [ 'memory', false ],
'timestamp' => [ 'timestamp', false ],
'used' => [ 'used', false ],
];
return $sortable_columns;
}
/**
* Enumerates bulk actions.
*
* @return array The bulk actions.
* @since 1.0.0
*/
public function get_bulk_actions() {
return [
'invalidate' => esc_html__( 'Invalidate', 'opcache-manager' ),
'force' => esc_html__( 'Force invalidate', 'opcache-manager' ),
'recompile' => esc_html__( 'Recompile', 'opcache-manager' ),
];
}
/**
* Generate the table navigation above or below the table
*
* @param string $which Position of extra control.
* @since 1.0.0
*/
protected function display_tablenav( $which ) {
if ( 'top' === $which ) {
wp_nonce_field( 'bulk-opcm-tools', '_wpnonce', false );
}
echo '
';
if ( $this->has_items() ) {
echo '
';
$this->bulk_actions( $which );
echo '
';
}
$this->extra_tablenav( $which );
$this->pagination( $which );
echo '
';
echo '
';
}
/**
* Extra controls to be displayed between bulk actions and pagination.
*
* @param string $which Position of extra control.
* @since 1.0.0
*/
public function extra_tablenav( $which ) {
$list = $this;
$args = compact( 'list', 'which' );
foreach ( $args as $key => $val ) {
$$key = $val;
}
if ( 'top' === $which || 'bottom' === $which ) {
include OPCM_ADMIN_DIR . 'partials/opcache-manager-admin-tools-lines.php';
}
}
/**
* Prepares the list to be displayed.
*
* @since 1.0.0
*/
public function prepare_items() {
$this->set_pagination_args(
[
'total_items' => count( $this->scripts ),
'per_page' => $this->limit,
'total_pages' => ceil( count( $this->scripts ) / $this->limit ),
]
);
$current_page = $this->get_pagenum();
$columns = $this->get_columns();
$hidden = $this->get_hidden_columns();
$sortable = $this->get_sortable_columns();
$this->_column_headers = [ $columns, $hidden, $sortable ];
$data = $this->scripts;
usort(
$data,
function ( $a, $b ) {
if ( 'script' === $this->orderby ) {
$result = strcmp( strtolower( $a[ $this->orderby ] ), strtolower( $b[ $this->orderby ] ) );
} else {
$result = intval( $a[ $this->orderby ] ) < intval( $b[ $this->orderby ] ) ? 1 : -1;
}
return ( 'asc' === $this->order ) ? -$result : $result;
}
);
$this->items = array_slice( $data, ( ( $current_page - 1 ) * $this->limit ), $this->limit );
}
/**
* Get available lines breakdowns.
*
* @since 1.0.0
*/
public function get_line_number_select() {
$_disp = [ 50, 100, 250, 500 ];
$result = [];
foreach ( $_disp as $d ) {
$l = [];
$l['value'] = $d;
// phpcs:ignore
$l['text'] = sprintf( esc_html__( 'Display %d files per page', 'opcache-manager' ), $d );
$l['selected'] = ( intval( $d ) === intval( $this->limit ) ? 'selected="selected" ' : '' );
$result[] = $l;
}
return $result;
}
/**
* Pagination links.
*
* @param string $which Position of extra control.
* @since 1.0.0
*/
protected function pagination( $which ) {
if ( empty( $this->_pagination_args ) ) {
return;
}
$total_items = (int) $this->_pagination_args['total_items'];
$total_pages = (int) $this->_pagination_args['total_pages'];
$infinite_scroll = false;
if ( isset( $this->_pagination_args['infinite_scroll'] ) ) {
$infinite_scroll = $this->_pagination_args['infinite_scroll'];
}
if ( 'top' === $which && $total_pages > 1 ) {
$this->screen->render_screen_reader_content( 'heading_pagination' );
}
// phpcs:ignore
$output = '' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '';
$current = (int) $this->get_pagenum();
$removable_query_args = wp_removable_query_args();
$current_url = $this->url;
$current_url = remove_query_arg( $removable_query_args, $current_url );
$page_links = [];
$total_pages_before = '';
$total_pages_after = '';
$disable_first = false;
$disable_last = false;
$disable_prev = false;
$disable_next = false;
if ( 1 === $current ) {
$disable_first = true;
$disable_prev = true;
}
if ( 2 === $current ) {
$disable_first = true;
}
if ( $current === $total_pages ) {
$disable_last = true;
$disable_next = true;
}
if ( $current === $total_pages - 1 ) {
$disable_last = true;
}
if ( $disable_first ) {
$page_links[] = '«';
} else {
$page_links[] = sprintf(
"%s%s",
$this->get_url( remove_query_arg( 'paged', $current_url ), true ),
__( 'First page' ),
'«'
);
}
if ( $disable_prev ) {
$page_links[] = '‹';
} else {
$page_links[] = sprintf(
"%s%s",
$this->get_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ), true ),
__( 'Previous page' ),
'‹'
);
}
if ( 'bottom' === $which ) {
$html_current_page = $current;
$total_pages_before = '' . __( 'Current Page' ) . '';
} else {
$html_current_page = sprintf(
"%s",
'',
$current,
strlen( $total_pages )
);
}
$html_total_pages = sprintf( "%s", number_format_i18n( $total_pages ) );
// phpcs:ignore
$page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
if ( $disable_next ) {
$page_links[] = '›';
} else {
$page_links[] = sprintf(
"%s%s",
$this->get_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ), true ),
__( 'Next page' ),
'›'
);
}
if ( $disable_last ) {
$page_links[] = '»';
} else {
$page_links[] = sprintf(
"%s%s",
$this->get_url( add_query_arg( 'paged', $total_pages, $current_url ), true ),
__( 'Last page' ),
'»'
);
}
$pagination_links_class = 'pagination-links';
if ( ! empty( $infinite_scroll ) ) {
$pagination_links_class .= ' hide-if-js';
}
$output .= "\n';
if ( $total_pages ) {
$page_class = $total_pages < 2 ? ' one-page' : '';
} else {
$page_class = ' no-pages';
}
$this->_pagination = "$output
";
// phpcs:ignore
echo $this->_pagination;
}
/**
* Print column headers, accounting for hidden and sortable columns.
*
* @staticvar int $cb_counter.
* @param bool $with_id Whether to set the id attribute or not.
* @since 1.0.0
*/
public function print_column_headers( $with_id = true ) {
list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info();
if ( ! empty( $columns['cb'] ) ) {
static $cb_counter = 1;
$columns['cb'] = '';
$cb_counter++;
}
foreach ( $columns as $column_key => $column_display_name ) {
$class = [ 'manage-column', "column-$column_key" ];
if ( in_array( $column_key, $hidden, true ) ) {
$class[] = 'hidden';
}
if ( 'cb' === $column_key ) {
$class[] = 'check-column';
} elseif ( in_array( $column_key, [ 'posts', 'comments', 'links' ], true ) ) {
$class[] = 'num';
}
if ( $column_key === $primary ) {
$class[] = 'column-primary';
}
if ( isset( $sortable[ $column_key ] ) ) {
list( $orderby, $desc_first ) = $sortable[ $column_key ];
if ( $this->orderby === $orderby ) {
$order = 'asc' === $this->order ? 'desc' : 'asc';
$class[] = 'sorted';
$class[] = $this->order;
} else {
$order = $desc_first ? 'desc' : 'asc';
$class[] = 'sortable';
$class[] = $desc_first ? 'asc' : 'desc';
}
$column_display_name = '' . $column_display_name . '';
}
$tag = ( 'cb' === $column_key ) ? 'td' : 'th';
$scope = ( 'th' === $tag ) ? 'scope="col"' : '';
$id = $with_id ? "id='$column_key'" : '';
if ( ! empty( $class ) ) {
$class = "class='" . join( ' ', $class ) . "'";
}
// phpcs:ignore
echo "<$tag $scope $id $class>$column_display_name$tag>";
}
}
/**
* Display a warning if needed.
*
* @since 1.0.0
*/
public function warning() {
$message = '';
if ( function_exists( 'opcache_get_status' ) ) {
$raw = opcache_get_status( false );
if ( ! (bool) $raw['opcache_enabled'] ) {
$message = esc_html__( 'OPcache is not enabled on this site. There\'s nothing to see here.', 'opcache-manager' );
}
if ( (bool) $raw['restart_pending'] ) {
$message = esc_html__( 'A full reset is currently pending. Displayed values may be inaccurate.', 'opcache-manager' );
}
if ( (bool) $raw['restart_in_progress'] ) {
$message = esc_html__( 'A full reset is currently in progress. Displayed values may be inaccurate.', 'opcache-manager' );
}
} else {
$message = esc_html__( 'OPcache is not enabled on this site. There\'s nothing to see here.', 'opcache-manager' );
}
if ( '' !== $message ) {
// phpcs:ignore
echo '';
}
}
/**
* Get the cleaned url.
*
* @param boolean $url Optional. The url, false for current url.
* @param boolean $limit Optional. Has the limit to be in the url.
* @return string The url cleaned, ready to use.
* @since 1.0.0
*/
public function get_url( $url = false, $limit = false ) {
global $wp;
$url = remove_query_arg( 'limit', $url );
if ( $limit ) {
$url .= ( false === strpos( $url, '?' ) ? '?' : '&' ) . 'limit=' . $this->limit;
}
return esc_url( $url );
}
/**
* Initializes all the list properties.
*
* @since 1.0.0
*/
public function process_args() {
if ( ! ( $this->nonce = filter_input( INPUT_POST, '_wpnonce' ) ) ) {
$this->nonce = filter_input( INPUT_GET, '_wpnonce' );
}
$this->url = set_url_scheme( 'http://' . filter_input( INPUT_SERVER, 'HTTP_HOST' ) . filter_input( INPUT_SERVER, 'REQUEST_URI' ) );
$this->limit = filter_input( INPUT_GET, 'limit', FILTER_SANITIZE_NUMBER_INT );
foreach ( [ 'top', 'bottom' ] as $which ) {
if ( wp_verify_nonce( $this->nonce, 'bulk-opcm-tools' ) && array_key_exists( 'dolimit-' . $which, $_POST ) ) {
$this->limit = filter_input( INPUT_POST, 'limit-' . $which, FILTER_SANITIZE_NUMBER_INT );
}
}
if ( 0 === intval( $this->limit ) ) {
$this->limit = filter_input( INPUT_POST, 'limit-top', FILTER_SANITIZE_NUMBER_INT );
}
if ( 0 === intval( $this->limit ) ) {
$this->limit = 50;
}
$this->paged = filter_input( INPUT_GET, 'paged', FILTER_SANITIZE_NUMBER_INT );
if ( ! $this->paged ) {
$this->paged = filter_input( INPUT_POST, 'paged', FILTER_SANITIZE_NUMBER_INT );
if ( ! $this->paged ) {
$this->paged = 1;
}
}
$this->order = filter_input( INPUT_GET, 'order', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( ! $this->order ) {
$this->order = 'desc';
}
$this->orderby = filter_input( INPUT_GET, 'orderby', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
if ( ! $this->orderby ) {
$this->orderby = 'script';
}
foreach ( [ 'top', 'bottom' ] as $which ) {
if ( wp_verify_nonce( $this->nonce, 'bulk-opcm-tools' ) && array_key_exists( 'dowarmup-' . $which, $_POST ) ) {
$this->action = 'warmup';
}
if ( wp_verify_nonce( $this->nonce, 'bulk-opcm-tools' ) && array_key_exists( 'doinvalidate-' . $which, $_POST ) ) {
$this->action = 'reset';
}
}
if ( array_key_exists( 'quick-action', $_GET ) && wp_verify_nonce( $this->nonce, 'quick-action-opcm-tools' ) ) {
$this->action = filter_input( INPUT_GET, 'quick-action', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
}
if ( '' === $this->action ) {
$action = '-1';
if ( '-1' === $action && wp_verify_nonce( $this->nonce, 'bulk-opcm-tools' ) && array_key_exists( 'action', $_POST ) ) {
$action = filter_input( INPUT_POST, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
}
if ( '-1' === $action && wp_verify_nonce( $this->nonce, 'bulk-opcm-tools' ) && array_key_exists( 'action2', $_POST ) ) {
$action = filter_input( INPUT_POST, 'action2', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
}
if ( '-1' !== $action && wp_verify_nonce( $this->nonce, 'bulk-opcm-tools' ) && array_key_exists( 'bulk', $_POST ) ) {
$this->bulk = filter_input( INPUT_POST, 'bulk', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FORCE_ARRAY );
if ( 0 < count( $this->bulk ) ) {
$this->action = $action;
}
}
}
}
/**
* Processes the selected action.
*
* @since 1.0.0
*/
public function process_action() {
switch ( $this->action ) {
case 'warmup':
if ( Environment::is_wordpress_multisite() ) {
// phpcs:ignore
$message = esc_html( sprintf( __( 'Network warm-up has been initiated. %d relevant files.', 'opcache-manager' ), OPcache::warmup( false, true ) ) );
} else {
// phpcs:ignore
$message = esc_html( sprintf( __( 'Site warm-up has been initiated. %d relevant files.', 'opcache-manager' ), OPcache::warmup( false, true ) ) );
}
$code = 0;
break;
case 'reset':
OPcache::reset( false );
$message = esc_html__( 'Site invalidation has been initiated. It may take a few seconds to complete.', 'opcache-manager' );
$code = 0;
break;
case 'invalidate':
// phpcs:ignore
$message = esc_html( sprintf( __( 'Invalidation done: %d file(s).', 'opcache-manager' ), OPcache::invalidate( $this->bulk, false ) ) );
$code = 0;
break;
case 'force':
// phpcs:ignore
$message = esc_html( sprintf( __( 'Forced invalidation done: %d file(s).', 'opcache-manager' ), OPcache::invalidate( $this->bulk, true ) ) );
$code = 0;
break;
case 'recompile':
// phpcs:ignore
$message = esc_html( sprintf( __( 'Recompilation done: %d file(s).', 'opcache-manager' ), OPcache::recompile( $this->bulk, true ) ) );
$code = 0;
break;
default:
return;
}
if ( 0 === $code ) {
add_settings_error( 'opcache_manager_no_error', $code, $message, 'updated' );
} else {
add_settings_error( 'opcache_manager_error', $code, $message, 'error' );
}
}
}