HCE Project DC service web UI  0.2
Hierarchical Cluster Engine DC service web UI
 All Classes Namespaces Files Functions Variables Pages
CronExpression.php
Go to the documentation of this file.
1 <?php
2 
18 {
19  const MINUTE = 0;
20  const HOUR = 1;
21  const DAY = 2;
22  const MONTH = 3;
23  const WEEKDAY = 4;
24  const YEAR = 5;
25 
29  private $cronParts;
30 
34  private $fieldFactory;
35 
39  private static $order = array(self::YEAR, self::MONTH, self::DAY, self::WEEKDAY, self::HOUR, self::MINUTE);
40 
58  public static function factory($expression, FieldFactory $fieldFactory = null)
59  {
60  $mappings = array(
61  '@yearly' => '0 0 1 1 *',
62  '@annually' => '0 0 1 1 *',
63  '@monthly' => '0 0 1 * *',
64  '@weekly' => '0 0 * * 0',
65  '@daily' => '0 0 * * *',
66  '@hourly' => '0 * * * *',
67  );
68 
69  if (isset($mappings[$expression])) {
70  $expression = $mappings[$expression];
71  }
72 
73  return new self($expression, $fieldFactory ?: new FieldFactory());
74  }
75 
82  public function __construct($expression, FieldFactory $fieldFactory)
83  {
84  $this->fieldFactory = $fieldFactory;
85  $this->setExpression($expression);
86  }
87 
97  public function setExpression($value)
98  {
99  $this->cronParts = explode(' ', $value);
100  if (count($this->cronParts) < 5) {
101  throw new InvalidArgumentException(
102  $value.' is not a valid CRON expression'
103  );
104  }
105 
106  foreach ($this->cronParts as $position => $part) {
107  $this->setPart($position, $part);
108  }
109 
110  return $this;
111  }
112 
123  public function setPart($position, $value)
124  {
125  if (!$this->fieldFactory->getField($position)->validate($value)) {
126  throw new InvalidArgumentException(
127  'Invalid CRON field value '.$value.' as position '.$position
128  );
129  }
130 
131  $this->cronParts[$position] = $value;
132 
133  return $this;
134  }
135 
153  public function getNextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
154  {
155  return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate);
156  }
157 
172  public function getPreviousRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
173  {
174  return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate);
175  }
176 
188  public function getMultipleRunDates($total, $currentTime = 'now', $invert = false, $allowCurrentDate = false)
189  {
190  $matches = array();
191  for ($i = 0; $i < max(0, $total); $i++) {
192  $matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate);
193  }
194 
195  return $matches;
196  }
197 
207  public function getExpression($part = null)
208  {
209  if (null === $part) {
210  return implode(' ', $this->cronParts);
211  } elseif (array_key_exists($part, $this->cronParts)) {
212  return $this->cronParts[$part];
213  }
214 
215  return;
216  }
217 
223  public function __toString()
224  {
225  return $this->getExpression();
226  }
227 
237  public function isDue($currentTime = null)
238  {
239  if (null === $currentTime || 'now' === $currentTime) {
240  $currentDate = date('Y-m-d H:i');
241  $currentTime = strtotime($currentDate);
242  } elseif ($currentTime instanceof DateTime) {
243  $currentDate = $currentTime->format('Y-m-d H:i');
244  $currentTime = strtotime($currentDate);
245  } else {
246  $currentTime = new DateTime($currentTime);
247  $currentTime->setTime($currentTime->format('H'), $currentTime->format('i'), 0);
248  $currentDate = $currentTime->format('Y-m-d H:i');
249  $currentTime = $currentTime->getTimeStamp();
250  }
251 
252  return $this->getNextRunDate($currentDate, 0, true)->getTimestamp() == $currentTime;
253  }
254 
268  protected function getRunDate($currentTime = null, $nth = 0, $invert = false, $allowCurrentDate = false)
269  {
270  $currentDate = $currentTime instanceof DateTime
271  ? $currentTime
272  : new DateTime($currentTime ?: 'now');
273 
274  // set the timezone
275  $currentDate->setTimezone(new DateTimeZone(date_default_timezone_get()));
276 
277  $currentDate->setTime($currentDate->format('H'), $currentDate->format('i'), 0);
278  $nextRun = clone $currentDate;
279  $nth = (int) $nth;
280 
281  // Set a hard limit to bail on an impossible date
282  for ($i = 0; $i < 1000; $i++) {
283  foreach (self::$order as $position) {
284  $part = $this->getExpression($position);
285  if (null === $part) {
286  continue;
287  }
288 
289  $satisfied = false;
290  // Get the field object used to validate this part
291  $field = $this->fieldFactory->getField($position);
292  // Check if this is singular or a list
293  if (strpos($part, ',') === false) {
294  $satisfied = $field->isSatisfiedBy($nextRun, $part);
295  } else {
296  foreach (array_map('trim', explode(',', $part)) as $listPart) {
297  if ($field->isSatisfiedBy($nextRun, $listPart)) {
298  $satisfied = true;
299  break;
300  }
301  }
302  }
303 
304  // If the field is not satisfied, then start over
305  if (!$satisfied) {
306  $field->increment($nextRun, $invert);
307  continue 2;
308  }
309  }
310 
311  // Skip this match if needed
312  if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) {
313  $this->fieldFactory->getField(0)->increment($nextRun, $invert);
314  continue;
315  }
316 
317  return $nextRun;
318  }
319 
320  // @codeCoverageIgnoreStart
321  throw new RuntimeException('Impossible CRON expression');
322  // @codeCoverageIgnoreEnd
323  }
324 }