WP-Cron — это не настоящий системный cron.
Он работает так:
- Пользователь открывает любую страницу сайта
- WordPress проверяет: “есть ли задачи, время которых уже наступило?”
- Если есть — запускает их прямо в этом же запросе (в фоне)
Пример
При сохранении опций в админке устанавливаем кастомную CRON задачу.

// Call
AutoBumpModule::register();
// Class
<?php
class AutoBumpModule
{
private const CRON_HOOK = 'auto_bump_admin_profiles';
public static function register(): void
{
add_filter('cron_schedules', [__CLASS__, 'addCustomCronSchedules']);
add_action('init', [__CLASS__, 'scheduleCronEvent']);
add_action(self::CRON_HOOK, [__CLASS__, 'autoBumpProfiles']);
add_filter('acf/fields/post_object/result', [__CLASS__, 'addPostIdToTitle'], 10, 4);
}
public static function addCustomCronSchedules(array $schedules): array
{
$schedules['every_15_minutes'] = [
'interval' => 15 * 60,
'display' => 'Every 15 Minutes',
];
$schedules['every_30_minutes'] = [
'interval' => 30 * 60,
'display' => 'Every 30 Minutes',
];
$schedules['every_2_hours'] = [
'interval' => 2 * 60 * 60,
'display' => 'Every 2 Hours',
];
$schedules['every_3_hours'] = [
'interval' => 3 * 60 * 60,
'display' => 'Every 3 Hours',
];
$schedules['every_6_hours'] = [
'interval' => 6 * 60 * 60,
'display' => 'Every 6 Hours',
];
return $schedules;
}
public static function scheduleCronEvent(): void
{
$enabled = get_field('sprmbl_auto_bump_enabled', 'option');
$interval = get_field('sprmbl_auto_bump_interval', 'option') ?: 'every_30_minutes';
$timestamp = wp_next_scheduled(self::CRON_HOOK);
if ($enabled) {
if ($timestamp) {
$currentSchedule = wp_get_schedule(self::CRON_HOOK);
if ($currentSchedule !== $interval) {
wp_clear_scheduled_hook(self::CRON_HOOK);
wp_schedule_event(time(), $interval, self::CRON_HOOK);
}
} else {
wp_schedule_event(time(), $interval, self::CRON_HOOK);
}
} elseif ($timestamp) {
wp_clear_scheduled_hook(self::CRON_HOOK);
}
}
public static function autoBumpProfiles(): void
{
$enabled = get_field('sprmbl_auto_bump_enabled', 'option');
if (!$enabled) {
return;
}
$profileIds = get_field('sprmbl_auto_bump_profiles', 'option') ?: [];
$bumpCount = (int) (get_field('sprmbl_auto_bump_count', 'option') ?: 2);
if (empty($profileIds)) {
return;
}
// Проверяем: если первая анкета в общем списке — это анкета админа, ничего не делаем
if (self::isAdminProfileOnTop($profileIds)) {
update_field('sprmbl_auto_bump_last_run', current_time('Y-m-d H:i:s') . ' (есть в топе)', 'option');
return;
}
$activeProfiles = self::getActiveProfiles($profileIds);
if (empty($activeProfiles)) {
return;
}
// Сортируем: кто дальше от топа — первым
usort($activeProfiles, function ($a, $b) {
return $b['position'] - $a['position'];
});
$bumped = 0;
foreach (array_slice($activeProfiles, 0, $bumpCount) as $profileData) {
if (self::bumpProfile($profileData['id'])) {
$bumped++;
}
}
if ($bumped > 0) {
UserCabinet::clearAllCache();
}
update_field('sprmbl_auto_bump_last_run', current_time('Y-m-d H:i:s') . " (поднято: $bumped)", 'option');
}
private static function isAdminProfileOnTop(array $adminProfileIds): bool
{
$query = new \WP_Query([
'post_type' => 'models',
'posts_per_page' => 1,
'post_status' => 'publish',
'orderby' => 'modified',
'order' => 'DESC',
'meta_query' => [
[
'key' => 'model_active_before',
'value' => date('Y-m-d'),
'compare' => '>=',
'type' => 'DATE',
],
],
'fields' => 'ids',
]);
if (empty($query->posts)) {
return false;
}
return in_array($query->posts[0], $adminProfileIds);
}
private static function getActiveProfiles(array $profileIds): array
{
$profiles = [];
foreach ($profileIds as $postId) {
$post = get_post($postId);
if (!$post || $post->post_status !== 'publish' || $post->post_type !== 'models') {
continue;
}
$activeBefore = get_field('model_active_before', $postId);
if (empty($activeBefore) || strtotime($activeBefore) < strtotime('today')) {
continue;
}
$position = UserCabinet::getPostPosition($postId);
$profiles[] = [
'id' => $postId,
'position' => $position ?? 999999,
];
}
return $profiles;
}
public static function addPostIdToTitle(string $title, \WP_Post $post, array $field, $postId): string
{
if ($field['key'] === 'field_699882cb2b0db') {
$title = $title . ' (ID: ' . $post->ID . ')';
}
return $title;
}
private static function bumpProfile(int $postId): bool
{
$currentTime = current_time('mysql');
$result = wp_update_post([
'ID' => $postId,
'post_modified' => $currentTime,
'post_modified_gmt' => get_gmt_from_date($currentTime),
], true);
UserCabinet::clearAllCache();
return !is_wp_error($result);
}
}
Схема:
Посетитель → страница → WP проверяет расписание → запускает autoBumpProfiles()
Будет ли работать на локалке?
Да, но только если ты открываешь сайт в браузере. Сам по себе cron не тикает — нужен HTTP-запрос для его “пинга”.
Практически это значит:
- Зашёл на сайт → через нужный интервал (15/30 мин и т.д.) при следующем визите — сработает
- Если сайт никто не открывает — не сработает
Как проверить что всё подключилось
- Можно проверить через WP-CLI:
wp cron event list
После того как зайдёшь на сайт (чтобы scheduleCronEvent() отработал на init), в списке должно появиться событие auto_bump_admin_profiles.
- Или запустить вручную:
wp cron event run auto_bump_admin_profiles
На продакшне обычно WP-Cron отключают (define(‘DISABLE_WP_CRON’, true)) и настраивают реальный системный cron.
Тогда задачи выполняются строго по расписанию, не завися от трафика.
Также можно смотреть CRON задачи в плагине WP Crontrol.
