Files
wpdev cd5da4143e chore(2meet-data-optimizer): uninstall message points to bridge-restore-native; stress tester drops wc_product
- The refused-uninstall notice said no tool writes the data back to native
  tables; wp tmdo bridge-restore-native does.
- The post stress tester no longer maps product to the retired wc_product table.
- Verify: unit 711, integration 611, phpcs 0
- PR: PR-139 (manual F-7, F-8)
2026-09-28 18:26:15 +08:00

138 lines
4.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* WP Data Optimizer — Uninstall script.
*
* Runs only when the plugin is deleted via the WordPress admin.
* Removes all WPDO tables and options. Does NOT remove HPCT tables.
*
* Refuses (wp_die, before any snapshot or DROP) while any site still holds data
* that exists only in wpdo_* tables — see TMDO_Installer::uninstall_blockers().
* wp_die() also stops WordPress from deleting the plugin files.
*
* v2.2.0 hardening: take a `pre_uninstall` snapshot BEFORE dropping tables.
* The snapshot file under wp-content/uploads/wpdo-backups/ survives plugin
* deletion (uploads dir is never auto-purged), so the user can re-install +
* `wp wpdo snapshot restore <id> --apply` to recover.
*
* Multisite: every site is checked and cleaned. The plugin may have been active
* per site, and it is always inactive by the time it can be deleted, so network
* activation cannot tell which sites hold wpdo_* tables.
*
* @package WP_Data_Optimizer
*/
declare(strict_types=1);
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
exit;
}
global $wpdb;
// ── Load shared cleanup logic + snapshot classes ─────────────────────────
$wpdo_bootstrap_files = array(
__DIR__ . '/includes/class-tmdo-logger.php',
__DIR__ . '/includes/class-tmdo-feature-flags.php',
__DIR__ . '/includes/class-tmdo-installer.php',
__DIR__ . '/includes/snapshots/class-tmdo-snapshot-manager.php',
__DIR__ . '/includes/snapshots/class-tmdo-snapshot-writer.php',
__DIR__ . '/includes/snapshots/class-tmdo-snapshot-reader.php',
__DIR__ . '/includes/snapshots/class-tmdo-snapshot-pruner.php',
);
$wpdo_bootstrap_ok = true;
foreach ( $wpdo_bootstrap_files as $wpdo_bootstrap_file ) {
if ( ! is_file( $wpdo_bootstrap_file ) ) {
$wpdo_bootstrap_ok = false;
continue;
}
require_once $wpdo_bootstrap_file;
}
// ── Sites to check and clean (0 = single site, no blog switching) ────────
$wpdo_site_ids = is_multisite()
? array_map(
'intval',
get_sites(
array(
'fields' => 'ids',
'number' => 0,
)
)
)
: array( 0 );
$wpdo_for_each_site = static function ( callable $callback ) use ( $wpdo_site_ids ): void {
foreach ( $wpdo_site_ids as $wpdo_blog_id ) {
if ( $wpdo_blog_id ) {
switch_to_blog( $wpdo_blog_id );
}
try {
$callback( $wpdo_blog_id );
} finally {
if ( $wpdo_blog_id ) {
restore_current_blog();
}
}
}
};
// ── Refuse while any site holds data that exists only in wpdo_* tables ───
if ( ! $wpdo_bootstrap_ok ) {
wp_die( esc_html( '2meet Data Optimizer:無法載入資料安全檢查,為避免資料遺失,已停止解除安裝。' ) );
}
$wpdo_blocked = array();
$wpdo_for_each_site(
static function ( int $blog_id ) use ( &$wpdo_blocked ): void {
foreach ( TMDO_Installer::uninstall_blockers() as $reason ) {
$wpdo_blocked[] = ( $blog_id ? "站台 #{$blog_id}:" : '' ) . $reason;
}
}
);
if ( $wpdo_blocked ) {
wp_die(
'<p>' . esc_html( '2meet Data Optimizer 已停止解除安裝:以下資料只存在 wpdo_* 表,刪除外掛會一併刪除這些表。' ) . '</p>'
. '<ul><li>' . implode( '</li><li>', array_map( 'esc_html', $wpdo_blocked ) ) . '</li></ul>'
. '<p>' . esc_html( '請先以 wp tmdo bridge-restore-native 把資料寫回 WordPress 原生表,再解除安裝。若只想移除外掛程式,可手動刪除外掛目錄,資料表會保留。' ) . '</p>'
);
}
// ── v2.2.0 M1.5: pre-uninstall snapshot (main-site context only) ─────────
// Snapshot is taken once on the main site — it captures the catalog of all
// wpdo_snapshots rows (which is per-site so per-site snapshots survive the
// site-level table DROP that happens later). Best-effort.
if ( class_exists( 'TMDO_Snapshot_Manager' ) ) {
$wpdo_snapshot = TMDO_Snapshot_Manager::create(
'pre_uninstall',
array(),
array(
'notes' => 'Auto-snapshot taken at plugin uninstall time',
'retention_days' => 365,
'inline_threshold_bytes' => 0,
)
);
if ( ! empty( $wpdo_snapshot['ok'] ) ) {
$wpdo_snapshot_id = $wpdo_snapshot['snapshot_id'];
$wpdo_backup_dir = TMDO_Snapshot_Manager::backup_dir();
if ( is_dir( $wpdo_backup_dir ) ) {
file_put_contents(
trailingslashit( $wpdo_backup_dir ) . 'README-pre-uninstall.txt',
"WPDO pre-uninstall snapshot: {$wpdo_snapshot_id}\n"
. 'Created: ' . gmdate( 'Y-m-d H:i:s' ) . " UTC\n\n"
. "To restore after re-installing wp-data-optimizer:\n"
. " 1. Re-install the plugin (it will re-create wp_wpdo_snapshots).\n"
. " 2. Manually re-insert this snapshot's catalog row, or use wp wpdo snapshot list to confirm.\n"
. " 3. Run: wp wpdo snapshot restore {$wpdo_snapshot_id} --apply\n"
);
}
}
}
// ── Drop wpdo_* tables and options on every site ─────────────────────────
$wpdo_for_each_site(
static function (): void {
TMDO_Installer::drop_all_tables_for_current_blog();
}
);