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