* The admin optimize tool
* @subpackage LiteSpeed/src
* @author LiteSpeed Technologies <info@litespeedtech.com>
defined('WPINC') || exit();
class DB_Optm extends Root
private static $_hide_more = false;
private static $TYPES = array(
const TYPE_CONV_TB = 'conv_innodb';
* Show if there are more sites in hidden
public static function hide_more()
return self::$_hide_more;
* Clean/Optimize WP tables
* @param string $type The type to clean
* @param bool $ignore_multisite If ignore multisite check
* @return int The rows that will be affected
public function db_count($type, $ignore_multisite = false)
foreach (self::$TYPES as $v) {
$num += $this->db_count($v);
if (!$ignore_multisite) {
if (is_multisite() && is_network_admin()) {
$blogs = Activation::get_network_ids();
foreach ($blogs as $k => $blog_id) {
self::$_hide_more = true;
switch_to_blog($blog_id);
$num += $this->db_count($type, true);
$rev_max = (int) $this->conf(Base::O_DB_OPTM_REVISIONS_MAX);
$rev_age = (int) $this->conf(Base::O_DB_OPTM_REVISIONS_AGE);
$sql_add = " and post_modified < DATE_SUB( NOW(), INTERVAL $rev_age DAY ) ";
$sql = "SELECT COUNT(*) FROM `$wpdb->posts` WHERE post_type = 'revision' $sql_add";
return $wpdb->get_var($sql);
$sql = "SELECT COUNT(*)-$rev_max FROM `$wpdb->posts` WHERE post_type = 'revision' $sql_add GROUP BY post_parent HAVING count(*)>$rev_max";
$res = $wpdb->get_results($sql, ARRAY_N);
Utility::compatibility();
return array_sum(array_column($res, 0));
case 'orphaned_post_meta':
return $wpdb->get_var("SELECT COUNT(*) FROM `$wpdb->postmeta` a LEFT JOIN `$wpdb->posts` b ON b.ID=a.post_id WHERE b.ID IS NULL");
return $wpdb->get_var("SELECT COUNT(*) FROM `$wpdb->posts` WHERE post_status = 'auto-draft'");
return $wpdb->get_var("SELECT COUNT(*) FROM `$wpdb->posts` WHERE post_status = 'trash'");
return $wpdb->get_var("SELECT COUNT(*) FROM `$wpdb->comments` WHERE comment_approved = 'spam'");
return $wpdb->get_var("SELECT COUNT(*) FROM `$wpdb->comments` WHERE comment_approved = 'trash'");
case 'trackback-pingback':
return $wpdb->get_var("SELECT COUNT(*) FROM `$wpdb->comments` WHERE comment_type = 'trackback' OR comment_type = 'pingback'");
case 'expired_transient':
return $wpdb->get_var("SELECT COUNT(*) FROM `$wpdb->options` WHERE option_name LIKE '_transient_timeout%' AND option_value < " . time());
return $wpdb->get_var("SELECT COUNT(*) FROM `$wpdb->options` WHERE option_name LIKE '%_transient_%'");
return $wpdb->get_var("SELECT COUNT(*) FROM information_schema.tables WHERE TABLE_SCHEMA = '" . DB_NAME . "' and ENGINE <> 'InnoDB' and DATA_FREE > 0");
* Clean/Optimize WP tables
* @since 3.0 changed to private
private function _db_clean($type)
foreach (self::$TYPES as $v) {
return __('Clean all successfully.', 'litespeed-cache');
$rev_max = (int) $this->conf(Base::O_DB_OPTM_REVISIONS_MAX);
$rev_age = (int) $this->conf(Base::O_DB_OPTM_REVISIONS_AGE);
$postmeta = "`$wpdb->postmeta`";
$posts = "`$wpdb->posts`";
$sql_postmeta_join = function ($table) use ($postmeta, $posts) {
ON $posts.ID = $postmeta.post_id
$sql_where = "WHERE $posts.post_type = 'revision'";
$sql_add = $rev_age ? "AND $posts.post_modified < DATE_SUB( NOW(), INTERVAL $rev_age DAY )" : '';
$sql_where = "$sql_where $sql_add";
$sql_postmeta = $sql_postmeta_join($posts);
$wpdb->query("DELETE $postmeta FROM $sql_postmeta $sql_where");
$wpdb->query("DELETE FROM $posts $sql_where");
SELECT COUNT(*) - $rev_max
WHERE post_type = 'revision'
HAVING COUNT(*) > $rev_max
$res = $wpdb->get_results($sql);
$sql_postmeta = $sql_postmeta_join("(SELECT ID FROM $posts $sql_where) AS $posts");
$args = array($v->post_parent, $v->del_max);
$sql = $wpdb->prepare("DELETE $postmeta FROM $sql_postmeta", $args);
$sql = $wpdb->prepare("DELETE FROM $posts $sql_where", $args);
return __('Clean post revisions successfully.', 'litespeed-cache');
case 'orphaned_post_meta':
$wpdb->query("DELETE a FROM `$wpdb->postmeta` a LEFT JOIN `$wpdb->posts` b ON b.ID=a.post_id WHERE b.ID IS NULL");
return __('Clean orphaned post meta successfully.', 'litespeed-cache');
$wpdb->query("DELETE FROM `$wpdb->posts` WHERE post_status = 'auto-draft'");
return __('Clean auto drafts successfully.', 'litespeed-cache');
$wpdb->query("DELETE FROM `$wpdb->posts` WHERE post_status = 'trash'");
return __('Clean trashed posts and pages successfully.', 'litespeed-cache');
$wpdb->query("DELETE FROM `$wpdb->comments` WHERE comment_approved = 'spam'");
return __('Clean spam comments successfully.', 'litespeed-cache');
$wpdb->query("DELETE FROM `$wpdb->comments` WHERE comment_approved = 'trash'");
return __('Clean trashed comments successfully.', 'litespeed-cache');
case 'trackback-pingback':
$wpdb->query("DELETE FROM `$wpdb->comments` WHERE comment_type = 'trackback' OR comment_type = 'pingback'");
return __('Clean trackbacks and pingbacks successfully.', 'litespeed-cache');
case 'expired_transient':
$wpdb->query("DELETE FROM `$wpdb->options` WHERE option_name LIKE '_transient_timeout%' AND option_value < " . time());
return __('Clean expired transients successfully.', 'litespeed-cache');
$wpdb->query("DELETE FROM `$wpdb->options` WHERE option_name LIKE '%\\_transient\\_%'");
return __('Clean all transients successfully.', 'litespeed-cache');
$sql = "SELECT table_name, DATA_FREE FROM information_schema.tables WHERE TABLE_SCHEMA = '" . DB_NAME . "' and ENGINE <> 'InnoDB' and DATA_FREE > 0";
$result = $wpdb->get_results($sql);
foreach ($result as $row) {
$wpdb->query('OPTIMIZE TABLE ' . $row->table_name);
return __('Optimized all tables.', 'litespeed-cache');
public function list_myisam()
$q = "SELECT * FROM information_schema.tables WHERE TABLE_SCHEMA = '" . DB_NAME . "' and ENGINE = 'myisam' AND TABLE_NAME LIKE '{$wpdb->prefix}%'";
return $wpdb->get_results($q);
* Convert tables to InnoDB
private function _conv_innodb()
if (empty($_GET['tb'])) {
Admin_Display::error('No table to convert');
$list = $this->list_myisam();
if ($v->TABLE_NAME == $_GET['tb']) {
Admin_Display::error('No existing table');
$q = 'ALTER TABLE ' . DB_NAME . '.' . $tb . ' ENGINE = InnoDB';
Debug2::debug("[DB] Converted $tb to InnoDB");
$msg = __('Converted to InnoDB successfully.', 'litespeed-cache');
Admin_Display::succeed($msg);
* Count all autoload size
public function autoload_summary()
$summary = $wpdb->get_row("SELECT SUM(LENGTH(option_value)) AS autoload_size,COUNT(*) AS autload_entries FROM `$wpdb->options` WHERE autoload='yes'");
$summary->autoload_toplist = $wpdb->get_results(
"SELECT option_name, LENGTH(option_value) AS option_value_length FROM `$wpdb->options` WHERE autoload='yes' ORDER BY option_value_length DESC LIMIT 20"
* Handle all request actions from main cls
public function handler()
$type = Router::verify_type();
case in_array($type, self::$TYPES):
if (is_multisite() && is_network_admin()) {
$blogs = Activation::get_network_ids();
foreach ($blogs as $blog_id) {
switch_to_blog($blog_id);
$msg = $this->_db_clean($type);
$msg = $this->_db_clean($type);
Admin_Display::succeed($msg);