WordPress как на ладони
Наставник Трепачёв Д.П., phphtml.net wordpress jino

wp_schedule_event() WP 2.1

Ставит в очередь хук-событие, который WordPress будет выполнять в указанный вами интервал времени.

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

Крон задачи выполняются асинхронно. Т.е. для выполнения ожидаемых крон задач, WordPress отправляет запрос на файл http://site.ru/wp-cron.php. Этот файл самодостаточный: он устанавливает константу define('DOING_CRON', true);, затем подгружает среду WordPress и выполняет все ожидаемые задачи. Поэтому для дебага крон функции нужно напрямую обращаться к файлу http://site.ru/wp-cron.php. (см. пункт "дебаг крона").

При отправке запроса в файл http://site.ru/wp-cron.php и выполнении очередной задачи WordPress автоматический добавляет новую задачу в планировщик и удаляет текущую, используя при этом: wp_reschedule_event(), wp_unschedule_event().

Чтобы удалить имеющееся событие используйте wp_clear_scheduled_hook()

Константы WordPress которые могут пригодится при создании задач:

  • HOUR_IN_SECONDS - час в секундах - 60*60 = 3600
  • DAY_IN_SECONDS - день в секундах - 60*60*24 = 86400
  • WEEK_IN_SECONDS - неделя в секундах - 60*60*24*7 = 604800
  • YEAR_IN_SECONDS - год в секундах - 60*60*24*7*365 = 220752000

Зачем нужно событие cron_schedules. Казалось бы почему просто не указать интервал при добавлении задачи и все, зачем нужно добавлять интервал через фильтр, чтобы потом wp_schedule_event() использовал этот фильтр. Дело в том, что интервал должен существовать всегда. При выполнении одной крон задачи, WP на основе этого интервала создает такое же следующее задание. И поэтому ему нужны данные интервала...

Используется в: wp_reschedule_event().
Хуки из функции:
Возвращает

nul/false. null когда планирование завершено. false в случае неудачи.

Использование

wp_schedule_event( $timestamp, $recurrence, $hook, $args );

Параметры

$timestamp(число) (обязательный)

Начальная метка времени, с которой хук начнет работать. Нужно указывать в формате UNIX (32165487).

WP cron использует время UTC/GMT, а не локальное. Используйте функцию time(), которая всегда в GMT в WP. current_time('timestamp') - это локальное время в WP.
По умолчанию: нет

$recurrence(строка) (обязательный)

Как часто должно повторяться действие. Допустимые значения ниже. Вы можете создать свой интервал используя фильтр cron_schedules из функции wp_get_schedules().

  • hourly - ежечасно;
  • twicedaily - дважды в день;
  • daily - ежедневно.

По умолчанию: нет

$hook(строка) (обязательный)
Название хука, который нужно выполнять.
По умолчанию: нет
$args(массив)
Аргументы, которые нужно передать в выполняемый хук.
По умолчанию: нет
к началу

Дебаг Крона в WordPress

Правильно говорить - профилирование функции прикрепленной к хуку, который вызывается кроном. Но для краткости путь это будет "Дебаг Крона", никто же не против?

Крон задачи выполняются асинхронно и поэтому при загрузке страниц сайта вы не увидите ничего, даже если в функции крона выведите что-то на экран, пр: die('вижу').

Чтобы увидеть возможные ошибки, нужно поставить интервал поменьше и открыть файл http://site.ru/wp-cron.php. Обращаясь к этому файлу, вы увидите что возвращают ваши функции, если наступило время их выполнения. В том числе вы сможете увидеть PHP ошибки (включите константу WP_DEBUG в wp-config.php).

Если нужно, чтобы автоматические задачи временно не восполнялись пока вы "дебагите" новую функцию, то в файле /wp-config.php установите константу

define('DISABLE_WP_CRON', true);

Также, для дебага может понадобится получить весь список активных Крон событий, сделать это можно так:

print_r( _get_cron_array() );
к началу

Примеры

#1. Запланируем действие которое будет выполняться каждый час.

Запланируем ежечасное действие для плагина, вызовем wp_schedule_event при активации плагина (при другом вызове, мы получим множество запланированных событий!)

register_activation_hook(__FILE__, 'my_activation');
function my_activation() {
	// удалим на всякий случай все такие же задачи cron, чтобы добавить новые с "чистого листа"
	// это может понадобиться, если до этого подключалась такая же задача неправильно (без проверки что она уже есть)
	wp_clear_scheduled_hook( 'my_hourly_event' );

	// Проверим нет ли уже задачи с таким же хуком
	// этот пункт не нужен, потому что мы выше удалил все задачи...
	// if( ! wp_next_scheduled( 'my_hourly_event' ) ) 

	// добавим новую cron задачу
	wp_schedule_event( time(), 'hourly', 'my_hourly_event');
}

// Будет грамотно проверить выполняется ли крон, если нет - ничего не делаем
// Можно не проводить эту проверку, в принципе
if( defined('DOING_CRON') && DOING_CRON ){
	add_action('my_hourly_event', 'do_this_hourly');
	function do_this_hourly() {
		// делаем что-либо каждый час
	}
}

// При дезактивации плагина или в других случаях, обязательно нужно удалить ранее созданную задачу:
register_deactivation_hook( __FILE__, 'my_deactivation');
function my_deactivation() {
	wp_clear_scheduled_hook('my_hourly_event');
}
к началу

#2. Многоразовый вызов wp_schedule_event()

Этот пример не полагается на активацию плагина (через директорию плагинов), вместо этого он добавляет событие, если его не существует:

// добавляем запланированный хук
add_action('wp', 'my_activation');
function my_activation() {
	if( ! wp_next_scheduled( 'my_hourly_event' ) ) {
		wp_schedule_event( time(), 'hourly', 'my_hourly_event');
	}
}

// добавляем функцию к указанному хуку
add_action('my_hourly_event', 'do_this_hourly');
function do_this_hourly() {
	// делаем что-либо каждый час
}

Крон задача удаляется так:

wp_clear_scheduled_hook('my_hourly_event');

При этому удаляться все задачи с таким названием выполняемого хука. Их может быть несколько, если неправильно их регистрировать.

к началу

#3. Добавление нового интервала

Чтобы добавить новый интервал Cron в WordPress, используем фильтр cron_schedules.

Добавим интервал события - раз в 5 минут:

// регистрируем пятиминутный интервал
add_filter( 'cron_schedules', 'cron_add_five_min' );
function cron_add_five_min( $schedules ) {
	$schedules['five_min'] = array(
		'interval' => 60 * 5,
		'display' => 'Раз в 5 минут'
	);
	return $schedules;
}

// регистрируем событие
add_action('wp', 'my_activation');
function my_activation() {
	if ( ! wp_next_scheduled( 'my_five_min_event' ) ) {
		wp_schedule_event( time(), 'five_min', 'my_five_min_event');
	}
}
// добавляем функцию к указанному хуку
add_action('my_five_min_event', 'do_every_five_min');
function do_every_five_min() {
	// делаем что-либо каждые 5 минут
}
к началу

#4. Готовый Класс для удобного добавления крон событий

По ходу работы отвлекся и написал такой класс. С ним удобно создавать задачи крон, чтобы не перепутать ничего, все настройки указываются в общей переменной настроек, всю рутину выполняет класс. Ну разумеется сами функции-обработчики задач нужно будет писать отдельно.

/**
 * Удобное добавление крон задач. Код нужно вызывать как можно раньше, без всяких хуков.
 * Для добавления задачи: Kama_Cron::activation() при активации плагина, и можно при обновлении настроек.
 * Для удаления задач: Kama_Cron::deactivation() при деактивации плагина.
 *
 * Внимание: этот код должен подключаться как плагин или должен быть в главном файле плагина.
 * При несоблюдении этого условия, обратите внимание на вызов двух функций:
 * register_activation_hook() и register_deactivation_hook()
 * 
 * ver 0.1
 */

## Инициализация с настройками
new Kama_Cron(array(
	'id' => 'mycron', // уникальный идентификатор по которому потом можно обращаться к настройкам

	// Какие задачи добавить
	'events' => array(
		// hook name => $data
		'sdot_close_overdue_draft' => array(
			'interval_key'  => 'half_an_hover',
			'interval_name' => 'Каждые пол часа',
			'interval'      => HOUR_IN_SECONDS/2,
			'event_name'    => 'Закончить незавершенные тесты',
			'method'        => 'close_overdue_draft', // название метода класса Kama_Cron, или название внешней функции
			'args'          => array(),
		),
		// для разработки
		'sdot_close_overdue_draft2' => array(
			'interval_key'  => 'minute',
			'interval_name' => 'Каждую минуту',
			'interval'      => 60,
			'event_name'    => 'Закончить незавершенные тесты',
			'method'        => 'my_func_for_cron_exec',
			'args'          => array(),
			'start_time'    => 0, // 0 - now
		),
	),

	// Какие задачи удалить, если вдруг в результате разработки появились лишние
	'clear' => array(
		//'hook_name' => array( 'args' => array() ),
	),
));

// Внешняя функцию крон указанная в настройках класса Kama_Cron
function my_func_for_cron_exec(){
	if( Kama_Cron::DEBUG )
		die( '<br><br>next '. date('d M Y H:i', wp_next_scheduled('sdot_close_overdue_draft') ) . ' and now ' . date('d M Y H:i') );

	// здесь любой код, который должен выполниться по расписанию
}

register_activation_hook( __FILE__, array('Kama_Cron','activation') ); // активация для плагина

register_deactivation_hook( __FILE__, array('Kama_Cron','deactivation') ); // деактивация для плагина

/**
 * Класс Kama_Cron
 * ver see parent
 */
class Kama_Cron {

	const DEBUG = 0; // в рабочем режиме должно быть 0. Для дебага переходим на http://site.ru/wp-cron.php

	static $opts;

	public $id;

	function __construct( $args ){
		$opt_def = array(
			'id'    => '', // уникальный идентификатор по которому потом можно обращаться к настройкам

			// Какие задачи добавить
			'events' => array(
				// hook name => $data
				'sdot_close_overdue_draft' => array(
					'interval_key'  => 'half_an_hover',
					'interval_name' => 'Каждые пол часа',
					'interval'      => HOUR_IN_SECONDS/2,
					'event_name'    => 'Закончить незавершенные тесты',
					'method'        => 'close_overdue_draft', // название метода класса Kama_Cron, или название внешней функции
					'args'          => array(),
				),
			),

			// Какие задачи удалить, если вдруг в результате разработки появились лишние
			'clear' => array(
				//'hook_name' => array( 'args' => array() ),
			),
		);

		$opt = (object) array_merge( $opt_def, $args );

		if( ! $this->id = $opt->id ) wp_die('ID не указан при создании экземпляра Kama_Cron');

		self::$opt[$this->id] = $opt;

		add_filter( 'cron_schedules', array( & $this, 'add_intervals' ) );

		if( defined('DOING_CRON') && DOING_CRON ){
			foreach( self::$opt[$this->id]->events as $key => $data ){
				if( method_exists( $this, $data['method'] ) ) $func = array( & $this, $data['method'] );
				elseif( function_exists( $data['method'] ) )  $func = $data['method'];

				if( isset($func) )
					add_action( $key, $func );
			}
		}

		if( Kama_Cron::DEBUG )
			echo print_r( _get_cron_array(), 1 ) . '<br><br>now time - '. time();
	}

	## Доп. интервалы, чтобы регнуть задачу
	function add_intervals( $schedules ){
		foreach( self::$opt[$this->id]->events as $key => $data ){
			$schedules[ $data['interval_key'] ] = array(
				'interval' => $data['interval'],
				'display'  => $data['interval_name'],
			);
		}

		return $schedules;
	}

	## Добавляет крон задачу. Вызывается при активации плагина или на обновлении настроек
	static function activation(){
		self::deactivation(); // сначала удалим все задачи

		// теперь добавим
		foreach( self::$opts as $opt ){
			foreach( $opt->events as $key => $data )
				wp_schedule_event( (@ $data['start_time']?:time()), $data['interval_key'], $key );
		}
	}

	## Удаляет крон задачу. Вызывается при дезактивации плагина
	static function deactivation(){
		foreach( self::$opts as $opt ){
			// удалим задачи с таким же названием
			foreach( $opt->events as $key => $data ) wp_clear_scheduled_hook( $key, $data['args'] );

			// удалим произвольно указанные задачи не из класса...
			foreach( $opt->clear as $key => $data )  wp_clear_scheduled_hook( $key, $data['args'] );
		}
	}

	### Функции из параметра $data['method'] -----------------------------------------

	## Главная Функция - закрывает просроченные черновики
	function close_overdue_draft(){
		global $wpdb;

		if( Kama_Cron::DEBUG )
			die( '<br><br>next '. date('d M Y H:i', wp_next_scheduled('sdot_close_overdue_draft') ) . ' and now ' . date('d M Y H:i') );
	}

}

П.С. Последняя версия кода не тестировалась, поэтому если что пишите в комменты и если все работает тоже дайте знать плз. Спасибо!

к началу

#5 Полный пример создания крон задачи

Это пример из комментариев от Campusboy, автора сайта wp-plus.ru, за что скажем ему отдельное спасибо!

Что делает пример: получает курс рубля по отношению к доллару и записывает результат в опции. Делается это всего 2 раза в сутки, для чего собственно и нужен крон. Далее, где нужно данные можно очень быстро получить из опции, куда они были записаны.

Получение данных валют работает на основе библиотеки PHP Simple HTML DOM Parse

// Cron
// Регистрируем расписание при активации плагина
register_activation_hook(__FILE__, 'activation_geting_course_dollar');
function activation_geting_course_dollar() {
	wp_clear_scheduled_hook( 'geting_course_dollar' );
	wp_schedule_event( time(), 'twicedaily', 'geting_course_dollar');
}

// Удаляем расписание при деактивации плагина
register_deactivation_hook( __FILE__, 'deactivation_geting_course_dollar');
function deactivation_geting_course_dollar() {
	wp_clear_scheduled_hook('geting_course_dollar');
}

// Проверка существования расписания во время работы плагина на всякий пожарный случай
if( ! wp_next_scheduled( 'geting_course_dollar' ) ) {  
	wp_schedule_event( time(), 'twicedaily', 'geting_course_dollar');  
}

// Хук и функция, которая будет выполняться по Крону
add_action( 'geting_course_dollar', 'get_real_course_dollar' );  
function get_real_course_dollar(){
	include 'inc/simple_html_dom.php';
	$html = file_get_html( 'https://news.yandex.ru/quotes/1.html' );
	$value = $html->find('.quote_current_yes', 0)->find('.quote__value',0)->plaintext;
	$date = $html->find('.quote_current_yes', 0)->find('.quote__date',0)->plaintext;
	$course = array( 'dollar' => $value, 'date' => $date, 'check' => current_time('mysql',1) );
	update_option( 'i_price_course_dollar', $course, 'no' );
}

// Выводит данные курса
function the_course_dollar( $data = null ){
	echo get_course_dollar( $data );
}

// Возвращает данные курса
function get_course_dollar( $data = null ){
	$course = get_option('i_price_course_dollar');
	if ( !$data || $data == 'dollar') return $course['dollar'];
	if ( $data == 'date') return $course['date'];
	if ( $data == 'check') return $course['check'];
}
к началу

#6 Обновление еще не выполненной крон задачи

Допустим у нас в плагине есть настройка интервала какой-то крон задачи. И нам нужно, обновлять задачу в зависимости от указанной настройки.

Первое что нужно сделать это удалить текущую задачу и добавить новую. Также нужно учесть изменение настройки при изменении интервала для крон задачи.

Этот пример показывает как сделать такую операцию с плагином ACF. При обновлении опции которого нужно обновить крон задачу.

// функция выполнения крон задачи 'rs_cron_event'
add_action('rs_cron_event', 'rs_cron_rebuild_meta_products');
function rs_cron_rebuild_meta_products() {
	// выполняем задачу крон
	$headers = 'From: My Name <myname@mydomain.com>' . "\r\n";
	wp_mail('test@test.com', 'Тема', 'Содержание', $headers );
}

// добавим наш новый интервал для крона
add_filter( 'cron_schedules', 'rs_cron_interval');
function rs_cron_interval( $schedules, $value = '' ){
	// чтобы можно было указать значение жестко
	if( ! $value )
		$value = intval( get_option('options_rs_time_period') ?: 12 );

	$schedules['rs_time_period'] = array(
		'interval' => $value * HOUR_IN_SECONDS ,
		'display' => 'Задаётся в настройках плагина RS'
	);    

	return $schedules;
}

// Функция обновления поля
add_filter('acf/update_value/name=rs_time_period', 'my_acf_update_value', 10, 3);
function my_acf_update_value( $new_period, $post_id, $field  ) {

	$old_period = get_option('options_rs_time_period');

	// опция изменилась, перезапишем крон задачу с новой настройкой!
	if( $new_period != $old_period ){
		// удалим имеющуюся крон задачу
		$timestamp = wp_next_scheduled('rs_cron_event');
		wp_unschedule_event( $timestamp, 'rs_cron_event');

		// изменим интервал чтобы задача добавилось правильно...
		add_filter( 'cron_schedules', function( $schedules ) use ( $new_period ){
			return rs_cron_interval( $schedules, $new_period ); // костылёк жестко укажем интервал
		} );

		// добавим крон задача снова
		wp_reschedule_event( time(), 'rs_time_period', 'rs_cron_event' );
	}

	return $new_period;
}
к началу

Плагин для контроля Крон задач

Код wp schedule event: wp-includes/cron.php WP 4.8

<?php
function wp_schedule_event( $timestamp, $recurrence, $hook, $args = array()) {
	// Make sure timestamp is a positive integer
	if ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) {
		return false;
	}

	$crons = _get_cron_array();
	$schedules = wp_get_schedules();

	if ( !isset( $schedules[$recurrence] ) )
		return false;

	$event = (object) array( 'hook' => $hook, 'timestamp' => $timestamp, 'schedule' => $recurrence, 'args' => $args, 'interval' => $schedules[$recurrence]['interval'] );
	/** This filter is documented in wp-includes/cron.php */
	$event = apply_filters( 'schedule_event', $event );

	// A plugin disallowed this event
	if ( ! $event )
		return false;

	$key = md5(serialize($event->args));

	$crons[$event->timestamp][$event->hook][$key] = array( 'schedule' => $event->schedule, 'args' => $event->args, 'interval' => $event->interval );
	uksort( $crons, "strnatcasecmp" );
	_set_cron_array( $crons );
}

Cвязанные функции

Из метки: cron (расписание schedule)

Еще из раздела: Планировщик задач (Cron)

wp_schedule_event 30 комментариев
Полезные 4 Вопросы 2 Все
  • Дмитрий
    @

    Добрый день. Я новичок в WP и у меня есть вопросы по работе крона:

    Я сделал свой элемент для Visual Composer - простой прогресс бар состоящий из нескольких шагов. Можно ли сделать крон задачу, которая бы в выбранную дату обновляла этот элемент (выставляла следующий шаг прогрес бара) и обновляла страницу на которой он находится? Такое вообще возможно в вордпресе сделать? Спасибо большое за ответ!

    Ответить15 дней назад #

Здравствуйте, !

Ваш комментарий