Files
2meet-data-optimizer/uninstall.php
T
wpdev b917e45c8c
Anti-EAV Lint + Quality Gate / anti-eav-lint (pull_request) Successful in 14s
Tests / PHP Lint (pull_request) Successful in 1m0s
Tests / PHPCS (pull_request) Successful in 1m15s
Tests / Unit Tests (pull_request) Successful in 57s
Tests / Integration Tests (pull_request) Successful in 1m14s
Tests / PHPStan (pull_request) Successful in 34s
chore(2meet-data-optimizer): make the hardening branch pass PHPStan and PHPCS
- CI PHPStan (level 6, baseline for pre-existing debt) failed on 35 errors in
  code added on hardening/2.0: missing iterable value types (PHPDoc only), a
  redundant method_exists() in uninstall.php, and the new WP-CLI commands
  registered as array('Class', 'instanceMethod') — now array(new Class(), ...)
- Local runs had been masked by a PHPStan tmpDir shared with the act CI runner
  (cached container pointing at a removed phar); use a private tmpDir
- Verify: phpstan (private tmpDir) no errors, phpcs clean on changed files,
  phpunit Unit 658 OK; dev22 (running this branch) CLI post-status-sync /
  bridge-restore-native dry runs OK
2026-09-26 07:45:05 +08:00

138 lines
4.7 KiB
PHP
Raw 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 ─────────────────────────
$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',
);
$bootstrap_ok = true;
foreach ( $bootstrap_files as $f ) {
if ( ! is_file( $f ) ) {
$bootstrap_ok = false;
continue;
}
require_once $f;
}
// ── 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 ( ! $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( '目前沒有把這些資料寫回 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' ) ) {
$result = 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( $result['ok'] ) ) {
$snapshot_id = $result['snapshot_id'];
$dir = TMDO_Snapshot_Manager::backup_dir();
if ( is_dir( $dir ) ) {
file_put_contents(
trailingslashit( $dir ) . 'README-pre-uninstall.txt',
"WPDO pre-uninstall snapshot: {$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 {$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();
}
);