Kama_Cron

A small class for easily adding WP Cron tasks (jobs).

This Class allow you to create WordPress Cron tasks in a quick and simple way. In order not to confuse anything, all settings are specified in the first parameter when calling the class. The class takes care of the entire routine of correctly registering the Cron task and their intervals. The task handler (the function) need to be written separately in PHP!

Kama_Cron class code

GitHub
<?php

namespace Kama\WP;

/**
 * Convenient way to add cron tasks in WordPress.
 *
 * Usage Example:
 *
 * ```
 * new \Kama\WP\Kama_Cron( [
 *     'wpkama_cron_func' => [
 *         'callback'      => 'wpkama_cron_func', // PHP function to run on job
 *         'interval_name' => '10 min',           // you can set already registered interval: hourly, twicedaily, daily
 *     ],
 * ] );
 *
 * new \Kama\WP\Kama_Cron( [
 *     'single_job' => [
 *         'callback' => 'single_job_func',
 *         'start_time' => strtotime( '2021-06-05' ),
 *     ],
 * ] );
 *
 * new \Kama\WP\Kama_Cron( [
 *     'id'     => 'my_cron_jobs', // not required param
 *     'events' => [
 *         // first task
 *         'wpkama_cron_func' => [
 *             'callback'      => 'wpkama_cron_func', // PHP function to run on job
 *             'interval_name' => '10 minutes',       // you can set already registered interval: hourly, twicedaily, daily
 *         ],
 *         // second task
 *         'wpkama_cron_func_2' => [
 *             'callback'      => 'wpkama_cron_func_2',
 *             'interval_name' => '2 hours',
 *             'start_time'    => time() + DAY_IN_SECONDS, // start in 1 day
 *         ],
 *         // third task
 *         'wpkama_cron_func_3' => [
 *             'callback'      => 'wpkama_cron_func_3',
 *             'interval_name' => 'hourly', // this is already a known WP interval
 *         ],
 *     ],
 * ] );
 * ```
 *
 * @changelog: https://github.com/doiftrue/Kama_Cron/blob/master/changelog.md
 *
 * @author Kama (wp-kama.com)
 *
 * @version 1.3
 */
class Kama_Cron {

	/**
	 * Allowed arguments for constructor.
	 *
	 * @see __construct
	 * @var array
	 */
	protected static $default_args = [
		'id' => '',
		'auto_activate' => true,
		'events' => [
			'hook_name' => [
				'callback'      => [ __CLASS__, 'default_callback' ],
				'args'          => [],
				'interval_name' => '',
				'interval_sec'  => 0,
				'interval_desc' => '',
				'start_time'    => 0,
			],
		],
	];

	/**
	 * Collects every cron options called with this class.
	 * It may be useful for any external cases.
	 *
	 * @var array
	 */
	public static $instances = [];

	/**
	 * Must be 0 on production.
	 * For debugging go to: http://site.com/wp-cron.php
	 */
	public const DEBUG = false;

	/**
	 * ID cron args. Internal - not uses for cron.
	 *
	 * @var string
	 */
	protected $id;

	/**
	 * Current instance args.
	 *
	 * @var array
	 */
	protected $args;

	/**
	 * Constructor.
	 *
	 * @param array     $args {
	 *     Args.
	 *
	 *     @type string $id             A unique identifier that can then be used to access the settings externally.
	 *                                  Default: keys of the $events parameter.
	 *     @type bool   $auto_activate  true - automatically creates the specified event when visiting the admin panel.
	 *                                  In this case, you do not need to call {@see self::activate} method separately.
	 *     @type array  $events {
	 *        An array of events to add to the crown. The element key will be used in the cron hook.
	 *        The element value is an array of event parameters that can contain the following keys:
	 *
	 *        @type callable  $callback       The name of the cron task function.
	 *        @type mixed     $args           What parameters should be passed to the cron task function.
	 *        @type string    $interval_name  The name of the interval, for example: 'half_an_hover'.
	 *                                        You can specify the name in the following format:
	 *                                        `N (min|hour|day|month)s` — 10 minutes, 2 hours, 5 days, 2 months,
	 *                                        then the number will be taken to 'interval_sec' parameter.
	 *                                        You can specify an existing WP interval: hourly, twicedaily, daily.
	 *                                        Omite this parameter to register single cron job.
	 *        @type int       $interval_sec   Interval time, for example HOUR_IN_SECONDS / 2.
	 *                                        You don't need to specify this papameter when $interval_name one of:
	 *                                        N (min|hour|day|month)s, hourly, twicedaily, daily.
	 *        @type string    $interval_desc  Description of the interval, for example, 'Every half hour'.
	 *                                        You don't need to specify this param when $interval_name one of:
	 *                                        N (min|hour|day|month)s, hourly, twicedaily, daily.
	 *        @type int       $start_time     UNIX timestamp. When to start the event. Default: time().
	 *     }
	 *
	 * }
	 */
	public function __construct( array $args ){

		// if passed simple array of events
		if( empty( $args['events'] ) ){
			$args = [ 'events' => $args ];
		}

		// complete passed args using defaults
		$args += [
			'id' => implode( '|', array_keys( $args['events'] ) ),
			'auto_activate' => true,
		];

		// complete each passed 'event' using defaults
		foreach( $args['events'] as $indx => $_event ){
			$args['events'][ $indx ] += self::$default_args['events']['hook_name'];
		}

		$this->args = $args;

		self::$instances[ $this->args['id'] ] = $this;

		// after self::$opts
		add_filter( 'cron_schedules', [ $this, '_add_intervals' ] );

		// after 'cron_schedules'
		// activate only in: admin | WP_CLI | DOING_CRON
		if( $args['auto_activate'] && ( is_admin() || defined( 'WP_CLI' ) || defined( 'DOING_CRON' ) ) ){
			$this->activate();
		}

		// add cron hooks
		foreach( $args['events'] as $hook_name => $task_data ){
			add_action( $hook_name, $task_data['callback'], 10, count( $task_data['args'] ) );
		}

		self::debug_info();
	}

	/**
	 * Removes cron task.
	 * Should be called on plugin deactivation.
	 *
	 * @param string $id
	 */
	public function deactivate(){

		foreach( $this->args['events'] as $hook => $data ){
			wp_clear_scheduled_hook( $hook, $data['args'] );
		}
	}

	/**
	 * Add cron task.
	 * Should be called on plugin activation.
	 * Can be called somewhere else, for example, when updating the settings.
	 *
	 * @param string $id
	 */
	private function activate(){

		foreach( $this->args['events'] as $hook => $data ){

			if( wp_next_scheduled( $hook, $data['args'] ) ){
				continue;
			}

			if( $data['interval_name'] ){
				wp_schedule_event( $data['start_time'] ?: time(), $data['interval_name'], $hook, $data['args'] );
			}
			// single event
			elseif( ! $data['start_time'] ){
				trigger_error( __CLASS__ . ' ERROR: Start time not specified for single event' );
			}
			elseif( $data['start_time'] > time() ){
				wp_schedule_single_event( $data['start_time'], $hook, $data['args'] );
			}

		}
	}

	public function _add_intervals( $schedules ){

		foreach( $this->args['events'] as $data ){

			$interval_name = $data['interval_name'];

			if(
				// it is a single event.
				! $interval_name
				// already exists
				|| isset( $schedules[ $interval_name ] )
				// internal WP intervals
				|| in_array( $interval_name, [ 'hourly', 'twicedaily', 'daily' ] )
			){
				continue;
			}

			// allow set only `interval_name` parameter like: 10_min, 2_hours, 5_days, 2_month
			if( ! $data['interval_sec'] ){

				if( preg_match( '/^(\d+)[ _-](min(?:ute)?|hour|day|month)s?/', $interval_name, $mm ) ){
					$min = $minute = 60;
					$hour = $min * 60;
					$day = $hour * 24;
					$month = $day * 30;

					$data['interval_sec'] = $mm[1] * ${ $mm[2] };
				}
				else {
					/** @noinspection ForgottenDebugOutputInspection */
					wp_die( 'ERROR: Kama_Cron required `interval_sec` parameter not set. ' . print_r( debug_backtrace(), 1 ) );
				}
			}

			$schedules[ $interval_name ] = [
				'interval' => $data['interval_sec'],
				'display'  => $data['interval_desc'] ?: $data['interval_name'],
			];
		}

		return $schedules;
	}

	public static function default_callback(){

		echo "ERROR: One of Kama_Cron callback function not set.\n\nKama_Cron::\$instance = " .
		     print_r( self::$instances, 1 ) . "\n\n\n\n_get_cron_array() =" .
		     print_r( _get_cron_array(), 1 );
	}

	private static function debug_info(): void {

		if( ! ( self::DEBUG && defined( 'DOING_CRON' ) ) ){
			return;
		}

		add_action( 'wp_loaded', function() {

			echo sprintf( "Current time: %s\n\n\nExisting Intervals:\n%s\n\n\n%s",
				time(), print_r( wp_get_schedules(), 1 ), print_r( _get_cron_array(), 1 )
			);
		} );
	}

}

Installation

Copy the class code and paste it into the plugin or the functions.php theme. Or use composer:

composer require doiftrue/wp-kama-cron

Examples of usage Kama_Cron

You can call new \Kama\WP\Kama_Cron() at the earliest stage of WP loading, starting from the earliest action muplugins_loaded.

IMPORTANT! The new \Kama\WP\Kama_Cron() must be triggered (called) on Cron requests as well, since it registers the necessary WP hooks that do the staff on Cron requests. In other words, you cannot register a Cron task with this code and delete it.

Example 1: Simple usage without additional parameters

In this example specified Cron task will be registered automatically when you visit admin-panel or on any Cron Request.

wpkama_cron_hook - it's an internal name of WP hook you don't need to use it anywhere in your code - just specify understandable name (you may specify the name of the PHP function).

Use WP unknown interval:

new \Kama\WP\Kama_Cron( [
	'wpkama_cron_hook' => [
		'callback'      => 'wpkama_cron_func', // PHP function to run on job
		'interval_name' => '10 minutes', // or Use WP-known interval: hourly, twicedaily, daily

	],
] );

function wpkama_cron_func(){
	// your code to do the cron job
}

In this case the class will parse the 10 minutes string and fill in the parameters interval_sec and interval_desc itself.

Example 2: Let's create three cron jobs

Create 3 Cron tasks with different intervals. The tasks registered automatically when visiting admin-pane (it works very quick).

The code can be passed on any period of page load (php load) all necessary hooks will be added automatically. Let's past it into functions.php theme file.

//\Kama\WP\Kama_Cron::$DEBUG = 1; // enable debag

new \Kama\WP\Kama_Cron( [
	'id' => 'my_cron_jobs', // optional parameter
	'events' => array(
		// first job
		'wpkama_cron_func' => array(
			'callback'      => 'wpkama_cron_func', // name of the crown task function
			'interval_name' => '10 min',           // or WP-known: hourly, twicedaily, daily
		),
		// second job
		'wpkama_cron_func_2' => array(
			'callback'      => 'wpkama_cron_func_2',
			'start_time'    => time() + DAY_IN_SECONDS, // start in 1 day
			'interval_name' => '2 hours',
		),
		// third job
		'wpkama_cron_func_3' => array(
			'callback'      => 'wpkama_cron_func_3',
			'interval_name' => 'hourly', // known WP interval
		),
	),
] );

// Cron function
function wpkama_cron_func(){
	file_put_contents( dirname( ABSPATH ) .'/cron_check.txt', current_time('mysql') ."\n", FILE_APPEND );
}

// Cron function
function wpkama_cron_func_2(){
	// operations
}

// Cron function
function wpkama_cron_func_3(){
	// operations
}

Example 3:

You can delete all created cron jobs using it ID:

\Kama\WP\Kama_Cron::deactivate( 'my_cron_jobs' ); // delete jobs

Example 4: Registering single task

The task will only be completed once.

new \Kama\WP\Kama_Cron( [
	// single task
	'wpkama_cron_hook' => [
		'callback'   => 'wpkama_cron_func_4',
		'start_time' => time() + DAY_IN_SECONDS,
	],
] );

It is better to use native wp_schedule_single_event() function for this purpose.

Example 5: Registering tasks when activate a plugin

The code below shows how to activate and deactivate tasks on plugin activation / deactivation.

// Example of activation and deactivation, if the
// `auto_activate` parameter is not specified
register_activation_hook( __FILE__, function(){
	\Kama\WP\Kama_Cron::activate( 'my_cron_jobs_2' );
} );

register_deactivation_hook( __FILE__, function(){
	\Kama\WP\Kama_Cron::deactivate( 'my_cron_jobs_2' );
} );

new \Kama\WP\Kama_Cron( [
	'id' => 'my_cron_jobs_2',
	'auto_activate' => false, // we do activation manually
	'events' => [
		// first task
		'wpkama_cron_func_4' => [
			'callback'      => 'wpkama_cron_func_4',
			'interval_name' => 'twicedaily', // hourly, twicedaily, daily
		],
		// second task
		'wpkama_cron_func_5' => [
			'callback'      => 'wpkama_cron_func_5',
			'interval_name' => '2 hours',
		],
	],
] );

function wpkama_cron_func_4(){
	// operations
}

function wpkama_cron_func_5(){
	// operations
}

Constructor's parameters

$args(array)

Args.

  • id(string)
    A unique identifier that can then be used to access the settings externally.
    Default: keys of the $events parameter

  • auto_activate(true|false)
    true - automatically creates the specified event when visiting the admin panel. In this case, you do not need to call {@see self::activate} method separately.

  • events(array)
    An array of events to add to the crown. The element key will be used in the cron hook. The element value is an array of event parameters that can contain the following keys:

    • callback(callable)
      The name of the cron task function.

    • args(mixed)
      What parameters should be passed to the cron task function.

    • interval_name(string)
      The name of the interval, for example: 'half_an_hover'. You can specify the name in the following format: N (min|hour|day|month)s — 10 minutes, 2 hours, 5 days, 2 months, then the number will be taken to 'interval_sec' parameter. You can specify an existing WP interval: hourly, twicedaily, daily. Omite this parameter to register single cron job.

    • interval_sec(int)
      Interval time, for example HOUR_IN_SECONDS / 2. You don't need to specify this papameter when $interval_name one of: N (min|hour|day|month)s, hourly, twicedaily, daily.

    • interval_desc(string)
      Description of the interval, for example, 'Every half hour'. You don't need to specify this param when $interval_name one of: N (min|hour|day|month)s, hourly, twicedaily, daily.

    • start_time(int)
      UNIX timestamp. When to start the event.
      Default: time()