fix(2meet-data-optimizer): deactivate and uninstall clear every scheduled event and tmdo_* option
- One CRON_HOOKS list for deactivate() and uninstall; wp_unschedule_hook() also removes events scheduled with args (tmdo_drift_resync, backfill batches). - Fix the wrong wpdo_user_stress_test_batch name; add migration tick, entity backfill, auto-promote and all stress-test batch hooks. - Uninstall sweeps tmdo_* options and _transient_(timeout_)tmdo_* rows too. - Verify: InstallerCleanupTest (3 new), unit 711, integration 611, phpcs 0 - PR: PR-138 (manual F-4, F-5)
This commit is contained in:
@@ -780,31 +780,46 @@ class TMDO_Installer {
|
|||||||
return $status;
|
return $status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every cron hook the plugin schedules. Cleared with wp_unschedule_hook(),
|
||||||
|
* which also removes events scheduled with args (tmdo_drift_resync( 'post' ),
|
||||||
|
* backfill batches) that wp_clear_scheduled_hook( $hook ) leaves behind
|
||||||
|
* (2.4.2 PR-138).
|
||||||
|
*/
|
||||||
|
private const CRON_HOOKS = array(
|
||||||
|
'wpdo_warm_cleanup',
|
||||||
|
'wpdo_archive_sweep',
|
||||||
|
'wpdo_errors_gc',
|
||||||
|
'wpdo_flush_views',
|
||||||
|
'wpdo_remove_uae_plugin_dir',
|
||||||
|
'wpdo_daily_health_check',
|
||||||
|
'wpdo_snapshot_prune_daily',
|
||||||
|
'wpdo_fsm_automator_run',
|
||||||
|
'wpdo_collect_site_metrics',
|
||||||
|
'wpdo_health_snapshot_monthly',
|
||||||
|
'wpdo_post_shadow_verify',
|
||||||
|
'wpdo_term_comment_shadow_verify',
|
||||||
|
'tmdo_drift_resync',
|
||||||
|
'tmdo_ensure_group_indexes',
|
||||||
|
'wpdo_entity_backfill_batch',
|
||||||
|
'wpdo_migration_tick',
|
||||||
|
'wpdo_auto_promote_check',
|
||||||
|
'wpdo_post_stress_test_batch',
|
||||||
|
'wpdo_stress_test_batch',
|
||||||
|
'wpdo_term_stress_test_batch',
|
||||||
|
'wpdo_comment_stress_test_batch',
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called on plugin deactivation. Non-destructive.
|
* Called on plugin deactivation. Non-destructive.
|
||||||
*/
|
*/
|
||||||
public static function deactivate(): void {
|
public static function deactivate(): void {
|
||||||
wp_clear_scheduled_hook( 'wpdo_warm_cleanup' );
|
foreach ( self::CRON_HOOKS as $hook ) {
|
||||||
wp_clear_scheduled_hook( 'wpdo_archive_sweep' );
|
wp_unschedule_hook( $hook );
|
||||||
// v2.14.0: clear all plugin cron hooks (was incomplete pre-v2.14.0).
|
}
|
||||||
wp_clear_scheduled_hook( 'wpdo_errors_gc' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_flush_views' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_remove_uae_plugin_dir' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_daily_health_check' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_snapshot_prune_daily' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_fsm_automator_run' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_collect_site_metrics' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_health_snapshot_monthly' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_post_shadow_verify' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_term_comment_shadow_verify' );
|
|
||||||
wp_clear_scheduled_hook( 'tmdo_drift_resync' );
|
|
||||||
wp_clear_scheduled_hook( 'tmdo_ensure_group_indexes' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_post_stress_test_batch' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_user_stress_test_batch' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_term_stress_test_batch' );
|
|
||||||
wp_clear_scheduled_hook( 'wpdo_comment_stress_test_batch' );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ── v2.14.0: Multisite-aware site cleanup ─────────────────────────────────
|
// ── v2.14.0: Multisite-aware site cleanup ─────────────────────────────────
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -976,42 +991,25 @@ class TMDO_Installer {
|
|||||||
// Sweep any remaining wpdo_* options (stragglers introduced post-v2.14.0
|
// Sweep any remaining wpdo_* options (stragglers introduced post-v2.14.0
|
||||||
// or by 3rd-party hooks). Validates name pattern before delete to avoid
|
// or by 3rd-party hooks). Validates name pattern before delete to avoid
|
||||||
// accidental option removal.
|
// accidental option removal.
|
||||||
$residual = $wpdb->get_col(
|
// 1.4.2: tmdo_* options and their transients too (tmdo_schema_deferred,
|
||||||
$wpdb->prepare(
|
// tmdo_entity_indexes, tmdo_cov_* …) — a reinstall saw stale ones.
|
||||||
"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
|
foreach ( array( 'wpdo_', 'tmdo_', '_transient_tmdo_', '_transient_timeout_tmdo_' ) as $prefix ) {
|
||||||
$wpdb->esc_like( 'wpdo_' ) . '%'
|
$residual = $wpdb->get_col(
|
||||||
)
|
$wpdb->prepare(
|
||||||
);
|
"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
|
||||||
foreach ( (array) $residual as $opt ) {
|
$wpdb->esc_like( $prefix ) . '%'
|
||||||
if ( preg_match( '/^wpdo_[a-zA-Z0-9_]+$/', $opt ) && delete_option( $opt ) ) {
|
)
|
||||||
++$options_deleted;
|
);
|
||||||
|
foreach ( (array) $residual as $opt ) {
|
||||||
|
if ( preg_match( '/^(_transient_(timeout_)?)?(wpdo|tmdo)_[a-zA-Z0-9_]+$/', $opt ) && delete_option( $opt ) ) {
|
||||||
|
++$options_deleted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 5) Clear cron events ──────────────────────────────────────────────
|
// ── 5) Clear cron events ──────────────────────────────────────────────
|
||||||
$crons = array(
|
foreach ( self::CRON_HOOKS as $hook ) {
|
||||||
'wpdo_warm_cleanup',
|
if ( wp_unschedule_hook( $hook ) ) {
|
||||||
'wpdo_archive_sweep',
|
|
||||||
'wpdo_errors_gc',
|
|
||||||
'wpdo_flush_views',
|
|
||||||
'wpdo_remove_uae_plugin_dir',
|
|
||||||
'wpdo_daily_health_check',
|
|
||||||
'wpdo_snapshot_prune_daily',
|
|
||||||
'wpdo_fsm_automator_run',
|
|
||||||
'wpdo_collect_site_metrics',
|
|
||||||
'wpdo_health_snapshot_monthly',
|
|
||||||
'wpdo_post_shadow_verify',
|
|
||||||
'wpdo_term_comment_shadow_verify',
|
|
||||||
'tmdo_drift_resync',
|
|
||||||
'tmdo_ensure_group_indexes',
|
|
||||||
'wpdo_post_stress_test_batch',
|
|
||||||
'wpdo_user_stress_test_batch',
|
|
||||||
'wpdo_term_stress_test_batch',
|
|
||||||
'wpdo_comment_stress_test_batch',
|
|
||||||
);
|
|
||||||
foreach ( $crons as $hook ) {
|
|
||||||
if ( false !== wp_next_scheduled( $hook ) ) {
|
|
||||||
wp_clear_scheduled_hook( $hook );
|
|
||||||
++$crons_cleared;
|
++$crons_cleared;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ if ( ! function_exists( 'wp_schedule_event' ) ) {
|
|||||||
if ( ! function_exists( 'wp_schedule_single_event' ) ) {
|
if ( ! function_exists( 'wp_schedule_single_event' ) ) {
|
||||||
function wp_schedule_single_event( int $ts, string $hook ): bool { return true; }
|
function wp_schedule_single_event( int $ts, string $hook ): bool { return true; }
|
||||||
}
|
}
|
||||||
|
if ( ! function_exists( 'wp_unschedule_hook' ) ) { function wp_unschedule_hook( string $hook ): int|false { $GLOBALS['_wp_unscheduled'][] = $hook; return 0; } }
|
||||||
if ( ! function_exists( 'wp_clear_scheduled_hook' ) ) {
|
if ( ! function_exists( 'wp_clear_scheduled_hook' ) ) {
|
||||||
function wp_clear_scheduled_hook( string $hook ): int|false { return 0; }
|
function wp_clear_scheduled_hook( string $hook ): int|false { return 0; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,7 +303,8 @@ class DriftCheckTest extends TestCase {
|
|||||||
}
|
}
|
||||||
// Literal: uninstall.php does not load TMDO_Drift_Check.
|
// Literal: uninstall.php does not load TMDO_Drift_Check.
|
||||||
$installer = (string) file_get_contents( WPDO_PLUGIN_DIR . 'includes/class-tmdo-installer.php' );
|
$installer = (string) file_get_contents( WPDO_PLUGIN_DIR . 'includes/class-tmdo-installer.php' );
|
||||||
$this->assertSame( 2, substr_count( $installer, "'" . TMDO_Drift_Check::RESYNC_HOOK . "'" ), 'cleared on deactivate and uninstall' );
|
// One CRON_HOOKS list serves deactivate and uninstall (InstallerCleanupTest covers both).
|
||||||
|
$this->assertSame( 1, substr_count( $installer, "'" . TMDO_Drift_Check::RESYNC_HOOK . "'" ), 'listed in CRON_HOOKS' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_the_daily_tick_runs_on_the_daily_health_check_hook(): void {
|
public function test_the_daily_tick_runs_on_the_daily_health_check_hook(): void {
|
||||||
|
|||||||
@@ -68,6 +68,49 @@ class InstallerCleanupTest extends TestCase {
|
|||||||
$GLOBALS['_wp_options'] = array();
|
$GLOBALS['_wp_options'] = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Events scheduled with args (tmdo_drift_resync( 'post' ), backfill
|
||||||
|
* batches) are not cleared by wp_clear_scheduled_hook( $hook ); the user
|
||||||
|
* stress hook was misnamed and three hooks were missing (2.4.2 PR-138, F-4).
|
||||||
|
*/
|
||||||
|
private const ARG_AND_MISSED_HOOKS = array( 'tmdo_drift_resync', 'wpdo_entity_backfill_batch', 'wpdo_stress_test_batch', 'wpdo_migration_tick', 'wpdo_auto_promote_check' );
|
||||||
|
|
||||||
|
public function test_deactivate_unschedules_every_hook_whatever_its_args(): void {
|
||||||
|
$GLOBALS['_wp_unscheduled'] = array();
|
||||||
|
|
||||||
|
WPDO_Installer::deactivate();
|
||||||
|
|
||||||
|
foreach ( self::ARG_AND_MISSED_HOOKS as $hook ) {
|
||||||
|
$this->assertContains( $hook, $GLOBALS['_wp_unscheduled'], $hook );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_uninstall_unschedules_every_hook_whatever_its_args(): void {
|
||||||
|
$GLOBALS['_wp_unscheduled'] = array();
|
||||||
|
|
||||||
|
WPDO_Installer::drop_all_tables_for_current_blog();
|
||||||
|
|
||||||
|
foreach ( self::ARG_AND_MISSED_HOOKS as $hook ) {
|
||||||
|
$this->assertContains( $hook, $GLOBALS['_wp_unscheduled'], $hook );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** tmdo_* options and transients were left behind; a reinstall then saw stale deferrals and indexes (F-5). */
|
||||||
|
public function test_uninstall_removes_tmdo_options_and_transients(): void {
|
||||||
|
global $wpdb;
|
||||||
|
foreach ( array( 'tmdo_schema_deferred', 'tmdo_entity_indexes', '_transient_tmdo_cov_x', '_transient_timeout_tmdo_cov_x', 'hivepress_settings' ) as $name ) {
|
||||||
|
$wpdb->insert( $wpdb->options, array( 'option_name' => $name, 'option_value' => '1' ) );
|
||||||
|
$GLOBALS['_wp_options'][ $name ] = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
WPDO_Installer::drop_all_tables_for_current_blog();
|
||||||
|
|
||||||
|
foreach ( array( 'tmdo_schema_deferred', 'tmdo_entity_indexes', '_transient_tmdo_cov_x', '_transient_timeout_tmdo_cov_x' ) as $name ) {
|
||||||
|
$this->assertArrayNotHasKey( $name, $GLOBALS['_wp_options'], $name );
|
||||||
|
}
|
||||||
|
$this->assertArrayHasKey( 'hivepress_settings', $GLOBALS['_wp_options'], 'other plugins untouched' );
|
||||||
|
}
|
||||||
|
|
||||||
public function test_drops_static_tables(): void {
|
public function test_drops_static_tables(): void {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
|
|
||||||
|
|||||||
@@ -315,6 +315,7 @@ if ( ! function_exists( 'get_metadata' ) ) {
|
|||||||
if ( ! function_exists( 'wp_next_scheduled' ) ) { function wp_next_scheduled( string $hook ): int|false { return false; } }
|
if ( ! function_exists( 'wp_next_scheduled' ) ) { function wp_next_scheduled( string $hook ): int|false { return false; } }
|
||||||
if ( ! function_exists( 'wp_schedule_event' ) ) { function wp_schedule_event( int $t, string $r, string $h ): bool { return true; } }
|
if ( ! function_exists( 'wp_schedule_event' ) ) { function wp_schedule_event( int $t, string $r, string $h ): bool { return true; } }
|
||||||
if ( ! function_exists( 'wp_schedule_single_event' ) ) { function wp_schedule_single_event( int $ts, string $hook ): bool { $GLOBALS['_wp_scheduled_single'][] = $hook; return true; } }
|
if ( ! function_exists( 'wp_schedule_single_event' ) ) { function wp_schedule_single_event( int $ts, string $hook ): bool { $GLOBALS['_wp_scheduled_single'][] = $hook; return true; } }
|
||||||
|
if ( ! function_exists( 'wp_unschedule_hook' ) ) { function wp_unschedule_hook( string $hook ): int|false { $GLOBALS['_wp_unscheduled'][] = $hook; return 0; } }
|
||||||
if ( ! function_exists( 'wp_clear_scheduled_hook' ) ) { function wp_clear_scheduled_hook( string $hook ): int|false { return 0; } }
|
if ( ! function_exists( 'wp_clear_scheduled_hook' ) ) { function wp_clear_scheduled_hook( string $hook ): int|false { return 0; } }
|
||||||
if ( ! function_exists( 'sanitize_text_field' ) ) { function sanitize_text_field( string $s ): string { return trim( strip_tags( $s ) ); } }
|
if ( ! function_exists( 'sanitize_text_field' ) ) { function sanitize_text_field( string $s ): string { return trim( strip_tags( $s ) ); } }
|
||||||
if ( ! function_exists( '__' ) ) { function __( string $text, string $domain = 'default' ): string { return $text; } }
|
if ( ! function_exists( '__' ) ) { function __( string $text, string $domain = 'default' ): string { return $text; } }
|
||||||
|
|||||||
Reference in New Issue
Block a user