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:
parent
c3213e64fd
commit
d2384c1997
@ -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
30
src/RRuleInterface.php
Executable 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();
|
||||
}
|
83
src/RSet.php
83
src/RSet.php
@ -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
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user