Cron.php

Same filename in this branch
  1. 9 core/lib/Drupal/Core/ProxyClass/Cron.php
Same filename in other branches
  1. 7.x cron.php
  2. 8.9.x core/lib/Drupal/Core/ProxyClass/Cron.php
  3. 8.9.x core/lib/Drupal/Core/Cron.php
  4. 10 core/lib/Drupal/Core/ProxyClass/Cron.php
  5. 10 core/lib/Drupal/Core/Cron.php
  6. 11.x core/lib/Drupal/Core/ProxyClass/Cron.php
  7. 11.x core/lib/Drupal/Core/Cron.php

Namespace

Drupal\Core

File

core/lib/Drupal/Core/Cron.php

View source
<?php

namespace Drupal\Core;

use Drupal\Component\Datetime\TimeInterface;
use Drupal\Component\Utility\Environment;
use Drupal\Component\Utility\Timer;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\DelayableQueueInterface;
use Drupal\Core\Queue\QueueWorkerManagerInterface;
use Drupal\Core\Queue\DelayedRequeueException;
use Drupal\Core\Queue\RequeueException;
use Drupal\Core\Queue\SuspendQueueException;
use Drupal\Core\Session\AccountSwitcherInterface;
use Drupal\Core\Session\AnonymousUserSession;
use Drupal\Core\State\StateInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;

/**
 * The Drupal core Cron service.
 */
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();
            // Process cron queues.
            $this->processQueues();
            $this->setCronLastTime();
            // Release cron lock.
            $this->lock
                ->release('cron');
            // Return TRUE so other functions can check if it did run successfully
            $return = TRUE;
        }
        // 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',
            ]);
        }
    }

}

Classes

Title Deprecated Summary
Cron The Drupal core Cron service.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.