inc/class-wpsweep.php
<?php
/**
* WP-Sweep class-wpsweep.php
*
* @package wp-sweep
*/
/**
* WP-Sweep class
*
* @since 1.0.0
*/
class WPSweep {
/**
* Limit the number of items to show for sweep details
*
* @since 1.0.3
*
* @access public
* @var int
*/
public $limit_details = 500;
/**
* Static instance
*
* @since 1.0.0
*
* @access private
* @var WPSweep|null
*/
private static $instance;
/**
* Constructor method
*
* @since 1.0.0
*
* @access public
*/
public function __construct() {
// Add Plugin Hooks.
add_action( 'plugins_loaded', array( $this, 'add_hooks' ) );
// Load Translation.
load_plugin_textdomain( 'wp-sweep' );
// Plugin Activation/Deactivation.
register_activation_hook( WP_SWEEP_MAIN_FILE, array( $this, 'plugin_activation' ) );
register_deactivation_hook( WP_SWEEP_MAIN_FILE, array( $this, 'plugin_deactivation' ) );
}
/**
* Initializes the plugin object and returns its instance
*
* @since 1.0.0
*
* @access public
* @return object The plugin object instance
*/
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Init this plugin
*
* @since 1.0.0
*
* @access public
* @return void
*/
public function init() {
// include class for WP CLI command.
if ( defined( 'WP_CLI' ) ) {
require __DIR__ . '/class-wpsweep-command.php';
WP_CLI::add_command( 'sweep', 'WPSweep_Command' );
}
}
/**
* Adds all the plugin hooks
*
* @since 1.0.0
*
* @access public
* @return void
*/
public function add_hooks() {
// Actions.
add_action( 'init', array( $this, 'init' ) );
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
add_action( 'wp_ajax_sweep_details', array( $this, 'ajax_sweep_details' ) );
add_action( 'wp_ajax_sweep', array( $this, 'ajax_sweep' ) );
}
/**
* Enqueue JS/CSS files used for admin
*
* @since 1.0.3
*
* @access public
* @param string $hook Page hook.
* @return void
*/
public function admin_enqueue_scripts( $hook ) {
if ( 'wp-sweep/admin.php' !== $hook ) {
return;
}
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
wp_enqueue_script( 'wp-sweep', plugins_url( 'wp-sweep/js/wp-sweep.js' ), array( 'jquery' ), WP_SWEEP_VERSION, true );
} else {
wp_enqueue_script( 'wp-sweep', plugins_url( 'wp-sweep/js/wp-sweep.min.js' ), array( 'jquery' ), WP_SWEEP_VERSION, true );
}
wp_localize_script(
'wp-sweep', 'wp_sweep', array(
'text_close_warning' => __( 'Sweeping is in progress. If you leave now, the process won\'t be completed.', 'wp-sweep' ),
'text_sweep' => __( 'Sweep', 'wp-sweep' ),
'text_sweep_all' => __( 'Sweep All', 'wp-sweep' ),
'text_sweeping' => __( 'Sweeping...', 'wp-sweep' ),
'text_na' => __( 'N/A', 'wp-sweep' ),
)
);
}
/**
* Admin menu
*
* @since 1.0.3
*
* @access public
* @return void
*/
public function admin_menu() {
add_management_page( _x( 'Sweep', 'Page title', 'wp-sweep' ), _x( 'Sweep', 'Menu title', 'wp-sweep' ), 'activate_plugins', 'wp-sweep/admin.php' );
}
/**
* Sweep Details loaded via AJAX
*
* @since 1.0.3
*
* @access public
* @return void
*/
public function ajax_sweep_details() {
// Verify referer and check permissions.
if (
empty( $_GET['sweep_name'] )
|| ! check_admin_referer( 'wp_sweep_details_' . $_GET['sweep_name'] )
|| ! current_user_can( 'activate_plugins' )
) {
wp_send_json_error( array( 'error' => __( 'Invalid AJAX request.', 'wp-sweep' ) ) );
}
wp_send_json_success( $this->details( $_GET['sweep_name'] ) );
}
/**
* Sweep via AJAX
*
* @since 1.0.3
*
* @access public
* @return void
*/
public function ajax_sweep() {
// Verify referer and check permissions.
if (
empty( $_GET['sweep_name'] )
|| empty( $_GET['sweep_type'] )
|| ! check_admin_referer( 'wp_sweep_' . $_GET['sweep_name'] )
|| ! current_user_can( 'activate_plugins' )
) {
wp_send_json_error( array( 'error' => __( 'Invalid AJAX request.', 'wp-sweep' ) ) );
}
$sweep = $this->sweep( $_GET['sweep_name'] );
$count = $this->count( $_GET['sweep_name'] );
$total_count = $this->total_count( $_GET['sweep_type'] );
$total_stats = array();
switch ( $_GET['sweep_type'] ) {
case 'posts':
case 'postmeta':
$total_stats = array(
'posts' => $this->total_count( 'posts' ),
'postmeta' => $this->total_count( 'postmeta' ),
);
break;
case 'comments':
case 'commentmeta':
$total_stats = array(
'comments' => $this->total_count( 'comments' ),
'commentmeta' => $this->total_count( 'commentmeta' ),
);
break;
case 'users':
case 'usermeta':
$total_stats = array(
'users' => $this->total_count( 'users' ),
'usermeta' => $this->total_count( 'usermeta' ),
);
break;
case 'term_relationships':
case 'term_taxonomy':
case 'terms':
case 'termmeta':
$total_stats = array(
'term_relationships' => $this->total_count( 'term_relationships' ),
'term_taxonomy' => $this->total_count( 'term_taxonomy' ),
'terms' => $this->total_count( 'terms' ),
'termmeta' => $this->total_count( 'termmeta' ),
);
break;
case 'options':
$total_stats = array( 'options' => $this->total_count( 'options' ) );
break;
case 'tables':
$total_stats = array( 'tables' => $this->total_count( 'tables' ) );
break;
}
wp_send_json_success(
array(
'sweep' => $sweep,
'count' => $count,
'total' => $total_count,
'percentage' => $this->format_percentage( $count, $total_count ),
'stats' => $total_stats,
)
);
}
/**
* Count the number of total items belonging to each sweep
*
* @since 1.0.0
*
* @access public
* @param string $name Sweep name.
* @return int Number of items belonging to each sweep
*/
public function total_count( $name ) {
global $wpdb;
$count = 0;
switch ( $name ) {
case 'posts':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts" );
break;
case 'postmeta':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->postmeta" );
break;
case 'comments':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments" );
break;
case 'commentmeta':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->commentmeta" );
break;
case 'users':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->users" );
break;
case 'usermeta':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->usermeta" );
break;
case 'term_relationships':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_relationships" );
break;
case 'term_taxonomy':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_taxonomy" );
break;
case 'terms':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
break;
case 'termmeta':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->termmeta" );
break;
case 'options':
$count = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->options" );
break;
case 'tables':
$count = count( $wpdb->get_col( 'SHOW TABLES' ) );
break;
}
return apply_filters( 'wp_sweep_total_count', $count, $name );
}
/**
* Count the number of items belonging to each sweep
*
* @since 1.0.0
*
* @access public
* @param string $name Sweep name.
* @return int Number of items belonging to each sweep
*/
public function count( $name ) {
global $wpdb;
$count = 0;
switch ( $name ) {
case 'revisions':
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_type = %s", 'revision' ) );
break;
case 'auto_drafts':
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_status = %s", 'auto-draft' ) );
break;
case 'deleted_posts':
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_status = %s", 'trash' ) );
break;
case 'unapproved_comments':
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = %s", '0' ) );
break;
case 'spam_comments':
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE comment_approved = %s", 'spam' ) );
break;
case 'deleted_comments':
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(comment_ID) FROM $wpdb->comments WHERE (comment_approved = %s OR comment_approved = %s)", 'trash', 'post-trashed' ) );
break;
case 'transient_options':
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(option_id) FROM $wpdb->options WHERE option_name LIKE(%s)", '%\_transient\_%' ) );
break;
case 'orphan_postmeta':
$count = $wpdb->get_var( "SELECT COUNT(meta_id) FROM $wpdb->postmeta WHERE post_id NOT IN (SELECT ID FROM $wpdb->posts)" );
break;
case 'orphan_commentmeta':
$count = $wpdb->get_var( "SELECT COUNT(meta_id) FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments)" );
break;
case 'orphan_usermeta':
$count = $wpdb->get_var( "SELECT COUNT(umeta_id) FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users)" );
break;
case 'orphan_termmeta':
$count = $wpdb->get_var( "SELECT COUNT(meta_id) FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms)" );
break;
case 'orphan_term_relationships':
$orphan_term_relationships_sql = implode( "','", array_map( 'esc_sql', $this->get_excluded_taxonomies() ) );
$count = $wpdb->get_var( "SELECT COUNT(object_id) FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy NOT IN ('$orphan_term_relationships_sql') AND tr.object_id NOT IN (SELECT ID FROM $wpdb->posts)" ); // phpcs:ignore
break;
case 'unused_terms':
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(t.term_id) FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.count = %d AND t.term_id NOT IN (" . implode( ',', $this->get_excluded_termids() ) . ')', 0 ) ); // phpcs:ignore
break;
case 'duplicated_postmeta':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT COUNT(meta_id) AS count FROM $wpdb->postmeta GROUP BY post_id, meta_key, meta_value HAVING count > %d", 1 ) );
if ( is_array( $query ) ) {
$count = array_sum( array_map( 'intval', $query ) );
}
break;
case 'duplicated_commentmeta':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT COUNT(meta_id) AS count FROM $wpdb->commentmeta GROUP BY comment_id, meta_key, meta_value HAVING count > %d", 1 ) );
if ( is_array( $query ) ) {
$count = array_sum( array_map( 'intval', $query ) );
}
break;
case 'duplicated_usermeta':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT COUNT(umeta_id) AS count FROM $wpdb->usermeta GROUP BY user_id, meta_key, meta_value HAVING count > %d", 1 ) );
if ( is_array( $query ) ) {
$count = array_sum( array_map( 'intval', $query ) );
}
break;
case 'duplicated_termmeta':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT COUNT(meta_id) AS count FROM $wpdb->termmeta GROUP BY term_id, meta_key, meta_value HAVING count > %d", 1 ) );
if ( is_array( $query ) ) {
$count = array_sum( array_map( 'intval', $query ) );
}
break;
case 'optimize_database':
$count = count( $wpdb->get_col( 'SHOW TABLES' ) );
break;
case 'oembed_postmeta':
$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(meta_id) FROM $wpdb->postmeta WHERE meta_key LIKE(%s)", '%_oembed_%' ) );
break;
}
return apply_filters( 'wp_sweep_count', $count, $name );
}
/**
* Return more details about a sweep
*
* @since 1.0.3
*
* @access public
* @param string $name Sweep name.
* @return array Details of items belonging to each sweep
*/
public function details( $name ) {
global $wpdb;
$details = array();
switch ( $name ) {
case 'revisions':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE post_type = %s LIMIT %d", 'revision', $this->limit_details ) );
break;
case 'auto_drafts':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE post_status = %s LIMIT %d", 'auto-draft', $this->limit_details ) );
break;
case 'deleted_posts':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT post_title FROM $wpdb->posts WHERE post_status = %s LIMIT %d", 'trash', $this->limit_details ) );
break;
case 'unapproved_comments':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT comment_author FROM $wpdb->comments WHERE comment_approved = %s LIMIT %d", '0', $this->limit_details ) );
break;
case 'spam_comments':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT comment_author FROM $wpdb->comments WHERE comment_approved = %s LIMIT %d", 'spam', $this->limit_details ) );
break;
case 'deleted_comments':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT comment_author FROM $wpdb->comments WHERE (comment_approved = %s OR comment_approved = %s) LIMIT %d", 'trash', 'post-trashed', $this->limit_details ) );
break;
case 'transient_options':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE(%s) LIMIT %d", '%\_transient\_%', $this->limit_details ) );
break;
case 'orphan_postmeta':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->postmeta WHERE post_id NOT IN (SELECT ID FROM $wpdb->posts) LIMIT %d", $this->limit_details ) );
break;
case 'orphan_commentmeta':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments) LIMIT %d", $this->limit_details ) );
break;
case 'orphan_usermeta':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users) LIMIT %d", $this->limit_details ) );
break;
case 'orphan_termmeta':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms) LIMIT %d", $this->limit_details ) );
break;
case 'orphan_term_relationships':
$orphan_term_relationships_sql = implode( "','", array_map( 'esc_sql', $this->get_excluded_taxonomies() ) );
$details = $wpdb->get_col( $wpdb->prepare( "SELECT tt.taxonomy FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy NOT IN ('$orphan_term_relationships_sql') AND tr.object_id NOT IN (SELECT ID FROM $wpdb->posts) LIMIT %d", $this->limit_details ) ); // phpcs:ignore
break;
case 'unused_terms':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT t.name FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.count = %d AND t.term_id NOT IN (" . implode( ',', $this->get_excluded_termids() ) . ') LIMIT %d', 0, $this->limit_details ) ); // phpcs:ignore
break;
case 'duplicated_postmeta':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(meta_id) AS count, meta_key FROM $wpdb->postmeta GROUP BY post_id, meta_key, meta_value HAVING count > %d LIMIT %d", 1, $this->limit_details ) );
$details = array();
if ( $query ) {
foreach ( $query as $meta ) {
$details[] = $meta->meta_key;
}
}
break;
case 'duplicated_commentmeta':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(meta_id) AS count, meta_key FROM $wpdb->commentmeta GROUP BY comment_id, meta_key, meta_value HAVING count > %d LIMIT %d", 1, $this->limit_details ) );
$details = array();
if ( $query ) {
foreach ( $query as $meta ) {
$details[] = $meta->meta_key;
}
}
break;
case 'duplicated_usermeta':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(umeta_id) AS count, meta_key FROM $wpdb->usermeta GROUP BY user_id, meta_key, meta_value HAVING count > %d LIMIT %d", 1, $this->limit_details ) );
$details = array();
if ( $query ) {
foreach ( $query as $meta ) {
$details[] = $meta->meta_key;
}
}
break;
case 'duplicated_termmeta':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(meta_id) AS count, meta_key FROM $wpdb->termmeta GROUP BY term_id, meta_key, meta_value HAVING count > %d LIMIT %d", 1, $this->limit_details ) );
$details = array();
if ( $query ) {
foreach ( $query as $meta ) {
$details[] = $meta->meta_key;
}
}
break;
case 'optimize_database':
$details = $wpdb->get_col( 'SHOW TABLES' );
break;
case 'oembed_postmeta':
$details = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM $wpdb->postmeta WHERE meta_key LIKE(%s) LIMIT %d", '%_oembed_%', $this->limit_details ) );
break;
}
return apply_filters( 'wp_sweep_details', $details, $name );
}
/**
* Does the sweeping/cleaning up
*
* @since 1.0.0
*
* @access public
* @param string $name Sweep name.
* @return string Processed message
*/
public function sweep( $name ) {
global $wpdb;
$message = '';
switch ( $name ) {
case 'revisions':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_type = %s", 'revision' ) );
if ( $query ) {
foreach ( $query as $id ) {
wp_delete_post_revision( (int) $id );
}
// translators: %s is Revisions count.
$message = sprintf( __( '%s Revisions Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'auto_drafts':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_status = %s", 'auto-draft' ) );
if ( $query ) {
foreach ( $query as $id ) {
wp_delete_post( (int) $id, true );
}
// translators: %s is the Auto Drafts count.
$message = sprintf( __( '%s Auto Drafts Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'deleted_posts':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_status = %s", 'trash' ) );
if ( $query ) {
foreach ( $query as $id ) {
wp_delete_post( $id, true );
}
// translators: %s is the Deleted Posts count.
$message = sprintf( __( '%s Deleted Posts Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'unapproved_comments':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_approved = %s", '0' ) );
if ( $query ) {
foreach ( $query as $id ) {
wp_delete_comment( (int) $id, true );
}
// translators: %s is the Unapproved Comments count.
$message = sprintf( __( '%s Unapproved Comments Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'spam_comments':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_approved = %s", 'spam' ) );
if ( $query ) {
foreach ( $query as $id ) {
wp_delete_comment( (int) $id, true );
}
// translators: %s is the Spam Comments count.
$message = sprintf( __( '%s Spam Comments Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'deleted_comments':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE (comment_approved = %s OR comment_approved = %s)", 'trash', 'post-trashed' ) );
if ( $query ) {
foreach ( $query as $id ) {
wp_delete_comment( (int) $id, true );
}
// translators: %s is the Trash Comments count.
$message = sprintf( __( '%s Trash Comments Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'transient_options':
$query = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE(%s)", '%\_transient\_%' ) );
if ( $query ) {
foreach ( $query as $option_name ) {
if ( strpos( $option_name, '_site_transient_' ) !== false ) {
delete_site_transient( str_replace( '_site_transient_', '', $option_name ) );
} else {
delete_transient( str_replace( '_transient_', '', $option_name ) );
}
}
// translators: %s is the Transient Options count.
$message = sprintf( __( '%s Transient Options Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'orphan_postmeta':
$query = $wpdb->get_results( "SELECT post_id, meta_key FROM $wpdb->postmeta WHERE post_id NOT IN (SELECT ID FROM $wpdb->posts)" );
if ( $query ) {
foreach ( $query as $meta ) {
$post_id = (int) $meta->post_id;
if ( 0 === $post_id ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta->meta_key ) );
} else {
delete_post_meta( $post_id, $meta->meta_key );
}
}
// translators: %s is the Orphaned Post Meta count.
$message = sprintf( __( '%s Orphaned Post Meta Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'orphan_commentmeta':
$query = $wpdb->get_results( "SELECT comment_id, meta_key FROM $wpdb->commentmeta WHERE comment_id NOT IN (SELECT comment_ID FROM $wpdb->comments)" );
if ( $query ) {
foreach ( $query as $meta ) {
$comment_id = (int) $meta->comment_id;
if ( 0 === $comment_id ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->commentmeta WHERE comment_id = %d AND meta_key = %s", $comment_id, $meta->meta_key ) );
} else {
delete_comment_meta( $comment_id, $meta->meta_key );
}
}
// translators: %s is the Orphaned Comment Meta count.
$message = sprintf( __( '%s Orphaned Comment Meta Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'orphan_usermeta':
$query = $wpdb->get_results( "SELECT user_id, meta_key FROM $wpdb->usermeta WHERE user_id NOT IN (SELECT ID FROM $wpdb->users)" );
if ( $query ) {
foreach ( $query as $meta ) {
$user_id = (int) $meta->user_id;
if ( 0 === $user_id ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->usermeta WHERE user_id = %d AND meta_key = %s", $user_id, $meta->meta_key ) );
} else {
delete_user_meta( $user_id, $meta->meta_key );
}
}
// translators: %s is the Orphaned User Meta count.
$message = sprintf( __( '%s Orphaned User Meta Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'orphan_termmeta':
$query = $wpdb->get_results( "SELECT term_id, meta_key FROM $wpdb->termmeta WHERE term_id NOT IN (SELECT term_id FROM $wpdb->terms)" );
if ( $query ) {
foreach ( $query as $meta ) {
$term_id = (int) $meta->term_id;
if ( 0 === $term_id ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->termmeta WHERE term_id = %d AND meta_key = %s", $term_id, $meta->meta_key ) );
} else {
delete_term_meta( $term_id, $meta->meta_key );
}
}
// translators: %s is the Orphaned Term Meta count.
$message = sprintf( __( '%s Orphaned Term Meta Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'orphan_term_relationships':
$query = $wpdb->get_results( "SELECT tr.object_id, tr.term_taxonomy_id, tt.term_id, tt.taxonomy FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->term_taxonomy AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy NOT IN ('" . implode( '\',\'', $this->get_excluded_taxonomies() ) . "') AND tr.object_id NOT IN (SELECT ID FROM $wpdb->posts)" ); // phpcs:ignore
if ( $query ) {
foreach ( $query as $tax ) {
$wp_remove_object_terms = wp_remove_object_terms( (int) $tax->object_id, (int) $tax->term_id, $tax->taxonomy );
if ( true !== $wp_remove_object_terms ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id = %d", $tax->object_id, $tax->term_taxonomy_id ) );
}
}
// translators: %s is the Orphaned Term Relationships count.
$message = sprintf( __( '%s Orphaned Term Relationships Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'unused_terms':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT tt.term_taxonomy_id, t.term_id, tt.taxonomy FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.count = %d AND t.term_id NOT IN (" . implode( ',', $this->get_excluded_termids() ) . ')', 0 ) ); // phpcs:ignore
if ( $query ) {
$check_wp_terms = false;
foreach ( $query as $tax ) {
if ( taxonomy_exists( $tax->taxonomy ) ) {
wp_delete_term( (int) $tax->term_id, $tax->taxonomy );
} else {
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", (int) $tax->term_taxonomy_id ) );
$check_wp_terms = true;
}
}
// We need this for invalid taxonomies.
if ( $check_wp_terms ) {
$wpdb->get_results( "DELETE FROM $wpdb->terms WHERE term_id NOT IN (SELECT term_id FROM $wpdb->term_taxonomy)" );
}
// translators: %s is the Unused Terms count.
$message = sprintf( __( '%s Unused Terms Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'duplicated_postmeta':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT GROUP_CONCAT(meta_id ORDER BY meta_id DESC) AS ids, post_id, COUNT(*) AS count FROM $wpdb->postmeta GROUP BY post_id, meta_key, meta_value HAVING count > %d", 1 ) );
if ( $query ) {
foreach ( $query as $meta ) {
$ids = array_map( 'intval', explode( ',', $meta->ids ) );
array_pop( $ids );
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE meta_id IN (" . implode( ',', $ids ) . ') AND post_id = %d', (int) $meta->post_id ) ); // phpcs:ignore
}
// translators: %s is the Duplicated Post Meta count.
$message = sprintf( __( '%s Duplicated Post Meta Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'duplicated_commentmeta':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT GROUP_CONCAT(meta_id ORDER BY meta_id DESC) AS ids, comment_id, COUNT(*) AS count FROM $wpdb->commentmeta GROUP BY comment_id, meta_key, meta_value HAVING count > %d", 1 ) );
if ( $query ) {
foreach ( $query as $meta ) {
$ids = array_map( 'intval', explode( ',', $meta->ids ) );
array_pop( $ids );
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->commentmeta WHERE meta_id IN (" . implode( ',', $ids ) . ') AND comment_id = %d', (int) $meta->comment_id ) ); // phpcs:ignore
}
// translators: %s is the Duplicated Comment Meta count.
$message = sprintf( __( '%s Duplicated Comment Meta Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'duplicated_usermeta':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT GROUP_CONCAT(umeta_id ORDER BY umeta_id DESC) AS ids, user_id, COUNT(*) AS count FROM $wpdb->usermeta GROUP BY user_id, meta_key, meta_value HAVING count > %d", 1 ) );
if ( $query ) {
foreach ( $query as $meta ) {
$ids = array_map( 'intval', explode( ',', $meta->ids ) );
array_pop( $ids );
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->usermeta WHERE umeta_id IN (" . implode( ',', $ids ) . ') AND user_id = %d', (int) $meta->user_id ) ); // phpcs:ignore
}
// translators: %s is the Duplicated User Meta count.
$message = sprintf( __( '%s Duplicated User Meta Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'duplicated_termmeta':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT GROUP_CONCAT(meta_id ORDER BY meta_id DESC) AS ids, term_id, COUNT(*) AS count FROM $wpdb->termmeta GROUP BY term_id, meta_key, meta_value HAVING count > %d", 1 ) );
if ( $query ) {
foreach ( $query as $meta ) {
$ids = array_map( 'intval', explode( ',', $meta->ids ) );
array_pop( $ids );
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->termmeta WHERE meta_id IN (" . implode( ',', $ids ) . ') AND term_id = %d', (int) $meta->term_id ) ); // phpcs:ignore
}
// translators: %s is the Duplicated Term Meta count.
$message = sprintf( __( '%s Duplicated Term Meta Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'optimize_database':
$query = $wpdb->get_col( 'SHOW TABLES' );
if ( $query ) {
$tables = implode( ',', $query );
$wpdb->query( "OPTIMIZE TABLE $tables" ); // phpcs:ignore
// translators: %s is the Tables count.
$message = sprintf( __( '%s Tables Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
case 'oembed_postmeta':
$query = $wpdb->get_results( $wpdb->prepare( "SELECT post_id, meta_key FROM $wpdb->postmeta WHERE meta_key LIKE(%s)", '%_oembed_%' ) );
if ( $query ) {
foreach ( $query as $meta ) {
$post_id = (int) $meta->post_id;
if ( 0 === $post_id ) {
$wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $meta->meta_key ) );
} else {
delete_post_meta( $post_id, $meta->meta_key );
}
}
// translators: %s is the oEmbed Caches count.
$message = sprintf( __( '%s oEmbed Caches In Post Meta Processed', 'wp-sweep' ), number_format_i18n( count( $query ) ) );
}
break;
}
return apply_filters( 'wp_sweep_sweep', $message, $name );
}
/**
* Format number to percentage, taking care of division by 0.
* Props @barisunver https://github.com/barisunver
*
* @since 1.0.2
*
* @access public
* @param int $current Current number.
* @param int $total Total number.
* @return string Number in percentage
*/
public function format_percentage( $current, $total ) {
return ( $total > 0 ? round( ( $current / $total ) * 100, 2 ) : 0 ) . '%';
}
/**
* Get excluded taxonomies
*
* @since 1.0.8
*
* @access private
* @return array Excluded taxonomies
*/
private function get_excluded_taxonomies() {
$excluded_taxonomies = array();
$excluded_taxonomies[] = 'link_category';
return apply_filters( 'wp_sweep_excluded_taxonomies', $excluded_taxonomies );
}
/**
* Get excluded term IDs
*
* @since 1.0.3
*
* @access private
* @return array Excluded term IDs
*/
private function get_excluded_termids() {
$default_term_ids = $this->get_default_taxonomy_termids();
if ( ! is_array( $default_term_ids ) ) {
$default_term_ids = array();
}
$parent_term_ids = $this->get_parent_termids();
if ( ! is_array( $parent_term_ids ) ) {
$parent_term_ids = array();
}
$excluded_termids = array_merge( $default_term_ids, $parent_term_ids );
return apply_filters( 'wp_sweep_excluded_termids', $excluded_termids );
}
/**
* Get all default taxonomy term IDs
*
* @since 1.0.3
*
* @access private
* @return array Default taxonomy term IDs
*/
private function get_default_taxonomy_termids() {
$taxonomies = get_taxonomies();
$default_term_ids = array();
if ( $taxonomies ) {
$tax = array_keys( $taxonomies );
if ( $tax ) {
foreach ( $tax as $t ) {
$term_id = (int) get_option( 'default_' . $t );
if ( $term_id > 0 ) {
$default_term_ids[] = $term_id;
}
}
}
}
return $default_term_ids;
}
/**
* Get terms that has a parent term
*
* @since 1.0.3
*
* @access private
* @return array Parent term IDs
*/
private function get_parent_termids() {
global $wpdb;
return $wpdb->get_col( $wpdb->prepare( "SELECT tt.parent FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.parent > %d", 0 ) );
}
/**
* What to do when the plugin is being deactivated
*
* @since 1.0.0
*
* @access public
* @param boolean $network_wide Is network wide.
* @return void
*/
public function plugin_activation( $network_wide ) {
if ( is_multisite() && $network_wide ) {
$ms_sites = (array) get_sites();
if ( 0 < count( $ms_sites ) ) {
foreach ( $ms_sites as $ms_site ) {
switch_to_blog( $ms_site->blog_id );
$this->plugin_activated();
restore_current_blog();
}
}
} else {
$this->plugin_activated();
}
}
/**
* Perform plugin activation tasks
*
* @since 1.0.0
*
* @access private
* @return void
*/
private function plugin_activated() {
}
/**
* What to do when the plugin is being activated
*
* @since 1.0.0
*
* @access public
* @param boolean $network_wide Is network wide.
* @return void
*/
public function plugin_deactivation( $network_wide ) {
if ( is_multisite() && $network_wide ) {
$ms_sites = (array) get_sites();
if ( 0 < count( $ms_sites ) ) {
foreach ( $ms_sites as $ms_site ) {
switch_to_blog( $ms_site->blog_id );
$this->plugin_deactivated();
restore_current_blog();
}
}
} else {
$this->plugin_deactivated();
}
}
/**
* Perform plugin deactivation tasks
*
* @since 1.0.0
*
* @access private
* @return void
*/
private function plugin_deactivated() {
}
}