Задачи по расписанию
Раз в сутки очистить старые записи, каждый понедельник отправить отчёт, каждые пять минут проверить оплаты. Раньше для каждой такой задачи писали строку в системном cron. Компонент Scheduler хранит расписание в коде приложения.
Установка
composer require symfony/schedulerScheduler работает поверх Messenger: расписание — источник сообщений, которые выполняет обычный обработчик.
Задача по cron-выражению
namespace App\Scheduler;
use Symfony\Component\Scheduler\Attribute\AsCronTask;
#[AsCronTask('0 3 * * *')]
class CleanupOldSessions
{
public function __construct(private SessionRepository $sessions) {}
public function __invoke(): void
{
$this->sessions->deleteOlderThan(new \DateTimeImmutable('-30 days'));
}
}Класс выполняется каждый день в 3:00. Для cron-выражений нужен пакет dragonmantank/cron-expression.
Формат выражения
Пять полей: минута, час, день месяца, месяц, день недели.
| Выражение | Когда |
|---|---|
* * * * * | Каждую минуту |
*/5 * * * * | Каждые пять минут |
0 * * * * | В начале каждого часа |
0 3 * * * | Каждый день в 3:00 |
0 9 * * 1 | По понедельникам в 9:00 |
0 0 1 * * | Первого числа каждого месяца |
Задача через равные промежутки
use Symfony\Component\Scheduler\Attribute\AsPeriodicTask;
#[AsPeriodicTask(frequency: '10 minutes')]
class CheckPendingPayments
{
public function __invoke(): void
{
// проверка оплат
}
}Команда по расписанию
Тем же атрибутом можно пометить консольную команду:
#[AsCommand(name: 'app:send-report')]
#[AsCronTask('0 9 * * 1')]
class SendReportCommand extends Command
{
// …
}Запуск
Расписание выполняет обработчик Messenger:
php bin/console messenger:consume scheduler_defaultНа сервере его держат запущенным под управлением Supervisor или systemd — как и обработчик очереди.
Просмотр расписания
php bin/console debug:schedulerКоманда показывает все задачи и время следующего запуска каждой.
Свой график
Когда расписание зависит от настроек или данных, его собирают в классе:
use Symfony\Component\Scheduler\Attribute\AsSchedule;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
#[AsSchedule('reports')]
class ReportSchedule implements ScheduleProviderInterface
{
public function getSchedule(): Schedule
{
return (new Schedule())
->add(RecurringMessage::cron('0 9 * * 1', new SendWeeklyReport()))
->add(RecurringMessage::every('1 hour', new RefreshRates()));
}
}Такой график запускают отдельно: messenger:consume scheduler_reports.
Частая ошибка
Если запустить два обработчика одного расписания — например, на двух серверах, — каждая задача выполнится дважды: отчёт уйдёт два раза, оплата проверится параллельно. Держите один обработчик на график или подключите блокировку методом lock() у Schedule — тогда задачу возьмёт только один процесс.