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');
}
}