WordPress как на ладони
Очень Удобный и Быстрый Хостинг для сайтов на WordPress. Пользуюсь сам и вам рекомендую!

Kama_Cron

Небольшой класс для удобного добавления крон событий (задач) в WordPress.

Класс позволяет быстро и удобно создавать cron задачи, чтобы не перепутать ничего, все настройки указываются в параметре при вызове класса, всю рутину по правильной регистрации крон задач и их интервалов класс берет на себя. Функции-обработчики задач нужно писать отдельно!

Код класса Kama_Cron

GitHub
<?php

namespace Kama\WP;

/**
 * Convenient way to add cron tasks in WordPress.
 *
 * INFO: For debugging go to: http://site.com/wp-cron.php
 *
 * Usage Example:
 *
 * ```php
 * 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'    => strtotime('tomorrow 6am'), // start tomorrow at 6:00am + site gtm_offset
 *         ],
 *         // 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)
 *
 * @requires-php 7.1
 * @version 1.4
 */
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,
			],
		],
	];

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

	/**
	 * Container for every instance.
	 * To have acces to instance use `Kama_Cron::get()` method.
	 *
	 * @var array
	 */
	protected static $instances = [];

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

	/**
	 * 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(). If you need to start event
	 *                                        at, for example, tomorrow 6 AM (with site time), you must get timestamp and fix
	 *                                        it with site gtm_offset: `strtotime('tomorrow 6am') - (int) get_option('gtm_offset')`.
	 *     }
	 *
	 * }
	 */
	public function __construct( array $args ){

		$this->set_args( $args );
		$this->init();

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

	/**
	 * Gets instance by id.
	 */
	public static function get( string $instance_id ): self {

		return self::$instances[ $instance_id ] ?? new self( [ 'id' => 'stub', 'events' => [] ] );
	}

	protected function set_args( array $args ): void {

		// if direct events data passed
		if( ! isset( $args['events'] ) ){
			$args = [ 'events' => $args ];
		}

		// add default values to $args
		$args += [
			'id' => implode( '|', array_keys( $args['events'] ) ),
			'auto_activate' => self::$default_args['auto_activate'],
		];

		// add default values to each "event"
		foreach( $args['events'] as $indx => $_event ){
			$args['events'][ $indx ] += self::$default_args['events']['hook_name'];
		}

		$this->args = $args;
	}

	protected function init(): void {

		if( ! $this->args['events'] ){
			return;
		}

		add_filter( 'cron_schedules', [ $this, 'add_intervals_callback' ] );

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

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

	/**
	 * Removes all cron tasks of current instance.
	 * Should be called on plugin deactivation.
	 */
	public function deactivate(): void {

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

	/**
	 * Add all cron tasks of current instance.
	 * Should be called on plugin activation.
	 * Can be called somewhere else, for example, when updating the settings.
	 */
	public function activate(): void {

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

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

			if( $data['interval_name'] ){
				$wp_error = wp_schedule_event( $data['start_time'] ?: time(), $data['interval_name'], $hook, $data['args'], true );
			}
			// single event
			elseif( ! $data['start_time'] ){
				$msg = "ERROR: nor `interval_name` OR `start_time` was not set for the Kama Cron event `$hook`.";
				_doing_it_wrong( __METHOD__, $msg, '' );
			}
			elseif( $data['start_time'] > time() ){
				$wp_error = wp_schedule_single_event( $data['start_time'], $hook, $data['args'], true );
			}

			if ( is_wp_error( $wp_error ?? null ) ) {
				trigger_error( __METHOD__ . ': ' . $wp_error->get_error_message() );
			}
		}
	}

	/**
	 * @private
	 */
	public function add_intervals_callback( $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 {
					echo 'ERROR: Kama_Cron required `interval_sec` parameter not set.';
					/** @noinspection ForgottenDebugOutputInspection */
					echo "\n\n". debug_print_backtrace();
					die();
				}
			}

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

		return $schedules;
	}

	public static function default_callback(): void {

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

}

Установка

Cкопируйте код класса и вставьте его в плагин или functions.php темы.

Через composer:

composer require doiftrue/wp-kama-cron

Примеры

По умолчанию задачи регистрируются автоматически (это работает очень быстро) при посещении админ-панели ИЛИ при WP_CLI запросе ИЛИ при любом крон запросе. Если автоматическая регистрация не нужна, то укажите параметр 'auto_activate' => false и активируйте задачи вручную с помощью метода activate(). См. пример ниже.

Вызывать Kama_Cron можно на самом раннем этапе загрузки WP, начиная с самого раннего события muplugins_loaded.

ВАЖНО! Код Kama_Cron должен также работать в крон запросах, так как он регистрирует необходимые WP хуки, которые будут отрабатывать при крон задачах. Другими словами, нельзя зарегистрировать крон задачу с помощью этого кода и удалить его.

Повторяющаяся задача

В этом примере указанная задача Cron будет зарегистрирована автоматически при посещении админ-панели или при любому Cron запросе.

wpkama_cron_hook - это внутреннее имя хука WP, вам не нужно использовать его где-либо в своем коде - просто укажите понятное и уникальное имя.

Используем известный WP интервал (hourly):

new \Kama\WP\Kama_Cron( [
	'wpkama_core_data_check_update' => [
		'callback'      => 'wpkama_core_data_check_update',
		'interval_name' => 'hourly',
	]
] );

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

Используем НЕизвестный WP интервал (10 minutes):

new \Kama\WP\Kama_Cron( [
	'wpkama_cron_hook' => [
		'callback'      => 'wpkama_cron_func',
		'interval_name' => '10 minutes',
	],
] );

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

В этом случае класс распарсит строку 10 minutes и сам заполнит параметры interval_sec и interval_desc.

В interval_name вы можете указать имя в следующем формате: N (min|minutes|hour|day|month)s - 10 minutes, 2 hours, 5 days, 2 months. ИЛИ вы можете указать существующий интервал WP: hourly, twicedaily, daily.

Одиночная задача

Одиночная задача (одноразовая):

new \Kama\WP\Kama_Cron( [
	'single_job' => [
		'callback' => 'single_job_func',
		'start_time' => 1679205600, //= strtotime('tomorrow 6am') - (int) get_option('gtm_offset'),
	],
] );

Повторяемая одиночная задача (один раз в день):

new \Kama\WP\Kama_Cron( [
	'single_job' => [
		'callback' => 'single_job_func',
		// start event every day at 6am by site time
		'start_time' => strtotime('tomorrow 6am') - (int) get_option('gtm_offset'),
	],
] );

Регистрируйте несколько задач одновременно:

Создадим 4 крон задачи с разными интервалами.

Код добавляем куда угодно, например в functions.php или плагин.

new \Kama\WP\Kama_Cron( [
	'id'     => 'my_cron_jobs',
	'events' => [
		// first task
		'wpkama_cron_func' => [
			'callback'      => [ MyCronCallbacks::class, 'wpkama_cron_func' ],
			'interval_name' => '10 min',
		],
		//
		'wpkama_cron_func_2' => [
			'callback'      => [ MyCronCallbacks::class, 'wpkama_cron_func_2' ],
			'interval_name' => '2 hours',
			'start_time'    => 1679205600, // start at specified UNIX time
		],
		// second task
		'wpkama_cron_func_3' => [
			'callback'      => [ MyCronCallbacks::class, 'wpkama_cron_func_3' ],
			'interval_name' => '2 hours',
			'start_time'    => strtotime('tomorrow 6am'), // запустить в 6 утра (к этому времени будет добавлено время сайта)
		],
		//
		'wpkama_cron_func_4' => [
			'callback'      => [ MyCronCallbacks::class, 'wpkama_cron_func_4' ],
			'interval_name' => 'hourly', // this is already a known WP interval
		],
	],
] );

class MyCronCallbacks {

	public static function wpkama_cron_func(){
		$file = dirname( ABSPATH ) .'/__cron_check.txt';
		$content = current_time('mysql') ."\n";
		file_put_contents( $file, $content, FILE_APPEND );
	}

	public static function wpkama_cron_func_2(){
		// do something
	}

	public static function wpkama_cron_func_3(){
		// do something
	}

	public static function wpkama_cron_func_4(){
		// do something
	}
}

Регистрация задач при активации плагина

Код ниже показывает, как активировать и деактивировать задачи при активации/деактивации плагина.

ВАЖНО: в этом случае параметр auto_activate должен быть false: 'auto_activate' => false!

// Пример активации и деактивации, если не указан параметр auto_activate
register_activation_hook( __FILE__, function(){
	\Kama\WP\Kama_Cron::get( 'my_cron_jobs_2' )->activate();
} );

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

new \Kama\WP\Kama_Cron( [
	'id' => 'my_cron_jobs_2',
	'auto_activate' => false, // !IMPORTANT
	'events' => [
		'wpkama_cron_func_4' => [
			'callback'      => 'wpkama_cron_func_4',
			'interval_name' => 'twicedaily',
		],
		'wpkama_cron_func_5' => [
			'callback'      => 'wpkama_cron_func_5',
			'interval_name' => '2 hours',
		],
	],
] );

function wpkama_cron_func_4(){
	// code here
}

function wpkama_cron_func_5(){
	// code here
}

INFO: Метод deactivate() деактивирует все задания из текущего пакета (в примере выше это два задания).

Параметры конструктора

$args(массив)

Args.

  • id(строка)
    A unique identifier that can then be used to access the settings externally.
    По умолчанию: 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(массив)
    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(разное)
      What parameters should be passed to the cron task function.

    • 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.

    • 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(строка)
      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. If you need to start event at, for example, tomorrow 6 AM (with site time), you must get timestamp and fix it with site gtm_offset: strtotime('tomorrow 6am') - (int) get_option('gtm_offset').
      По умолчанию: time()

Дебаг

Полезный код для дебага:

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

Также для дебага можете использовать плагин: WP Crontrol

Или используйте WP-CLI: wp cron.

11 комментариев
Полезные 1 Все
    Войти