Examples

Database Cleanup

Clean up expired data on a daily schedule:

use WPZylos\Framework\Scheduler\Schedule;

$schedule = $app->make(Schedule::class);

// Delete expired transients daily at 2 AM
$schedule->call(function () {
    global $wpdb;
    $wpdb->query(
        "DELETE a, b FROM {$wpdb->options} a, {$wpdb->options} b
         WHERE a.option_name LIKE '_transient_timeout_%'
         AND a.option_value < UNIX_TIMESTAMP()
         AND b.option_name = CONCAT('_transient_', SUBSTRING(a.option_name, 20))"
    );
})->dailyAt('02:00')->name('cleanup-expired-transients');

// Purge old revision posts monthly
$schedule->call(function () {
    global $wpdb;
    $wpdb->query(
        "DELETE FROM {$wpdb->posts}
         WHERE post_type = 'revision'
         AND post_date < DATE_SUB(NOW(), INTERVAL 90 DAY)"
    );
})->monthlyOn(1, '03:00')->name('purge-old-revisions');

Email Notifications

// Daily digest at 8 AM
$schedule->call(function () {
    $users = get_users([
        'meta_key'   => 'receive_digest',
        'meta_value' => '1',
    ]);

    foreach ($users as $user) {
        $posts = get_posts([
            'date_query'    => [['after' => '24 hours ago']],
            'posts_per_page' => 10,
        ]);

        if (!empty($posts)) {
            $body = "Here are today's new posts:\n\n";
            foreach ($posts as $post) {
                $body .= "- {$post->post_title}: " . get_permalink($post) . "\n";
            }

            wp_mail($user->user_email, 'Daily Digest', $body);
        }
    }
})->dailyAt('08:00')
  ->name('daily-digest-email')
  ->withoutOverlapping();

// Weekly summary every Monday at 9 AM
$schedule->call(function () {
    $admin_email = get_option('admin_email');
    $stats = [
        'posts'    => wp_count_posts()->publish,
        'comments' => wp_count_comments()->approved,
        'users'    => count_users()['total_users'],
    ];

    $body = sprintf(
        "Weekly Stats:\n- Published Posts: %d\n- Approved Comments: %d\n- Total Users: %d",
        $stats['posts'],
        $stats['comments'],
        $stats['users']
    );

    wp_mail($admin_email, 'Weekly Site Report', $body);
})->weeklyOn(1, '09:00')->name('weekly-site-report');

Cache Management

// Flush object cache every 6 hours
$schedule->call(function () {
    wp_cache_flush();
})->hourly() // Check every hour
  ->when(fn() => (int) date('G') % 6 === 0) // But only at 0, 6, 12, 18
  ->name('periodic-cache-flush');

// Warm up popular post cache every 30 minutes
$schedule->call(function () {
    $popular_ids = get_option('popular_post_ids', []);
    foreach ($popular_ids as $id) {
        // Force cache regeneration
        clean_post_cache($id);
        get_post($id);
        get_post_meta($id);
    }
})->everyThirtyMinutes()->name('warm-popular-cache');

Queue Job Dispatching

Combine scheduling with the wpzylos-queue package:

// Process pending orders every 5 minutes
$schedule->job(ProcessPendingOrders::class)
    ->everyFiveMinutes()
    ->name('process-pending-orders')
    ->withoutOverlapping(30);

// Send newsletters every Monday
$schedule->job(SendWeeklyNewsletter::class)
    ->weeklyOn(1, '06:00')
    ->name('weekly-newsletter')
    ->withoutOverlapping();

// Generate reports on the 1st of each month
$schedule->job(GenerateMonthlyReport::class)
    ->monthlyOn(1, '01:00')
    ->name('monthly-report');

WP-CLI Commands

// Flush rewrite rules daily
$schedule->command('rewrite flush')
    ->daily()
    ->name('flush-rewrites');

// Run search-replace on staging
$schedule->command('search-replace "http://staging.local" "https://staging.local" --dry-run')
    ->weekly()
    ->when(fn() => wp_get_environment_type() === 'staging')
    ->name('staging-url-check');

Conditional Scheduling

// Only during business hours
$schedule->call(function () {
    // Sync inventory with external API
})->everyFifteenMinutes()
  ->between('08:00', '18:00')
  ->name('sync-inventory');

// Only when WooCommerce is active
$schedule->call(function () {
    // Process WooCommerce orders
})->everyFiveMinutes()
  ->when(fn() => class_exists('WooCommerce'))
  ->name('woo-order-processor');

// Skip on weekends
$schedule->call(function () {
    // Weekday-only reports
})->dailyAt('09:00')
  ->skip(fn() => in_array((int) date('w'), [0, 6]))
  ->name('weekday-report');

Multisite Network Tasks

// Run only on the main site of a multisite network
$schedule->call(function () {
    // Network-wide user cleanup
    $users = get_users([
        'meta_key'   => 'last_login',
        'meta_value' => date('Y-m-d', strtotime('-365 days')),
        'meta_compare' => '<',
    ]);

    foreach ($users as $user) {
        // Mark inactive users
        update_user_meta($user->ID, 'status', 'inactive');
    }
})->monthly()
  ->onOneServer()
  ->name('network-inactive-users');

Health Monitoring

// Check external API availability every 10 minutes
$schedule->call(function () {
    $response = wp_remote_get('https://api.example.com/health');

    if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
        // Alert admin
        wp_mail(
            get_option('admin_email'),
            'API Health Alert',
            'The external API is not responding.'
        );

        update_option('api_status', 'down');
    } else {
        update_option('api_status', 'up');
    }
})->everyTenMinutes()
  ->name('api-health-check')
  ->withoutOverlapping(15);

Complete Plugin Example

use WPZylos\Framework\Scheduler\Schedule;
use WPZylos\Framework\Scheduler\SchedulerServiceProvider;

class MyPlugin
{
    public function boot($app): void
    {
        $app->register(new SchedulerServiceProvider());

        $schedule = $app->make(Schedule::class);

        $this->registerMaintenanceTasks($schedule);
        $this->registerNotificationTasks($schedule);
        $this->registerSyncTasks($schedule);
    }

    private function registerMaintenanceTasks(Schedule $schedule): void
    {
        $schedule->call([$this, 'cleanExpiredTokens'])
            ->daily()
            ->name('clean-tokens');

        $schedule->call([$this, 'optimizeTables'])
            ->weeklyOn(0, '04:00')
            ->name('optimize-db');
    }

    private function registerNotificationTasks(Schedule $schedule): void
    {
        $schedule->call([$this, 'sendDigest'])
            ->dailyAt('08:00')
            ->withoutOverlapping()
            ->name('daily-digest');
    }

    private function registerSyncTasks(Schedule $schedule): void
    {
        $schedule->job(SyncInventory::class)
            ->everyFifteenMinutes()
            ->between('06:00', '22:00')
            ->withoutOverlapping(30)
            ->name('sync-inventory');
    }
}