mirror of
https://github.com/rlanvin/php-rrule.git
synced 2025-02-22 11:54:14 +01:00
Fix #25
- Fix parser handling of UNTIL when DTSTART is not provided - Accept invalid RFC strings generated by the JS lib but triggers a Notice message
This commit is contained in:
parent
26d147b729
commit
0b3a2c9a32
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
- `RRule::offsetGet` and `RSet::offsetGet` throw `InvalidArgumentException` for illegal offset types [#22](https://github.com/rlanvin/php-rrule/issues/22)
|
- `RRule::offsetGet` and `RSet::offsetGet` throw `InvalidArgumentException` for illegal offset types [#22](https://github.com/rlanvin/php-rrule/issues/22)
|
||||||
- Update exception message for UNTIL parse error [#23](https://github.com/rlanvin/php-rrule/pull/23)
|
- Update exception message for UNTIL parse error [#23](https://github.com/rlanvin/php-rrule/pull/23)
|
||||||
|
- Fix parser handling of UNTIL when DTSTART is not provided [#25](https://github.com/rlanvin/php-rrule/issues/25)
|
||||||
|
- Accept invalid RFC strings generated by the JS lib but triggers a Notice message [#25](https://github.com/rlanvin/php-rrule/issues/25)
|
||||||
|
|
||||||
## [1.4.0] - 2016-11-11
|
## [1.4.0] - 2016-11-11
|
||||||
|
|
||||||
|
@ -103,7 +103,8 @@ class RRule implements RRuleInterface
|
|||||||
* Frequency names.
|
* Frequency names.
|
||||||
* Used internally for conversion but public if a reference list is needed.
|
* Used internally for conversion but public if a reference list is needed.
|
||||||
*
|
*
|
||||||
* @todo should probably be protected, with a static getter instead.
|
* @todo should probably be protected, with a static getter instead to avoid
|
||||||
|
* unintended modification.
|
||||||
*
|
*
|
||||||
* @var array The name as the key
|
* @var array The name as the key
|
||||||
*/
|
*/
|
||||||
@ -121,7 +122,8 @@ class RRule implements RRuleInterface
|
|||||||
* Weekdays numbered from 1 (ISO-8601 or `date('N')`).
|
* Weekdays numbered from 1 (ISO-8601 or `date('N')`).
|
||||||
* Used internally but public if a reference list is needed.
|
* Used internally but public if a reference list is needed.
|
||||||
*
|
*
|
||||||
* @todo should probably be protected, with a static getter instead.
|
* @todo should probably be protected, with a static getter instead
|
||||||
|
* to avoid unintended modification
|
||||||
*
|
*
|
||||||
* @var array The name as the key
|
* @var array The name as the key
|
||||||
*/
|
*/
|
||||||
@ -653,9 +655,9 @@ class RRule implements RRuleInterface
|
|||||||
{
|
{
|
||||||
$parts = array();
|
$parts = array();
|
||||||
|
|
||||||
$string = trim($string);
|
$string = strtoupper(trim($string));
|
||||||
|
|
||||||
$dtstart_type = 'date';
|
$dtstart_type = null;
|
||||||
$rfc_date_regexp = '/\d{6}(T\d{6})?Z?/'; // a bit loose
|
$rfc_date_regexp = '/\d{6}(T\d{6})?Z?/'; // a bit loose
|
||||||
|
|
||||||
foreach ( explode("\n", $string) as $line ) {
|
foreach ( explode("\n", $string) as $line ) {
|
||||||
@ -681,7 +683,9 @@ class RRule implements RRuleInterface
|
|||||||
|
|
||||||
switch ( $property_name ) {
|
switch ( $property_name ) {
|
||||||
case 'DTSTART':
|
case 'DTSTART':
|
||||||
|
case 'dtstart':
|
||||||
$tmp = null;
|
$tmp = null;
|
||||||
|
$dtstart_type = 'date';
|
||||||
if ( ! preg_match($rfc_date_regexp, $property_value) ) {
|
if ( ! preg_match($rfc_date_regexp, $property_value) ) {
|
||||||
throw new \InvalidArgumentException(
|
throw new \InvalidArgumentException(
|
||||||
'Invalid DTSTART property: date or date time format incorrect'
|
'Invalid DTSTART property: date or date time format incorrect'
|
||||||
@ -715,6 +719,7 @@ class RRule implements RRuleInterface
|
|||||||
$parts['DTSTART'] = new \DateTime($property_value, $tmp);
|
$parts['DTSTART'] = new \DateTime($property_value, $tmp);
|
||||||
break;
|
break;
|
||||||
case 'RRULE':
|
case 'RRULE':
|
||||||
|
case 'rrule':
|
||||||
foreach ( explode(';',$property_value) as $pair ) {
|
foreach ( explode(';',$property_value) as $pair ) {
|
||||||
$pair = explode('=', $pair);
|
$pair = explode('=', $pair);
|
||||||
if ( ! isset($pair[1]) || isset($pair[2]) ) {
|
if ( ! isset($pair[1]) || isset($pair[2]) ) {
|
||||||
@ -754,6 +759,14 @@ class RRule implements RRuleInterface
|
|||||||
|
|
||||||
$value = new \DateTime($value);
|
$value = new \DateTime($value);
|
||||||
}
|
}
|
||||||
|
elseif ( $key === 'DTSTART' ) {
|
||||||
|
if ( isset($parts['DTSTART']) ) {
|
||||||
|
throw new \InvalidArgumentException('DTSTART cannot be part of RRULE and has already been defined');
|
||||||
|
}
|
||||||
|
// this is an invalid rule, however we'll support it since the JS lib is broken
|
||||||
|
// see https://github.com/rlanvin/php-rrule/issues/25
|
||||||
|
trigger_error("This string is not compliant with the RFC (DTSTART cannot be part of RRULE). It is accepted as is for compability reasons only.", E_USER_NOTICE);
|
||||||
|
}
|
||||||
$parts[$key] = $value;
|
$parts[$key] = $value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1721,47 +1721,188 @@ class RRuleTest extends PHPUnit_Framework_TestCase
|
|||||||
public function rfcStrings()
|
public function rfcStrings()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
|
// full RFC string
|
||||||
array('DTSTART;TZID=America/New_York:19970901T090000
|
array('DTSTART;TZID=America/New_York:19970901T090000
|
||||||
RRULE:FREQ=HOURLY;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTH=1;BYHOUR=1'),
|
RRULE:FREQ=HOURLY;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTH=1;BYHOUR=1',
|
||||||
|
null // todo
|
||||||
|
),
|
||||||
array('DTSTART;TZID=America/New_York:19970901T090000
|
array('DTSTART;TZID=America/New_York:19970901T090000
|
||||||
RRULE:FREQ=DAILY;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTH=1'),
|
RRULE:FREQ=DAILY;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTH=1',
|
||||||
|
null // todo
|
||||||
|
),
|
||||||
array('DTSTART;TZID=America/New_York:19970901T090000
|
array('DTSTART;TZID=America/New_York:19970901T090000
|
||||||
RRULE:FREQ=DAILY;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTH=1;BYHOUR=12;BYMINUTE=15,30'),
|
RRULE:FREQ=DAILY;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTH=1;BYHOUR=12;BYMINUTE=15,30',
|
||||||
|
null
|
||||||
|
),
|
||||||
array('DTSTART;TZID=America/New_York:19970901T090000
|
array('DTSTART;TZID=America/New_York:19970901T090000
|
||||||
RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR'),
|
RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR',
|
||||||
|
null // todo
|
||||||
|
),
|
||||||
array('DTSTART;TZID=America/New_York:19970901T090000
|
array('DTSTART;TZID=America/New_York:19970901T090000
|
||||||
RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR'),
|
RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR',
|
||||||
|
null // todo
|
||||||
|
),
|
||||||
array('DTSTART;TZID=America/New_York:19970901T090000
|
array('DTSTART;TZID=America/New_York:19970901T090000
|
||||||
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTH=1'),
|
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTH=1',
|
||||||
|
array()
|
||||||
|
),
|
||||||
array('DTSTART;TZID=America/New_York:19970901T090000
|
array('DTSTART;TZID=America/New_York:19970901T090000
|
||||||
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTHDAY=1,2,5,31,-1,-3,-15'),
|
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTHDAY=1,2,5,31,-1,-3,-15',
|
||||||
|
array(
|
||||||
|
date_create('1997-09-01 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-09-05 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-10-01 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-10-17 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-10-29 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-10-31 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-11-05 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-11-28 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-12-01 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-12-05 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-12-17 09:00:00', new DateTimeZone('America/New_York'))
|
||||||
|
)
|
||||||
|
),
|
||||||
array('DTSTART;TZID=America/New_York:19970901T090000
|
array('DTSTART;TZID=America/New_York:19970901T090000
|
||||||
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTHDAY=1,2,5,31,-1,-3,-15;BYSETPOS=-1'),
|
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTHDAY=1,2,5,31,-1,-3,-15;BYSETPOS=-1',
|
||||||
|
array(
|
||||||
|
date_create('1997-09-05 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-10-31 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-11-28 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
)
|
||||||
|
),
|
||||||
array('DTSTART;TZID=America/New_York:19970901T090000
|
array('DTSTART;TZID=America/New_York:19970901T090000
|
||||||
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTHDAY=1,2,5,31,-1,-3,-15;BYSETPOS=-1,1'),
|
RRULE:FREQ=MONTHLY;INTERVAL=1;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR;BYMONTHDAY=1,2,5,31,-1,-3,-15;BYSETPOS=-1,1',
|
||||||
|
array(
|
||||||
|
date_create('1997-09-01 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-09-05 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-10-01 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-10-31 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-11-05 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-11-28 09:00:00', new DateTimeZone('America/New_York')),
|
||||||
|
date_create('1997-12-01 09:00:00', new DateTimeZone('America/New_York'))
|
||||||
|
)
|
||||||
|
),
|
||||||
array(' DTSTART;TZID=America/New_York:19970512T090000
|
array(' DTSTART;TZID=America/New_York:19970512T090000
|
||||||
RRULE:FREQ=YEARLY;BYWEEKNO=20,30,40;BYDAY=MO'),
|
RRULE:FREQ=YEARLY;BYWEEKNO=20,30,40;BYDAY=MO',
|
||||||
|
null
|
||||||
|
),
|
||||||
array('DTSTART;TZID=America/New_York:19970512T090000
|
array('DTSTART;TZID=America/New_York:19970512T090000
|
||||||
RRULE:FREQ=YEARLY;BYYEARDAY=1,-1,10,-50;BYDAY=MO'),
|
RRULE:FREQ=YEARLY;BYYEARDAY=1,-1,10,-50;BYDAY=MO',
|
||||||
|
null
|
||||||
|
),
|
||||||
array('DTSTART:19970512T090000Z
|
array('DTSTART:19970512T090000Z
|
||||||
RRULE:FREQ=YEARLY'),
|
RRULE:FREQ=YEARLY',
|
||||||
|
null
|
||||||
|
),
|
||||||
array('DTSTART:19970512T090000
|
array('DTSTART:19970512T090000
|
||||||
RRULE:FREQ=YEARLY'),
|
RRULE:FREQ=YEARLY',
|
||||||
|
null
|
||||||
|
),
|
||||||
array('DTSTART:19970512
|
array('DTSTART:19970512
|
||||||
RRULE:FREQ=YEARLY'),
|
RRULE:FREQ=YEARLY',
|
||||||
|
null
|
||||||
|
),
|
||||||
|
|
||||||
|
// case insensitive
|
||||||
|
array("dtstart:19970902T090000\nrrule:freq=yearly;count=3",
|
||||||
|
array(date_create('1997-09-02 09:00:00'),date_create('1998-09-02 09:00:00'),date_create('1999-09-02 09:00:00'))
|
||||||
|
),
|
||||||
|
|
||||||
|
// empty lines
|
||||||
|
array("\nDTSTART:19970512\nRRULE:FREQ=YEARLY;COUNT=3\n\n",
|
||||||
|
array(date_create('1997-05-12'),date_create('1998-05-12'),date_create('1999-05-12'))
|
||||||
|
),
|
||||||
|
|
||||||
|
// no DTSTART
|
||||||
|
array("RRULE:FREQ=YEARLY;COUNT=3",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
array("RRULE:FREQ=YEARLY;UNTIL=20170202",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
array("RRULE:FREQ=YEARLY;UNTIL=20170202T090000",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
array("RRULE:FREQ=YEARLY;UNTIL=20170202T090000Z",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
|
||||||
|
// just the RRULE property
|
||||||
|
array('FREQ=DAILY',
|
||||||
|
null
|
||||||
|
),
|
||||||
|
array("FREQ=YEARLY;UNTIL=20170202",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
array("FREQ=YEARLY;UNTIL=20170202T090000",
|
||||||
|
null
|
||||||
|
),
|
||||||
|
array("FREQ=YEARLY;UNTIL=20170202T090000Z",
|
||||||
|
null
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider rfcStrings
|
* @dataProvider rfcStrings
|
||||||
*/
|
*/
|
||||||
public function testRfcStrings($str)
|
public function testRfcStringParser($str, $occurrences)
|
||||||
{
|
{
|
||||||
$rule = new RRule($str);
|
$rule = new RRule($str);
|
||||||
|
|
||||||
// test that parsing the string produces the same result
|
// test that parsing the string produces the same result
|
||||||
// as generating the string from a rule
|
// as generating the string from a rule
|
||||||
$this->assertEquals($rule, new RRule($rule->rfcString()));
|
$this->assertEquals($rule, new RRule($rule->rfcString()));
|
||||||
|
|
||||||
|
if ( $occurrences ) {
|
||||||
|
$this->assertEquals($occurrences, $rule->getOccurrences());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://github.com/rlanvin/php-rrule/issues/25
|
||||||
|
*/
|
||||||
|
public function quirkyRfcStrings()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('DTSTART=20160202T000000Z;FREQ=DAILY;UNTIL=20160205T000000Z',
|
||||||
|
array(
|
||||||
|
date_create('2016-02-02', new DateTimeZone('UTC')),
|
||||||
|
date_create('2016-02-03', new DateTimeZone('UTC')),
|
||||||
|
date_create('2016-02-04', new DateTimeZone('UTC')),
|
||||||
|
date_create('2016-02-05', new DateTimeZone('UTC'))
|
||||||
|
)
|
||||||
|
),
|
||||||
|
array('RRULE:DTSTART=20160202T000000Z;FREQ=DAILY;UNTIL=20160205T000000Z',
|
||||||
|
array(
|
||||||
|
date_create('2016-02-02', new DateTimeZone('UTC')),
|
||||||
|
date_create('2016-02-03', new DateTimeZone('UTC')),
|
||||||
|
date_create('2016-02-04', new DateTimeZone('UTC')),
|
||||||
|
date_create('2016-02-05', new DateTimeZone('UTC'))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider quirkyRfcStrings
|
||||||
|
* @expectedException PHPUnit_Framework_Error_Notice
|
||||||
|
*/
|
||||||
|
public function testQuirkyRfcStringsParserNotice($str,$occurrences)
|
||||||
|
{
|
||||||
|
$rule = new RRule($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider quirkyRfcStrings
|
||||||
|
*/
|
||||||
|
public function testQuirkyRfcStringsParser($str,$occurrences)
|
||||||
|
{
|
||||||
|
$rule = @ new RRule($str);
|
||||||
|
|
||||||
|
if ( $occurrences ) {
|
||||||
|
$this->assertEquals($occurrences, $rule->getOccurrences());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function invalidRfcStrings()
|
public function invalidRfcStrings()
|
||||||
@ -1779,6 +1920,10 @@ class RRuleTest extends PHPUnit_Framework_TestCase
|
|||||||
array('DTSTART:20060624
|
array('DTSTART:20060624
|
||||||
RRULE:FREQ=DAILY;UNTIL=2006-06-24'),
|
RRULE:FREQ=DAILY;UNTIL=2006-06-24'),
|
||||||
|
|
||||||
|
// multiple dtstart
|
||||||
|
array('DTSTART:20060624
|
||||||
|
RRULE:DTSTART=20060630;FREQ=DAILY;UNTIL=20060624'),
|
||||||
|
|
||||||
// test combinations of DTSTART and UNTIL which are invalid
|
// test combinations of DTSTART and UNTIL which are invalid
|
||||||
array('DTSTART;TZID=Australia/Sydney:20160624
|
array('DTSTART;TZID=Australia/Sydney:20160624
|
||||||
RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20160628'),
|
RRULE:FREQ=DAILY;INTERVAL=1;UNTIL=20160628'),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user