From 5f112e7d0464d98282443b78870cdccabe42aae9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 14:47:35 +0200 Subject: Adding upstream version 1:1.1.2. Signed-off-by: Daniel Baumann --- .../cron-expression/src/Cron/AbstractField.php | 286 ++++++++++++++ .../cron-expression/src/Cron/CronExpression.php | 413 +++++++++++++++++++++ .../cron-expression/src/Cron/DayOfMonthField.php | 145 ++++++++ .../cron-expression/src/Cron/DayOfWeekField.php | 196 ++++++++++ .../cron-expression/src/Cron/FieldFactory.php | 54 +++ .../cron-expression/src/Cron/FieldInterface.php | 41 ++ .../cron-expression/src/Cron/HoursField.php | 85 +++++ .../cron-expression/src/Cron/MinutesField.php | 75 ++++ .../cron-expression/src/Cron/MonthField.php | 59 +++ 9 files changed, 1354 insertions(+) create mode 100644 vendor/dragonmantank/cron-expression/src/Cron/AbstractField.php create mode 100644 vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php create mode 100644 vendor/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php create mode 100644 vendor/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php create mode 100644 vendor/dragonmantank/cron-expression/src/Cron/FieldFactory.php create mode 100644 vendor/dragonmantank/cron-expression/src/Cron/FieldInterface.php create mode 100644 vendor/dragonmantank/cron-expression/src/Cron/HoursField.php create mode 100644 vendor/dragonmantank/cron-expression/src/Cron/MinutesField.php create mode 100644 vendor/dragonmantank/cron-expression/src/Cron/MonthField.php (limited to 'vendor/dragonmantank/cron-expression/src') diff --git a/vendor/dragonmantank/cron-expression/src/Cron/AbstractField.php b/vendor/dragonmantank/cron-expression/src/Cron/AbstractField.php new file mode 100644 index 0000000..8b1072a --- /dev/null +++ b/vendor/dragonmantank/cron-expression/src/Cron/AbstractField.php @@ -0,0 +1,286 @@ +fullRange = range($this->rangeStart, $this->rangeEnd); + } + + /** + * Check to see if a field is satisfied by a value + * + * @param string $dateValue Date value to check + * @param string $value Value to test + * + * @return bool + */ + public function isSatisfied($dateValue, $value) + { + if ($this->isIncrementsOfRanges($value)) { + return $this->isInIncrementsOfRanges($dateValue, $value); + } elseif ($this->isRange($value)) { + return $this->isInRange($dateValue, $value); + } + + return $value == '*' || $dateValue == $value; + } + + /** + * Check if a value is a range + * + * @param string $value Value to test + * + * @return bool + */ + public function isRange($value) + { + return strpos($value, '-') !== false; + } + + /** + * Check if a value is an increments of ranges + * + * @param string $value Value to test + * + * @return bool + */ + public function isIncrementsOfRanges($value) + { + return strpos($value, '/') !== false; + } + + /** + * Test if a value is within a range + * + * @param string $dateValue Set date value + * @param string $value Value to test + * + * @return bool + */ + public function isInRange($dateValue, $value) + { + $parts = array_map(function($value) { + $value = trim($value); + $value = $this->convertLiterals($value); + return $value; + }, + explode('-', $value, 2) + ); + + + return $dateValue >= $parts[0] && $dateValue <= $parts[1]; + } + + /** + * Test if a value is within an increments of ranges (offset[-to]/step size) + * + * @param string $dateValue Set date value + * @param string $value Value to test + * + * @return bool + */ + public function isInIncrementsOfRanges($dateValue, $value) + { + $chunks = array_map('trim', explode('/', $value, 2)); + $range = $chunks[0]; + $step = isset($chunks[1]) ? $chunks[1] : 0; + + // No step or 0 steps aren't cool + if (is_null($step) || '0' === $step || 0 === $step) { + return false; + } + + // Expand the * to a full range + if ('*' == $range) { + $range = $this->rangeStart . '-' . $this->rangeEnd; + } + + // Generate the requested small range + $rangeChunks = explode('-', $range, 2); + $rangeStart = $rangeChunks[0]; + $rangeEnd = isset($rangeChunks[1]) ? $rangeChunks[1] : $rangeStart; + + if ($rangeStart < $this->rangeStart || $rangeStart > $this->rangeEnd || $rangeStart > $rangeEnd) { + throw new \OutOfRangeException('Invalid range start requested'); + } + + if ($rangeEnd < $this->rangeStart || $rangeEnd > $this->rangeEnd || $rangeEnd < $rangeStart) { + throw new \OutOfRangeException('Invalid range end requested'); + } + + // Steps larger than the range need to wrap around and be handled slightly differently than smaller steps + if ($step >= $this->rangeEnd) { + $thisRange = [$this->fullRange[$step % count($this->fullRange)]]; + } else { + $thisRange = range($rangeStart, $rangeEnd, $step); + } + + return in_array($dateValue, $thisRange); + } + + /** + * Returns a range of values for the given cron expression + * + * @param string $expression The expression to evaluate + * @param int $max Maximum offset for range + * + * @return array + */ + public function getRangeForExpression($expression, $max) + { + $values = array(); + $expression = $this->convertLiterals($expression); + + if (strpos($expression, ',') !== false) { + $ranges = explode(',', $expression); + $values = []; + foreach ($ranges as $range) { + $expanded = $this->getRangeForExpression($range, $this->rangeEnd); + $values = array_merge($values, $expanded); + } + return $values; + } + + if ($this->isRange($expression) || $this->isIncrementsOfRanges($expression)) { + if (!$this->isIncrementsOfRanges($expression)) { + list ($offset, $to) = explode('-', $expression); + $offset = $this->convertLiterals($offset); + $to = $this->convertLiterals($to); + $stepSize = 1; + } + else { + $range = array_map('trim', explode('/', $expression, 2)); + $stepSize = isset($range[1]) ? $range[1] : 0; + $range = $range[0]; + $range = explode('-', $range, 2); + $offset = $range[0]; + $to = isset($range[1]) ? $range[1] : $max; + } + $offset = $offset == '*' ? $this->rangeStart : $offset; + if ($stepSize >= $this->rangeEnd) { + $values = [$this->fullRange[$stepSize % count($this->fullRange)]]; + } else { + for ($i = $offset; $i <= $to; $i += $stepSize) { + $values[] = (int)$i; + } + } + sort($values); + } + else { + $values = array($expression); + } + + return $values; + } + + /** + * Convert literal + * + * @param string $value + * @return string + */ + protected function convertLiterals($value) + { + if (count($this->literals)) { + $key = array_search($value, $this->literals); + if ($key !== false) { + return (string) $key; + } + } + + return $value; + } + + /** + * Checks to see if a value is valid for the field + * + * @param string $value + * @return bool + */ + public function validate($value) + { + $value = $this->convertLiterals($value); + + // All fields allow * as a valid value + if ('*' === $value) { + return true; + } + + if (strpos($value, '/') !== false) { + list($range, $step) = explode('/', $value); + return $this->validate($range) && filter_var($step, FILTER_VALIDATE_INT); + } + + // Validate each chunk of a list individually + if (strpos($value, ',') !== false) { + foreach (explode(',', $value) as $listItem) { + if (!$this->validate($listItem)) { + return false; + } + } + return true; + } + + if (strpos($value, '-') !== false) { + if (substr_count($value, '-') > 1) { + return false; + } + + $chunks = explode('-', $value); + $chunks[0] = $this->convertLiterals($chunks[0]); + $chunks[1] = $this->convertLiterals($chunks[1]); + + if ('*' == $chunks[0] || '*' == $chunks[1]) { + return false; + } + + return $this->validate($chunks[0]) && $this->validate($chunks[1]); + } + + if (!is_numeric($value)) { + return false; + } + + if (is_float($value) || strpos($value, '.') !== false) { + return false; + } + + // We should have a numeric by now, so coerce this into an integer + $value = (int) $value; + + return in_array($value, $this->fullRange, true); + } +} diff --git a/vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php b/vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php new file mode 100644 index 0000000..dbb93ce --- /dev/null +++ b/vendor/dragonmantank/cron-expression/src/Cron/CronExpression.php @@ -0,0 +1,413 @@ + '0 0 1 1 *', + '@annually' => '0 0 1 1 *', + '@monthly' => '0 0 1 * *', + '@weekly' => '0 0 * * 0', + '@daily' => '0 0 * * *', + '@hourly' => '0 * * * *' + ); + + if (isset($mappings[$expression])) { + $expression = $mappings[$expression]; + } + + return new static($expression, $fieldFactory ?: new FieldFactory()); + } + + /** + * Validate a CronExpression. + * + * @param string $expression The CRON expression to validate. + * + * @return bool True if a valid CRON expression was passed. False if not. + * @see \Cron\CronExpression::factory + */ + public static function isValidExpression($expression) + { + try { + self::factory($expression); + } catch (InvalidArgumentException $e) { + return false; + } + + return true; + } + + /** + * Parse a CRON expression + * + * @param string $expression CRON expression (e.g. '8 * * * *') + * @param FieldFactory|null $fieldFactory Factory to create cron fields + */ + public function __construct($expression, FieldFactory $fieldFactory = null) + { + $this->fieldFactory = $fieldFactory ?: new FieldFactory(); + $this->setExpression($expression); + } + + /** + * Set or change the CRON expression + * + * @param string $value CRON expression (e.g. 8 * * * *) + * + * @return CronExpression + * @throws \InvalidArgumentException if not a valid CRON expression + */ + public function setExpression($value) + { + $this->cronParts = preg_split('/\s/', $value, -1, PREG_SPLIT_NO_EMPTY); + if (count($this->cronParts) < 5) { + throw new InvalidArgumentException( + $value . ' is not a valid CRON expression' + ); + } + + foreach ($this->cronParts as $position => $part) { + $this->setPart($position, $part); + } + + return $this; + } + + /** + * Set part of the CRON expression + * + * @param int $position The position of the CRON expression to set + * @param string $value The value to set + * + * @return CronExpression + * @throws \InvalidArgumentException if the value is not valid for the part + */ + public function setPart($position, $value) + { + if (!$this->fieldFactory->getField($position)->validate($value)) { + throw new InvalidArgumentException( + 'Invalid CRON field value ' . $value . ' at position ' . $position + ); + } + + $this->cronParts[$position] = $value; + + return $this; + } + + /** + * Set max iteration count for searching next run dates + * + * @param int $maxIterationCount Max iteration count when searching for next run date + * + * @return CronExpression + */ + public function setMaxIterationCount($maxIterationCount) + { + $this->maxIterationCount = $maxIterationCount; + + return $this; + } + + /** + * Get a next run date relative to the current date or a specific date + * + * @param string|\DateTimeInterface $currentTime Relative calculation date + * @param int $nth Number of matches to skip before returning a + * matching next run date. 0, the default, will return the + * current date and time if the next run date falls on the + * current date and time. Setting this value to 1 will + * skip the first match and go to the second match. + * Setting this value to 2 will skip the first 2 + * matches and so on. + * @param bool $allowCurrentDate Set to TRUE to return the current date if + * it matches the cron expression. + * @param null|string $timeZone TimeZone to use instead of the system default + * + * @return \DateTime + * @throws \RuntimeException on too many iterations + */ + public function getNextRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false, $timeZone = null) + { + return $this->getRunDate($currentTime, $nth, false, $allowCurrentDate, $timeZone); + } + + /** + * Get a previous run date relative to the current date or a specific date + * + * @param string|\DateTimeInterface $currentTime Relative calculation date + * @param int $nth Number of matches to skip before returning + * @param bool $allowCurrentDate Set to TRUE to return the + * current date if it matches the cron expression + * @param null|string $timeZone TimeZone to use instead of the system default + * + * @return \DateTime + * @throws \RuntimeException on too many iterations + * @see \Cron\CronExpression::getNextRunDate + */ + public function getPreviousRunDate($currentTime = 'now', $nth = 0, $allowCurrentDate = false, $timeZone = null) + { + return $this->getRunDate($currentTime, $nth, true, $allowCurrentDate, $timeZone); + } + + /** + * Get multiple run dates starting at the current date or a specific date + * + * @param int $total Set the total number of dates to calculate + * @param string|\DateTimeInterface $currentTime Relative calculation date + * @param bool $invert Set to TRUE to retrieve previous dates + * @param bool $allowCurrentDate Set to TRUE to return the + * current date if it matches the cron expression + * @param null|string $timeZone TimeZone to use instead of the system default + * + * @return \DateTime[] Returns an array of run dates + */ + public function getMultipleRunDates($total, $currentTime = 'now', $invert = false, $allowCurrentDate = false, $timeZone = null) + { + $matches = array(); + for ($i = 0; $i < max(0, $total); $i++) { + try { + $matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate, $timeZone); + } catch (RuntimeException $e) { + break; + } + } + + return $matches; + } + + /** + * Get all or part of the CRON expression + * + * @param string $part Specify the part to retrieve or NULL to get the full + * cron schedule string. + * + * @return string|null Returns the CRON expression, a part of the + * CRON expression, or NULL if the part was specified but not found + */ + public function getExpression($part = null) + { + if (null === $part) { + return implode(' ', $this->cronParts); + } elseif (array_key_exists($part, $this->cronParts)) { + return $this->cronParts[$part]; + } + + return null; + } + + /** + * Helper method to output the full expression. + * + * @return string Full CRON expression + */ + public function __toString() + { + return $this->getExpression(); + } + + /** + * Determine if the cron is due to run based on the current date or a + * specific date. This method assumes that the current number of + * seconds are irrelevant, and should be called once per minute. + * + * @param string|\DateTimeInterface $currentTime Relative calculation date + * @param null|string $timeZone TimeZone to use instead of the system default + * + * @return bool Returns TRUE if the cron is due to run or FALSE if not + */ + public function isDue($currentTime = 'now', $timeZone = null) + { + $timeZone = $this->determineTimeZone($currentTime, $timeZone); + + if ('now' === $currentTime) { + $currentTime = new DateTime(); + } elseif ($currentTime instanceof DateTime) { + // + } elseif ($currentTime instanceof DateTimeImmutable) { + $currentTime = DateTime::createFromFormat('U', $currentTime->format('U')); + } else { + $currentTime = new DateTime($currentTime); + } + $currentTime->setTimeZone(new DateTimeZone($timeZone)); + + // drop the seconds to 0 + $currentTime = DateTime::createFromFormat('Y-m-d H:i', $currentTime->format('Y-m-d H:i')); + + try { + return $this->getNextRunDate($currentTime, 0, true)->getTimestamp() === $currentTime->getTimestamp(); + } catch (Exception $e) { + return false; + } + } + + /** + * Get the next or previous run date of the expression relative to a date + * + * @param string|\DateTimeInterface $currentTime Relative calculation date + * @param int $nth Number of matches to skip before returning + * @param bool $invert Set to TRUE to go backwards in time + * @param bool $allowCurrentDate Set to TRUE to return the + * current date if it matches the cron expression + * @param string|null $timeZone TimeZone to use instead of the system default + * + * @return \DateTime + * @throws \RuntimeException on too many iterations + */ + protected function getRunDate($currentTime = null, $nth = 0, $invert = false, $allowCurrentDate = false, $timeZone = null) + { + $timeZone = $this->determineTimeZone($currentTime, $timeZone); + + if ($currentTime instanceof DateTime) { + $currentDate = clone $currentTime; + } elseif ($currentTime instanceof DateTimeImmutable) { + $currentDate = DateTime::createFromFormat('U', $currentTime->format('U')); + } else { + $currentDate = new DateTime($currentTime ?: 'now'); + } + + $currentDate->setTimeZone(new DateTimeZone($timeZone)); + $currentDate->setTime($currentDate->format('H'), $currentDate->format('i'), 0); + $nextRun = clone $currentDate; + $nth = (int) $nth; + + // We don't have to satisfy * or null fields + $parts = array(); + $fields = array(); + foreach (self::$order as $position) { + $part = $this->getExpression($position); + if (null === $part || '*' === $part) { + continue; + } + $parts[$position] = $part; + $fields[$position] = $this->fieldFactory->getField($position); + } + + // Set a hard limit to bail on an impossible date + for ($i = 0; $i < $this->maxIterationCount; $i++) { + + foreach ($parts as $position => $part) { + $satisfied = false; + // Get the field object used to validate this part + $field = $fields[$position]; + // Check if this is singular or a list + if (strpos($part, ',') === false) { + $satisfied = $field->isSatisfiedBy($nextRun, $part); + } else { + foreach (array_map('trim', explode(',', $part)) as $listPart) { + if ($field->isSatisfiedBy($nextRun, $listPart)) { + $satisfied = true; + break; + } + } + } + + // If the field is not satisfied, then start over + if (!$satisfied) { + $field->increment($nextRun, $invert, $part); + continue 2; + } + } + + // Skip this match if needed + if ((!$allowCurrentDate && $nextRun == $currentDate) || --$nth > -1) { + $this->fieldFactory->getField(0)->increment($nextRun, $invert, isset($parts[0]) ? $parts[0] : null); + continue; + } + + return $nextRun; + } + + // @codeCoverageIgnoreStart + throw new RuntimeException('Impossible CRON expression'); + // @codeCoverageIgnoreEnd + } + + /** + * Workout what timeZone should be used. + * + * @param string|\DateTimeInterface $currentTime Relative calculation date + * @param string|null $timeZone TimeZone to use instead of the system default + * + * @return string + */ + protected function determineTimeZone($currentTime, $timeZone) + { + if (! is_null($timeZone)) { + return $timeZone; + } + + if ($currentTime instanceOf DateTimeInterface) { + return $currentTime->getTimeZone()->getName(); + } + + return date_default_timezone_get(); + } +} diff --git a/vendor/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php b/vendor/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php new file mode 100644 index 0000000..d4552e0 --- /dev/null +++ b/vendor/dragonmantank/cron-expression/src/Cron/DayOfMonthField.php @@ -0,0 +1,145 @@ + + */ +class DayOfMonthField extends AbstractField +{ + /** + * @inheritDoc + */ + protected $rangeStart = 1; + + /** + * @inheritDoc + */ + protected $rangeEnd = 31; + + /** + * Get the nearest day of the week for a given day in a month + * + * @param int $currentYear Current year + * @param int $currentMonth Current month + * @param int $targetDay Target day of the month + * + * @return \DateTime Returns the nearest date + */ + private static function getNearestWeekday($currentYear, $currentMonth, $targetDay) + { + $tday = str_pad($targetDay, 2, '0', STR_PAD_LEFT); + $target = DateTime::createFromFormat('Y-m-d', "$currentYear-$currentMonth-$tday"); + $currentWeekday = (int) $target->format('N'); + + if ($currentWeekday < 6) { + return $target; + } + + $lastDayOfMonth = $target->format('t'); + + foreach (array(-1, 1, -2, 2) as $i) { + $adjusted = $targetDay + $i; + if ($adjusted > 0 && $adjusted <= $lastDayOfMonth) { + $target->setDate($currentYear, $currentMonth, $adjusted); + if ($target->format('N') < 6 && $target->format('m') == $currentMonth) { + return $target; + } + } + } + } + + /** + * @inheritDoc + */ + public function isSatisfiedBy(DateTimeInterface $date, $value) + { + // ? states that the field value is to be skipped + if ($value == '?') { + return true; + } + + $fieldValue = $date->format('d'); + + // Check to see if this is the last day of the month + if ($value == 'L') { + return $fieldValue == $date->format('t'); + } + + // Check to see if this is the nearest weekday to a particular value + if (strpos($value, 'W')) { + // Parse the target day + $targetDay = substr($value, 0, strpos($value, 'W')); + // Find out if the current day is the nearest day of the week + return $date->format('j') == self::getNearestWeekday( + $date->format('Y'), + $date->format('m'), + $targetDay + )->format('j'); + } + + return $this->isSatisfied($date->format('d'), $value); + } + + /** + * @inheritDoc + * + * @param \DateTime|\DateTimeImmutable &$date + */ + public function increment(DateTimeInterface &$date, $invert = false) + { + if ($invert) { + $date = $date->modify('previous day')->setTime(23, 59); + } else { + $date = $date->modify('next day')->setTime(0, 0); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function validate($value) + { + $basicChecks = parent::validate($value); + + // Validate that a list don't have W or L + if (strpos($value, ',') !== false && (strpos($value, 'W') !== false || strpos($value, 'L') !== false)) { + return false; + } + + if (!$basicChecks) { + + if ($value === 'L') { + return true; + } + + if (preg_match('/^(.*)W$/', $value, $matches)) { + return $this->validate($matches[1]); + } + + return false; + } + + return $basicChecks; + } +} diff --git a/vendor/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php b/vendor/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php new file mode 100644 index 0000000..d4ba315 --- /dev/null +++ b/vendor/dragonmantank/cron-expression/src/Cron/DayOfWeekField.php @@ -0,0 +1,196 @@ + 'MON', 2 => 'TUE', 3 => 'WED', 4 => 'THU', 5 => 'FRI', 6 => 'SAT', 7 => 'SUN']; + + /** + * Constructor + */ + public function __construct() + { + $this->nthRange = range(1, 5); + parent::__construct(); + } + + /** + * @inheritDoc + * + * @param \DateTime|\DateTimeImmutable $date + */ + public function isSatisfiedBy(DateTimeInterface $date, $value) + { + if ($value == '?') { + return true; + } + + // Convert text day of the week values to integers + $value = $this->convertLiterals($value); + + $currentYear = $date->format('Y'); + $currentMonth = $date->format('m'); + $lastDayOfMonth = $date->format('t'); + + // Find out if this is the last specific weekday of the month + if (strpos($value, 'L')) { + $weekday = (int) $this->convertLiterals(substr($value, 0, strpos($value, 'L'))); + $weekday %= 7; + + $tdate = clone $date; + $tdate = $tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth); + while ($tdate->format('w') != $weekday) { + $tdateClone = new DateTime(); + $tdate = $tdateClone + ->setTimezone($tdate->getTimezone()) + ->setDate($currentYear, $currentMonth, --$lastDayOfMonth); + } + + return $date->format('j') == $lastDayOfMonth; + } + + // Handle # hash tokens + if (strpos($value, '#')) { + list($weekday, $nth) = explode('#', $value); + + if (!is_numeric($nth)) { + throw new InvalidArgumentException("Hashed weekdays must be numeric, {$nth} given"); + } else { + $nth = (int) $nth; + } + + // 0 and 7 are both Sunday, however 7 matches date('N') format ISO-8601 + if ($weekday === '0') { + $weekday = 7; + } + + $weekday = $this->convertLiterals($weekday); + + // Validate the hash fields + if ($weekday < 0 || $weekday > 7) { + throw new InvalidArgumentException("Weekday must be a value between 0 and 7. {$weekday} given"); + } + + if (!in_array($nth, $this->nthRange)) { + throw new InvalidArgumentException("There are never more than 5 or less than 1 of a given weekday in a month, {$nth} given"); + } + + // The current weekday must match the targeted weekday to proceed + if ($date->format('N') != $weekday) { + return false; + } + + $tdate = clone $date; + $tdate = $tdate->setDate($currentYear, $currentMonth, 1); + $dayCount = 0; + $currentDay = 1; + while ($currentDay < $lastDayOfMonth + 1) { + if ($tdate->format('N') == $weekday) { + if (++$dayCount >= $nth) { + break; + } + } + $tdate = $tdate->setDate($currentYear, $currentMonth, ++$currentDay); + } + + return $date->format('j') == $currentDay; + } + + // Handle day of the week values + if (strpos($value, '-')) { + $parts = explode('-', $value); + if ($parts[0] == '7') { + $parts[0] = '0'; + } elseif ($parts[1] == '0') { + $parts[1] = '7'; + } + $value = implode('-', $parts); + } + + // Test to see which Sunday to use -- 0 == 7 == Sunday + $format = in_array(7, str_split($value)) ? 'N' : 'w'; + $fieldValue = $date->format($format); + + return $this->isSatisfied($fieldValue, $value); + } + + /** + * @inheritDoc + * + * @param \DateTime|\DateTimeImmutable &$date + */ + public function increment(DateTimeInterface &$date, $invert = false) + { + if ($invert) { + $date = $date->modify('-1 day')->setTime(23, 59, 0); + } else { + $date = $date->modify('+1 day')->setTime(0, 0, 0); + } + + return $this; + } + + /** + * @inheritDoc + */ + public function validate($value) + { + $basicChecks = parent::validate($value); + + if (!$basicChecks) { + // Handle the # value + if (strpos($value, '#') !== false) { + $chunks = explode('#', $value); + $chunks[0] = $this->convertLiterals($chunks[0]); + + if (parent::validate($chunks[0]) && is_numeric($chunks[1]) && in_array($chunks[1], $this->nthRange)) { + return true; + } + } + + if (preg_match('/^(.*)L$/', $value, $matches)) { + return $this->validate($matches[1]); + } + + return false; + } + + return $basicChecks; + } +} diff --git a/vendor/dragonmantank/cron-expression/src/Cron/FieldFactory.php b/vendor/dragonmantank/cron-expression/src/Cron/FieldFactory.php new file mode 100644 index 0000000..545e4b8 --- /dev/null +++ b/vendor/dragonmantank/cron-expression/src/Cron/FieldFactory.php @@ -0,0 +1,54 @@ +fields[$position])) { + switch ($position) { + case 0: + $this->fields[$position] = new MinutesField(); + break; + case 1: + $this->fields[$position] = new HoursField(); + break; + case 2: + $this->fields[$position] = new DayOfMonthField(); + break; + case 3: + $this->fields[$position] = new MonthField(); + break; + case 4: + $this->fields[$position] = new DayOfWeekField(); + break; + default: + throw new InvalidArgumentException( + ($position + 1) . ' is not a valid position' + ); + } + } + + return $this->fields[$position]; + } +} diff --git a/vendor/dragonmantank/cron-expression/src/Cron/FieldInterface.php b/vendor/dragonmantank/cron-expression/src/Cron/FieldInterface.php new file mode 100644 index 0000000..f8366ea --- /dev/null +++ b/vendor/dragonmantank/cron-expression/src/Cron/FieldInterface.php @@ -0,0 +1,41 @@ +isSatisfied($date->format('H'), $value); + } + + /** + * {@inheritDoc} + * + * @param \DateTime|\DateTimeImmutable &$date + * @param string|null $parts + */ + public function increment(DateTimeInterface &$date, $invert = false, $parts = null) + { + // Change timezone to UTC temporarily. This will + // allow us to go back or forwards and hour even + // if DST will be changed between the hours. + if (is_null($parts) || $parts == '*') { + $timezone = $date->getTimezone(); + $date = $date->setTimezone(new DateTimeZone('UTC')); + $date = $date->modify(($invert ? '-' : '+') . '1 hour'); + $date = $date->setTimezone($timezone); + + $date = $date->setTime($date->format('H'), $invert ? 59 : 0); + return $this; + } + + $parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts); + $hours = array(); + foreach ($parts as $part) { + $hours = array_merge($hours, $this->getRangeForExpression($part, 23)); + } + + $current_hour = $date->format('H'); + $position = $invert ? count($hours) - 1 : 0; + if (count($hours) > 1) { + for ($i = 0; $i < count($hours) - 1; $i++) { + if ((!$invert && $current_hour >= $hours[$i] && $current_hour < $hours[$i + 1]) || + ($invert && $current_hour > $hours[$i] && $current_hour <= $hours[$i + 1])) { + $position = $invert ? $i : $i + 1; + break; + } + } + } + + $hour = $hours[$position]; + if ((!$invert && $date->format('H') >= $hour) || ($invert && $date->format('H') <= $hour)) { + $date = $date->modify(($invert ? '-' : '+') . '1 day'); + $date = $date->setTime($invert ? 23 : 0, $invert ? 59 : 0); + } + else { + $date = $date->setTime($hour, $invert ? 59 : 0); + } + + return $this; + } +} diff --git a/vendor/dragonmantank/cron-expression/src/Cron/MinutesField.php b/vendor/dragonmantank/cron-expression/src/Cron/MinutesField.php new file mode 100644 index 0000000..fecc9b6 --- /dev/null +++ b/vendor/dragonmantank/cron-expression/src/Cron/MinutesField.php @@ -0,0 +1,75 @@ +isSatisfied($date->format('i'), $value); + } + + /** + * {@inheritDoc} + * + * @param \DateTime|\DateTimeImmutable &$date + * @param string|null $parts + */ + public function increment(DateTimeInterface &$date, $invert = false, $parts = null) + { + if (is_null($parts)) { + $date = $date->modify(($invert ? '-' : '+') . '1 minute'); + return $this; + } + + $parts = strpos($parts, ',') !== false ? explode(',', $parts) : array($parts); + $minutes = array(); + foreach ($parts as $part) { + $minutes = array_merge($minutes, $this->getRangeForExpression($part, 59)); + } + + $current_minute = $date->format('i'); + $position = $invert ? count($minutes) - 1 : 0; + if (count($minutes) > 1) { + for ($i = 0; $i < count($minutes) - 1; $i++) { + if ((!$invert && $current_minute >= $minutes[$i] && $current_minute < $minutes[$i + 1]) || + ($invert && $current_minute > $minutes[$i] && $current_minute <= $minutes[$i + 1])) { + $position = $invert ? $i : $i + 1; + break; + } + } + } + + if ((!$invert && $current_minute >= $minutes[$position]) || ($invert && $current_minute <= $minutes[$position])) { + $date = $date->modify(($invert ? '-' : '+') . '1 hour'); + $date = $date->setTime($date->format('H'), $invert ? 59 : 0); + } + else { + $date = $date->setTime($date->format('H'), $minutes[$position]); + } + + return $this; + } +} diff --git a/vendor/dragonmantank/cron-expression/src/Cron/MonthField.php b/vendor/dragonmantank/cron-expression/src/Cron/MonthField.php new file mode 100644 index 0000000..afc9caf --- /dev/null +++ b/vendor/dragonmantank/cron-expression/src/Cron/MonthField.php @@ -0,0 +1,59 @@ + 'JAN', 2 => 'FEB', 3 => 'MAR', 4 => 'APR', 5 => 'MAY', 6 => 'JUN', 7 => 'JUL', + 8 => 'AUG', 9 => 'SEP', 10 => 'OCT', 11 => 'NOV', 12 => 'DEC']; + + /** + * @inheritDoc + */ + public function isSatisfiedBy(DateTimeInterface $date, $value) + { + if ($value == '?') { + return true; + } + + $value = $this->convertLiterals($value); + + return $this->isSatisfied($date->format('m'), $value); + } + + /** + * @inheritDoc + * + * @param \DateTime|\DateTimeImmutable &$date + */ + public function increment(DateTimeInterface &$date, $invert = false) + { + if ($invert) { + $date = $date->modify('last day of previous month')->setTime(23, 59); + } else { + $date = $date->modify('first day of next month')->setTime(0, 0); + } + + return $this; + } + + +} -- cgit v1.2.3