Version 1
[yaffs-website] / web / core / lib / Drupal / Core / Cron.php
1 <?php
2
3 namespace Drupal\Core;
4
5 use Drupal\Component\Utility\Timer;
6 use Drupal\Core\Extension\ModuleHandlerInterface;
7 use Drupal\Core\Queue\QueueWorkerManagerInterface;
8 use Drupal\Core\Queue\RequeueException;
9 use Drupal\Core\State\StateInterface;
10 use Drupal\Core\Lock\LockBackendInterface;
11 use Drupal\Core\Queue\QueueFactory;
12 use Drupal\Core\Session\AnonymousUserSession;
13 use Drupal\Core\Session\AccountSwitcherInterface;
14 use Drupal\Core\Queue\SuspendQueueException;
15 use Psr\Log\LoggerInterface;
16 use Psr\Log\NullLogger;
17
18 /**
19  * The Drupal core Cron service.
20  */
21 class Cron implements CronInterface {
22
23   /**
24    * The module handler service.
25    *
26    * @var \Drupal\Core\Extension\ModuleHandlerInterface
27    */
28   protected $moduleHandler;
29
30   /**
31    * The lock service.
32    *
33    * @var \Drupal\Core\Lock\LockBackendInterface
34    */
35   protected $lock;
36
37   /**
38    * The queue service.
39    *
40    * @var \Drupal\Core\Queue\QueueFactory
41    */
42   protected $queueFactory;
43
44   /**
45    * The state service.
46    *
47    * @var \Drupal\Core\State\StateInterface
48    */
49   protected $state;
50
51   /**
52    * The account switcher service.
53    *
54    * @var \Drupal\Core\Session\AccountSwitcherInterface
55    */
56   protected $accountSwitcher;
57
58   /**
59    * A logger instance.
60    *
61    * @var \Psr\Log\LoggerInterface
62    */
63   protected $logger;
64
65   /**
66    * The queue plugin manager.
67    *
68    * @var \Drupal\Core\Queue\QueueWorkerManagerInterface
69    */
70   protected $queueManager;
71
72   /**
73    * Constructs a cron object.
74    *
75    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
76    *   The module handler
77    * @param \Drupal\Core\Lock\LockBackendInterface $lock
78    *   The lock service.
79    * @param \Drupal\Core\Queue\QueueFactory $queue_factory
80    *   The queue service.
81    * @param \Drupal\Core\State\StateInterface $state
82    *   The state service.
83    * @param \Drupal\Core\Session\AccountSwitcherInterface $account_switcher
84    *    The account switching service.
85    * @param \Psr\Log\LoggerInterface $logger
86    *   A logger instance.
87    * @param \Drupal\Core\Queue\QueueWorkerManagerInterface $queue_manager
88    *   The queue plugin manager.
89    */
90   public function __construct(ModuleHandlerInterface $module_handler, LockBackendInterface $lock, QueueFactory $queue_factory, StateInterface $state, AccountSwitcherInterface $account_switcher, LoggerInterface $logger, QueueWorkerManagerInterface $queue_manager) {
91     $this->moduleHandler = $module_handler;
92     $this->lock = $lock;
93     $this->queueFactory = $queue_factory;
94     $this->state = $state;
95     $this->accountSwitcher = $account_switcher;
96     $this->logger = $logger;
97     $this->queueManager = $queue_manager;
98   }
99
100   /**
101    * {@inheritdoc}
102    */
103   public function run() {
104     // Allow execution to continue even if the request gets cancelled.
105     @ignore_user_abort(TRUE);
106
107     // Force the current user to anonymous to ensure consistent permissions on
108     // cron runs.
109     $this->accountSwitcher->switchTo(new AnonymousUserSession());
110
111     // Try to allocate enough time to run all the hook_cron implementations.
112     drupal_set_time_limit(240);
113
114     $return = FALSE;
115
116     // Try to acquire cron lock.
117     if (!$this->lock->acquire('cron', 900.0)) {
118       // Cron is still running normally.
119       $this->logger->warning('Attempting to re-run cron while it is already running.');
120     }
121     else {
122       $this->invokeCronHandlers();
123       $this->setCronLastTime();
124
125       // Release cron lock.
126       $this->lock->release('cron');
127
128       // Return TRUE so other functions can check if it did run successfully
129       $return = TRUE;
130     }
131
132     // Process cron queues.
133     $this->processQueues();
134
135     // Restore the user.
136     $this->accountSwitcher->switchBack();
137
138     return $return;
139   }
140
141   /**
142    * Records and logs the request time for this cron invocation.
143    */
144   protected function setCronLastTime() {
145     // Record cron time.
146     $this->state->set('system.cron_last', REQUEST_TIME);
147     $this->logger->notice('Cron run completed.');
148   }
149
150   /**
151    * Processes cron queues.
152    */
153   protected function processQueues() {
154     // Grab the defined cron queues.
155     foreach ($this->queueManager->getDefinitions() as $queue_name => $info) {
156       if (isset($info['cron'])) {
157         // Make sure every queue exists. There is no harm in trying to recreate
158         // an existing queue.
159         $this->queueFactory->get($queue_name)->createQueue();
160
161         $queue_worker = $this->queueManager->createInstance($queue_name);
162         $end = time() + (isset($info['cron']['time']) ? $info['cron']['time'] : 15);
163         $queue = $this->queueFactory->get($queue_name);
164         $lease_time = isset($info['cron']['time']) ?: NULL;
165         while (time() < $end && ($item = $queue->claimItem($lease_time))) {
166           try {
167             $queue_worker->processItem($item->data);
168             $queue->deleteItem($item);
169           }
170           catch (RequeueException $e) {
171             // The worker requested the task be immediately requeued.
172             $queue->releaseItem($item);
173           }
174           catch (SuspendQueueException $e) {
175             // If the worker indicates there is a problem with the whole queue,
176             // release the item and skip to the next queue.
177             $queue->releaseItem($item);
178
179             watchdog_exception('cron', $e);
180
181             // Skip to the next queue.
182             continue 2;
183           }
184           catch (\Exception $e) {
185             // In case of any other kind of exception, log it and leave the item
186             // in the queue to be processed again later.
187             watchdog_exception('cron', $e);
188           }
189         }
190       }
191     }
192   }
193
194   /**
195    * Invokes any cron handlers implementing hook_cron.
196    */
197   protected function invokeCronHandlers() {
198     $module_previous = '';
199
200     // If detailed logging isn't enabled, don't log individual execution times.
201     $time_logging_enabled = \Drupal::config('system.cron')->get('logging');
202     $logger = $time_logging_enabled ? $this->logger : new NullLogger();
203
204     // Iterate through the modules calling their cron handlers (if any):
205     foreach ($this->moduleHandler->getImplementations('cron') as $module) {
206
207       if (!$module_previous) {
208         $logger->notice('Starting execution of @module_cron().', [
209           '@module' => $module,
210         ]);
211       }
212       else {
213         $logger->notice('Starting execution of @module_cron(), execution of @module_previous_cron() took @time.', [
214           '@module' => $module,
215           '@module_previous' => $module_previous,
216           '@time' => Timer::read('cron_' . $module_previous) . 'ms',
217         ]);
218       }
219       Timer::start('cron_' . $module);
220
221       // Do not let an exception thrown by one module disturb another.
222       try {
223         $this->moduleHandler->invoke($module, 'cron');
224       }
225       catch (\Exception $e) {
226         watchdog_exception('cron', $e);
227       }
228
229       Timer::stop('cron_' . $module);
230       $module_previous = $module;
231     }
232     if ($module_previous) {
233       $logger->notice('Execution of @module_previous_cron() took @time.', [
234         '@module_previous' => $module_previous,
235         '@time' => Timer::read('cron_' . $module_previous) . 'ms',
236       ]);
237     }
238   }
239
240 }