GeneratorsCoreCron Event

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.

acme-sync-products.php
Saved just now
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' );