WP Cron (планировщик) в WordPress
Представим что нам нужно, чтобы через 5 часов выполнилась какая-то PHP функция или чтобы эта функция выполнялась каждый день. В решении такой задачи нам поможет WordPress Крон — планировщик задач. Давайте разберемся как он работает и как его использовать.
Название Cron взято из UNIX-подобных операционных систем. Там Cron — это планировщик заданий в задачу которого входит периодическое выполнение указанных действий в определённое время.
Kama_Cron — маленький класс для удобной работы с Кроном WordPress.
- Крон задачи самого WordPress
- Как работает крон (шаг за шагом)
- Работает ли крон на сайте, как проверить?
- Альтернативный вариант инициализации крона
- Как отключить крон?
- Функции крона
- Создание крон задач
- Повторяющиеся задачи
- #1 Создаем крон задачу при активации плагина
- #2 Создаем крон задачу если её еще нет
- #3 Еще примеры
- Одноразовые задачи
- #1 Запланируем событие через час с текущего момента
- #2 Еще примеры
- Интервалы для крон задач
- Удаление крон задач
- Дебаг Крона в WordPress
- Плагины для контроля Крон задач
- Полезные ссылки
Крон задачи самого WordPress
WordPress использует крон для своих нужд, так в нем есть базовые крон задачи:
- Проверка обновлений для плагинов, тем, переводов и ядра (два раза в день).
- wp_version_check → wp_version_check()
- wp_update_plugins → wp_update_plugins()
- wp_update_themes → wp_update_themes()
- Удаление комментариев и записей из корзины (раз в день).
- wp_scheduled_delete → wp_scheduled_delete()
- Удаление просроченных временных опций (раз в день).
- delete_expired_transients → delete_expired_transients()
- Удаление черновиков (auto-draft) (раз в день).
- wp_scheduled_auto_draft_delete → wp_delete_auto_drafts()
- Удаление старых файлов экспорта (раз в день) (с WP 4.9.6)
- wp_privacy_delete_old_export_files → wp_privacy_delete_old_export_files()
Как работает крон (шаг за шагом)
-
При посещении любой страницы сайта (при любом запросе к сайту), в том числе при AJAX, REST запросах, т.е. всегда на событии
init
срабатывает функцияwp_cron()
.if ( ! defined( 'DOING_CRON' ) ) add_action( 'init', 'wp_cron' );
-
wp_cron()
проверяет существует ли хоть одно задание с подошедшем временем. Если есть, то вызывает функциюspawn_cron()
. -
spawn_cron()
запускает крон! Отправляет не блокирующий HTTP запрос на файл крона/wp-cron.php
в котором передает текущую метку времени вида:microtime(true)
, например:1538326680.8330409526824951171875
.Тут проверяется, когда был в последний раз запущен крон, если менее 60 секунд, то функция ничего не делает. Изменить этот интервал можно через константу
define( 'WP_CRON_LOCK_TIMEOUT', 60 )
, указать в константе можно максимум 600 (10 минут), если указать больше, то она будет игнорироваться.Далее проверяется есть ли хоть одно подошедшее задание, если есть то во временную опцию
'doing_cron'
записывается текущая метка времени, когда был запущен крон и отправляется неблокирующий запрос на файл/wp-cron.php
, там эта метка времени используется для разных проверок. -
Файл
/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 запросы. Крон активируется через неблокирующий 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
. Или можно оттестировать функцию крона отдельно, и потом просто повесить её на хук крона и убедится что хук сработал в нужное время.
Чтобы увидеть ошибки (если они есть), нужно поставить интервал поменьше (чтобы поймать выполнение нашего задания) и перейти по уже упомянутой ссылке http://site.ru/wp-cron.php.
Обращаясь к файлу wp-cron.php напрямую, вы будите насильно инициализировать запуск крона WordPress и увидите, что возвращают ваши функции, если наступило время их выполнения. В том числе можно будет увидеть PHP ошибки, если включена константа WP_DEBUG
в wp-config.php
.
Может быть полезным временно отключить крон, чтобы никакие крон-задачи не выполнялись на сайте пока вы работаете над функцией крона.
Для отключения крона добавьте в wp-config.php
такую константу:
define( 'DISABLE_WP_CRON', true );
Получить весь список текущих крон задач можно функцией _get_cron_array()
:
Плагины для контроля Крон задач
- WP Crontrol — отличный плагин для просмотра и управления крон задачами WordPress.
- Advanced Cron Manager – debug & control — плагин по сути описан в этом скриншоте.
Есть ещё замена Cron:Action Scheduler использовал её для системы авто-аукционов.
Неплохая либа
Что-то мощное судя по всему...
Если не изменяет память, Костя Ковшенин очень советовал пользоватьсяCavalcade .