update v1.0.6

This commit is contained in:
sujitprasad
2016-02-16 22:42:08 +05:30
parent e6b579d67b
commit 073a49a8af
587 changed files with 21487 additions and 22766 deletions

View File

@@ -68,15 +68,4 @@ Requirements
- PHP 5.3+
- PHPUnit is required to run the unit tests
- Composer is required to run the unit tests
CHANGELOG
=========
1.0.3 (2013-11-23)
------------------
* Only set default timezone if the given $currentTime is not a DateTime instance ([#34](https://github.com/mtdowling/cron-expression/issues/34))
* Fixes issue [#28](https://github.com/mtdowling/cron-expression/issues/28) where PHP increments of ranges were failing due to PHP casting hyphens to 0
* Now supports expressions with any number of extra spaces, tabs, or newlines
* Using static instead of self in `CronExpression::factory`
- Composer is required to run the unit tests

View File

@@ -13,7 +13,7 @@
"php": ">=5.3.2"
},
"require-dev": {
"phpunit/phpunit": "4.*"
"phpunit/phpunit": "~4.0|~5.0"
},
"autoload": {
"psr-0": {

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="Cron">
<directory>./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/Cron</directory>
</whitelist>
</filter>
</phpunit>

View File

@@ -101,4 +101,43 @@ abstract class AbstractField implements FieldInterface
return false;
}
/**
* 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();
if ($this->isRange($expression) || $this->isIncrementsOfRanges($expression)) {
if (!$this->isIncrementsOfRanges($expression)) {
list ($offset, $to) = explode('-', $expression);
$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 == '*' ? 0 : $offset;
for ($i = $offset; $i <= $to; $i += $stepSize) {
$values[] = $i;
}
sort($values);
}
else {
$values = array($expression);
}
return $values;
}
}

View File

@@ -33,6 +33,11 @@ class CronExpression
*/
private $fieldFactory;
/**
* @var int Max iteration count when searching for next run date
*/
private $maxIterationCount = 1000;
/**
* @var array Order in which to test of cron parts
*/
@@ -72,6 +77,25 @@ class CronExpression
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
*
@@ -121,7 +145,7 @@ class CronExpression
{
if (!$this->fieldFactory->getField($position)->validate($value)) {
throw new \InvalidArgumentException(
'Invalid CRON field value ' . $value . ' as position ' . $position
'Invalid CRON field value ' . $value . ' at position ' . $position
);
}
@@ -130,6 +154,20 @@ class CronExpression
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
*
@@ -183,7 +221,11 @@ class CronExpression
{
$matches = array();
for ($i = 0; $i < max(0, $total); $i++) {
$matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate);
try {
$matches[] = $this->getRunDate($currentTime, $i, $invert, $allowCurrentDate);
} catch (\RuntimeException $e) {
break;
}
}
return $matches;
@@ -239,6 +281,11 @@ class CronExpression
$currentDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
$currentDate = $currentDate->format('Y-m-d H:i');
$currentTime = strtotime($currentDate);
} elseif ($currentTime instanceof \DateTimeImmutable) {
$currentDate = \DateTime::createFromFormat('U', $currentTime->format('U'));
$currentDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
$currentDate = $currentDate->format('Y-m-d H:i');
$currentTime = strtotime($currentDate);
} else {
$currentTime = new \DateTime($currentTime);
$currentTime->setTime($currentTime->format('H'), $currentTime->format('i'), 0);
@@ -269,6 +316,9 @@ class CronExpression
{
if ($currentTime instanceof \DateTime) {
$currentDate = clone $currentTime;
} elseif ($currentTime instanceof \DateTimeImmutable) {
$currentDate = \DateTime::createFromFormat('U', $currentTime->format('U'));
$currentDate->setTimezone($currentTime->getTimezone());
} else {
$currentDate = new \DateTime($currentTime ?: 'now');
$currentDate->setTimezone(new \DateTimeZone(date_default_timezone_get()));
@@ -291,7 +341,7 @@ class CronExpression
}
// Set a hard limit to bail on an impossible date
for ($i = 0; $i < 1000; $i++) {
for ($i = 0; $i < $this->maxIterationCount; $i++) {
foreach ($parts as $position => $part) {
$satisfied = false;
@@ -311,14 +361,14 @@ class CronExpression
// If the field is not satisfied, then start over
if (!$satisfied) {
$field->increment($nextRun, $invert);
$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);
$this->fieldFactory->getField(0)->increment($nextRun, $invert, isset($parts[0]) ? $parts[0] : null);
continue;
}

View File

@@ -36,7 +36,10 @@ class DayOfWeekField extends AbstractField
$tdate = clone $date;
$tdate->setDate($currentYear, $currentMonth, $lastDayOfMonth);
while ($tdate->format('w') != $weekday) {
$tdate->setDate($currentYear, $currentMonth, --$lastDayOfMonth);
$tdateClone = new \DateTime();
$tdate = $tdateClone
->setTimezone($tdate->getTimezone())
->setDate($currentYear, $currentMonth, --$lastDayOfMonth);
}
return $date->format('j') == $lastDayOfMonth;

View File

@@ -12,21 +12,51 @@ class HoursField extends AbstractField
return $this->isSatisfied($date->format('H'), $value);
}
public function increment(\DateTime $date, $invert = false)
public function increment(\DateTime $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.
$timezone = $date->getTimezone();
$date->setTimezone(new \DateTimeZone('UTC'));
if ($invert) {
$date->modify('-1 hour');
$date->setTime($date->format('H'), 59);
} else {
$date->modify('+1 hour');
$date->setTime($date->format('H'), 0);
if (is_null($parts) || $parts == '*') {
$timezone = $date->getTimezone();
$date->setTimezone(new \DateTimeZone('UTC'));
if ($invert) {
$date->modify('-1 hour');
} else {
$date->modify('+1 hour');
}
$date->setTimezone($timezone);
$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->modify(($invert ? '-' : '+') . '1 day');
$date->setTime($invert ? 23 : 0, $invert ? 59 : 0);
}
else {
$date->setTime($hour, $invert ? 59 : 0);
}
$date->setTimezone($timezone);
return $this;
}

View File

@@ -12,12 +12,41 @@ class MinutesField extends AbstractField
return $this->isSatisfied($date->format('i'), $value);
}
public function increment(\DateTime $date, $invert = false)
public function increment(\DateTime $date, $invert = false, $parts = null)
{
if ($invert) {
$date->modify('-1 minute');
} else {
$date->modify('+1 minute');
if (is_null($parts)) {
if ($invert) {
$date->modify('-1 minute');
} else {
$date->modify('+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->modify(($invert ? '-' : '+') . '1 hour');
$date->setTime($date->format('H'), $invert ? 59 : 0);
}
else {
$date->setTime($date->format('H'), $minutes[$position]);
}
return $this;

View File

@@ -277,6 +277,27 @@ class CronExpressionTest extends \PHPUnit_Framework_TestCase
), $cron->getMultipleRunDates(4, '2008-11-09 00:00:00', false, true));
}
/**
* @covers Cron\CronExpression::getMultipleRunDates
* @covers Cron\CronExpression::setMaxIterationCount
*/
public function testProvidesMultipleRunDatesForTheFarFuture() {
// Fails with the default 1000 iteration limit
$cron = CronExpression::factory('0 0 12 1 * */2');
$cron->setMaxIterationCount(2000);
$this->assertEquals(array(
new DateTime('2016-01-12 00:00:00'),
new DateTime('2018-01-12 00:00:00'),
new DateTime('2020-01-12 00:00:00'),
new DateTime('2022-01-12 00:00:00'),
new DateTime('2024-01-12 00:00:00'),
new DateTime('2026-01-12 00:00:00'),
new DateTime('2028-01-12 00:00:00'),
new DateTime('2030-01-12 00:00:00'),
new DateTime('2032-01-12 00:00:00'),
), $cron->getMultipleRunDates(9, '2015-04-28 00:00:00', false, true));
}
/**
* @covers Cron\CronExpression
*/
@@ -373,4 +394,19 @@ class CronExpressionTest extends \PHPUnit_Framework_TestCase
$cron->getPreviousRunDate($now);
$this->assertEquals($strNow, $now->format(\DateTime::ISO8601));
}
/**
* @covers Cron\CronExpression::__construct
* @covers Cron\CronExpression::factory
* @covers Cron\CronExpression::isValidExpression
* @covers Cron\CronExpression::setExpression
* @covers Cron\CronExpression::setPart
*/
public function testValidationWorks()
{
// Invalid. Only four values
$this->assertFalse(CronExpression::isValidExpression('* * * 1'));
// Valid
$this->assertTrue(CronExpression::isValidExpression('* * * * 1'));
}
}

View File

@@ -35,4 +35,40 @@ class HoursFieldTest extends \PHPUnit_Framework_TestCase
$f->increment($d, true);
$this->assertEquals('2011-03-15 10:59:00', $d->format('Y-m-d H:i:s'));
}
/**
* @covers Cron\HoursField::increment
*/
public function testIncrementsDateWithThirtyMinuteOffsetTimezone()
{
$tz = date_default_timezone_get();
date_default_timezone_set('America/St_Johns');
$d = new DateTime('2011-03-15 11:15:00');
$f = new HoursField();
$f->increment($d);
$this->assertEquals('2011-03-15 12:00:00', $d->format('Y-m-d H:i:s'));
$d->setTime(11, 15, 0);
$f->increment($d, true);
$this->assertEquals('2011-03-15 10:59:00', $d->format('Y-m-d H:i:s'));
date_default_timezone_set($tz);
}
/**
* @covers Cron\HoursField::increment
*/
public function testIncrementDateWithFifteenMinuteOffsetTimezone()
{
$tz = date_default_timezone_get();
date_default_timezone_set('Asia/Kathmandu');
$d = new DateTime('2011-03-15 11:15:00');
$f = new HoursField();
$f->increment($d);
$this->assertEquals('2011-03-15 12:00:00', $d->format('Y-m-d H:i:s'));
$d->setTime(11, 15, 0);
$f->increment($d, true);
$this->assertEquals('2011-03-15 10:59:00', $d->format('Y-m-d H:i:s'));
date_default_timezone_set($tz);
}
}

View File

@@ -37,6 +37,25 @@ class MonthFieldTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('2011-02-28 23:59:00', $d->format('Y-m-d H:i:s'));
}
/**
* @covers Cron\MonthField::increment
*/
public function testIncrementsDateWithThirtyMinuteTimezone()
{
$tz = date_default_timezone_get();
date_default_timezone_set('America/St_Johns');
$d = new DateTime('2011-03-31 11:59:59');
$f = new MonthField();
$f->increment($d);
$this->assertEquals('2011-04-01 00:00:00', $d->format('Y-m-d H:i:s'));
$d = new DateTime('2011-03-15 11:15:00');
$f->increment($d, true);
$this->assertEquals('2011-02-28 23:59:00', $d->format('Y-m-d H:i:s'));
date_default_timezone_set($tz);
}
/**
* @covers Cron\MonthField::increment
*/