ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Sabre\VObject\Recur\EventIterator Class Reference

This class is used to determine new for a recurring event, when the next events occur. More...

+ Inheritance diagram for Sabre\VObject\Recur\EventIterator:
+ Collaboration diagram for Sabre\VObject\Recur\EventIterator:

Public Member Functions

 __construct ($input, $uid=null, DateTimeZone $timeZone=null)
 Creates the iterator. More...
 
 current ()
 Returns the date for the current position of the iterator. More...
 
 getDtStart ()
 This method returns the start date for the current iteration of the event. More...
 
 getDtEnd ()
 This method returns the end date for the current iteration of the event. More...
 
 getEventObject ()
 Returns a VEVENT for the current iterations of the event. More...
 
 key ()
 Returns the current position of the iterator. More...
 
 valid ()
 This is called after next, to see if the iterator is still at a valid position, or if it's at the end. More...
 
 rewind ()
 Sets the iterator back to the starting point. More...
 
 next ()
 Advances the iterator with one step. More...
 
 fastForward (DateTimeInterface $dateTime)
 Quickly jump to a date in the future. More...
 
 isInfinite ()
 Returns true if this recurring event never ends. More...
 

Protected Attributes

 $timeZone
 
 $allDay = false
 
 $recurIterator
 
 $eventDuration
 The duration, in seconds, of the master event. More...
 
 $masterEvent
 
 $overriddenEvents = []
 
 $overriddenEventsIndex
 
 $exceptions = []
 
 $counter
 
 $startDate
 
 $currentDate
 
 $nextDate
 
 $currentOverriddenEvent
 

Detailed Description

This class is used to determine new for a recurring event, when the next events occur.

This iterator may loop infinitely in the future, therefore it is important that if you use this class, you set hard limits for the amount of iterations you want to handle.

Note that currently there is not full support for the entire iCalendar specification, as it's very complex and contains a lot of permutations that's not yet used very often in software.

For the focus has been on features as they actually appear in Calendaring software, but this may well get expanded as needed / on demand

The following RRULE properties are supported

  • UNTIL
  • INTERVAL
  • COUNT
  • FREQ=DAILY
    • BYDAY
    • BYHOUR
    • BYMONTH
  • FREQ=WEEKLY
    • BYDAY
    • BYHOUR
    • WKST
  • FREQ=MONTHLY
    • BYMONTHDAY
    • BYDAY
    • BYSETPOS
  • FREQ=YEARLY
    • BYMONTH
    • BYYEARDAY
    • BYWEEKNO
    • BYMONTHDAY (only if BYMONTH is also set)
    • BYDAY (only if BYMONTH is also set)

Anything beyond this is 'undefined', which means that it may get ignored, or you may get unexpected results. The effect is that in some applications the specified recurrence may look incorrect, or is missing.

The recurrence iterator also does not yet support THISANDFUTURE.

Author
Evert Pot (http://evertpot.com/) @license http://sabre.io/license/ Modified BSD License

Definition at line 61 of file EventIterator.php.

Constructor & Destructor Documentation

◆ __construct()

Sabre\VObject\Recur\EventIterator::__construct (   $input,
  $uid = null,
DateTimeZone  $timeZone = null 
)

Creates the iterator.

There's three ways to set up the iterator.

  1. You can pass a VCALENDAR component and a UID.
  2. You can pass an array of VEVENTs (all UIDS should match).
  3. You can pass a single VEVENT component.

Only the second method is recomended. The other 1 and 3 will be removed at some point in the future.

The $uid parameter is only required for the first method.

Parameters
Component | array$input
string | null$uid
DateTimeZone$timeZoneReference timezone for floating dates and times.

Definition at line 96 of file EventIterator.php.

96 {
97
98 if (is_null($timeZone)) {
99 $timeZone = new DateTimeZone('UTC');
100 }
101 $this->timeZone = $timeZone;
102
103 if (is_array($input)) {
104 $events = $input;
105 } elseif ($input instanceof VEvent) {
106 // Single instance mode.
107 $events = [$input];
108 } else {
109 // Calendar + UID mode.
110 $uid = (string)$uid;
111 if (!$uid) {
112 throw new InvalidArgumentException('The UID argument is required when a VCALENDAR is passed to this constructor');
113 }
114 if (!isset($input->VEVENT)) {
115 throw new InvalidArgumentException('No events found in this calendar');
116 }
117 $events = $input->getByUID($uid);
118
119 }
120
121 foreach ($events as $vevent) {
122
123 if (!isset($vevent->{'RECURRENCE-ID'})) {
124
125 $this->masterEvent = $vevent;
126
127 } else {
128
129 $this->exceptions[
130 $vevent->{'RECURRENCE-ID'}->getDateTime($this->timeZone)->getTimeStamp()
131 ] = true;
132 $this->overriddenEvents[] = $vevent;
133
134 }
135
136 }
137
138 if (!$this->masterEvent) {
139 // No base event was found. CalDAV does allow cases where only
140 // overridden instances are stored.
141 //
142 // In this particular case, we're just going to grab the first
143 // event and use that instead. This may not always give the
144 // desired result.
145 if (!count($this->overriddenEvents)) {
146 throw new InvalidArgumentException('This VCALENDAR did not have an event with UID: ' . $uid);
147 }
148 $this->masterEvent = array_shift($this->overriddenEvents);
149 }
150
151 $this->startDate = $this->masterEvent->DTSTART->getDateTime($this->timeZone);
152 $this->allDay = !$this->masterEvent->DTSTART->hasTime();
153
154 if (isset($this->masterEvent->EXDATE)) {
155
156 foreach ($this->masterEvent->EXDATE as $exDate) {
157
158 foreach ($exDate->getDateTimes($this->timeZone) as $dt) {
159 $this->exceptions[$dt->getTimeStamp()] = true;
160 }
161
162 }
163
164 }
165
166 if (isset($this->masterEvent->DTEND)) {
167 $this->eventDuration =
168 $this->masterEvent->DTEND->getDateTime($this->timeZone)->getTimeStamp() -
169 $this->startDate->getTimeStamp();
170 } elseif (isset($this->masterEvent->DURATION)) {
171 $duration = $this->masterEvent->DURATION->getDateInterval();
172 $end = clone $this->startDate;
173 $end = $end->add($duration);
174 $this->eventDuration = $end->getTimeStamp() - $this->startDate->getTimeStamp();
175 } elseif ($this->allDay) {
176 $this->eventDuration = 3600 * 24;
177 } else {
178 $this->eventDuration = 0;
179 }
180
181 if (isset($this->masterEvent->RDATE)) {
182 $this->recurIterator = new RDateIterator(
183 $this->masterEvent->RDATE->getParts(),
184 $this->startDate
185 );
186 } elseif (isset($this->masterEvent->RRULE)) {
187 $this->recurIterator = new RRuleIterator(
188 $this->masterEvent->RRULE->getParts(),
189 $this->startDate
190 );
191 } else {
192 $this->recurIterator = new RRuleIterator(
193 [
194 'FREQ' => 'DAILY',
195 'COUNT' => 1,
196 ],
197 $this->startDate
198 );
199 }
200
201 $this->rewind();
202 if (!$this->valid()) {
203 throw new NoInstancesException('This recurrence rule does not generate any valid instances');
204 }
205
206 }
VEvent component.
Definition: VEvent.php:19
valid()
This is called after next, to see if the iterator is still at a valid position, or if it's at the end...
rewind()
Sets the iterator back to the starting point.
if($argc< 2) $events
foreach($paths as $path) if($argc< 3) $input

References $events, Sabre\VObject\$input, and Sabre\VObject\Recur\EventIterator\$timeZone.

Member Function Documentation

◆ current()

Sabre\VObject\Recur\EventIterator::current ( )

Returns the date for the current position of the iterator.

Returns
DateTimeImmutable

Definition at line 213 of file EventIterator.php.

213 {
214
215 if ($this->currentDate) {
216 return clone $this->currentDate;
217 }
218
219 }

References $currentDate.

◆ fastForward()

Sabre\VObject\Recur\EventIterator::fastForward ( DateTimeInterface  $dateTime)

Quickly jump to a date in the future.

Parameters
DateTimeInterface$dateTime

Definition at line 410 of file EventIterator.php.

410 {
411
412 while ($this->valid() && $this->getDtEnd() <= $dateTime) {
413 $this->next();
414 }
415
416 }
next()
Advances the iterator with one step.
getDtEnd()
This method returns the end date for the current iteration of the event.

◆ getDtEnd()

Sabre\VObject\Recur\EventIterator::getDtEnd ( )

This method returns the end date for the current iteration of the event.

Returns
DateTimeImmutable

Definition at line 241 of file EventIterator.php.

241 {
242
243 if (!$this->valid()) {
244 return;
245 }
246 $end = clone $this->currentDate;
247 return $end->modify('+' . $this->eventDuration . ' seconds');
248
249 }

References $currentDate, and $end.

◆ getDtStart()

Sabre\VObject\Recur\EventIterator::getDtStart ( )

This method returns the start date for the current iteration of the event.

Returns
DateTimeImmutable

Definition at line 227 of file EventIterator.php.

227 {
228
229 if ($this->currentDate) {
230 return clone $this->currentDate;
231 }
232
233 }

References $currentDate.

◆ getEventObject()

Sabre\VObject\Recur\EventIterator::getEventObject ( )

Returns a VEVENT for the current iterations of the event.

This VEVENT will have a recurrence id, and it's DTSTART and DTEND altered.

Returns
VEvent

Definition at line 259 of file EventIterator.php.

259 {
260
261 if ($this->currentOverriddenEvent) {
263 }
264
265 $event = clone $this->masterEvent;
266
267 // Ignoring the following block, because PHPUnit's code coverage
268 // ignores most of these lines, and this messes with our stats.
269 //
270 // @codeCoverageIgnoreStart
271 unset(
272 $event->RRULE,
273 $event->EXDATE,
274 $event->RDATE,
275 $event->EXRULE,
276 $event->{'RECURRENCE-ID'}
277 );
278 // @codeCoverageIgnoreEnd
279
280 $event->DTSTART->setDateTime($this->getDtStart(), $event->DTSTART->isFloating());
281 if (isset($event->DTEND)) {
282 $event->DTEND->setDateTime($this->getDtEnd(), $event->DTEND->isFloating());
283 }
284 $recurid = clone $event->DTSTART;
285 $recurid->name = 'RECURRENCE-ID';
286 $event->add($recurid);
287 return $event;
288
289 }
getDtStart()
This method returns the start date for the current iteration of the event.

◆ isInfinite()

Sabre\VObject\Recur\EventIterator::isInfinite ( )

Returns true if this recurring event never ends.

Returns
bool

Definition at line 423 of file EventIterator.php.

423 {
424
425 return $this->recurIterator->isInfinite();
426
427 }

◆ key()

Sabre\VObject\Recur\EventIterator::key ( )

Returns the current position of the iterator.

This is for us simply a 0-based index.

Returns
int

Definition at line 298 of file EventIterator.php.

298 {
299
300 // The counter is always 1 ahead.
301 return $this->counter - 1;
302
303 }

◆ next()

Sabre\VObject\Recur\EventIterator::next ( )

Advances the iterator with one step.

Returns
void

Definition at line 349 of file EventIterator.php.

349 {
350
351 $this->currentOverriddenEvent = null;
352 $this->counter++;
353 if ($this->nextDate) {
354 // We had a stored value.
356 $this->nextDate = null;
357 } else {
358 // We need to ask rruleparser for the next date.
359 // We need to do this until we find a date that's not in the
360 // exception list.
361 do {
362 if (!$this->recurIterator->valid()) {
363 $nextDate = null;
364 break;
365 }
366 $nextDate = $this->recurIterator->current();
367 $this->recurIterator->next();
368 } while (isset($this->exceptions[$nextDate->getTimeStamp()]));
369
370 }
371
372
373 // $nextDate now contains what rrule thinks is the next one, but an
374 // overridden event may cut ahead.
375 if ($this->overriddenEventsIndex) {
376
377 $offsets = end($this->overriddenEventsIndex);
378 $timestamp = key($this->overriddenEventsIndex);
379 $offset = end($offsets);
380 if (!$nextDate || $timestamp < $nextDate->getTimeStamp()) {
381 // Overridden event comes first.
382 $this->currentOverriddenEvent = $this->overriddenEvents[$offset];
383
384 // Putting the rrule next date aside.
385 $this->nextDate = $nextDate;
386 $this->currentDate = $this->currentOverriddenEvent->DTSTART->getDateTime($this->timeZone);
387
388 // Ensuring that this item will only be used once.
389 array_pop($this->overriddenEventsIndex[$timestamp]);
390 if (!$this->overriddenEventsIndex[$timestamp]) {
391 array_pop($this->overriddenEventsIndex);
392 }
393
394 // Exit point!
395 return;
396
397 }
398
399 }
400
401 $this->currentDate = $nextDate;
402
403 }
foreach($mandatory_scripts as $file) $timestamp
Definition: buildRTE.php:81
key()
Returns the current position of the iterator.

References $timestamp.

◆ rewind()

Sabre\VObject\Recur\EventIterator::rewind ( )

Sets the iterator back to the starting point.

Definition at line 323 of file EventIterator.php.

323 {
324
325 $this->recurIterator->rewind();
326 // re-creating overridden event index.
327 $index = [];
328 foreach ($this->overriddenEvents as $key => $event) {
329 $stamp = $event->DTSTART->getDateTime($this->timeZone)->getTimeStamp();
330 $index[$stamp][] = $key;
331 }
332 krsort($index);
333 $this->counter = 0;
334 $this->overriddenEventsIndex = $index;
335 $this->currentOverriddenEvent = null;
336
337 $this->nextDate = null;
338 $this->currentDate = clone $this->startDate;
339
340 $this->next();
341
342 }
$key
Definition: croninfo.php:18
$index
Definition: metadata.php:60

References $index, and $key.

◆ valid()

Sabre\VObject\Recur\EventIterator::valid ( )

This is called after next, to see if the iterator is still at a valid position, or if it's at the end.

Returns
bool

Definition at line 311 of file EventIterator.php.

311 {
312
313 if ($this->counter > Settings::$maxRecurrences && Settings::$maxRecurrences !== -1) {
314 throw new MaxInstancesExceededException('Recurring events are only allowed to generate ' . Settings::$maxRecurrences);
315 }
316 return !!$this->currentDate;
317
318 }
This exception will get thrown when a recurrence rule generated more than the maximum number of insta...
static $maxRecurrences
The maximum number of recurrences that will be generated.
Definition: Settings.php:54

References $currentDate.

Field Documentation

◆ $allDay

Sabre\VObject\Recur\EventIterator::$allDay = false
protected

Definition at line 75 of file EventIterator.php.

◆ $counter

Sabre\VObject\Recur\EventIterator::$counter
protected

Definition at line 480 of file EventIterator.php.

◆ $currentDate

Sabre\VObject\Recur\EventIterator::$currentDate
protected

Definition at line 494 of file EventIterator.php.

◆ $currentOverriddenEvent

Sabre\VObject\Recur\EventIterator::$currentOverriddenEvent
protected

Definition at line 511 of file EventIterator.php.

◆ $eventDuration

Sabre\VObject\Recur\EventIterator::$eventDuration
protected

The duration, in seconds, of the master event.

We use this to calculate the DTEND for subsequent events.

Definition at line 441 of file EventIterator.php.

◆ $exceptions

Sabre\VObject\Recur\EventIterator::$exceptions = []
protected

Definition at line 473 of file EventIterator.php.

◆ $masterEvent

Sabre\VObject\Recur\EventIterator::$masterEvent
protected

Definition at line 448 of file EventIterator.php.

◆ $nextDate

Sabre\VObject\Recur\EventIterator::$nextDate
protected

Definition at line 504 of file EventIterator.php.

◆ $overriddenEvents

Sabre\VObject\Recur\EventIterator::$overriddenEvents = []
protected

Definition at line 455 of file EventIterator.php.

◆ $overriddenEventsIndex

Sabre\VObject\Recur\EventIterator::$overriddenEventsIndex
protected

Definition at line 465 of file EventIterator.php.

◆ $recurIterator

Sabre\VObject\Recur\EventIterator::$recurIterator
protected

Definition at line 434 of file EventIterator.php.

◆ $startDate

Sabre\VObject\Recur\EventIterator::$startDate
protected

Definition at line 487 of file EventIterator.php.

◆ $timeZone

Sabre\VObject\Recur\EventIterator::$timeZone
protected

Definition at line 68 of file EventIterator.php.

Referenced by Sabre\VObject\Recur\EventIterator\__construct().


The documentation for this class was generated from the following file: