Cron Event Generator
Scheduling is the easy half. This writes the guard that stops duplicates, the lock that stops overlap, and the cleanup that runs on deactivation.
The event
This is the action name WP-Cron fires. Anything else on the site can hook it too — including WP-CLI and you, by hand.
acme_sync_products runs daily, starting tomorrow at 03:00 server time. Guarded against double-scheduling. Locked against overlap for five minutes.
What it does
wp_remote_get() with a timeout, both failure branches handled, and a last-run stamp. The shape most integrations need.
Arguments
none
No arguments — simpler to unschedule, since wp_next_scheduled() has to be called with the exact same args to find the event.
Safety net
wp_next_scheduled() guard
The one line between one scheduled event and forty.
Unschedule on deactivation
wp_clear_scheduled_hook() so the event does not outlive the plugin.
Overlap lock
A transient that stops two simultaneous requests both running the job.
The right home for cron: scheduled on activation, cleared on deactivation.
<?php // Generated with WP CodeKit — powered by GrowQuest (https://growquest.io). /** * Plugin Name: acme sync products * Description: Scheduled task: acme_sync_products. * Version: 1.0.0 * Requires PHP: 7.4 * Text Domain: acme */ defined( 'ABSPATH' ) || exit; /** * Schedule the event, once. */ function acme_schedule() { if ( wp_next_scheduled( 'acme_sync_products' ) ) { return; } wp_schedule_event( strtotime( 'tomorrow 03:00' ), 'daily', 'acme_sync_products' ); } /** * The work itself. */ function acme_run() { if ( get_transient( 'acme_running' ) ) { return; } set_transient( 'acme_running', time(), 5 * MINUTE_IN_SECONDS ); $response = wp_remote_get( 'https://api.example.com/products', array( 'timeout' => 20, ) ); if ( is_wp_error( $response ) ) { error_log( '[cron] fetch failed: ' . $response->get_error_message() ); return; } $items = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $items ) ) { return; } foreach ( $items as $item ) { // Upsert each item here. } update_option( 'acme_last_sync', time(), false ); delete_transient( 'acme_running' ); } add_action( 'acme_sync_products', 'acme_run' ); /** * Clear the event. Runs on deactivation — an orphaned event survives the plugin. */ function acme_unschedule() { wp_clear_scheduled_hook( 'acme_sync_products' ); } register_activation_hook( __FILE__, 'acme_schedule' ); register_deactivation_hook( __FILE__, 'acme_unschedule' );