Usage

Scheduling Tasks

Closure Tasks

The most common way to schedule work is with a closure:

use WPZylos\Framework\Scheduler\Schedule;

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

$schedule->call(function () {
    // Your task logic here
    $wpdb = $GLOBALS['wpdb'];
    $wpdb->query("DELETE FROM {$wpdb->prefix}sessions WHERE expired_at < NOW()");
})->daily()->name('cleanup-sessions');

WP-CLI Command Tasks

Schedule WP-CLI commands to run on a recurring basis:

$schedule->command('cache flush')
    ->hourly()
    ->name('flush-cache');

$schedule->command('cron event run --due-now')
    ->everyFiveMinutes()
    ->name('run-pending-cron');

Note: WP-CLI commands only execute when WP_CLI is defined and the WP_CLI class is available.

Queue Job Tasks

Dispatch queue jobs on a schedule:

$schedule->job(ProcessPendingOrders::class)
    ->everyFiveMinutes()
    ->name('process-orders');

$schedule->job(SendWeeklyReport::class)
    ->weeklyOn(1, '09:00')  // Monday at 9:00 AM
    ->name('weekly-report');

Jobs are dispatched through the wpzylos_dispatch_job filter. If the queue system isn't available, jobs fall back to synchronous execution.

Frequency Methods

Minute Intervals

$schedule->call($fn)->everyMinute();            // Every 60 seconds
$schedule->call($fn)->everyFiveMinutes();       // Every 5 minutes
$schedule->call($fn)->everyTenMinutes();        // Every 10 minutes
$schedule->call($fn)->everyFifteenMinutes();    // Every 15 minutes
$schedule->call($fn)->everyThirtyMinutes();     // Every 30 minutes

Hourly

$schedule->call($fn)->hourly();                 // Every hour
$schedule->call($fn)->hourlyAt(15);             // At :15 past each hour

Daily

$schedule->call($fn)->daily();                  // At midnight
$schedule->call($fn)->dailyAt('13:30');          // At 1:30 PM

Weekly

$schedule->call($fn)->weekly();                 // Sunday at midnight
$schedule->call($fn)->weeklyOn(3, '10:00');     // Wednesday at 10:00 AM

Day values: 0 = Sunday, 1 = Monday, ..., 6 = Saturday

Monthly

$schedule->call($fn)->monthly();                // 1st at midnight
$schedule->call($fn)->monthlyOn(15, '09:00');   // 15th at 9:00 AM

Constraints

Conditional Execution

// Only run when the condition is true
$schedule->call($fn)
    ->daily()
    ->when(fn() => get_option('feature_enabled') === '1')
    ->name('conditional-task');

Skip Execution

// Skip when the condition is true
$schedule->call($fn)
    ->hourly()
    ->skip(fn() => defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
    ->name('skip-during-autosave');

Time Range

// Only execute between specific hours
$schedule->call($fn)
    ->everyFiveMinutes()
    ->between('09:00', '17:00')
    ->name('business-hours-task');

Overlap Prevention

Prevent a task from running if a previous execution is still active:

$schedule->call(function () {
    // Long-running import that might take > 5 minutes
    $this->importLargeDataset();
})->everyFiveMinutes()
  ->withoutOverlapping()
  ->name('data-import');

The lock uses WordPress transients with a default expiration of 24 hours (1440 minutes). Customize the lock duration:

$schedule->call($fn)
    ->hourly()
    ->withoutOverlapping(120)  // Lock expires after 2 hours
    ->name('long-task');

Multisite Support

For multisite networks, ensure a task only runs on one site:

$schedule->call(function () {
    // Network-wide cleanup
})->daily()
  ->onOneServer()
  ->name('network-cleanup');

Task Naming

Always name your tasks — names are used for lock keys and debugging:

$schedule->call($fn)->daily()->name('meaningful-task-name');

If no name is provided, a hash-based key is auto-generated, but named tasks are much easier to debug and manage.

Retrieving Tasks

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

// Get all registered tasks
$allTasks = $schedule->getTasks();

// Get only tasks that are due now
$dueTasks = $schedule->getDueTasks();

// Check if any tasks are registered
if ($schedule->hasTasks()) {
    // ...
}

Running Tasks Manually

// Run all due tasks (returns count of executed tasks)
$executed = $schedule->run();
echo "Executed {$executed} tasks.";

Error Handling

When a task throws an exception, the scheduler catches it and fires a WordPress action:

add_action('wpzylos_scheduler_task_failed', function (ScheduledTask $task, \Throwable $e) {
    error_log("Task '{$task->getName()}' failed: {$e->getMessage()}");
}, 10, 2);

Other due tasks continue to execute even if one fails.