base_prefix . self::ORIGINAL_FRAGMENT; if ( ! str_starts_with( $original_table, $orig_prefix ) ) { return ''; } return $wpdb->base_prefix . self::BACKUP_FRAGMENT . substr( $original_table, strlen( $orig_prefix ) ); } /** * Return the original table name for a given backup table. * * @param string $backup_table Full backup table name (including base_prefix). * @return string Original table name, or empty string if not a backup table. */ public static function original_name( string $backup_table ): string { global $wpdb; $bak_prefix = $wpdb->base_prefix . self::BACKUP_FRAGMENT; if ( ! str_starts_with( $backup_table, $bak_prefix ) ) { return ''; } return $wpdb->base_prefix . self::ORIGINAL_FRAGMENT . substr( $backup_table, strlen( $bak_prefix ) ); } /** * Return all tables in the database with the original MLP2 prefix. * * @return string[] Array of full table names. */ public static function get_original_tables(): array { global $wpdb; $like = $wpdb->esc_like( $wpdb->base_prefix . self::ORIGINAL_FRAGMENT ) . '%'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $tables = $wpdb->get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', $like ) ); return is_array( $tables ) ? $tables : array(); } /** * Return all tables in the database with the backup prefix. * * @return string[] Array of full table names. */ public static function get_backup_tables(): array { global $wpdb; $like = $wpdb->esc_like( $wpdb->base_prefix . self::BACKUP_FRAGMENT ) . '%'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $tables = $wpdb->get_col( $wpdb->prepare( 'SHOW TABLES LIKE %s', $like ) ); return is_array( $tables ) ? $tables : array(); } /** * Whether any original MLP2 tables exist in the database. * * @return bool */ public static function has_original_tables(): bool { return count( self::get_original_tables() ) > 0; } /** * Whether any backup (renamed) MLP2 tables exist in the database. * * @return bool */ public static function has_backup_tables(): bool { return count( self::get_backup_tables() ) > 0; } /** * Rename all original MLP2 tables to their backup equivalents. * * Tables that already have a backup copy are skipped. Tables that cannot * be converted to a valid backup name are recorded as errors. * * @return array { * @type string[] $renamed Tables successfully renamed. * @type string[] $skipped Tables skipped (backup name already exists). * @type string[] $errors Tables that could not be renamed. * } */ public static function rename_to_backup(): array { global $wpdb; $result = array( 'renamed' => array(), 'skipped' => array(), 'errors' => array(), ); foreach ( self::get_original_tables() as $table ) { $backup = self::backup_name( $table ); if ( '' === $backup ) { $result['errors'][] = $table; continue; } // Skip tables that belong to this plugin. $suffix = substr( $table, strlen( $wpdb->base_prefix . self::ORIGINAL_FRAGMENT ) ); if ( in_array( $suffix, self::PLUGIN_TABLE_SUFFIXES, true ) ) { $result['skipped'][] = $table; continue; } if ( self::table_exists( $backup ) ) { $result['skipped'][] = $table; continue; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $renamed = $wpdb->query( "RENAME TABLE `{$table}` TO `{$backup}`" ); if ( false === $renamed ) { $result['errors'][] = $table; } else { $result['renamed'][] = $table; } } return $result; } /** * Restore backup tables to their original names. * * Intended as a recovery mechanism if the rename needs to be undone before * MLP5 has been activated. Tables whose original name already exists in the * database are skipped. * * @return array { * @type string[] $restored Tables successfully restored. * @type string[] $skipped Tables skipped (original name already exists). * @type string[] $errors Tables that could not be restored. * } */ public static function restore_from_backup(): array { global $wpdb; $result = array( 'restored' => array(), 'skipped' => array(), 'errors' => array(), ); foreach ( self::get_backup_tables() as $table ) { $original = self::original_name( $table ); if ( '' === $original ) { $result['errors'][] = $table; continue; } if ( self::table_exists( $original ) ) { $result['skipped'][] = $table; continue; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $restored = $wpdb->query( "RENAME TABLE `{$table}` TO `{$original}`" ); if ( false === $restored ) { $result['errors'][] = $table; } else { $result['restored'][] = $table; } } return $result; } /** * Check whether a table exists in the database. * * @param string $table Full table name. * @return bool */ private static function table_exists( string $table ): bool { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) ) === $table; } }