PluginProbe ʕ •ᴥ•ʔ
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin / 2.9.0
WP Mail SMTP by WPForms – The Most Popular SMTP and Email Log Plugin v2.9.0
4.9.0 0.9.6 1.0.0 1.0.1 1.0.2 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.2 1.7.0 1.7.1 1.8.0 1.8.1 1.9.0 2.0.0 2.0.1 2.1.1 2.2.1 2.3.1 2.4.0 2.5.0 2.5.1 2.6.0 2.7.0 2.8.0 2.9.0 3.0.1 3.0.2 3.0.3 3.1.0 3.10.0 3.11.0 3.11.1 3.2.0 3.2.1 3.3.0 3.4.0 3.5.0 3.5.1 3.5.2 3.6.1 3.7.0 3.8.0 3.8.2 3.9.0 4.0.1 4.1.0 4.1.1 4.2.0 4.3.0 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.8.0 trunk 0.10.0 0.10.1 0.11.1 0.11.2 0.3.1 0.3.2 0.4 0.4.1 0.4.2 0.5.0 0.5.1 0.5.2 0.6 0.7 0.8 0.8.2 0.8.3 0.8.4 0.8.5 0.8.6 0.8.7 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5
wp-mail-smtp / vendor / woocommerce / action-scheduler / lib / cron-expression / CronExpression.php
wp-mail-smtp / vendor / woocommerce / action-scheduler / lib / cron-expression Last commit date
CronExpression.php 5 years ago CronExpression_AbstractField.php 5 years ago CronExpression_DayOfMonthField.php 5 years ago CronExpression_DayOfWeekField.php 5 years ago CronExpression_FieldFactory.php 5 years ago CronExpression_FieldInterface.php 5 years ago CronExpression_HoursField.php 5 years ago CronExpression_MinutesField.php 5 years ago CronExpression_MonthField.php 5 years ago CronExpression_YearField.php 5 years ago LICENSE 5 years ago
CronExpression.php
319 lines
1 <?php
2
3 /**
4 * CRON expression parser that can determine whether or not a CRON expression is
5 * due to run, the next run date and previous run date of a CRON expression.
6 * The determinations made by this class are accurate if checked run once per
7 * minute (seconds are dropped from date time comparisons).
8 *
9 * Schedule parts must map to:
10 * minute [0-59], hour [0-23], day of month, month [1-12|JAN-DEC], day of week
11 * [1-7|MON-SUN], and an optional year.
12 *
13 * @author Michael Dowling <mtdowling@gmail.com>
14 * @link http://en.wikipedia.org/wiki/Cron
15 */
16 class CronExpression
17 {
18 const MINUTE = 0;
19 const HOUR = 1;
20 const DAY = 2;
21 const MONTH = 3;
22 const WEEKDAY = 4;
23 const YEAR = 5;
24
25 /**
26 * @var array CRON expression parts
27 */
28 private $cronParts;
29
30 /**
31 * @var CronExpression_FieldFactory CRON field factory
32 */
33 private $fieldFactory;
34
35 /**
36 * @var array Order in which to test of cron parts
37 */
38 private static $order = array(self::YEAR, self::MONTH, self::DAY, self::WEEKDAY, self::HOUR, self::MINUTE);
39
40 /**
41 * Factory method to create a new CronExpression.
42 *
43 * @param string $expression The CRON expression to create. There are
44 * several special predefined values which can be used to substitute the
45 * CRON expression:
46 *
47 * @yearly, @annually) - Run once a year, midnight, Jan. 1 - 0 0 1 1 *
48 * @monthly - Run once a month, midnight, first of month - 0 0 1 * *
49 * @weekly - Run once a week, midnight on Sun - 0 0 * * 0
50 * @daily - Run once a day, midnight - 0 0 * * *
51 * @hourly - Run once an hour, first minute - 0 * * * *
52 *
53 *@param CronExpression_FieldFactory $fieldFactory (optional) Field factory to use
54 *
55 * @return CronExpression
56 */
57 public static function factory($expression, CronExpression_FieldFactory $fieldFactory = null)
58 {
59 $mappings = array(
60 '@yearly' => '0 0 1 1 *',
61 '@annually' => '0 0 1 1 *',
62 '@monthly' => '0 0 1 * *',
63 '@weekly' => '0 0 * * 0',
64 '@daily' => '0 0 * * *',
65 '@hourly' => '0 * * * *'
66 );
67
68 if (isset($mappings[$expression])) {
69 $expression = $mappings[$expression];
70 }
71
72 return new self($expression, $fieldFactory ? $fieldFactory : new CronExpression_FieldFactory());
73 }
74
75 /**
76 * Parse a CRON expression
77 *
78 * @param string $expression CRON expression (e.g. '8 * * * *')
79 * @param CronExpression_FieldFactory $fieldFactory Factory to create cron fields
80 */
81 public function __construct($expression, CronExpression_FieldFactory $fieldFactory)
82 {
83 $this->fieldFactory = $fieldFactory;
84 $this->setExpression($expression);
85 }
86
87 /**
88 * Set or change the CRON expression
89 *
90 * @param string $value CRON expression (e.g. 8 * * * *)
91 *
92 * @return CronExpression
93 * @throws InvalidArgumentException if not a valid CRON expression
94 */
95 public function setExpression($value)
96 {
97 $this->cronParts = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY);
98 if (count($this->cronParts) < 5) {
99 throw new InvalidArgumentException(
100 $value . ' is not a valid CRON expression'
101 );
102 }
103
104 foreach ($this->cronParts as $position => $part) {
105 $this->setPart($position, $part);
106 }
107
108 return $this;
109 }
110
111 /**
112 * Set part of the CRON expression
113 *
114 * @param int $position The position of the CRON expression to set
115 * @param string $value The value to set
116 *
117 * @return CronExpression
118 * @throws InvalidArgumentException if the value is not valid for the part
119 */
120 public function setPart($position, $value)
121 {
122 if (!$this->fieldFactory->getField($position)->validate($value)) {
123 throw new InvalidArgumentException(
124 'Invalid CRON field value ' . $value . ' as position ' . $position
125 );
126 }
127
128 $this->cronParts[$position] = $value;
129
130 return $this;
131 }
132
133 /**
134 * Get a next run date relative to the current date or a specific date
135 *
136 * @param string|DateTime $currentTime (optional) Relative calculation date
137 * @param int $nth (optional) Number of matches to skip before returning a
138 * matching next run date. 0, the default, will return the current
139 * date and time if the next run date falls on the current date and
140 * time. Setting this value to 1 will skip the first match and go to
141 * the second match. Setting this value to 2 will skip the first 2
142 * matches and so on.
143 * @param bool $allowCurrentDate (optional) Set to TRUE to return the
144 * current date if it matches the cron expression
145 *
146 * @return DateTime
147 * @throws RuntimeException on too many iterations
148 */
149 public function getNextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
150 {
151 return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate);
152 }
153
154 /**
155 * Get a previous run date relative to the current date or a specific date
156 *
157 * @param string|DateTime $currentTime (optional) Relative calculation date
158 * @param int $nth (optional) Number of matches to skip before returning
159 * @param bool $allowCurrentDate (optional) Set to TRUE to return the
160 * current date if it matches the cron expression
161 *
162 * @return DateTime
163 * @throws RuntimeException on too many iterations
164 * @see CronExpression::getNextRunDate
165 */
166 public function getPreviousRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false)
167 {
168 return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate);
169 }
170
171 /**
172 * Get multiple run dates starting at the current date or a specific date
173 *
174 * @param int $total Set the total number of dates to calculate
175 * @param string|DateTime $currentTime (optional) Relative calculation date
176 * @param bool $invert (optional) Set to TRUE to retrieve previous dates
177 * @param bool $allowCurrentDate (optional) Set to TRUE to return the
178 * current date if it matches the cron expression
179 *
180 * @return array Returns an array of run dates
181 */
182 public function getMultipleRunDates($total, $currentTime = 'now', $invert = false, $allowCurrentDate = false)
183 {
184 $matches = array();
185 for ($i = 0; $i < max(0, $total); $i++) {
186 $matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate);
187 }
188
189 return $matches;
190 }
191
192 /**
193 * Get all or part of the CRON expression
194 *
195 * @param string $part (optional) Specify the part to retrieve or NULL to
196 * get the full cron schedule string.
197 *
198 * @return string|null Returns the CRON expression, a part of the
199 * CRON expression, or NULL if the part was specified but not found
200 */
201 public function getExpression($part = null)
202 {
203 if (null === $part) {
204 return implode(' ', $this->cronParts);
205 } elseif (array_key_exists($part, $this->cronParts)) {
206 return $this->cronParts[$part];
207 }
208
209 return null;
210 }
211
212 /**
213 * Helper method to output the full expression.
214 *
215 * @return string Full CRON expression
216 */
217 public function __toString()
218 {
219 return $this->getExpression();
220 }
221
222 /**
223 * Determine if the cron is due to run based on the current date or a
224 * specific date. This method assumes that the current number of
225 * seconds are irrelevant, and should be called once per minute.
226 *
227 * @param string|DateTime $currentTime (optional) Relative calculation date
228 *
229 * @return bool Returns TRUE if the cron is due to run or FALSE if not
230 */
231 public function isDue($currentTime = 'now')
232 {
233 if ('now' === $currentTime) {
234 $currentDate = date('Y-m-d H:i');
235 $currentTime = strtotime($currentDate);
236 } elseif ($currentTime instanceof DateTime) {
237 $currentDate = $currentTime->format('Y-m-d H:i');
238 $currentTime = strtotime($currentDate);
239 } else {
240 $currentTime = new DateTime($currentTime);
241 $currentTime->setTime($currentTime->format('H'), $currentTime->format('i'), 0);
242 $currentDate = $currentTime->format('Y-m-d H:i');
243 $currentTime = (int)($currentTime->getTimestamp());
244 }
245
246 return $this->getNextRunDate($currentDate, 0, true)->getTimestamp() == $currentTime;
247 }
248
249 /**
250 * Get the next or previous run date of the expression relative to a date
251 *
252 * @param string|DateTime $currentTime (optional) Relative calculation date
253 * @param int $nth (optional) Number of matches to skip before returning
254 * @param bool $invert (optional) Set to TRUE to go backwards in time
255 * @param bool $allowCurrentDate (optional) Set to TRUE to return the
256 * current date if it matches the cron expression
257 *
258 * @return DateTime
259 * @throws RuntimeException on too many iterations
260 */
261 protected function getRunDate($currentTime = null, $nth = 0, $invert = false, $allowCurrentDate = false)
262 {
263 if ($currentTime instanceof DateTime) {
264 $currentDate = $currentTime;
265 } else {
266 $currentDate = new DateTime($currentTime ? $currentTime : 'now');
267 $currentDate->setTimezone(new DateTimeZone(date_default_timezone_get()));
268 }
269
270 $currentDate->setTime($currentDate->format('H'), $currentDate->format('i'), 0);
271 $nextRun = clone $currentDate;
272 $nth = (int) $nth;
273
274 // Set a hard limit to bail on an impossible date
275 for ($i = 0; $i < 1000; $i++) {
276
277 foreach (self::$order as $position) {
278 $part = $this->getExpression($position);
279 if (null === $part) {
280 continue;
281 }
282
283 $satisfied = false;
284 // Get the field object used to validate this part
285 $field = $this->fieldFactory->getField($position);
286 // Check if this is singular or a list
287 if (strpos($part, ',') === false) {
288 $satisfied = $field->isSatisfiedBy($nextRun, $part);
289 } else {
290 foreach (array_map('trim', explode(',', $part)) as $listPart) {
291 if ($field->isSatisfiedBy($nextRun, $listPart)) {
292 $satisfied = true;
293 break;
294 }
295 }
296 }
297
298 // If the field is not satisfied, then start over
299 if (!$satisfied) {
300 $field->increment($nextRun, $invert);
301 continue 2;
302 }
303 }
304
305 // Skip this match if needed
306 if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) {
307 $this->fieldFactory->getField(0)->increment($nextRun, $invert);
308 continue;
309 }
310
311 return $nextRun;
312 }
313
314 // @codeCoverageIgnoreStart
315 throw new RuntimeException('Impossible CRON expression');
316 // @codeCoverageIgnoreEnd
317 }
318 }
319