Projet

Général

Profil

Paste
Télécharger (6,08 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / job_scheduler / JobScheduler.inc @ 87dbc3bf

1
<?php
2

    
3
/**
4
 * @file
5
 * JobScheduler class.
6
 */
7

    
8
/**
9
 * Use to make Job Scheduler exceptions identifiable by type.
10
 */
11
class JobSchedulerException extends Exception {}
12

    
13
/**
14
 * Manage scheduled jobs.
15
 */
16
class JobScheduler {
17
  /**
18
   * The name of this scheduler.
19
   */
20
  protected $name;
21

    
22
  /**
23
   * Produces a single instance of JobScheduler for a schedule name.
24
   */
25
  public static function get($name) {
26
    static $schedulers;
27
    // Instantiante a new scheduler for $name if we haven't done so yet.
28
    if (!isset($schedulers[$name])) {
29
      $class = variable_get('job_scheduler_class_' . $name, 'JobScheduler');
30
      $schedulers[$name] = new $class($name);
31
    }
32
    return $schedulers[$name];
33
  }
34

    
35
  /**
36
   * Creates a JobScheduler object.
37
   */
38
  protected function __construct($name) {
39
    $this->name = $name;
40
  }
41

    
42
  /**
43
   * Returns scheduler info.
44
   *
45
   * @see hook_cron_job_scheduler_info()
46
   *
47
   * @throws JobSchedulerException.
48
   */
49
  public function info() {
50
    if ($info = job_scheduler_info($this->name)) {
51
      return $info;
52
    }
53
    throw new JobSchedulerException('Could not find Job Scheduler cron information for ' . check_plain($this->name));
54
  }
55

    
56
  /**
57
   * Add a job to the schedule, replace any existing job.
58
   *
59
   * A job is uniquely identified by $job = array(type, id).
60
   *
61
   * @param $job
62
   *   An array that must contain the following keys:
63
   *   'type'     - A string identifier of the type of job.
64
   *   'id'       - A numeric identifier of the job.
65
   *   'period'   - The time when the task should be executed.
66
   *   'periodic' - True if the task should be repeated periodically.
67
   *
68
   * @code
69
   * function worker_callback($job) {
70
   *   // Work off job.
71
   *   // Set next time to be called. If this portion of the code is not
72
   *   // reached for some reason, the scheduler will keep periodically invoking
73
   *   // the callback() with the period value initially specified.
74
   *   $scheduler->set($job);
75
   * }
76
   * @endcode
77
   */
78
  public function set($job) {
79
    $job['name'] = $this->name;
80
    $job['last'] = REQUEST_TIME;
81
    if (!empty($job['crontab'])) {
82
      $crontab = new JobSchedulerCronTab($job['crontab']);
83
      $job['next'] = $crontab->nextTime(REQUEST_TIME);
84
    }
85
    else {
86
      $job['next'] = REQUEST_TIME + $job['period'];
87
    }
88
    $job['scheduled'] = 0;
89
    $this->remove($job);
90
    drupal_write_record('job_schedule', $job);
91
  }
92

    
93
  /**
94
   * Reserve a job.
95
   */
96
  protected function reserve($job) {
97
    $job['name'] = $this->name;
98
    $job['scheduled'] =
99
    $job['last'] = REQUEST_TIME;
100
    $job['next'] = $job['period'] + REQUEST_TIME;
101
    drupal_write_record('job_schedule', $job, array('name', 'type', 'id'));
102
  }
103

    
104
  /**
105
   * Remove a job from the schedule, replace any existing job.
106
   *
107
   * A job is uniquely identified by $job = array(type, id).
108
   */
109
  public function remove($job) {
110
    db_delete('job_schedule')
111
      ->condition('name', $this->name)
112
      ->condition('type', $job['type'])
113
      ->condition('id', isset($job['id']) ? $job['id'] : 0)
114
      ->execute();
115
  }
116

    
117
  /**
118
   * Remove all jobs for a given type.
119
   */
120
  public function removeAll($type) {
121
    db_delete('job_schedule')
122
      ->condition('name', $this->name)
123
      ->condition('type', $type)
124
      ->execute();
125
  }
126

    
127
  /**
128
   * Dispatches a job.
129
   *
130
   * Executes a worker callback or if schedule declares a queue name, queues a
131
   * job for execution.
132
   *
133
   * @param $job
134
   *   A $job array as passed into set() or read from job_schedule table.
135
   *
136
   * @throws Exception
137
   *   Exceptions thrown by code called by this method are passed on.
138
   */
139
  public function dispatch($job) {
140
    $info = $this->info();
141
    if (!$job['periodic']) {
142
      $this->remove($job);
143
    }
144
    if (!empty($info['queue name'])) {
145
      if (DrupalQueue::get($info['queue name'])->createItem($job)) {
146
        $this->reserve($job);
147
      }
148
    }
149
    else {
150
      $this->execute($job);
151
    }
152
  }
153

    
154
  /**
155
   * Executes a job that
156
   *
157
   * @param $job
158
   *   A $job array as passed into set() or read from job_schedule table.
159
   *
160
   * @throws Exception
161
   *   Exceptions thrown by code called by this method are passed on.
162
   */
163
  public function execute($job) {
164
    $info = $this->info();
165
    // If the job is periodic, re-schedule it before calling the worker, just in case
166
    if ($job['periodic']) {
167
      $job['last'] = REQUEST_TIME;
168
      $this->reschedule($job);
169
    }
170
    if (function_exists($info['worker callback'])) {
171
      call_user_func($info['worker callback'], $job);
172
    }
173
    else {
174
      // @todo If worker doesn't exist anymore we should do something about it, remove and throw exception?
175
      $this->remove($job);
176
      throw new JobSchedulerException('Could not find worker callback function: ' . $info['worker callback']);
177
    }
178
  }
179

    
180
  /**
181
   * Re-schedule a job if intended to run again
182
   *
183
   * (If cannot determine the next time, drop the job)
184
   */
185
  public function reschedule($job) {
186
    $job['scheduled'] = 0;
187
    if (!empty($job['crontab'])) {
188
      $crontab = new JobSchedulerCronTab($job['crontab']);
189
      $job['next'] = $crontab->nextTime($job['last']);
190
    }
191
    else {
192
      $job['next'] = $job['last'] + $job['period'];
193
    }
194

    
195
    if ($job['next']) {
196
      drupal_write_record('job_schedule', $job, array('item_id'));
197
    }
198
    else {
199
      // If no next time, it may mean it wont run again the next year (crontab)
200
      $this->remove($job);
201
    }
202
  }
203

    
204
  /**
205
   * Check whether a job exists in the queue and update its parameters if so
206
   */
207
  public function check($job) {
208
    $job += array('id' => 0, 'period' => 0, 'crontab' => '');
209
    $existing = db_select('job_schedule')
210
      ->fields('job_schedule')
211
      ->condition('name', $this->name)
212
      ->condition('type', $job['type'])
213
      ->condition('id', $job['id'])
214
      ->execute()
215
      ->fetchAssoc();
216
    // If existing, and changed period or crontab, we need to reschedule the job
217
    if ($existing) {
218
      if ($job['period'] != $existing['period'] || $job['crontab'] != $existing['crontab']) {
219
        $existing['period'] = $job['period'];
220
        $existing['crontab'] = $job['crontab'];
221
        $this->reschedule($existing);
222
      }
223
      return $existing;
224
    }
225
  }
226
}