backup_dir = wp_upload_dir()['basedir'] . '/acmt-backups'; $this->init(); } /** * Initialize the backup manager */ public function init() { // Hook into cleanup operations to create automatic backups add_action( 'acmt_before_cleanup', array( $this, 'create_automatic_backup' ), 10, 1 ); // Ensure backup directory exists $this->ensure_backup_directory(); } /** * Ensure backup directory exists and is protected */ private function ensure_backup_directory() { if ( ! file_exists( $this->backup_dir ) ) { wp_mkdir_p( $this->backup_dir ); } // Create .htaccess file to protect backup directory $htaccess_file = $this->backup_dir . '/.htaccess'; if ( ! file_exists( $htaccess_file ) ) { $htaccess_content = "# Protect backup files\nOrder deny,allow\nDeny from all"; file_put_contents( $htaccess_file, $htaccess_content ); } // Create index.php file to prevent directory listing $index_file = $this->backup_dir . '/index.php'; if ( ! file_exists( $index_file ) ) { file_put_contents( $index_file, 'create_backup( 'auto', $cleanup_type ); } /** * Create a backup of relevant data * * @param string $type Backup type ('manual' or 'auto') * @param string $cleanup_type Type of cleanup (optional) * @return bool|string Backup ID on success, false on failure */ public function create_backup( $type = 'manual', $cleanup_type = '' ) { global $wpdb; try { $backup_id = 'acmt_backup_' . time() . '_' . wp_generate_password( 8, false ); $backup_file = $this->backup_dir . '/' . $backup_id . '.sql'; // Get tables to backup based on cleanup type $tables_to_backup = $this->get_tables_for_backup( $cleanup_type ); if ( empty( $tables_to_backup ) ) { return false; } $backup_content = $this->generate_backup_content( $tables_to_backup ); if ( false === file_put_contents( $backup_file, $backup_content ) ) { return false; } // Log backup creation $this->log_backup_creation( $backup_id, $type, $cleanup_type, filesize( $backup_file ) ); // Clean up old backups $this->cleanup_old_backups(); return $backup_id; } catch ( Exception $e ) { error_log( 'ACMT Backup Error: ' . $e->getMessage() ); return false; } } /** * Get tables that need to be backed up based on cleanup type * * @param string $cleanup_type Type of cleanup operation * @return array Array of table names to backup */ private function get_tables_for_backup( $cleanup_type ) { global $wpdb; $tables = array(); switch ( $cleanup_type ) { case 'clean_drafts': case 'clean_auto_drafts': case 'clean_trashed_posts': case 'clean_post_revisions': $tables[] = $wpdb->posts; $tables[] = $wpdb->postmeta; break; case 'clean_unapproved_comments': case 'clean_spam_comments': $tables[] = $wpdb->comments; $tables[] = $wpdb->commentmeta; break; case 'clean_orphaned_media': $tables[] = $wpdb->posts; $tables[] = $wpdb->postmeta; break; case 'clean_transients': $tables[] = $wpdb->options; break; default: // For manual backups or unknown types, backup core content tables $tables = array( $wpdb->posts, $wpdb->postmeta, $wpdb->comments, $wpdb->commentmeta, $wpdb->options ); break; } return array_unique( $tables ); } /** * Generate backup content for specified tables * * @param array $tables Array of table names * @return string SQL backup content */ private function generate_backup_content( $tables ) { global $wpdb; $backup_content = "-- Advanced Clean Master Backup\n"; $backup_content .= "-- Generated on: " . current_time( 'mysql' ) . "\n"; $backup_content .= "-- WordPress Version: " . get_bloginfo( 'version' ) . "\n\n"; foreach ( $tables as $table ) { $backup_content .= "-- Table: {$table}\n"; $backup_content .= "DROP TABLE IF EXISTS `{$table}_acmt_backup`;\n"; $backup_content .= "CREATE TABLE `{$table}_acmt_backup` LIKE `{$table}`;\n"; $backup_content .= "INSERT INTO `{$table}_acmt_backup` SELECT * FROM `{$table}`;\n\n"; } return $backup_content; } /** * Log backup creation in database * * @param string $backup_id Backup identifier * @param string $type Backup type * @param string $cleanup_type Cleanup type * @param int $file_size Backup file size */ private function log_backup_creation( $backup_id, $type, $cleanup_type, $file_size ) { global $wpdb; $wpdb->insert( $wpdb->prefix . 'acmt_backups', array( 'backup_id' => $backup_id, 'backup_type' => $type, 'cleanup_type' => $cleanup_type, 'file_size' => $file_size, 'created_on' => current_time( 'mysql' ), 'status' => 'completed' ), array( '%s', '%s', '%s', '%d', '%s', '%s' ) ); } /** * Clean up old backups to maintain storage limits */ private function cleanup_old_backups() { global $wpdb; // Get backups older than max_backups limit $old_backups = $wpdb->get_results( $wpdb->prepare( "SELECT backup_id FROM {$wpdb->prefix}acmt_backups ORDER BY created_on DESC LIMIT %d, 999999", $this->max_backups ) ); foreach ( $old_backups as $backup ) { $this->delete_backup( $backup->backup_id ); } } /** * Delete a backup file and database record * * @param string $backup_id Backup identifier * @return bool Success status */ public function delete_backup( $backup_id ) { global $wpdb; // Sanitize backup ID $backup_id = sanitize_text_field( $backup_id ); // Delete backup file $backup_file = $this->backup_dir . '/' . $backup_id . '.sql'; if ( file_exists( $backup_file ) ) { unlink( $backup_file ); } // Delete database record $wpdb->delete( $wpdb->prefix . 'acmt_backups', array( 'backup_id' => $backup_id ), array( '%s' ) ); return true; } /** * Get list of available backups * * @return array Array of backup information */ public function get_backups() { global $wpdb; $cache_key = 'acmt_backups_list'; $backups = wp_cache_get( $cache_key ); if ( false === $backups ) { $backups = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}acmt_backups ORDER BY created_on DESC" ); wp_cache_set( $cache_key, $backups, '', 3600 ); } return $backups; } /** * Restore backup from file * * @param string $backup_id Backup identifier * @return bool Success status */ public function restore_backup( $backup_id ) { global $wpdb; $backup_file = $this->backup_dir . '/' . $backup_id . '.sql'; if ( ! file_exists( $backup_file ) ) { return false; } try { $sql_content = file_get_contents( $backup_file ); if ( false === $sql_content ) { return false; } // Execute SQL statements $statements = explode( ';', $sql_content ); foreach ( $statements as $statement ) { $statement = trim( $statement ); if ( ! empty( $statement ) && ! preg_match( '/^--/', $statement ) ) { $wpdb->query( $statement ); } } return true; } catch ( Exception $e ) { error_log( 'ACMT Restore Error: ' . $e->getMessage() ); return false; } } /** * Get backup statistics * * @return array Backup statistics */ public function get_backup_stats() { global $wpdb; $stats = array( 'total_backups' => 0, 'total_size' => 0, 'auto_backups' => 0, 'manual_backups' => 0, 'latest_backup' => null ); $backups = $this->get_backups(); if ( ! empty( $backups ) ) { $stats['total_backups'] = count( $backups ); $stats['latest_backup'] = $backups[0]; foreach ( $backups as $backup ) { $stats['total_size'] += $backup->file_size; if ( $backup->backup_type === 'auto' ) { $stats['auto_backups']++; } else { $stats['manual_backups']++; } } } return $stats; } /** * Download backup file */ public function download_backup( $backup_id ) { if ( empty( $backup_id ) ) { wp_die( 'Backup ID not specified.' ); } $backup_file = $this->backup_dir . '/' . $backup_id . '.sql'; if ( ! file_exists( $backup_file ) ) { wp_die( 'Backup file not found.' ); } // Set headers for file download header( 'Content-Type: application/octet-stream' ); header( 'Content-Disposition: attachment; filename="' . $backup_id . '.sql"' ); header( 'Content-Length: ' . filesize( $backup_file ) ); readfile( $backup_file ); exit; } }