WP Cron (планувальник завдань)

Допустимо, нам потрібно, щоб через 5 годин виконалась якась PHP функція. Або потрібно, щоб ця функція виконувалася щодня. Вирішити такі завдання допоможе WordPress Крон – планувальник завдань . Нижче, давайте розберемося, як він працює і як його використовувати.

Назва Cron взято з UNIX-подібних операційних систем. Там Cron — це планувальник завдань, завданням якого є періодичне виконання зазначених дій у певний час.

Дивіться на тему:




Крон завдання WordPress (з коробки)

WordPress використовує крон для своїх потреб, так у ньому є базові крон завдання :




Як працює крон (крок за кроком)

  1. При відвідуванні будь-якої сторінки сайту (при будь-якому запиті на сайт), у тому числі при AJAX, REST запитах, тобто. завжди на події init спрацьовує функція wp_cron() .
    if ( ! defined( 'DOING_CRON' ) )
    	add_action('init', 'wp_cron');
  2. wp_loaded .
  3. _wp_cron() перевіряє константу DISABLE_WP_CRON, якщо вона відключена, то перевіряє чи існує хоч одне завдання з часом. Якщо є, викликає функцію spawn_cron() .
  4. файл крона /wp-cron.php в якому передає поточну мітку часу виду: microtime(true) , наприклад: 1538326680.8330409526824951171875 .

    Тут перевіряється, коли востаннє запущено крон, якщо менше 60 секунд, то функція нічого не робить. Змінити цей інтервал можна через константу WP_CRON_LOCK_TIMEOUT . Вказати в константі можна максимум 600 (10 хвилин), якщо вказати більше, вона буде ігноруватися. Приклад:

    define( 'WP_CRON_LOCK_TIMEOUT', 60 )

    Далі перевіряється чи є хоч одне завдання, що підійшло, якщо є то в тимчасову опцію ‘doing_cron’ записується поточна мітка часу, коли був запущений крон і відправляється неблокуючий запит на файл /wp-cron.php , там ця мітка часу використовується для різних перевірок.

  5. wp_reschedule_event() . Нове завдання буде виконано через вказаний в інтервалі проміжок часу.

    Далі, запускається зазначений під час реєстрації крон завдання хук-подія, тобто. запускається поточне завдання:

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

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

    Як обробляються довгі крон завдання. Допустимо, у крон є кілька завдань. Було надіслано «перший» крон запит і перше завдання почало виконуватися. Але виконується воно довго (більше 60 секунд) в цей час був відправлений другий крон запит. У цьому випадку чергове (наступне) завдання у «першому» крон запиті НЕ виконуватиметься — «перший» запит виконає «довге» завдання і зупиниться. Другий запит почне виконувати завдання наступні після «довгого» завдання, яке виконує перший запит.

нотатки

Всі крон завдання зберігаються в опції get_option(‘cron’) .

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

Іншими словами: крон завдання виконуються асинхронно . Тобто. Для виконання очікуваних крон завдань WordPress відправляє запит на файл http://example.com/wp-cron.php . Цей файл є самодостатнім: він встановлює константу define(‘DOING_CRON’, true) , потім підвантажує середовище WordPress і виконує всі очікувані завдання.




Як перевірити, чи працює крон на сайті?




Варіант 1

Це можна зробити на сторінці адмінки “Здоров’я сайту”. Заходимо на сторінку і трохи чекаємо (поки чекаємо на WP пробуде зробити «петльовий запит») якщо він не вдається, ми побачимо наступне повідомлення і це означає, що крон запит не працює.

У здоров’я сайту ви не побачите помилок, якщо Cron відключений має намір через константу DISABLE_WP_CRON.




Варіант 2

  1. Встановіть плагін WP Crontrol . (Після перевірки його можна видалити).
  2. Перейдіть на сторінку плагіна Події Крон і якщо крон не працює, ви побачите попередження про те, що деякі завдання вже прострочені:




Варіант 3

Швидкий спосіб переконатися, що крон справно працює – це опублікувати запис з датою пізніше поточної (вона буде поставлена ​​в чергу на публікацію) і подивитися опублікується вона чи ні.

Якщо крон працює, запис опублікується в заданий час. А якщо крон не працює, то побачимо таку картину:




Причини, через які WP-Cron може не працювати

Сron може не працювати з різних причин:

  • Він вимкнений навмисно. Встановлено константу конфігурації DISABLE_WP_CRON, але не встановлено альтернативний варіант запуску.
  • Ваш сервер не вміє надсилати запити HTTP. Cron активується через неблокуючий HTTP-запит WP до себе.
  • Плагін може навмисно чи навмисно порушити роботу Cron.
  • Фатальна помилка, спричинена плагіном або темою, може порушити роботу Cron.

Більше причин див.




Увімкнення іншого механізму запуску крона

За замовчуванням крон запускається запитом POST, за допомогою wp_remote_post() з будь-якої відвіданої сторінки сайту, якщо час підійшов. Але якщо у вас на сервері такий варіант не працює, можна включити альтернативний варіант запуску крона. Для цього додайте таку константу до файлу wp-config.php :

define( 'ALTERNATE_WP_CRON', true);

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




Запуск WP-cron лише з сервера

Щоб увімкнути запуск крон завдань тільки з сервера, потрібно вимкнути вбудований запуск крон завдань WP (читайте нижче) і підключити запуск крона з сервера .




Як вимкнути крон?

Щоб вимкнути запуск WP-Крона, потрібно зайти у файл wp-config.php і додати туди наступний рядок:

define( 'DISABLE_WP_CRON', true);

Ця опція вимкнути ініціалізацію крона WP. При цьому, якщо відправити прямий запит на файл /wp-cron.php, то всі завдання, що «дозріли», будуть виконані, як би крон працював.

Відключати крон вкрай не рекомендується, тому що через нього 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_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:00 з поточного моменту.
	}
}

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://example.com/wp-cron.php . Або можна відтестувати функцію крона окремо, і потім просто повісити її на хук крона і переконається, що хук спрацював у потрібний час.

Щоб побачити помилки (якщо вони є), потрібно поставити менший інтервал (щоб зловити виконання нашого завдання) і перейти за вже згаданим посиланням http://example.com/wp-cron.php.

Звертаючись до файлу 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
*/




Запуск крона із сервера

У WP крон запускається нестабільно і не можна, наприклад, вказати точний час запуску. Так виходить, тому що для ініціалізації чергового крон завдання потрібно щоб хтось (можливо бот) відвідали сайт. Тільки при відвідуванні сайту відбувається перевірка та запуск крона якщо час настав. Так, якщо на сайт ніхто не заходить місяць, то крон не працюватиме місяць.

Частково вирішити цю проблему, можна створити крон завдання на сервері, яка буде виконуватися в потрібний час або, наприклад, кожні 10 хвилин. Завдання це має відвідувати файл ініціалізації крона http://example.com/wp-cron.php . Наприклад, можна запускати таку команду по крону:

# Виконувати команду кожні 5 хвилин
*/5 * * * * wget -O /dev/null -q 'https://example.com/wp-cron.php'

# виконувати команду щогодини
* */1 * * * wget -O /dev/null -q 'https://example.com/wp-cron.php'

У цьому випадку, крон завдання WP спрацьовуватимуть навіть якщо сайт у потрібний момент ніхто не відвідує.

Формат Crontab linux (докладніше читайте тут ):

* * * * * Команда, яка буде виконана
| | | | |
| | | | └─ День тижня (0 - 7) (неділя = 0 або 7)
| | | └─── Місяць (1 - 12)
| | └───── День місяця (1 - 31)
| └─────── Час (0 - 23)
└───────── Хвилина (0 - 59)




Плагіни для контролю Крон завдань




Корисні посилання

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *