close(); } } /** * Invalidate a bounded selected-site set when the optional automatic worker is deactivated. * * @param bool $network_wide Whether the operation covers the current WordPress network. * * @return void */ public static function deactivate_addon( bool $network_wide ): void { if ( ! Migrations::ready( 'automatic' ) ) { return; } $db = RequestsAutomaticState::connection(); if ( is_wp_error( $db ) ) { return; } $network = Scope::network(); $site = get_current_blog_id(); try { $table = RequestsAutomaticSchema::table( 'automatic_state', $db ); $sites = $network_wide && is_multisite() ? $db->get_col( $db->prepare( "SELECT site_id FROM %i WHERE network_id=%d AND mode<>'off' ORDER BY site_id LIMIT 50", $table, $network ) ) : array( $site ); if ( '' !== $db->last_error || ! is_array( $sites ) ) { // @phpstan-ignore function.alreadyNarrowedType (The fixed worker get_col and single-site fallback produce arrays; retain the explicit site-inventory compatibility guard.) return; } foreach ( $sites as $candidate ) { $switched = get_current_blog_id() !== (int) $candidate; if ( $switched ) { switch_to_blog( (int) $candidate ); } $local = null; try { if ( Scope::network() !== $network || get_current_blog_id() !== (int) $candidate ) { return; } $local = RequestsAutomaticState::connection(); if ( is_wp_error( $local ) ) { return; } $state = RequestsAutomaticState::read( $local ); if ( ! is_wp_error( $state ) && 'off' !== $state['mode'] ) { RequestsAutomaticState::invalidate( $local, $state, 'addon_deactivated' ); } wp_clear_scheduled_hook( 'logged_ai_automatic_worker' ); } finally { if ( $local instanceof \wpdb ) { $local->close(); } if ( $switched ) { restore_current_blog(); } } } // Remaining network sites fail open immediately through persisted activation checks and finish in per-site cleanup. } finally { $db->close(); } } }