Выполнение PHP-сценариев по расписанию: cron-события и планировщики

Раздел: Администрирование сервера -> Администрирование PHP (CRON)

Основы cron-задач для PHP

Для регулярного выполнения PHP-сценариев по расписанию в Unix-подобных системах применяется планировщик cron. Статья рассматривает основные подходы к организации cron-событий в PHP, их цели, ограничения и способы решения типичных проблем. Подробные примеры помогут настроить выполнение скриптов с минимальными усилиями.

Как наиболее эффективно запускать PHP-скрипты по расписанию?

Основное решение: использование системного cron с вызовом PHP CLI напрямую. Это самый надёжный и быстрый метод, не зависящий от веб-сервера. Настройка выполняется через crontab или файлы в /etc/cron.d/.

Пример записи в crontab для выполнения скрипта каждые 15 минут:

*/15 * * * * /usr/bin/php /home/user/cron/script.php >> /var/log/cron.log 2>&1

Cron event php (cron событие php)

Пояснение шагов:

  1. */15 * * * * – время запуска: каждые 15 минут.
  2. /usr/bin/php – полный путь к интерпретатору PHP (узнать командой which php).
  3. /home/user/cron/script.php – полный путь к скрипту.
  4. >> /var/log/cron.log 2>&1 – перенаправление stdout и stderr в лог-файл.

Типичная ошибка: использование относительного пути или отсутствие прав на выполнение. Решение – указывать абсолютные пути и дать права chmod +x script.php при необходимости.

Ещё одна проблема – скрипт не выполняется из-за отсутствия переменных окружения (например, PATH). Рекомендуется в начале скрипта задавать нужные константы или использовать полные пути внутри кода.

Как организовать cron-задание без доступа к системе (например, на shared-хостинге)?

Используется веб-крон – внешний сервис, который по расписанию обращается к URL вашего скрипта через HTTP. Этот метод подходит для хостингов, где нет прямого доступа к crontab.

Пример записи в crontab внешнего сервера (или настройки в панели хостинга):

*/5 * * * * curl -s -o /dev/null 'https://example.com/cron.php'

Альтернатива – использовать wget или PHP-функции для инициирования запроса из другого скрипта. Важно защитить URL от несанкционированного доступа (например, токеном).

Проблема: если скрипт выполняется дольше интервала, возможны перекрытия. Решение – реализовать блокировку через файл или MySQL.

Также веб-крон может привести к уязвимости, если URL станет известен посторонним – обязательно используйте secret-ключ или IP-фильтрацию.

Как реализовать внутренний планировщик на PHP без внешнего cron?

Иногда требуется выполнять задачи в рамках того же процесса, например, для демонов или ботов. Создаётся бесконечный цикл, проверяющий время последнего запуска.

$interval = 300; // 5 минут
while (true) {
    // выполнение задачи
    doTask();
    sleep($interval);
}

Такой подход плохо масштабируется и требует управления памятью. Более продвинутый вариант – использование библиотеки ReactPHP или Amp для асинхронных событий.

Проблема: при сбое процесса планировщик останавливается. Для надёжности используют supervisord или systemd для перезапуска.

Типичная ошибка: утечка памяти – в цикле необходимо контролировать использование ресурсов.

Как применить облачный сервис для cron-задач PHP?

Сторонние сервисы (EasyCron, Cron-Job, SetCronJob) берут на себя расписание и отправляют HTTP-запросы к вашему приложению. Это удобно, когда не нужно управлять сервером.

Пример использования API EasyCron для создания задания:

POST /add?url=https://mysite.com/cron.php&interval=10

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

Ограничение: бесплатные тарифы часто имеют лимит на количество вызовов в месяц. Также полагаться на внешний сервис – значит зависеть от его доступности.

Расширенные примеры и сценарии

1. Crontab с различными интервалами и ротацией логов

Пример crontab для еженочного и ежечасного выполнения с раздельными логами:

Пример
# Ежедневно в 2:30
30 2 * * * /usr/bin/php /opt/tasks/daily_report.php >> /var/log/daily_$(date +\%Y\%m\%d).log 2>&1

# Каждый час в 15 минут
15 * * * * /usr/bin/php /opt/tasks/hourly_check.php | tee -a /var/log/hourly.log

Результат: файлы daily_20250215.log и hourly.log сохраняют вывод. Ошибки тоже попадают в лог.

/var/log/daily_20250215.log
/var/log/hourly.log

2. Предотвращение дублирования запусков (PID-файл)

Скрипт проверяет наличие PID-файла и завершается, если задача уже выполняется.

Пример
$pidFile = '/tmp/my_cron.pid';

if (file_exists($pidFile)) {
    $pid = file_get_contents($pidFile);
    // проверка, жив ли процесс (только Unix)
    if (posix_kill((int)$pid, 0)) {
        echo "Task already running (PID $pid)\n";
        exit(0);
    }
}

file_put_contents($pidFile, getmypid());
register_shutdown_function(function() use ($pidFile) {
    unlink($pidFile);
});

// основная работа
echo "Task started at " . date('Y-m-d H:i:s') . "\n";
sleep(30);
echo "Task finished\n";

Вывод при первом запуске:

Task started at 2025-02-15 10:00:00
Task finished

При втором одновременном запуске:

Task already running (PID 12345)

3. Использование библиотеки cron-expression для гибкого расписания

Библиотека cron-expression (устанавливается через Composer) позволяет вычислять следующее время выполнения.

Пример
require 'vendor/autoload.php';

use Cron\CronExpression;

$expression = CronExpression::factory('*/10 * * * *');
echo "Next run: " . $expression->getNextRunDate()->format('Y-m-d H:i:s') . "\n";
echo "Previous run: " . $expression->getPreviousRunDate()->format('Y-m-d H:i:s') . "\n";

Результат:

Next run: 2025-02-15 10:10:00
Previous run: 2025-02-15 10:00:00

4. Cron-задача с использованием Symfony Console (Laravel / Symfony)

В современных фреймворках команды регистрируются как сервисы и запускаются через php artisan schedule:run. Пример консольной команды в Symfony:

Пример
// src/Command/CleanupCommand.php
namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class CleanupCommand extends Command
{
    protected static $defaultName = 'app:cleanup';

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $output->writeln('Cleaning temporary files...');
        // логика очистки
        return Command::SUCCESS;
    }
}

Запись в crontab для вызова фреймворковского планировщика (Laravel):

Пример
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1

5. Docker-контейнер с внутренним cron и PHP

В образе Docker запускается cron-демон, который выполняет PHP-скрипты. Пример Dockerfile:

Пример
FROM php:8.2-cli

RUN apt-get update && apt-get install -y cron && \
    echo "* * * * * root /usr/local/bin/php /var/www/html/cron.php >> /var/log/cron.log 2>&1" > /etc/cron.d/my-cron && \
    chmod 0644 /etc/cron.d/my-cron && \
    touch /var/log/cron.log

CMD ["cron", "-f"]

Контейнер будет запускать cron.php каждую минуту. Логи доступны внутри контейнера.

6. Отправка электронной почты из cron-скрипта с помощью PHPMailer

Пример
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require 'vendor/autoload.php';

$mail = new PHPMailer(true);
try {
    $mail->isSMTP();
    $mail->Host = 'smtp.example.com';
    $mail->SMTPAuth = true;
    $mail->Username = 'user@example.com';
    $mail->Password = 'secret';
    $mail->setFrom('cron@example.com', 'Cron Job');
    $mail->addAddress('admin@example.com');
    $mail->Subject = 'Cron report';
    $mail->Body = 'Task completed at ' . date('Y-m-d H:i:s');
    $mail->send();
    echo 'Email sent successfully';
} catch (Exception $e) {
    echo "Mailer Error: {$mail->ErrorInfo}\n";
}

Вывод:

Email sent successfully

7. Обработка больших объёмов данных в cron-задаче с разбивкой на пакеты

Пример
$batchSize = 500;
$offset = 0;
$total = 10000;

while ($offset < $total) {
    // выборка $batchSize записей из БД
    $records = getRecords($batchSize, $offset);
    foreach ($records as $record) {
        process($record);
    }
    $offset += $batchSize;
    // предотвращение слишком долгого выполнения
    if (memory_get_usage() > 100 * 1024 * 1024) break;
}
echo "Processed $offset records\n";

Cron событие PHP - comments

En
Cron event php (php)