PluginProbe ʕ •ᴥ•ʔ
MailPoet – Newsletters, Email Marketing, and Automation / 5.36.0
MailPoet – Newsletters, Email Marketing, and Automation v5.36.0
5.36.0 5.35.1 5.35.0 5.34.3 5.34.2 5.34.1 5.34.0 5.33.1 5.33.0 5.32.0 5.31.0 5.30.0 5.29.0 5.28.1 5.28.0 5.27.0 5.26.0 5.26.1 5.25.0 5.24.0 4.43.0 4.43.1 4.44.0 4.44.1 4.45.0 4.46.0 4.47.0 4.48.0 4.48.1 4.48.2 4.49.0 4.49.1 4.5.0 4.5.1 4.5.2 4.50.0 4.50.1 4.51.0 4.51.1 4.51.2 4.52.0 4.53.0 4.54.0 4.55.0 4.56.0 4.57.0 4.58.0 4.58.1 4.58.2 4.6.0 4.6.1 4.6.2 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 5.0.0 5.0.1 5.0.2 5.1.0 5.1.1 5.10.0 5.10.1 5.11.0 5.12.0 5.12.1 5.12.10 5.12.11 5.12.12 5.12.13 5.12.2 5.12.3 5.12.4 5.12.5 5.12.6 5.12.7 5.12.8 5.12.9 5.13.0 5.13.1 5.13.2 5.14.0 5.14.1 5.14.2 5.14.3 5.15.0 5.15.1 5.16.0 5.16.1 5.16.2 5.16.3 5.16.4 5.17.0 5.17.1 5.17.2 5.17.3 5.17.4 5.17.5 5.17.6 5.18.0 5.19.0 5.2.0 5.2.1 5.2.2 5.2.3 5.20.0 5.21.0 5.21.1 5.21.2 5.21.3 5.22.0 5.22.1 5.22.2 5.22.3 5.22.4 5.23.0 5.23.1 5.23.2 5.3.0 5.3.1 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.4.0 5.4.1 5.4.2 5.5.0 5.5.1 5.5.2 5.6.0 5.6.1 5.6.2 5.6.3 5.6.4 5.7.0 5.7.1 5.8.0 5.8.1 5.9.0 3.0.0-beta.15 3.7.1 3.0.0-beta.16 3.7.2 3.0.0-beta.17 3.7.3 3.0.0-beta.18 3.7.4 3.0.0-beta.19 3.7.5 3.0.0-beta.2 3.7.6 3.0.0-beta.20 3.7.8 3.0.0-beta.21 3.70.0 3.0.0-beta.22 3.71.0 3.0.0-beta.23 3.71.1 3.0.0-beta.23.1 3.71.2 3.0.0-beta.23.2 3.71.3 3.0.0-beta.24 3.72.0 3.0.0-beta.25 3.73.0 3.0.0-beta.26 3.73.1 3.0.0-beta.27 3.73.2 3.0.0-beta.28 3.74.0 3.0.0-beta.29 3.74.1 3.0.0-beta.3 3.74.2 3.0.0-beta.30 3.74.3 3.0.0-beta.31 3.75.0 3.0.0-beta.32 3.75.1 3.0.0-beta.33 3.76.0 3.0.0-beta.33.1 3.77.0 3.0.0-beta.34.0.0 3.77.1 3.0.0-beta.36.0.0 3.78.0 3.0.0-beta.36.0.1 3.79.0 3.0.0-beta.36.2.0 3.8 3.0.0-beta.36.3.0 3.8.1 3.0.0-beta.36.3.1 3.8.2 3.0.0-beta.37.0.0 3.8.3 3.0.0-beta.4 3.8.4 3.0.0-beta.5 3.8.5 3.0.0-beta.6 3.8.6 3.0.0-beta.7 3.80.0 3.0.0-beta.7.1 3.81.0 3.0.0-beta.8 3.82.0 3.0.0-beta.9 3.83.0 3.0.0-rc.1.0.0 3.84.0 3.0.0-rc.1.0.1 3.84.1 3.0.0-rc.1.0.2 3.85.0 3.0.0-rc.1.0.3 3.85.1 3.0.0-rc.1.0.4 3.86.0 3.0.0-rc.2.0.0 3.87.0 3.0.0-rc.2.0.1 3.87.1 3.0.0-rc.2.0.2 3.87.2 3.0.0-rc.2.0.3 3.88.0 3.0.1 3.88.1 3.0.2 3.88.2 3.0.3 3.89.0 3.0.4 3.89.1 3.0.5 3.89.2 3.0.6 3.89.3 3.0.7 3.89.4 3.0.8 3.9.0 3.0.9 3.9.1 3.1.0 3.90.0 3.10 3.90.1 3.10.1 3.90.2 3.100.0 3.91.0 3.100.1 3.91.1 3.100.2 3.92.0 3.101.0 3.92.1 3.101.1 3.93.0 3.102.0 3.93.1 3.102.1 3.94.0 3.103.0 3.95.0 3.103.1 3.95.1 3.11.0 3.96.0 3.11.1 3.96.1 3.11.2 3.97.0 3.11.3 3.98.0 3.11.4 3.98.1 3.11.5 3.99.0 3.12.0 3.99.1 3.12.1 4.0.0 3.13.0 4.0.1 3.14.0 4.1.0 3.14.1 4.1.1 3.15.0 4.10.0 3.16.0 4.11.0 3.16.1 4.11.1 3.16.2 4.12.0 3.16.3 4.12.1 3.17.0 4.12.2 3.17.1 4.13.0 3.17.2 4.14.0 3.18.0 4.15.0 3.18.1 4.16.0 3.18.2 4.17.0 3.19.0 4.17.1 3.19.1 4.18.0 3.19.2 4.18.1 3.19.3 4.19.0 3.2.0 4.2.0 3.2.1 4.20.0 3.2.2 4.20.1 3.2.3 4.20.2 3.2.4 4.21.0 3.2.5 4.22.0 3.20.0 4.22.1 3.21.0 4.22.2 3.21.1 4.23.0 3.22.0 4.24.0 3.23.0 4.25.0 3.23.1 4.26.0 3.23.2 4.26.1 3.24.0 4.27.0 3.25.0 4.28.0 3.25.1 4.29.0 3.26.0 4.3.0 3.26.1 4.3.1 3.27.0 4.30.0 3.28.0 4.31.0 3.29.0 4.31.1 3.3.0 4.32.0 3.3.1 4.33.0 3.3.2 4.34.0 3.3.3 4.35.0 3.3.4 4.35.1 3.3.5 4.36.0 3.3.6 4.37.0 3.30.0 4.38.0 3.31.0 4.39.0 3.31.1 4.4.0 3.32.0 4.40.0 3.32.1 4.41.0 3.32.2 4.41.1 3.33.0 4.41.2 3.34.0 4.41.3 3.34.1 4.42.0 3.34.2 4.42.1 3.34.3 3.34.4 3.35.0 3.35.1 3.35.3 3.35.4 3.36.0 3.37.0 3.37.1 3.37.2 3.37.3 3.38.0 3.38.1 3.39.0 3.39.1 3.39.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.40.0 3.40.1 3.41.0 3.41.1 3.41.2 3.42.0 3.42.1 3.42.2 3.42.3 3.43.0 3.43.1 3.44.0 3.45.0 3.45.1 3.46.0 3.46.1 3.46.10 3.46.11 3.46.12 3.46.13 3.46.14 3.46.2 3.46.3 3.46.4 3.46.5 3.46.6 3.46.7 3.46.8 3.46.9 3.47.0 3.47.1 3.47.10 3.47.11 3.47.2 3.47.3 3.47.5 3.47.6 3.47.7 3.47.9 3.48.0 3.48.1 3.49.0 3.49.1 3.5.0 3.5.1 3.50.0 3.51.0 3.51.1 3.51.2 3.52.0 3.53.0 3.54.0 3.54.1 3.54.2 3.54.3 3.55.0 3.55.1 3.56.0 3.56.1 3.56.2 3.57.0 3.57.1 3.58.0 3.59.0 3.59.1 3.59.2 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.60.0 3.60.1 3.60.10 3.60.11 3.60.12 3.60.2 3.60.3 3.60.4 3.60.6 3.60.7 3.60.8 3.60.9 3.61.0 3.62.0 3.62.1 3.63.0 3.64.0 3.64.1 3.64.2 3.64.3 3.65.0 trunk 3.65.1 3.0.0 3.66.0 3.0.0-beta.1 3.67.0 3.0.0-beta.10 3.67.1 3.0.0-beta.11 3.68.0 3.0.0-beta.12 3.69.0 3.0.0-beta.13 3.69.1 3.0.0-beta.14 3.7.0
mailpoet / vendor / dragonmantank / cron-expression / src / Cron / CronExpression.php
mailpoet / vendor / dragonmantank / cron-expression / src / Cron Last commit date
AbstractField.php 1 year ago CronExpression.php 8 months ago DayOfMonthField.php 1 year ago DayOfWeekField.php 1 year ago FieldFactory.php 1 year ago FieldFactoryInterface.php 1 year ago FieldInterface.php 1 year ago HoursField.php 1 year ago MinutesField.php 1 year ago MonthField.php 1 year ago index.php 1 year ago
CronExpression.php
309 lines
1 <?php
2 declare(strict_types=1);
3 namespace Cron;
4 if (!defined('ABSPATH')) exit;
5 use DateTime;
6 use DateTimeImmutable;
7 use DateTimeInterface;
8 use DateTimeZone;
9 use Exception;
10 use InvalidArgumentException;
11 use LogicException;
12 use RuntimeException;
13 use Webmozart\Assert\Assert;
14 class CronExpression
15 {
16 public const MINUTE = 0;
17 public const HOUR = 1;
18 public const DAY = 2;
19 public const MONTH = 3;
20 public const WEEKDAY = 4;
21 public const YEAR = 5;
22 public const MAPPINGS = [
23 '@yearly' => '0 0 1 1 *',
24 '@annually' => '0 0 1 1 *',
25 '@monthly' => '0 0 1 * *',
26 '@weekly' => '0 0 * * 0',
27 '@daily' => '0 0 * * *',
28 '@midnight' => '0 0 * * *',
29 '@hourly' => '0 * * * *',
30 ];
31 protected $cronParts;
32 protected $fieldFactory;
33 protected $maxIterationCount = 1000;
34 protected static $order = [
35 self::YEAR,
36 self::MONTH,
37 self::DAY,
38 self::WEEKDAY,
39 self::HOUR,
40 self::MINUTE,
41 ];
42 private static $registeredAliases = self::MAPPINGS;
43 public static function registerAlias(string $alias, string $expression): void
44 {
45 try {
46 new self($expression);
47 } catch (InvalidArgumentException $exception) {
48 throw new LogicException("The expression `$expression` is invalid", 0, $exception);
49 }
50 $shortcut = strtolower($alias);
51 if (1 !== preg_match('/^@\w+$/', $shortcut)) {
52 throw new LogicException("The alias `$alias` is invalid. It must start with an `@` character and contain alphanumeric (letters, numbers, regardless of case) plus underscore (_).");
53 }
54 if (isset(self::$registeredAliases[$shortcut])) {
55 throw new LogicException("The alias `$alias` is already registered.");
56 }
57 self::$registeredAliases[$shortcut] = $expression;
58 }
59 public static function unregisterAlias(string $alias): bool
60 {
61 $shortcut = strtolower($alias);
62 if (isset(self::MAPPINGS[$shortcut])) {
63 throw new LogicException("The alias `$alias` is a built-in alias; it can not be unregistered.");
64 }
65 if (!isset(self::$registeredAliases[$shortcut])) {
66 return false;
67 }
68 unset(self::$registeredAliases[$shortcut]);
69 return true;
70 }
71 public static function supportsAlias(string $alias): bool
72 {
73 return isset(self::$registeredAliases[strtolower($alias)]);
74 }
75 public static function getAliases(): array
76 {
77 return self::$registeredAliases;
78 }
79 public static function factory(string $expression,?FieldFactoryInterface $fieldFactory = null): CronExpression
80 {
81 return new static($expression, $fieldFactory);
82 }
83 public static function isValidExpression(string $expression): bool
84 {
85 try {
86 new CronExpression($expression);
87 } catch (InvalidArgumentException $e) {
88 return false;
89 }
90 return true;
91 }
92 public function __construct(string $expression,?FieldFactoryInterface $fieldFactory = null)
93 {
94 $shortcut = strtolower($expression);
95 $expression = self::$registeredAliases[$shortcut] ?? $expression;
96 $this->fieldFactory = $fieldFactory ?: new FieldFactory();
97 $this->setExpression($expression);
98 }
99 public function setExpression(string $value): CronExpression
100 {
101 $split = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY);
102 Assert::isArray($split);
103 $notEnoughParts = \count($split) < 5;
104 $questionMarkInInvalidPart = array_key_exists(0, $split) && $split[0] === '?'
105 || array_key_exists(1, $split) && $split[1] === '?'
106 || array_key_exists(3, $split) && $split[3] === '?';
107 $tooManyQuestionMarks = array_key_exists(2, $split) && $split[2] === '?'
108 && array_key_exists(4, $split) && $split[4] === '?';
109 if ($notEnoughParts || $questionMarkInInvalidPart || $tooManyQuestionMarks) {
110 throw new InvalidArgumentException(
111 $value . ' is not a valid CRON expression'
112 );
113 }
114 $this->cronParts = $split;
115 foreach ($this->cronParts as $position => $part) {
116 $this->setPart($position, $part);
117 }
118 return $this;
119 }
120 public function setPart(int $position, string $value): CronExpression
121 {
122 if (!$this->fieldFactory->getField($position)->validate($value)) {
123 throw new InvalidArgumentException(
124 'Invalid CRON field value ' . $value . ' at position ' . $position
125 );
126 }
127 $this->cronParts[$position] = $value;
128 return $this;
129 }
130 public function setMaxIterationCount(int $maxIterationCount): CronExpression
131 {
132 $this->maxIterationCount = $maxIterationCount;
133 return $this;
134 }
135 public function getNextRunDate($currentTime = 'now', int $nth = 0, bool $allowCurrentDate = false, $timeZone = null): DateTime
136 {
137 return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate, $timeZone);
138 }
139 public function getPreviousRunDate($currentTime = 'now', int $nth = 0, bool $allowCurrentDate = false, $timeZone = null): DateTime
140 {
141 return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate, $timeZone);
142 }
143 public function getMultipleRunDates(int $total, $currentTime = 'now', bool $invert = false, bool $allowCurrentDate = false, $timeZone = null): array
144 {
145 $timeZone = $this->determineTimeZone($currentTime, $timeZone);
146 if ('now' === $currentTime) {
147 $currentTime = new DateTime();
148 } elseif ($currentTime instanceof DateTime) {
149 $currentTime = clone $currentTime;
150 } elseif ($currentTime instanceof DateTimeImmutable) {
151 $currentTime = DateTime::createFromFormat('U', $currentTime->format('U'));
152 } elseif (\is_string($currentTime)) {
153 $currentTime = new DateTime($currentTime);
154 }
155 Assert::isInstanceOf($currentTime, DateTime::class);
156 $currentTime->setTimezone(new DateTimeZone($timeZone));
157 $matches = [];
158 for ($i = 0; $i < $total; ++$i) {
159 try {
160 $result = $this->getRunDate($currentTime, 0, $invert, $allowCurrentDate, $timeZone);
161 } catch (RuntimeException $e) {
162 break;
163 }
164 $allowCurrentDate = false;
165 $currentTime = clone $result;
166 $matches[] = $result;
167 }
168 return $matches;
169 }
170 public function getExpression($part = null): ?string
171 {
172 if (null === $part) {
173 return implode(' ', $this->cronParts);
174 }
175 if (array_key_exists($part, $this->cronParts)) {
176 return $this->cronParts[$part];
177 }
178 return null;
179 }
180 public function getParts()
181 {
182 return $this->cronParts;
183 }
184 public function __toString(): string
185 {
186 return (string) $this->getExpression();
187 }
188 public function isDue($currentTime = 'now', $timeZone = null): bool
189 {
190 $timeZone = $this->determineTimeZone($currentTime, $timeZone);
191 if ('now' === $currentTime) {
192 $currentTime = new DateTime();
193 } elseif ($currentTime instanceof DateTime) {
194 $currentTime = clone $currentTime;
195 } elseif ($currentTime instanceof DateTimeImmutable) {
196 $currentTime = DateTime::createFromFormat('U', $currentTime->format('U'));
197 } elseif (\is_string($currentTime)) {
198 $currentTime = new DateTime($currentTime);
199 }
200 Assert::isInstanceOf($currentTime, DateTime::class);
201 $currentTime->setTimezone(new DateTimeZone($timeZone));
202 // drop the seconds to 0
203 $currentTime->setTime((int) $currentTime->format('H'), (int) $currentTime->format('i'), 0);
204 try {
205 return $this->getNextRunDate($currentTime, 0, true)->getTimestamp() === $currentTime->getTimestamp();
206 } catch (Exception $e) {
207 return false;
208 }
209 }
210 protected function getRunDate($currentTime = null, int $nth = 0, bool $invert = false, bool $allowCurrentDate = false, $timeZone = null): DateTime
211 {
212 $timeZone = $this->determineTimeZone($currentTime, $timeZone);
213 if ($currentTime instanceof DateTime) {
214 $currentDate = clone $currentTime;
215 } elseif ($currentTime instanceof DateTimeImmutable) {
216 $currentDate = DateTime::createFromFormat('U', $currentTime->format('U'));
217 } elseif (\is_string($currentTime)) {
218 $currentDate = new DateTime($currentTime);
219 } else {
220 $currentDate = new DateTime('now');
221 }
222 Assert::isInstanceOf($currentDate, DateTime::class);
223 $currentDate->setTimezone(new DateTimeZone($timeZone));
224 // Workaround for setTime causing an offset change: https://bugs.php.net/bug.php?id=81074
225 $currentDate = DateTime::createFromFormat("!Y-m-d H:iO", $currentDate->format("Y-m-d H:iP"), $currentDate->getTimezone());
226 if ($currentDate === false) {
227 throw new \RuntimeException('Unable to create date from format');
228 }
229 $currentDate->setTimezone(new DateTimeZone($timeZone));
230 $nextRun = clone $currentDate;
231 // We don't have to satisfy * or null fields
232 $parts = [];
233 $fields = [];
234 foreach (self::$order as $position) {
235 $part = $this->getExpression($position);
236 if (null === $part || '*' === $part) {
237 continue;
238 }
239 $parts[$position] = $part;
240 $fields[$position] = $this->fieldFactory->getField($position);
241 }
242 if (isset($parts[self::DAY]) && isset($parts[self::WEEKDAY])) {
243 $domExpression = sprintf('%s %s %s %s *', $this->getExpression(0), $this->getExpression(1), $this->getExpression(2), $this->getExpression(3));
244 $dowExpression = sprintf('%s %s * %s %s', $this->getExpression(0), $this->getExpression(1), $this->getExpression(3), $this->getExpression(4));
245 $domExpression = new self($domExpression);
246 $dowExpression = new self($dowExpression);
247 $domRunDates = $domExpression->getMultipleRunDates($nth + 1, $currentTime, $invert, $allowCurrentDate, $timeZone);
248 $dowRunDates = $dowExpression->getMultipleRunDates($nth + 1, $currentTime, $invert, $allowCurrentDate, $timeZone);
249 if ($parts[self::DAY] === '?' || $parts[self::DAY] === '*') {
250 $domRunDates = [];
251 }
252 if ($parts[self::WEEKDAY] === '?' || $parts[self::WEEKDAY] === '*') {
253 $dowRunDates = [];
254 }
255 $combined = array_merge($domRunDates, $dowRunDates);
256 usort($combined, function ($a, $b) {
257 return $a->format('Y-m-d H:i:s') <=> $b->format('Y-m-d H:i:s');
258 });
259 if ($invert) {
260 $combined = array_reverse($combined);
261 }
262 return $combined[$nth];
263 }
264 // Set a hard limit to bail on an impossible date
265 for ($i = 0; $i < $this->maxIterationCount; ++$i) {
266 foreach ($parts as $position => $part) {
267 $satisfied = false;
268 // Get the field object used to validate this part
269 $field = $fields[$position];
270 // Check if this is singular or a list
271 if (false === strpos($part, ',')) {
272 $satisfied = $field->isSatisfiedBy($nextRun, $part, $invert);
273 } else {
274 foreach (array_map('trim', explode(',', $part)) as $listPart) {
275 if ($field->isSatisfiedBy($nextRun, $listPart, $invert)) {
276 $satisfied = true;
277 break;
278 }
279 }
280 }
281 // If the field is not satisfied, then start over
282 if (!$satisfied) {
283 $field->increment($nextRun, $invert, $part);
284 continue 2;
285 }
286 }
287 // Skip this match if needed
288 if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) {
289 $this->fieldFactory->getField(self::MINUTE)->increment($nextRun, $invert, $parts[self::MINUTE] ?? null);
290 continue;
291 }
292 return $nextRun;
293 }
294 // @codeCoverageIgnoreStart
295 throw new RuntimeException('Impossible CRON expression');
296 // @codeCoverageIgnoreEnd
297 }
298 protected function determineTimeZone($currentTime, ?string $timeZone): string
299 {
300 if (null !== $timeZone) {
301 return $timeZone;
302 }
303 if ($currentTime instanceof DateTimeInterface) {
304 return $currentTime->getTimezone()->getName();
305 }
306 return date_default_timezone_get();
307 }
308 }
309