You are here

class Cron in Drupal 10

Same name in this branch
  1. 10 core/lib/Drupal/Core/Cron.php \Drupal\Core\Cron
  2. 10 core/lib/Drupal/Core/ProxyClass/Cron.php \Drupal\Core\ProxyClass\Cron
Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Cron.php \Drupal\Core\Cron
  2. 9 core/lib/Drupal/Core/Cron.php \Drupal\Core\Cron

The Drupal core Cron service.

Hierarchy

Expanded class hierarchy of Cron

1 file declares its use of Cron
CronTest.php in core/tests/Drupal/Tests/Core/CronTest.php
11 string references to 'Cron'
core.services.yml in core/core.services.yml
core/core.services.yml
drupal7.php in core/modules/tracker/tests/fixtures/drupal7.php
A database agnostic dump for testing purposes.
drupal7.php in core/modules/rdf/tests/fixtures/drupal7.php
A database agnostic dump for testing purposes.
drupal7.php in core/modules/migrate_drupal/tests/fixtures/drupal7.php
A database agnostic dump for testing purposes.
MenuLinkTest::providerSource in core/modules/menu_link_content/tests/src/Kernel/Plugin/migrate/source/MenuLinkTest.php
The data provider.

... See full list

1 service uses Cron
cron in core/core.services.yml
Drupal\Core\Cron

File

core/lib/Drupal/Core/Cron.php, line 25

Namespace

Drupal\Core
View source
class Cron implements CronInterface {

  /**
   * The module handler service.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The lock service.
   *
   * @var \Drupal\Core\Lock\LockBackendInterface
   */
  protected $lock;

  /**
   * The queue service.
   *
   * @var \Drupal\Core\Queue\QueueFactory
   */
  protected $queueFactory;

  /**
   * The state service.
   *
   * @var \Drupal\Core\State\StateInterface
   */
  protected $state;

  /**
   * The account switcher service.
   *
   * @var \Drupal\Core\Session\AccountSwitcherInterface
   */
  protected $accountSwitcher;

  /**
   * A logger instance.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;

  /**
   * The queue plugin manager.
   *
   * @var \Drupal\Core\Queue\QueueWorkerManagerInterface
   */
  protected $queueManager;

  /**
   * The time service.
   *
   * @var \Drupal\Component\Datetime\TimeInterface
   */
  protected $time;

  /**
   * Constructs a cron object.
   *
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler
   * @param \Drupal\Core\Lock\LockBackendInterface $lock
   *   The lock service.
   * @param \Drupal\Core\Queue\QueueFactory $queue_factory
   *   The queue service.
   * @param \Drupal\Core\State\StateInterface $state
   *   The state service.
   * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
   *   The account switching service.
   * @param \Psr\Log\LoggerInterface $logger
   *   A logger instance.
   * @param \Drupal\Core\Queue\QueueWorkerManagerInterface $queue_manager
   *   The queue plugin manager.
   * @param \Drupal\Component\Datetime\TimeInterface $time
   *   The time service.
   */
  public function __construct(ModuleHandlerInterface $module_handler, LockBackendInterface $lock, QueueFactory $queue_factory, StateInterface $state, AccountSwitcherInterface $account_switcher, LoggerInterface $logger, QueueWorkerManagerInterface $queue_manager, TimeInterface $time = NULL) {
    $this->moduleHandler = $module_handler;
    $this->lock = $lock;
    $this->queueFactory = $queue_factory;
    $this->state = $state;
    $this->accountSwitcher = $account_switcher;
    $this->logger = $logger;
    $this->queueManager = $queue_manager;
    $this->time = $time ?: \Drupal::service('datetime.time');
  }

  /**
   * {@inheritdoc}
   */
  public function run() {

    // Allow execution to continue even if the request gets cancelled.
    @ignore_user_abort(TRUE);

    // Force the current user to anonymous to ensure consistent permissions on
    // cron runs.
    $this->accountSwitcher
      ->switchTo(new AnonymousUserSession());

    // Try to allocate enough time to run all the hook_cron implementations.
    Environment::setTimeLimit(240);
    $return = FALSE;

    // Try to acquire cron lock.
    if (!$this->lock
      ->acquire('cron', 900.0)) {

      // Cron is still running normally.
      $this->logger
        ->warning('Attempting to re-run cron while it is already running.');
    }
    else {
      $this
        ->invokeCronHandlers();
      $this
        ->setCronLastTime();

      // Release cron lock.
      $this->lock
        ->release('cron');

      // Return TRUE so other functions can check if it did run successfully
      $return = TRUE;
    }

    // Process cron queues.
    $this
      ->processQueues();

    // Restore the user.
    $this->accountSwitcher
      ->switchBack();
    return $return;
  }

  /**
   * Records and logs the request time for this cron invocation.
   */
  protected function setCronLastTime() {

    // Record cron time.
    $request_time = $this->time
      ->getRequestTime();
    $this->state
      ->set('system.cron_last', $request_time);
    $this->logger
      ->info('Cron run completed.');
  }

  /**
   * Processes cron queues.
   */
  protected function processQueues() {

    // Grab the defined cron queues.
    foreach ($this->queueManager
      ->getDefinitions() as $queue_name => $info) {
      if (isset($info['cron'])) {

        // Make sure every queue exists. There is no harm in trying to recreate
        // an existing queue.
        $this->queueFactory
          ->get($queue_name)
          ->createQueue();
        $queue_worker = $this->queueManager
          ->createInstance($queue_name);
        $end = $this->time
          ->getCurrentTime() + $info['cron']['time'];
        $queue = $this->queueFactory
          ->get($queue_name);
        $lease_time = $info['cron']['time'];
        while ($this->time
          ->getCurrentTime() < $end && ($item = $queue
          ->claimItem($lease_time))) {
          try {
            $queue_worker
              ->processItem($item->data);
            $queue
              ->deleteItem($item);
          } catch (DelayedRequeueException $e) {

            // The worker requested the task not be immediately re-queued.
            // - If the queue doesn't support ::delayItem(), we should leave the
            // item's current expiry time alone.
            // - If the queue does support ::delayItem(), we should allow the
            // queue to update the item's expiry using the requested delay.
            if ($queue instanceof DelayableQueueInterface) {

              // This queue can handle a custom delay; use the duration provided
              // by the exception.
              $queue
                ->delayItem($item, $e
                ->getDelay());
            }
          } catch (RequeueException $e) {

            // The worker requested the task be immediately requeued.
            $queue
              ->releaseItem($item);
          } catch (SuspendQueueException $e) {

            // If the worker indicates there is a problem with the whole queue,
            // release the item and skip to the next queue.
            $queue
              ->releaseItem($item);
            watchdog_exception('cron', $e);

            // Skip to the next queue.
            continue 2;
          } catch (\Exception $e) {

            // In case of any other kind of exception, log it and leave the item
            // in the queue to be processed again later.
            watchdog_exception('cron', $e);
          }
        }
      }
    }
  }

  /**
   * Invokes any cron handlers implementing hook_cron.
   */
  protected function invokeCronHandlers() {
    $module_previous = '';

    // If detailed logging isn't enabled, don't log individual execution times.
    $time_logging_enabled = \Drupal::config('system.cron')
      ->get('logging');
    $logger = $time_logging_enabled ? $this->logger : new NullLogger();

    // Iterate through the modules calling their cron handlers (if any):
    $this->moduleHandler
      ->invokeAllWith('cron', function (callable $hook, string $module) use (&$module_previous, $logger) {
      if (!$module_previous) {
        $logger
          ->info('Starting execution of @module_cron().', [
          '@module' => $module,
        ]);
      }
      else {
        $logger
          ->info('Starting execution of @module_cron(), execution of @module_previous_cron() took @time.', [
          '@module' => $module,
          '@module_previous' => $module_previous,
          '@time' => Timer::read('cron_' . $module_previous) . 'ms',
        ]);
      }
      Timer::start('cron_' . $module);

      // Do not let an exception thrown by one module disturb another.
      try {
        $hook();
      } catch (\Exception $e) {
        watchdog_exception('cron', $e);
      }
      Timer::stop('cron_' . $module);
      $module_previous = $module;
    });
    if ($module_previous) {
      $logger
        ->info('Execution of @module_previous_cron() took @time.', [
        '@module_previous' => $module_previous,
        '@time' => Timer::read('cron_' . $module_previous) . 'ms',
      ]);
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
Cron::$accountSwitcher protected property The account switcher service.
Cron::$lock protected property The lock service.
Cron::$logger protected property A logger instance.
Cron::$moduleHandler protected property The module handler service.
Cron::$queueFactory protected property The queue service.
Cron::$queueManager protected property The queue plugin manager.
Cron::$state protected property The state service.
Cron::$time protected property The time service.
Cron::invokeCronHandlers protected function Invokes any cron handlers implementing hook_cron.
Cron::processQueues protected function Processes cron queues.
Cron::run public function Executes a cron run. Overrides CronInterface::run
Cron::setCronLastTime protected function Records and logs the request time for this cron invocation.
Cron::__construct public function Constructs a cron object.