Skip to content

Commit

Permalink
Draft of a Proxy class to work with Event
Browse files Browse the repository at this point in the history
Ref #63
Ref #4
  • Loading branch information
rlanvin committed Jan 13, 2019
1 parent 5e84e08 commit b2f0dd1
Show file tree
Hide file tree
Showing 5 changed files with 350 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- `getNthOccurrencesBefore`
- `getNthOccurrencesAfter`
- `getNthOccurrencesFrom`
- New classes: `Proxy` and `Event` to work with events (a date + a duration) instead of simple occurrence (a date)

## [1.6.3] - 2019-01-13

Expand Down
102 changes: 102 additions & 0 deletions src/Event.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

/**
* Licensed under the MIT license.
*
* For the full copyright and license information, please view the LICENSE file.
*
* @author Rémi Lanvin <[email protected]>
* @link https://github.com/rlanvin/php-rrule
*/

namespace RRule;

/**
* Helper class to work with event (a start time and a duration)
*/
class Event
{
/**
* @var \DateTimeInterface
*/
protected $start_date;

/**
* @var \DateTimeInterface
*/
protected $end_date;

/**
* @var \DateInterval
*/
protected $duration;

public function __construct(\DateTimeInterface $start_date, $duration)
{
$this->start_date = $start_date;
$this->duration = self::parseDuration($duration);
}

/**
* @return bool
*/
public function occursAt($date)
{
$date = RRule::parseDate($date);

return $this->start_date >= $date && $this->getEnd() <= $date;
}

/**
* @return \DateTimeInterface
*/
public function getStart()
{
return clone $this->start_date;
}

/**
* @return \DateInterval
*/
public function getDuration()
{
return $this->duration;
}

/**
* @return \DateTimeInterface
*/
public function getEnd()
{
if ( $this->end_date === null ) {
$this->end_date = $this->start_date->add($this->duration);
}

return clone $this->end_date;
}

static public function parseDuration($duration)
{
if ( is_numeric($duration) ) {
if ( $duration < 0 ) {
throw new \InvalidArgumentException("Duration must be a positive integer");
}

// duration is a integer in seconds
return \DateInterval::createFromDateString("$duration seconds");
}

if ( is_string($duration) ) {
if ( $duration[0] == 'P' ) {
// 'P3M'
return new \DateInterval($duration);
}
else {
// '3 months'
return \DateInterval::createFromDateString($duration);
}
}

throw new \InvalidArgumentException('Could not parse the duration');
}
}
145 changes: 145 additions & 0 deletions src/Proxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
<?php

/**
* Licensed under the MIT license.
*
* For the full copyright and license information, please view the LICENSE file.
*
* @author Rémi Lanvin <[email protected]>
* @link https://github.com/rlanvin/php-rrule
*/

namespace RRule;

/**
* Simple proxy class to convert the results into something else.
*
* Designed as an example how to work with events (date+duration) instead of occurrences (date only).
*
* Example:
* $rrule = new Proxy(
* new RRule("DTSTART:20170101\nRRULE:FREQ=DAILY;COUNT=2;INTERVAL=10"),
* function (\DateTimeInterface $occurrence) {
* return new Event($occurrence, 3600);
* }
* );
*/
class Proxy implements \ArrayAccess, \Countable, \IteratorAggregate
{
/**
* @var RRuleInterface
*/
protected $rrule;

/**
* @var \Callable
*/
protected $factory;

public function __construct(RRuleInterface $rrule, callable $factory)
{
$this->rrule = $rrule;
$this->factory = $factory;
}

/**
* @return \Iterator
*/
public function getOccurrences($limit = null)
{
$occurrences = $this->rrule->getOccurrences($limit);
return new ProxyIterator(new \ArrayIterator($occurrences), $this->factory);
}

/**
* @return \Iterator
*/
public function getOccurrencesBetween($begin, $end, $limit = null)
{
$occurrences = $this->rrule->getOccurrencesBetween($begin, $end, $limit);
return new ProxyIterator(new \ArrayIterator($occurrences), $this->factory);
}

/**
* @return \Iterator
*/
public function getOccurrencesAfter($date, $inclusive = false, $limit = null)
{
$occurrences = $this->rrule->getNthOccurrenceAfter($date, $inclusive, $limit);
return new ProxyIterator(new \ArrayIterator($occurrences), $this->factory);
}

public function getNthOccurrenceAfter($date, $index)
{
$occurrence = $this->rrule->getNthOccurrenceAfter($date, $index);
return call_user_func_array($this->factory, [$occurrence]);
}

/**
* @return \Iterator
*/
public function getOccurrencesBefore($date, $inclusive = false, $limit = null)
{
$occurrences = $this->rrule->getOccurrencesBefore($date, $inclusive, $limit);
return new ProxyIterator(new \ArrayIterator($occurrences), $this->factory);
}

public function getNthOccurrenceBefore($date, $index)
{
$occurrence = $this->rrule->getNthOccurrenceBefore($date, $index);
return call_user_func_array($this->factory, [$occurrence]);
}

public function getNthOccurrenceFrom($date, $index)
{
$occurrence = $this->rrule->getNthOccurrenceFrom($date, $index);
return call_user_func_array($this->factory, [$occurrence]);
}

public function isFinite()
{
return $this->rrule->isFinite();
}

public function isInfinite()
{
return $this->rrule->isInfinite();
}

public function occursAt($date)
{
return $this->rrule->occursAt();
}

public function offsetExists($offset)
{
return $this->rrule->offsetExists($offset);
}

public function offsetGet($offset)
{
$occurrence = $this->rrule->offsetGet($offset);
return call_user_func_array($this->factory, [$occurrence]);
}

public function offsetSet($offset, $value)
{
return $this->rrule->offsetSet($offset, $value);
}

public function offsetUnset($offset)
{
return $this->rrule->offsetUnset($offset);
}

public function count()
{
return $this->rrule->count();
}

public function getIterator()
{
return new ProxyIterator($this->rrule, $this->factory);
}

}
20 changes: 20 additions & 0 deletions src/ProxyIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace RRule;

class ProxyIterator extends \IteratorIterator
{
protected $factory;

public function __construct(\Traversable $iterator, callable $factory)
{
$this->factory = $factory;
parent::__construct($iterator);
}

public function current()
{
return call_user_func_array($this->factory, [parent::current()]);
}

}
82 changes: 82 additions & 0 deletions tests/ProxyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace RRule\Tests;

use RRule\RRule;
use RRule\Proxy;
use PHPUnit\Framework\TestCase;

class ProxyTest extends TestCase
{
public function testIterator()
{
$proxy = new Proxy(
new RRule([
'FREQ' => 'MONTHLY',
'COUNT' => 3,
'BYMONTHDAY' => 31,
'DTSTART' => '1997-09-02'
]),
function (\DateTimeInterface $occurrence) {
return new \RRule\Event($occurrence, 3600);
}
);

$expected = [
new \RRule\Event(date_create('1997-10-31'), 3600),
new \RRule\Event(date_create('1997-12-31'), 3600),
new \RRule\Event(date_create('1998-01-31'), 3600),
];

$n = 0;
foreach ( $proxy as $event ) {
$this->assertEquals($expected[$n],$event);
$n++;
}
}

public function testGetOccurrences()
{
$proxy = new Proxy(
new RRule([
'FREQ' => 'MONTHLY',
'COUNT' => 3,
'BYMONTHDAY' => 31,
'DTSTART' => '1997-09-02'
]),
function (\DateTimeInterface $occurrence) {
return new \RRule\Event($occurrence, 3600);
}
);

$expected = [
new \RRule\Event(date_create('1997-10-31'), 3600),
new \RRule\Event(date_create('1997-12-31'), 3600),
new \RRule\Event(date_create('1998-01-31'), 3600),
];

$this->assertEquals($expected, iterator_to_array($proxy->getOccurrences()));
}

public function testGetOccurrencesBetween()
{
$proxy = new Proxy(
new RRule([
'FREQ' => 'MONTHLY',
'COUNT' => 3,
'BYMONTHDAY' => 31,
'DTSTART' => '1997-09-02'
]),
function (\DateTimeInterface $occurrence) {
return new \RRule\Event($occurrence, 3600);
}
);

$expected = [
new \RRule\Event(date_create('1997-10-31'), 3600),
new \RRule\Event(date_create('1997-12-31'), 3600)
];

$this->assertEquals($expected, iterator_to_array($proxy->getOccurrencesBetween('1997-10-31', '1997-12-31')));
}
}

0 comments on commit b2f0dd1

Please sign in to comment.