completejerry-mailoptin-safest-fix

MailOptin Safest Fix - Guaranteed to Work

Instead of relying on internal methods that could change, simulate what the cron healthcheck SHOULD do - but do it right:

<?php
/**
 * Plugin Name: MailOptin Pantheon Fix
 * Description: Force background queue processing on Pantheon hourly cron
 * Version: 1.0
 */

/**
 * Override the default healthcheck behavior to process directly instead of dispatching.
 */
add_action('wp_1_mo_wp_mail_bg_process_cron', 'mailoptin_force_queue_processing', 1);

function mailoptin_force_queue_processing() {
    global $wpdb;

    // Find the batch(es) waiting to be processed
    $batches = $wpdb->get_results(
        "SELECT option_name, option_value
         FROM {$wpdb->options}
         WHERE option_name LIKE 'wp_%_mo_wp_mail_bg_process_batch_%'",
        OBJECT
    );

    if (empty($batches)) {
        error_log('MailOptin Pantheon: No batches in queue');
        return;
    }

    error_log('MailOptin Pantheon: Found ' . count($batches) . ' batch(es) to process');

    // Get the processor instance
    if (!class_exists('MailOptin\RegisteredUsersConnect\WP_Mail_BG_Process')) {
        error_log('MailOptin Pantheon: Background processor class not found');
        return;
    }

    $processor = new MailOptin\RegisteredUsersConnect\WP_Mail_BG_Process();

    // Check if already processing (respect the lock)
    if (method_exists($processor, 'is_processing') && $processor->is_processing()) {
        error_log('MailOptin Pantheon: Already processing, skipping');
        return;
    }

    // Process each batch
    foreach ($batches as $batch_option) {
        $batch_data = maybe_unserialize($batch_option->option_value);

        if (!is_array($batch_data)) {
            error_log('MailOptin Pantheon: Invalid batch data format');
            continue;
        }

        $count = count($batch_data);
        error_log("MailOptin Pantheon: Processing batch with {$count} items");

        // Process each item in the batch
        foreach ($batch_data as $item) {
            if (!is_object($item)) {
                continue;
            }

            // Call the task() method directly on each item
            // This is what handle() does internally
            try {
                $result = $processor->task($item);

                // task() returns false when item is complete and should be removed
                if ($result === false) {
                    error_log('MailOptin Pantheon: Processed email for ' . $item->user_email);
                }
            } catch (Exception $e) {
                error_log('MailOptin Pantheon: Error processing item - ' . $e->getMessage());
            }
        }

        // Delete the processed batch
        delete_option($batch_option->option_name);
        error_log('MailOptin Pantheon: Deleted batch ' . $batch_option->option_name);
    }

    // Clear the scheduled cron event since queue is now empty
    $timestamp = wp_next_scheduled('wp_1_mo_wp_mail_bg_process_cron');
    if ($timestamp) {
        wp_unschedule_event($timestamp, 'wp_1_mo_wp_mail_bg_process_cron');
        error_log('MailOptin Pantheon: Cleared scheduled cron event');
    }

    error_log('MailOptin Pantheon: Queue processing complete');
}

Why This Is Safer

  1. Doesn't rely on protected methods - uses public task() method
  2. Direct database queries - we know exactly what batches exist
  3. Respects process locks - checks is_processing() first
  4. Detailed logging - you'll know exactly what happens
  5. Handles errors gracefully - won't crash if something's wrong
  6. Cleans up after itself - deletes batches and clears cron

How to Test IMMEDIATELY

You have 187 users already queued, so:

# 1. Deploy the mu-plugin to test first
# (Copy code to wp-content/mu-plugins/mailoptin-pantheon-fix.php)

# 2. Commit and push


# 3. Deploy to test environment

# 4. Manually trigger to test
terminus wp completejerry.test -- cron event run wp_1_mo_wp_mail_bg_process_cron

# 5. Check the logs

# You should see:
# "MailOptin Pantheon: Found 1 batch(es) to process"
# "MailOptin Pantheon: Processing batch with 187 items"
# "MailOptin Pantheon: Processed email for [email protected]"
# ... (187 times)
# "MailOptin Pantheon: Queue processing complete"

# 6. Verify queue is empty
terminus wp completejerry.test -- option list --search="*mo_wp_mail_bg_process_batch*" --format=count
# Should return: 0

# 7. Check Mailgun dashboard for 187 sent emails

# 8. If successful, deploy to live

Alternative: Even Simpler Test First

If you want to test the theory right now without deploying code:

# Just manually trigger the existing cron and see what happens
terminus wp completejerry.live -- cron event run wp_1_mo_wp_mail_bg_process_cron

# Check if queue is still there
terminus wp completejerry.live -- option get wp_1_mo_wp_mail_bg_process_batch_938991b3f8828ae826ddfcc76c72d63

# If it still exists (likely), then the async dispatch is definitely failing
# If it's gone, emails sent! Problem might have been a one-time thing