vendor/contao/core-bundle/src/Cron/Cron.php line 63

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of Contao.
  5.  *
  6.  * (c) Leo Feyer
  7.  *
  8.  * @license LGPL-3.0-or-later
  9.  */
  10. namespace Contao\CoreBundle\Cron;
  11. use Contao\CoreBundle\Entity\CronJob as CronJobEntity;
  12. use Contao\CoreBundle\Exception\CronExecutionSkippedException;
  13. use Contao\CoreBundle\Repository\CronJobRepository;
  14. use Cron\CronExpression;
  15. use Doctrine\ORM\EntityManagerInterface;
  16. use Psr\Log\LoggerInterface;
  17. class Cron
  18. {
  19.     public const SCOPE_WEB 'web';
  20.     public const SCOPE_CLI 'cli';
  21.     /**
  22.      * @var \Closure():CronJobRepository
  23.      */
  24.     private \Closure $repository;
  25.     /**
  26.      * @var \Closure():EntityManagerInterface
  27.      */
  28.     private \Closure $entityManager;
  29.     private ?LoggerInterface $logger;
  30.     /**
  31.      * @var array<CronJob>
  32.      */
  33.     private array $cronJobs = [];
  34.     /**
  35.      * @param \Closure():CronJobRepository      $repository
  36.      * @param \Closure():EntityManagerInterface $entityManager
  37.      */
  38.     public function __construct(\Closure $repository, \Closure $entityManagerLoggerInterface $logger null)
  39.     {
  40.         $this->repository $repository;
  41.         $this->entityManager $entityManager;
  42.         $this->logger $logger;
  43.     }
  44.     public function addCronJob(CronJob $cronjob): void
  45.     {
  46.         $this->cronJobs[] = $cronjob;
  47.     }
  48.     /**
  49.      * Run all the registered Contao cron jobs.
  50.      */
  51.     public function run(string $scope): void
  52.     {
  53.         // Validate scope
  54.         if (self::SCOPE_WEB !== $scope && self::SCOPE_CLI !== $scope) {
  55.             throw new \InvalidArgumentException('Invalid scope "'.$scope.'"');
  56.         }
  57.         /** @var CronJobRepository $repository */
  58.         $repository = ($this->repository)();
  59.         /** @var EntityManagerInterface $entityManager */
  60.         $entityManager = ($this->entityManager)();
  61.         /** @var array<CronJob> $cronJobsToBeRun */
  62.         $cronJobsToBeRun = [];
  63.         $now = new \DateTimeImmutable();
  64.         try {
  65.             // Lock cron table
  66.             $repository->lockTable();
  67.             // Go through each cron job
  68.             foreach ($this->cronJobs as $cron) {
  69.                 $interval $cron->getInterval();
  70.                 $name $cron->getName();
  71.                 // Determine the last run date
  72.                 $lastRunDate null;
  73.                 $lastRunEntity $repository->findOneByName($name);
  74.                 if (null !== $lastRunEntity) {
  75.                     $lastRunDate $lastRunEntity->getLastRun();
  76.                 } else {
  77.                     $lastRunEntity = new CronJobEntity($name);
  78.                     $entityManager->persist($lastRunEntity);
  79.                 }
  80.                 // Check if the cron should be run
  81.                 $expression CronExpression::factory($interval);
  82.                 if (null !== $lastRunDate && $now $expression->getNextRunDate($lastRunDate)) {
  83.                     continue;
  84.                 }
  85.                 // Store the previous run in case the cronjob skips itself
  86.                 $cron->setPreviousRun($lastRunEntity->getLastRun());
  87.                 // Update the cron entry
  88.                 $lastRunEntity->setLastRun($now);
  89.                 // Add job to the cron jobs to be run
  90.                 $cronJobsToBeRun[] = $cron;
  91.             }
  92.             $entityManager->flush();
  93.         } finally {
  94.             $repository->unlockTable();
  95.         }
  96.         $exception null;
  97.         // Execute all cron jobs to be run
  98.         foreach ($cronJobsToBeRun as $cron) {
  99.             try {
  100.                 if (null !== $this->logger) {
  101.                     $this->logger->debug(sprintf('Executing cron job "%s"'$cron->getName()));
  102.                 }
  103.                 $cron($scope);
  104.             } catch (CronExecutionSkippedException $e) {
  105.                 // Restore previous run date in case cronjob skips itself
  106.                 $lastRunEntity $repository->findOneByName($cron->getName());
  107.                 $lastRunEntity->setLastRun($cron->getPreviousRun());
  108.                 $entityManager->flush();
  109.             } catch (\Throwable $e) {
  110.                 // Catch any exceptions so that other cronjobs are still executed
  111.                 if (null !== $this->logger) {
  112.                     $this->logger->error((string) $e);
  113.                 }
  114.                 if (null === $exception) {
  115.                     $exception $e;
  116.                 }
  117.             }
  118.         }
  119.         // Throw the first exception
  120.         if (null !== $exception) {
  121.             throw $exception;
  122.         }
  123.     }
  124. }