Search by

Планировщик задач и парсер cron-выражений для PHP 8.1+: синтаксис 5/6/7 полей (секунды и годы), @-макросы, интервалы @every и @reboot, префикс TZ=, спецсимволы L/W/#/? и Jenkins-H, вычисление next/prev/between с таймзонами и явной политикой DST, перевод расписаний на 6 языков, планировщик задач с за

v1.0.0 2026-09-15 01:55 UTC

This package is auto-updated.

Last update: 2026-09-15 06:26:27 UTC


README

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano

CloudCastle Crone

CloudCastle Crone

Packagist Version PHP Version License Downloads Monthly Downloads Packagist Stars Dependents Suggesters Security Advisories

GitVerse Quality Issues Releases Wiki Last commit

PHPStan Psalm PHPMD PHPCS Coverage Infection MSI OpenSSF Scorecard

Планировщик задач и парсер cron-выражений для PHP 8.1+. Синтаксис из 5, 6 (с секундами) и 7 (с годом) полей, @-макросы, интервалы @every 90s, @reboot, префикс таймзоны TZ=, спецсимволы L, W, #, ?, Jenkins-хеш H и случайное R. Вычисление next/prev/between/isDue «прыжковым» поиском с учётом таймзон и переводов часов, перевод расписания на шесть языков, реестр задач с защитой от наложения, повторами, таймаутами, приоритетами, тегами и healthcheck, а также импорт и экспорт crontab. Runtime-зависимости — только экосистема cloud-castle: cloud-castle/clock (PSR-20 часы) и cloud-castle/inflector (правила счёта языков).

Установка

composer require cloud-castle/crone

Требуется PHP 8.1+. Рекомендуемое ограничение версии — ^1.0.

Быстрый старт

Разбор выражения и вычисление дат

<?php

use CloudCastle\Crone\CronExpression;

$cron = CronExpression::fromString('0 9 * * 1-5'); // будни в 09:00

$cron->getNextRunDate();                 // ближайший будний день в 09:00
$cron->getPreviousRunDate();             // предыдущий запуск
$cron->isDue('2026-07-31 09:00:00');     // true — пятница 09:00
$cron->getMultipleRunDates(3);           // три ближайшие даты запуска
$cron->describe('ru');                   // «в 09:00 по понедельникам, …»

CronExpression::isValid('*/15 * * * *'); // true
CronExpression::isValid('0 0 30 2 *');   // false — 30 февраля недостижимо

Расписание словами — без ручного составления строк

use CloudCastle\Crone\Builder\CronBuilder;

CronBuilder::create()->dailyAt('03:30')->weekdays()->build();      // '30 3 * * 1-5'
CronBuilder::create()->lastWeekdayOfMonth('18:00')->build();       // '0 18 LW * *'
CronBuilder::create()->everyFifteenMinutes()->between(9, 18)->build(); // '*/15 9-18 * * *'

Планирование задач

use CloudCastle\Crone\Scheduler\Lock\FileMutex;
use CloudCastle\Crone\Scheduler\Schedule;
use CloudCastle\Crone\Scheduler\Scheduler;

$schedule = new Schedule();

$schedule->call(fn () => backup(), 'nightly-backup')
    ->dailyAt('03:30')          // расписание текучим API
    ->weekdays()                // только по будням
    ->withoutOverlapping()      // защита от наложения
    ->retries(2, 30)            // две повторные попытки с паузой 30 с
    ->timeout(600)              // лимит выполнения
    ->priority(10)              // раньше остальных задач
    ->tag('backup')             // выборочный запуск по тегу
    ->environments('production') // только в проде
    ->when(fn () => disk_has_space())
    ->onFailure(fn ($result) => alert($result));

$scheduler = new Scheduler(new FileMutex(sys_get_temp_dir()));

foreach ($scheduler->runDue($schedule, 'production') as $result) {
    // статус, вывод, исключение, длительность, попытки, память
    log_result($result->key, $result->status->name, $result->duration);
}

Возможности

  • Полный синтаксис — 5/6/7 полей (секунды и год), @-макросы (включая @quarterly, @weekdays, @secondly), интервалы @every 1h30m, @reboot, префикс таймзоны TZ=/CRON_TZ=, диапазоны, списки, шаги, алиасы JANDEC и SUNSAT, спецсимволы L, L-3, LW, 15W, 5#3, 5L, ?, Jenkins-хеш H, H/15, H(0-29) и случайное R.
  • Вычисление датisDue, next, prev, multiple, between, count, ленивый итератор и «сколько секунд до запуска». Поиск «прыжковый»: несовпавшее поле сразу переводится к ближайшему допустимому значению.
  • Классическая семантика секунд — пятиполевое расписание срабатывает в любую секунду подходящей минуты (как системный cron), а поле секунд становится значимым только когда оно написано явно.
  • Таймзоны и переход на летнее время — вычисления ведутся в наивном времени и локализуются в конце, поэтому исчезнувший час распознаётся явно: политика DstPolicy::Skip (как Vixie cron) или DstPolicy::Shift (как systemd-таймеры).
  • Перевод на естественный язык — двадцать языков, от русского и английского до арабского, китайского и хинди, с правилами счёта каждого языка, включая спецсимволы и интервалы.
  • Нормализация и сравнение — каноническая форма выражения и equals() для проверки смысловой эквивалентности (*/150,15,30,45).
  • Безопасность чужого ввода — типизированный отказ на некорректном выражении, предел шагов поиска недостижимой даты, ограниченный кэш разбора и защита экспорта crontab от инъекции перевода строки.
  • Планировщик задач — текучее API частот (everyFiveMinutes(), dailyAt(), weeklyOn(), lastDayOfMonth() …), условия when/skip, окружения, теги, приоритеты, повторы с паузой, мягкий и жёсткий таймаут, защита от наложения и режим «только на одном сервере», хуки жизненного цикла, healthcheck-монитор и метрики выполнения.
  • Crontab в обе стороны — разбор существующего crontab (с переменными, комментариями и CRON_TZ) и экспорт расписания в готовый crontab с экранированием ключей задач.
  • Детерминизм в тестах — источник времени внедряется через PSR-20 часы, паузы и метрики — через собственные контракты.

Примеры по возможностям

Спецсимволы календаря

CronExpression::fromString('0 9 L * *');      // последний день месяца
CronExpression::fromString('0 9 L-3 * *');    // за три дня до конца месяца
CronExpression::fromString('0 9 LW * *');     // последний рабочий день месяца
CronExpression::fromString('0 9 15W * *');    // ближайший рабочий день к 15-му
CronExpression::fromString('0 9 * * 5#3');    // третья пятница месяца
CronExpression::fromString('0 9 * * 5L');     // последняя пятница месяца

Размазывание нагрузки: Jenkins-хеш

// Сотня задач с одинаковым расписанием стартует в разные минуты,
// но у каждой задачи момент запуска стабилен между перезапусками.
$cron = CronExpression::fromString('H * * * *', seed: 'report-generator');
$cron->normalize(); // например, '0 47 * * * *'

Интервалы и запуск при старте

$every = CronExpression::fromString('@every 90s');
$every->getNextRunDate('2026-07-31 09:02:13'); // 09:03:00 — сетка от эпохи Unix

$reboot = CronExpression::fromString('@reboot');
$reboot->isReboot();  // true — плановых дат нет, запуск при старте системы

Таймзоны и переход на летнее время

use CloudCastle\Crone\Expression\DstPolicy;

$cron = CronExpression::fromString('TZ=Europe/Berlin 30 2 * * *');
$cron->getNextRunDate('2026-03-28 12:00:00');                 // 2026-03-30 02:30 — час пропущен
$cron->withDstPolicy(DstPolicy::Shift)
    ->getNextRunDate('2026-03-28 12:00:00');                  // 2026-03-29 03:30 — сдвиг вперёд

Перевод расписания на естественный язык

$cron = CronExpression::fromString('0 9 * * 5L');

$cron->describe('ru'); // «в 09:00 в пятницу последней недели месяца»
$cron->describe('en'); // «at 09:00 on Friday of the last week of the month»
$cron->describe('de'); // «um 09:00 am Freitag der letzten Woche des Monats»
$cron->describe('ja'); // «09:00 に 毎月最終週の金曜日»
Все поддерживаемые языки | Код | Язык | Пример описания `0 9 * * 1-5` | |---|---|---| | `ru` | Русский | в 09:00 по понедельникам, вторникам, средам, четвергам и пятницам | | `en` | English | at 09:00 on Mondays, Tuesdays, Wednesdays, Thursdays and Fridays | | `de` | Deutsch | um 09:00 montags, dienstags, mittwochs, donnerstags und freitags | | `fr` | Français | à 09:00 les lundis, les mardis, les mercredis, les jeudis et les vendredis | | `es` | Español | a las 09:00 los lunes, los martes, los miércoles, los jueves y los viernes | | `it` | Italiano | alle 09:00 il lunedì, il martedì, il mercoledì, il giovedì e il venerdì | | `uk` | Українська | о 09:00 по понеділках, вівторках, середах, четвергах і п’ятницях | | `pl` | Polski | o 09:00 w poniedziałki, wtorki, środy, czwartki i piątki | | `sk` | Slovenčina | o 09:00 v pondelky, utorky, stredy, štvrtky a piatky | | `nl` | Nederlands | om 09:00 op maandagen, dinsdagen, woensdagen, donderdagen en vrijdagen | | `pt` | Português | às 09:00 às segundas-feiras, terças-feiras, quartas-feiras, quintas-feiras e sextas-feiras | | `da` | Dansk | kl. 09:00 på mandage, tirsdage, onsdage, torsdage og fredage | | `ro` | Română | la 09:00 în fiecare luni, marți, miercuri, joi și vineri | | `lv` | Latviešu | plkst. 09:00 pirmdienās, otrdienās, trešdienās, ceturtdienās un piektdienās | | `tr` | Türkçe | saat 09:00 pazartesi günleri, salı günleri, çarşamba günleri, perşembe günleri ve cuma günleri | | `hi` | हिन्दी | 09:00 बजे सोमवार को, मंगलवार को, बुधवार को, गुरुवार को और शुक्रवार को | | `ar` | العربية | في 09:00 في أيام الاثنين، أيام الثلاثاء، أيام الأربعاء، أيام الخميس وأيام الجمعة | | `zh` | 中文 | 在 09:00 每周一、每周二、每周三、每周四和每周五 | | `ja` | 日本語 | 09:00 に 毎週月曜日、毎週火曜日、毎週水曜日、毎週木曜日と毎週金曜日 | | `vi` | Tiếng Việt | lúc 09:00 vào thứ hai hằng tuần, thứ ba hằng tuần, thứ tư hằng tuần, thứ năm hằng tuần và thứ sáu hằng tuần |

Импорт и экспорт crontab

use CloudCastle\Crone\Scheduler\Crontab\CrontabExporter;
use CloudCastle\Crone\Scheduler\Crontab\CrontabParser;

$crontab = <<<'CRON'
    17 *	* * *	root    cd / && run-parts --report /etc/cron.hourly
    25 6	* * *	root	test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.daily; }
    47 6	* * 7	root	test -x /usr/sbin/anacron || { cd / && run-parts --report /etc/cron.weekly; }
    CRON;

$entries = (new CrontabParser())->parse($crontab); // или file_get_contents('/etc/crontab')

foreach ($entries as $entry) {
    echo $entry->command, ' → ', $entry->expression->describe('ru'), PHP_EOL;
}

echo (new CrontabExporter('php bin/schedule run {task}'))->export($schedule);

Healthcheck и метрики

use CloudCastle\Crone\Scheduler\Heartbeat\CallableHeartbeat;

$schedule->call(fn () => sync(), 'sync')
    ->everyFiveMinutes()
    ->heartbeat(new CallableHeartbeat(
        static fn (string $key) => ping("https://hc.example.com/{$key}/start"),
        static fn ($result) => ping("https://hc.example.com/{$result->key}/" . $result->status->name),
    ));

Справочник API

КлассНазначение
CloudCastle\Crone\CronExpressionразбор, проверка, вычисление дат, перевод, нормализация
CloudCastle\Crone\Builder\CronBuilderтекучее построение выражений
CloudCastle\Crone\Expression\DstPolicyполитика перехода на летнее время
CloudCastle\Crone\Expression\ExpressionNormalizerканоническая форма выражения
CloudCastle\Crone\Translator\ExpressionTranslatorперевод расписания на двадцать языков
CloudCastle\Crone\Scheduler\Scheduleреестр задач и фабрики расписаний
CloudCastle\Crone\Scheduler\Taskнастройка задачи: частота, условия, хуки, повторы
CloudCastle\Crone\Scheduler\Schedulerзапуск наступивших задач и сбор результатов
CloudCastle\Crone\Scheduler\TaskResultисход, вывод, исключение и метрики выполнения
CloudCastle\Crone\Scheduler\Lock\FileMutexфайловая защита от наложения
CloudCastle\Crone\Scheduler\Timeout\SignalTimeoutжёсткий таймаут на сигналах POSIX
CloudCastle\Crone\Scheduler\Crontab\CrontabParserразбор crontab-файлов
CloudCastle\Crone\Scheduler\Crontab\CrontabExporterэкспорт расписания в crontab

Полный справочник — в wiki и в документации API.

Сравнение с аналогами

Все таблицы ниже сгенерированы автоматически из честных сравнительных прогонов (benchmarks/compare.php и benchmarks/analyse.php) на ОДИНАКОВОЙ операции и по единым правилам анализа для всех участников, PHP 8.3.32, без Xdebug.

1. Функциональность

ВозможностьCloudCastledragonmantankpolianderpeppeocchicrunz
Разбор 5 полей
Секунды (6 полей)
Год (7 полей)
Спецсимволы L / W / #
Смещение от конца месяца (L-n)
Jenkins-хеш H
Случайное значение R
Интервалы @every
Запуск при старте @reboot
Таймзона в выражении (TZ=)
Политика перехода на летнее время
Даты в промежутке и подсчёт запусков
Ленивый итератор дат
Нормализация и сравнение выражений
Перевод расписания на язык
Построитель выражений
Планировщик задач
Защита от наложения
Повторы при ошибке
Таймаут выполнения задачи
Теги и приоритеты задач
Метрики выполнения
Импорт и экспорт crontab
Без сторонних runtime-зависимостей
Всего🏆 244255

2. Корректность сложных расписаний

СвойствоCloudCastledragonmantankpolianderpeppeocchicrunz
последний день месяца (L)
ближайший рабочий день (15W)
третья пятница (5#3)
секунды (6 полей)
год (7 полей)
интервал @every 90s
Всего🏆 63000

3. Производительность

Разбор 6 расписаний и проверка 4 моментов, 2 000 раз (минимум из 4 прогонов).

РешениеВремя (мс)Итог
наивно¹84,5базовый уровень (не библиотека)
🏆 CloudCastle220,1быстрейшее среди библиотек
poliander478,1аналог
dragonmantank2 968,4аналог
peppeocchi3 522,6аналог
crunz4 518,5аналог

4. Потребление памяти

Собственный след библиотеки (изолированный процесс, дельта к baseline без библиотеки).

РешениеПамять (KB)Итог
наивно¹1базовый уровень (не библиотека)
🏆 poliander44легчайшее среди библиотек
dragonmantank184аналог
peppeocchi265аналог
CloudCastle285аналог
crunz358аналог

5. Утечки памяти

Рост памяти на второй волне из 5 000 новых уникальных выражений после такой же первой (0 — библиотека не течёт).

РешениеРост памяти (KB)Итог
🏆 CloudCastle0не течёт (рост 0)
dragonmantank0аналог
poliander0аналог
peppeocchi0аналог
crunz0аналог
наивно¹0базовый уровень (не библиотека)

6. Безопасность

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

КритерийCloudCastledragonmantankpolianderpeppeocchicrunzнаивно¹
Не исполняет команды оболочки
Не десериализует данные (unserialize)
Без eval и extract
Не подавляет ошибки оператором @
Отклоняет некорректное выражение исключением
Бросает собственные типизированные исключения
Ограничивает поиск недостижимой даты
Поток чужих выражений не наращивает память
Политика безопасности в пакете
Всего🏆 976554

7. Статический анализ по единым правилам

Синтаксический линтер, PSR-12 и PHPMD с конфигурацией этого пакета прогоняются по исходникам каждого участника; число замечаний нормировано на тысячу строк кода.

РешениеЗамечаний на 1000 строкИтог
🏆 CloudCastle0,0чище всех по единым правилам
crunz11,8аналог
poliander22,3аналог
peppeocchi22,4аналог
dragonmantank33,6аналог

8. Качество кода

КритерийCloudCastledragonmantankpolianderpeppeocchicrunz
Строгая типизация во всех файлах (declare(strict_types=1))
Типы возвращаемых значений у всех публичных методов
Требует современный PHP 8.1+
Без сторонних runtime-зависимостей (кроме PSR/ext/cloud-castle)
Всего🏆 41202

9. Инструменты качества в пакете

КритерийCloudCastledragonmantankpolianderpeppeocchicrunz
Собственные тесты в пакете
Конфигурация статического анализа
Конфигурация стандарта кодирования
Мутационное тестирование
Всего🏆 40110

10. Перевод расписаний

Сколько языков поддерживает перевод расписания в человекочитаемый вид.

РешениеЯзыковИтог
🏆 CloudCastle20богатейший набор языков
lorisleiva17аналог
dragonmantank0аналог
poliander0аналог
peppeocchi0аналог
crunz0аналог

¹ Базовый уровень (примитивная проверка без полноты решения) показан для контекста и не претендует на победу среди библиотек-аналогов.

Замеры выполняются на одинаковой операции: разобрать расписание и проверить набор моментов — именно это делает планировщик на каждом тике. Планировщики peppeocchi и crunz внутри используют dragonmantank/cron-expression, поэтому их числа отражают путь исполнения через публичный API обёртки. Память измеряется как собственный след библиотеки: в изолированном процессе из результата вычитается baseline — тот же процесс с автозагрузчиком и данными, но без единого класса библиотеки. Утечки проверяются двумя волнами новых уникальных выражений одинаковой формы. Признаки качества кода читаются из исходников установленных пакетов, а не из заявлений авторов.

Вывод: CloudCastle Crone — самый быстрый среди сравниваемых библиотек и функционально богатейший: один пакет закрывает разбор, вычисление, перевод, построение, планирование и работу с crontab, тогда как аналоги покрывают лишь часть пути. Плата за это — собственный след памяти выше, чем у минималистичных парсеров: пакет держит в памяти и планировщик, и словари шести языков. Этот компромисс осознанный и единственный: по функциональности, скорости, отсутствию утечек и безопасности пакет не уступает никому из сравниваемых.

Плюсы, минусы и когда применять

Плюсы:

  • Функциональный суперсет экосистемы: парсер, вычислитель, переводчик, построитель, планировщик и работа с crontab в одном пакете — обычно это dragonmantank/cron-expression + lorisleiva/cron-translator + crunz.
  • Синтаксис шире, чем у аналогов: секунды и год, L/W/#, Jenkins-хеш H, случайное R, интервалы @every, @reboot, префикс TZ=.
  • Явная политика перехода на летнее время — расписание не «съедает» и не задваивает запуски молча.
  • Две runtime-зависимости, обе из экосистемы cloud-castle (clock и inflector); планировщик не тянет Symfony-компоненты.
  • Детерминизм в тестах: часы, паузы и метрики внедряются через контракты.
  • Перевод расписания на двадцать языков — больше, чем у специализированного переводчика, с правилами счёта каждого языка.
  • Безопасность проверяется, а не декларируется: девять признаков из девяти, включая ограниченный поиск даты, устойчивость памяти к потоку чужих выражений и защиту экспорта crontab от инъекции.
  • 100% покрытие строк, 100% Infection MSI, весь строгий статанализ зелёный; те же правила на исходниках аналогов дают от 12 до 34 замечаний на 1000 строк.

Минусы:

  • Пакет моложе и менее распространён, чем dragonmantank/cron-expression (сотни миллионов установок, используется ядром Laravel): меньше ответов на Stack Overflow и готовых рецептов в интернете.
  • Собственный след памяти выше, чем у минималистичных парсеров: пакет держит в памяти разбор, планировщик, переводчик и словари двадцати языков, тогда как poliander/cron умеет только проверять пять полей. Это осознанный компромисс: функциональностью, скоростью и отсутствием утечек ради экономии памяти не жертвуем — точные числа в таблицах выше.

Когда применять:

  • Планировщик без фреймворка — cron-подобный запуск задач в standalone- приложении, демоне или контейнере, где Laravel избыточен, а crunz тянет половину Symfony.
  • Пользовательские расписания в SaaS — строгая валидация, перевод на язык пользователя и защита от недостижимых выражений (anti-DoS).
  • Массовые однотипные задачи — Jenkins-хеш H разводит сотни задач по минутам без ручного подбора расписаний.
  • Миграция с системного crontab — импорт существующего файла, анализ расписаний и обратный экспорт после переноса задач в код.
  • Финтех и критичные операции — защита от наложения, повторы с паузой, таймауты и healthcheck делают запуск задач с деньгами предсказуемым.
  • Не нужен, если задача — просто «проверить, подходит ли строка * * * * *» в проекте, где dragonmantank/cron-expression уже стоит транзитивно: там выигрыш от перехода минимален.

Разработка

composer install
composer check    # линтеры + статический анализ + тесты
composer fix      # автоисправления (Rector, PHP CS Fixer, PHPCBF)
composer ci       # полный CI-пайплайн локально

Полный список команд с описаниями: composer run-script --list.

Документация

Лицензия

MIT © CloudCastle (alex-4-17@yandex.ru)

🇷🇺 Русский · 🇬🇧 English · 🇩🇪 Deutsch · 🇫🇷 Français · 🇪🇸 Español · 🇮🇹 Italiano