WordPress как на ладони
Мощный и не дорогой хостинг от Fornex.com Хостинг, VPS/VDS и отдельные сервера только на SSD дисках. 7 дней бесплатного тестирования.

WP Cron (планировщик) в WordPress

Представим что нам нужно, чтобы через 5 часов выполнилась какая-то PHP функция или чтобы эта функция выполнялась каждый день. В решении такой задачи нам поможет WordPress Крон — планировщик задач. Давайте разберемся как он работает и как его использовать.

Название Cron взято из UNIX-подобных операционных систем. Там Cron — это планировщик заданий в задачу которого входит периодическое выполнение указанных действий в определённое время.

Kama_Cron — маленький класс для удобной работы с Кроном WordPress.

Крон задачи самого WordPress

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

меню

Как работает крон (шаг за шагом)

  1. При посещении любой страницы сайта (при любом запросе к сайту), в том числе при AJAX, REST запросах, т.е. всегда на событии init срабатывает функция wp_cron().

    if ( ! defined( 'DOING_CRON' ) )
    	add_action( 'init', 'wp_cron' );
  2. wp_cron() проверяет существует ли хоть одно задание с подошедшем временем. Если есть, то вызывает функцию spawn_cron().

  3. spawn_cron() запускает крон! Отправляет не блокирующий HTTP запрос на файл крона /wp-cron.php в котором передает текущую метку времени вида: microtime(true), например: 1538326680.8330409526824951171875.

    Тут проверяется, когда был в последний раз запущен крон, если менее 60 секунд, то функция ничего не делает. Изменить этот интервал можно через константу define( 'WP_CRON_LOCK_TIMEOUT', 60 ), указать в константе можно максимум 600 (10 минут), если указать больше, то она будет игнорироваться.

    Далее проверяется есть ли хоть одно подошедшее задание, если есть то во временную опцию 'doing_cron' записывается текущая метка времени, когда был запущен крон и отправляется неблокирующий запрос на файл /wp-cron.php, там эта метка времени используется для разных проверок.

  4. Файл /wp-cron.php опять проверяет: не запускался ли недавно крон (менее 60 сек назад), есть ли подошедшие крон задания.

    Далее, проходит по всем «поспевшим» задачам и для каждого из них: удаляет текущую задачу из расписания, и если у задачи есть метка schadule, (оно повторяющееся), создает новое такое же задание с помощью функции wp_reschedule_event(). Новое задание будет выполнено через указанный в интервале промежуток времени.

    Далее, запускается указанный при регистрации крон задачи хук-событие, т.е. запускается текущее задание:

    do_action_ref_array( $hook, $v['args'] );

    Если обращаться к файлу /wp-cron.php напрямую, то он будет отрабатывать только раз в WP_CRON_LOCK_TIMEOUT секунд (60 секунд).

    Если в кроне много заданий и одно задание начало выполняться и выполняется так долго, что уже был отправлен очередной запрос на выполнение заданий крон, то очередная задача в первом крон запросе НЕ будет выполнена — первый запрос просто остановится после выполнения «долгой» задачи и очередные задания уже будут выполняться во втором запросе.

Заметки

Все крон задачи хранятся в опции get_option( 'cron' ).

Крон запрос запускается отдельно от текущей загрузки страницы и в нем отдельно грузится среда ВП и т.д. Текущий запрос (посещение страницы) только инициализирует крон (создает запрос на файл крона), если время подошло.

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

меню

Работает ли крон на сайте, как проверить?

Быстрый способ убедиться что крон исправно работает — это опубликовать запись с датой позже текущей (она будет поставлена в очередь на публикацию) и посмотреть опубликуется она или нет.

Если крон работает, то запись просто опубликуется в заданное время. А если крон не работает, то мы увидим такую картину:

Крон может не работать по двум причинам:

  • Он отключен. Как отключать смотрите ниже.
  • Ваш сервер не умеет отправлять HTTP запросы. Крон активируется через неблокирующий HTTP запрос WP к самому себе.
меню

Альтернативный вариант инициализации крона

По умолчанию крон запускается POST запросом, с помощью wp_remote_post(). Но если у вас на сервере такой вариант не работает, то можно включить альтернативный вариант запуска крона. Для этого добавьте такую константу в файл wp-config.php:

define( 'ALTERNATE_WP_CRON', true );

Такой альтернативный запуск крона создаст запрос через wp_redirect(), т.е. запрос будет отправлен не из PHP, а клиентом (браузером). Подробнее см. spawn_cron().

Как отключить крон?

Чтобы отключить крон, нужно зайти в файл wp-config.php и добавить туда следующую строчку:

define( 'DISABLE_WP_CRON', true );

Отключать крон крайне не рекомендуется, потому что через него WordPress чистит за собой всякие черновики и удаляет записи из корзины, также на базе крона могут работать некоторые плагины!

Функции крона

Добавление:

wp_schedule_event( $timestamp, $recurrence, $hook, $args )
Создает многоразовую крон-задачу. Устанавливает хук, который будет вызываться каждый раз через указанный интервал времени.
wp_schedule_single_event( $timestamp, $hook, $args )
Создает одноразовую крон-задачу. Устанавливает хук, который будет вызван всего один раз в указанное время.

Удаление:

wp_unschedule_event( $timestamp, $hook, $args )
Удаляет конкретную крон-задачу. Для удаления нужно знать все 3 параметра.
wp_clear_scheduled_hook( $hook, $args )
Отменяет (удаляет) запланированные cron задания по указанному имени хука и передаваемым параметрам. Работает на основе wp_unschedule_event().
wp_unschedule_hook( $hook )
Удаляет из расписания крон абсолютно все крон задачи по указанному хуку. Тут не важно какие параметры были указаны при регистрации задачи.

Остальное:

wp_next_scheduled( $hook, $args )
Возвращает метку времени (timestamp) когда должно сработать следующее по расписанию cron задание. Позволяет проверить есть ли в крон указанное задание.
wp_doing_cron()
Определяет является ли текущий запрос, запросом к Крону. Условный тег.
wp_get_schedules( )
Получает поддерживаемые Cron интервалы времени.

Весь список смотрите по этой ссылке.

меню

Создание крон задач

Для создания новых крон задач используется одна из функций: wp_schedule_event() или wp_schedule_single_event().

Вызывать функции создания крон задач нужно только один раз! Обычно это делается при активации плагина, а при деактивации эти крон задачи нужно удалять. Или можно сначала проверить нет ли уже указанной крон задачи, если нет, то добавить.

При вызове функции регистрации задачи она записывается в опцию сайта cron и работает уже от туда автономно.

Если задача есть в кроне, но она не срабатывает, значит во время крон запроса не подключается хук. Например, если выставлять задачу wp_schedule_single_event() через AJAX запрос и там же регистрировать хук этой задачи, то задача будет зарегистрирована в кроне, но функция в нужный момент выполняться не будет, потому что хук на который повешена функция срабатывает при AJAX запросе, а должен срабатывать при крон запросе! Поэтому сам хук нужно вешать в functions.php или в плагин или как-то еще, но не во время обработки аякс запроса.

Если при добавлении крон задачи, в планировщике уже есть задача с таким же названием и аргументами и время новой задачи не превышает 10 минут от времени имеющийся, то добавление новой крон-задачи будет проигнорировано.

Суть этого в том, чтобы не допустить планирования двух одинаковых событий в течение десяти минут друг от друга, но не препятствовать планированию одинаковых событий с разницей во времени больше десяти минут друг от друга.

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

меню

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

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

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 Создаем крон задачу если её еще нет

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

// добавляет новую крон задачу
add_action( 'admin_head', '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(){
	// делаем что-либо каждый час
}

Минус этого кода в том, что проверка происходит всегда при всех запросах, а добавляется задача всего один раз. Однако PHP затраты тут мизерные, сравнимые с обычным получением опции get_option(), поэтому на это можно закрыть глаза в угоду удобству.

меню

#3 Еще примеры

Смотрите в описании функции wp_schedule_event().

Одноразовые задачи

#1 Запланируем событие через час с текущего момента

// добавляет новую одноразовую крон задачу
add_action( 'admin_head', 'my_activation' );
function my_activation() {
	if( ! wp_next_scheduled( 'my_new_event' ) ) {
		wp_schedule_single_event( time() + 3600, 'my_new_event' );
		// time() + 3600 = 1 час с текущего момента.
	}
}

add_action( 'my_new_event','do_this_in_an_hour' );
function do_this_in_an_hour(){
	// делаем что-нибудь
}

#2 Еще примеры

Смотрите в описании функции wp_schedule_single_event().

Интервалы для крон задач

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

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

'hourly'     => array( 'interval' => HOUR_IN_SECONDS,      'display' => __( 'Once Hourly' ) ),
'twicedaily' => array( 'interval' => 12 * HOUR_IN_SECONDS, 'display' => __( 'Twice Daily' ) ),
'daily'      => array( 'interval' => DAY_IN_SECONDS,       'display' => __( 'Once Daily' ) ),

Добавлять новый интервал Cron нужно через фильтр cron_schedules.

Добавим интервал «5 минут» (делать что-либо каждые 5 минут):

// регистрируем 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;
}

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

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

Теперь можно использовать этот интервал при создании крон-задачи:

// регистрируем событие
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 минут
}

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

  • HOUR_IN_SECONDS - час в секундах - 60*60 = 3600
  • DAY_IN_SECONDS - день в секундах - 60*60*24 = 86400
  • WEEK_IN_SECONDS - неделя в секундах - 60*60*24*7 = 604800
меню

Удаление крон задач

Для удаления крон задач есть 3 функции:

wp_unschedule_event( $timestamp, $hook, $args )

Удаляет конкретную крон-задачу. Для удаления нужно знать все 3 параметра: метку времени, хук, передаваемые параметры.

wp_unschedule_event( 1540722222, 'publish_future_post', array(9227) );

// или так
$timestamp = wp_next_scheduled( 'my_schedule_hook', array(9227) );
wp_unschedule_event( $timestamp, 'my_schedule_hook', array(9227) );

Заметка: если параметр не передается, то его соответственно можно не указывать.

wp_clear_scheduled_hook( $hook, $args )

Удаляет все крон-задачи прикрепленные к указанному хуку и имеющие указанные параметры.

wp_clear_scheduled_hook( 'publish_future_post', array(9227) );
wp_unschedule_hook( $hook ).

Удаляет абсолютно все крон-задачи, прикрепленные к указанному хуку, неважно какая метка времени или какие передаются параметры.

wp_unschedule_hook( 'publish_future_post' );

Наглядно, как работают функции:

меню

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

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

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

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

define( 'DISABLE_WP_CRON', true );

Получить весь список текущих крон задач можно функцией _get_cron_array():

print_r( _get_cron_array() );

/*
Array
	[1538467983] => Array
			[wp_update_themes] => Array
					[40cd750bba9870f18aada2478b24840a] => Array
							[schedule] => twicedaily
							[args] => Array()
							[interval] => 43200

			[wp_version_check] => Array
					[40cd750bba9870f18aada2478b24840a] => Array
							[schedule] => twicedaily
							[args] => Array()
							[interval] => 43200

			[wp_update_plugins] => Array
					[40cd750bba9870f18aada2478b24840a] => Array
							[schedule] => twicedaily
							[args] => Array()
							[interval] => 43200

	[1538468836] => Array
			[wp_scheduled_delete] => Array
					[40cd750bba9870f18aada2478b24840a] => Array
							[schedule] => daily
							[args] => Array()
							[interval] => 86400

	[1538469436] => Array
			[wp_scheduled_auto_draft_delete] => Array
					[40cd750bba9870f18aada2478b24840a] => Array
							[schedule] => daily
							[args] => Array()
							[interval] => 86400

	[1538474556] => Array
			[delete_expired_transients] => Array
					[40cd750bba9870f18aada2478b24840a] => Array
							[schedule] => daily
							[args] => Array()
							[interval] => 86400

	[1540722222] => Array
			[publish_future_post] => Array
					[21966ef2cb5e27dd021560702f4ee618] => Array
							[schedule] => 
							[args] => Array
									[0] => 9227

	[1540711111] => Array
			[publish_future_post] => Array
					[21966ef2cb5e27dd021560702f4ee618] => Array
							[schedule] => 
							[args] => Array
									[0] => 9227

	[1540722222] => Array
			[publish_future_post] => Array
					[21966ef2cb5e27dd021560702f4ee618] => Array
							[schedule] => 
							[args] => Array
									[0] => 25

	[version] => 2
*/
меню

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

Полезные ссылки

3 коммента
  • llgruff47 cайт: vygodno.me

    Есть ещё замена Cron:
    Неплохая либа Action Scheduler использовал её для системы авто-аукционов.

    Robust scheduling library for use in WordPress plugins.
    Action Scheduler uses a WordPress custom post type, creatively named scheduled-action.

    1
    Ответить2 месяца назад #
Здравствуйте, !     Войти . Зарегистрироваться