1
0
mirror of https://github.com/rlanvin/php-rrule.git synced 2025-02-20 09:54:16 +01:00

Adding interface to make RSet behave like RRule

Recurrence rules and a recurrence sets now share the same interface.
Also added isFinite() and isInfinite() methods, and improved
the implementation of RSet (ref #7)
This commit is contained in:
rlanvin 2016-03-21 22:43:38 +02:00
parent c3213e64fd
commit d2384c1997
5 changed files with 274 additions and 34 deletions

View File

@ -83,7 +83,7 @@ function is_leap_year($year)
* @see https://tools.ietf.org/html/rfc5545
* @see https://labix.org/python-dateutil
*/
class RRule implements \Iterator, \ArrayAccess, \Countable
class RRule implements RRuleInterface
{
const SECONDLY = 7;
const MINUTELY = 6;
@ -628,6 +628,29 @@ class RRule implements \Iterator, \ArrayAccess, \Countable
return $this;
}
///////////////////////////////////////////////////////////////////////////////
// RRule interface
/**
* Return true if the rrule has an end condition, false otherwise
*
* @return bool
*/
public function isFinite()
{
return $this->count || $this->until;
}
/**
* Return true if the rrule has no end condition (infite)
*
* @return bool
*/
public function isInfinite()
{
return ! $this->count && ! $this->until;
}
/**
* Return all the occurrences in an array.
*
@ -637,7 +660,7 @@ class RRule implements \Iterator, \ArrayAccess, \Countable
*/
public function getOccurrences()
{
if ( ! $this->count && ! $this->until ) {
if ( $this->isInfinite() ) {
throw new \LogicException('Cannot get all occurrences of an infinite recurrence rule.');
}
@ -670,7 +693,7 @@ class RRule implements \Iterator, \ArrayAccess, \Countable
if ( $end !== null ) {
$end = self::parseDate($end);
}
elseif ( ! $this->count && ! $this->until ) {
elseif ( $this->isInfinite() ) {
throw new \LogicException('Cannot get all occurrences of an infinite recurrence rule.');
}
@ -975,7 +998,7 @@ class RRule implements \Iterator, \ArrayAccess, \Countable
*/
public function count()
{
if ( ! $this->count && ! $this->until ) {
if ( $this->isInfinite() ) {
throw new \LogicException('Cannot count an infinite recurrence rule.');
}

30
src/RRuleInterface.php Executable file
View File

@ -0,0 +1,30 @@
<?php
/**
* Licensed under the MIT license.
*
* For the full copyright and license information, please view the LICENSE file.
*
* @author Rémi Lanvin <remi@cloudconnected.fr>
* @link https://github.com/rlanvin/php-rrule
*/
namespace RRule;
interface RRuleInterface extends \Iterator, \ArrayAccess, \Countable
{
public function getOccurrences();
/**
* @param date|null $begin
* @param date|null $end
* @return array Returns an array of DateTime
*/
public function getOccurrencesBetween($begin, $end);
public function occursAt($date);
public function isFinite();
public function isInfinite();
}

View File

@ -14,7 +14,7 @@ namespace RRule;
/**
* Recurrence set
*/
class RSet implements \Iterator, \ArrayAccess, \Countable
class RSet implements RRuleInterface
{
protected $rdates = array();
protected $rrules = array();
@ -22,27 +22,38 @@ class RSet implements \Iterator, \ArrayAccess, \Countable
protected $exdates = array();
protected $exrules = array();
// cache variable
protected $total = null;
protected $infinite = null;
protected $cache = array();
public function __construct()
{
}
/**
* Add a RRule (or another RSet)
*/
public function addRRule($rrule)
{
if ( is_string($rrule) || is_array($rrule) ) {
$rrule = new RRule($rrule);
}
elseif ( ! $rrule instanceof \Iterator ) {
throw new \InvalidArgumentException('The rule must be a string, an array, an instance of RRule or an Iterator');
elseif ( ! $rrule instanceof RRuleInterface ) {
throw new \InvalidArgumentException('The rule must be a string, an array, or implement RRuleInterface');
}
// cloning because I want to iterate it without being disturbed
$this->rrules[] = clone $rrule;
$this->clearCache();
return $this;
}
/**
* Add a RRule with exclusion rules.
* In RFC 2445 but deprecated in RFC 5545
*/
public function addExRule($rrule)
@ -50,17 +61,22 @@ class RSet implements \Iterator, \ArrayAccess, \Countable
if ( is_string($rrule) || is_array($rrule) ) {
$rrule = new RRule($rrule);
}
elseif ( ! $rrule instanceof \Iterator ) {
throw new \InvalidArgumentException('The rule must be a string, an array, an instance of RRule or an Iterator');
elseif ( ! $rrule instanceof RRuleInterface ) {
throw new \InvalidArgumentException('The rule must be a string, an array or implement RRuleInterface');
}
// cloning because I want to iterate it without being disturbed
$this->exrules[] = clone $rrule;
$this->clearCache();
return $this;
}
public function addRDate($date)
/**
* Add a RDATE (renamed Date for simplicy, since we don't support full RDATE syntax at the moment)
*/
public function addDate($date)
{
try {
$this->rdates[] = RRule::parseDate($date);
@ -70,9 +86,14 @@ class RSet implements \Iterator, \ArrayAccess, \Countable
);
}
$this->clearCache();
return $this;
}
/**
* Add a EXDATE
*/
public function addExDate($date)
{
try {
@ -83,12 +104,50 @@ class RSet implements \Iterator, \ArrayAccess, \Countable
);
}
$this->clearCache();
return $this;
}
/**
* Clear the cache. Do NOT use while the class is iterating
* @return $this
*/
public function clearCache()
{
$this->total = null;
$this->infinite = null;
$this->cache = array();
return $this;
}
///////////////////////////////////////////////////////////////////////////////
// RRule interface
public function isFinite()
{
return ! $this->isInfinite();
}
public function isInfinite()
{
if ( $this->infinite === null ) {
$this->infinite = false;
foreach ( $this->rrules as $rrule ) {
if ( $rrule->isInfinite() ) {
$this->infinite = true;
break;
}
}
}
return $this->infinite;
}
public function getOccurrences()
{
// TODO: need a wait to test the presence of infinite RRULE
if ( $this->isInfinite() ) {
throw new \LogicException('Cannot get all occurrences of an infinite recurrence set.');
}
$res = array();
foreach ( $this as $occurrence ) {
@ -97,6 +156,16 @@ class RSet implements \Iterator, \ArrayAccess, \Countable
return $res;
}
public function getOccurrencesBetween($begin, $end)
{
throw new \Exception(__METHOD__.' is unimplemented');
}
public function occursAt($date)
{
throw new \Exception(__METHOD__.' is unimplemented');
}
///////////////////////////////////////////////////////////////////////////////
// Iterator interface

View File

@ -4,6 +4,39 @@ use RRule\RRule;
class RRuleTest extends PHPUnit_Framework_TestCase
{
public function testIsFinite()
{
$rrule = new RRule(array(
'freq' => 'yearly'
));
$this->assertTrue($rrule->isInfinite());
$this->assertFalse($rrule->isFinite());
$rrule = new RRule(array(
'freq' => 'yearly',
'count' => 10
));
$this->assertFalse($rrule->isInfinite());
$this->assertTrue($rrule->isFinite());
}
public function testIsLeapYear()
{
$this->assertFalse(\RRule\is_leap_year(1700));
$this->assertFalse(\RRule\is_leap_year(1800));
$this->assertFalse(\RRule\is_leap_year(1900));
$this->assertTrue(\RRule\is_leap_year(2000));
}
public function testCountable()
{
$rrule = new RRule(array(
'freq' => 'yearly',
'count' => 10
));
$this->assertEquals(10, count($rrule));
}
/**
* These rules are invalid according to the RFC
*/
@ -179,7 +212,6 @@ class RRuleTest extends PHPUnit_Framework_TestCase
}
}
/**
* MONTHY rules, mostly taken from the Python test suite
*/
@ -313,6 +345,7 @@ class RRuleTest extends PHPUnit_Framework_TestCase
date_create('1997-09-09 18:00:00')))
);
}
/**
* @dataProvider weeklyRules
*/
@ -379,6 +412,7 @@ class RRuleTest extends PHPUnit_Framework_TestCase
);
}
/**
* @dataProvider dailyRules
*/
@ -1654,14 +1688,6 @@ class RRuleTest extends PHPUnit_Framework_TestCase
$this->assertEquals($rule, new RRule($rule->rfcString()));
}
public function testIsLeapYear()
{
$this->assertFalse(\RRule\is_leap_year(1700));
$this->assertFalse(\RRule\is_leap_year(1800));
$this->assertFalse(\RRule\is_leap_year(1900));
$this->assertTrue(\RRule\is_leap_year(2000));
}
public function testTimezoneIsKeptIdentical()
{
$rrule = new RRule(array(
@ -1697,4 +1723,5 @@ class RRuleTest extends PHPUnit_Framework_TestCase
$this->assertEquals(date_create('1997-09-01 09:00:00', new DateTimeZone('America/New_York')), $rrule[0]);
}
}

View File

@ -1,59 +1,150 @@
<?php
use RRule\RSet;
use RRule\RRule;
class RSetTest extends PHPUnit_Framework_TestCase
{
public function testIsInfinite()
{
$rset = new RSet();
$this->assertFalse($rset->isInfinite());
$this->assertTrue($rset->isFinite());
$rset->addRRule(array(
'FREQ' => 'YEARLY',
'COUNT' => 10
));
$this->assertFalse($rset->isInfinite());
$this->assertTrue($rset->isFinite());
$rset->addRRule(array(
'FREQ' => 'YEARLY'
));
$this->assertTrue($rset->isInfinite());
$this->assertFalse($rset->isFinite());
}
public function testAddRRule()
{
$rrset = new RSet();
$rrset->addRRule(array(
$rset = new RSet();
$rset->addRRule(array(
'FREQ' => 'YEARLY',
'COUNT' => 2,
'BYDAY' => 'TU',
'DTSTART' => date_create('1997-09-02 09:00')
));
$rrset->addRRule(array(
$rset->addRRule(new RRule(array(
'FREQ' => 'YEARLY',
'COUNT' => 1,
'BYDAY' => 'TH',
'DTSTART' => date_create('1997-09-02 09:00')
));
)));
$this->assertEquals(array(
date_create('1997-09-02 09:00'),
date_create('1997-09-04 09:00'),
date_create('1997-09-09 09:00')
), $rrset->getOccurrences());
), $rset->getOccurrences());
}
public function testAddRDate()
public function testAddDate()
{
$rset = new RSet();
$rset->addRRule(array(
'FREQ' => 'YEARLY',
'COUNT' => 1,
'BYDAY' => 'TU',
'DTSTART' => date_create('1997-09-02 09:00')
));
$rset->addDate(date_create('1997-09-04 09:00'));
$rset->addDate(date_create('1997-09-09 09:00'));
$this->assertEquals(array(
date_create('1997-09-02 09:00'),
date_create('1997-09-04 09:00'),
date_create('1997-09-09 09:00')
), $rset->getOccurrences());
}
public function testAddExRule()
{
$rset = new RSet();
$rset->addRRule(array(
'FREQ' => 'YEARLY',
'COUNT' => 6,
'BYDAY' => 'TU,TH',
'DTSTART' => date_create('1997-09-02 09:00')
));
$rset->addExRule(array(
'FREQ' => 'YEARLY',
'COUNT' => 3,
'BYDAY' => 'TH',
'DTSTART' => date_create('1997-09-02 09:00')
));
$this->assertEquals(array(
date_create('1997-09-02 09:00'),
date_create('1997-09-09 09:00'),
date_create('1997-09-16 09:00')
), $rset->getOccurrences());
}
public function testAddExDate()
{
$rrset = new RSet();
$rrset->addRRule(array(
$rset = new RSet();
$rset->addRRule(array(
'FREQ' => 'YEARLY',
'COUNT' => 6,
'BYDAY' => 'TU, TH',
'DTSTART' => date_create('1997-09-02 09:00')
));
$rrset->addExdate('1997-09-04 09:00:00');
$rrset->addExdate('1997-09-11 09:00:00');
$rrset->addExdate('1997-09-18 09:00:00');
$rset->addExdate('1997-09-04 09:00:00');
$rset->addExdate('1997-09-11 09:00:00');
$rset->addExdate('1997-09-18 09:00:00');
$this->assertEquals(array(
date_create('1997-09-02 09:00'),
date_create('1997-09-09 09:00'),
date_create('1997-09-16 09:00')
), $rrset->getOccurrences());
), $rset->getOccurrences());
}
public function testAddDateAndExRule()
{
// TODO
}
public function testCountable()
{
// TODO
}
public function testRSetInRset()
{
$rset = new RSet();
$rset->addRRule($rset);
$rset->addDate('2016-03-21');
$this->assertEquals(
array(date_create('2016-03-21')),
$rset->getOccurrences(),
'Adding the RSet into itself does not explode'
);
$sub_rset = new RSet();
$sub_rset->addDate('2016-03-21 10:00');
$sub_rset->addDate('2016-03-21 11:00');
$rset = new RSet();
$rset->addRRule($sub_rset);
$this->assertEquals(array(
date_create('2016-03-21 10:00'),
date_create('2016-03-21 11:00')
), $rset->getOccurrences());
$rset->addExDate('2016-03-21 11:00');
$this->assertEquals(array(
date_create('2016-03-21 10:00')
), $rset->getOccurrences());
}
}