Completed
Push — developer ( d751a3...e1a3df )
by Błażej
164:44 queued 111:37
created
libraries/SabreDAV/VObject/Recur/EventIterator.php 1 patch
Indentation   +445 added lines, -445 removed lines patch added patch discarded remove patch
@@ -58,450 +58,450 @@
 block discarded – undo
58 58
  */
59 59
 class EventIterator implements \Iterator {
60 60
 
61
-    /**
62
-     * Reference timeZone for floating dates and times.
63
-     *
64
-     * @var DateTimeZone
65
-     */
66
-    protected $timeZone;
67
-
68
-    /**
69
-     * True if we're iterating an all-day event.
70
-     *
71
-     * @var bool
72
-     */
73
-    protected $allDay = false;
74
-
75
-    /**
76
-     * Creates the iterator.
77
-     *
78
-     * There's three ways to set up the iterator.
79
-     *
80
-     * 1. You can pass a VCALENDAR component and a UID.
81
-     * 2. You can pass an array of VEVENTs (all UIDS should match).
82
-     * 3. You can pass a single VEVENT component.
83
-     *
84
-     * Only the second method is recomended. The other 1 and 3 will be removed
85
-     * at some point in the future.
86
-     *
87
-     * The $uid parameter is only required for the first method.
88
-     *
89
-     * @param Component|array $input
90
-     * @param string|null $uid
91
-     * @param DateTimeZone $timeZone Reference timezone for floating dates and
92
-     *                               times.
93
-     */
94
-    public function __construct($input, $uid = null, DateTimeZone $timeZone = null) {
95
-
96
-        if (is_null($timeZone)) {
97
-            $timeZone = new DateTimeZone('UTC');
98
-        }
99
-        $this->timeZone = $timeZone;
100
-
101
-        if (is_array($input)) {
102
-            $events = $input;
103
-        } elseif ($input instanceof VEvent) {
104
-            // Single instance mode.
105
-            $events = [$input];
106
-        } else {
107
-            // Calendar + UID mode.
108
-            $uid = (string)$uid;
109
-            if (!$uid) {
110
-                throw new InvalidArgumentException('The UID argument is required when a VCALENDAR is passed to this constructor');
111
-            }
112
-            if (!isset($input->VEVENT)) {
113
-                throw new InvalidArgumentException('No events found in this calendar');
114
-            }
115
-            $events = $input->getByUID($uid);
116
-
117
-        }
118
-
119
-        foreach ($events as $vevent) {
120
-
121
-            if (!isset($vevent->{'RECURRENCE-ID'})) {
122
-
123
-                $this->masterEvent = $vevent;
124
-
125
-            } else {
126
-
127
-                $this->exceptions[
128
-                    $vevent->{'RECURRENCE-ID'}->getDateTime($this->timeZone)->getTimeStamp()
129
-                ] = true;
130
-                $this->overriddenEvents[] = $vevent;
131
-
132
-            }
133
-
134
-        }
135
-
136
-        if (!$this->masterEvent) {
137
-            // No base event was found. CalDAV does allow cases where only
138
-            // overridden instances are stored.
139
-            //
140
-            // In this particular case, we're just going to grab the first
141
-            // event and use that instead. This may not always give the
142
-            // desired result.
143
-            if (!count($this->overriddenEvents)) {
144
-                throw new InvalidArgumentException('This VCALENDAR did not have an event with UID: ' . $uid);
145
-            }
146
-            $this->masterEvent = array_shift($this->overriddenEvents);
147
-        }
148
-
149
-        $this->startDate = $this->masterEvent->DTSTART->getDateTime($this->timeZone);
150
-        $this->allDay = !$this->masterEvent->DTSTART->hasTime();
151
-
152
-        if (isset($this->masterEvent->EXDATE)) {
153
-
154
-            foreach ($this->masterEvent->EXDATE as $exDate) {
155
-
156
-                foreach ($exDate->getDateTimes($this->timeZone) as $dt) {
157
-                    $this->exceptions[$dt->getTimeStamp()] = true;
158
-                }
159
-
160
-            }
161
-
162
-        }
163
-
164
-        if (isset($this->masterEvent->DTEND)) {
165
-            $this->eventDuration =
166
-                $this->masterEvent->DTEND->getDateTime($this->timeZone)->getTimeStamp() -
167
-                $this->startDate->getTimeStamp();
168
-        } elseif (isset($this->masterEvent->DURATION)) {
169
-            $duration = $this->masterEvent->DURATION->getDateInterval();
170
-            $end = clone $this->startDate;
171
-            $end = $end->add($duration);
172
-            $this->eventDuration = $end->getTimeStamp() - $this->startDate->getTimeStamp();
173
-        } elseif ($this->allDay) {
174
-            $this->eventDuration = 3600 * 24;
175
-        } else {
176
-            $this->eventDuration = 0;
177
-        }
178
-
179
-        if (isset($this->masterEvent->RDATE)) {
180
-            $this->recurIterator = new RDateIterator(
181
-                $this->masterEvent->RDATE->getParts(),
182
-                $this->startDate
183
-            );
184
-        } elseif (isset($this->masterEvent->RRULE)) {
185
-            $this->recurIterator = new RRuleIterator(
186
-                $this->masterEvent->RRULE->getParts(),
187
-                $this->startDate
188
-            );
189
-        } else {
190
-            $this->recurIterator = new RRuleIterator(
191
-                [
192
-                    'FREQ'  => 'DAILY',
193
-                    'COUNT' => 1,
194
-                ],
195
-                $this->startDate
196
-            );
197
-        }
198
-
199
-        $this->rewind();
200
-        if (!$this->valid()) {
201
-            throw new NoInstancesException('This recurrence rule does not generate any valid instances');
202
-        }
203
-
204
-    }
205
-
206
-    /**
207
-     * Returns the date for the current position of the iterator.
208
-     *
209
-     * @return DateTimeImmutable
210
-     */
211
-    public function current() {
212
-
213
-        if ($this->currentDate) {
214
-            return clone $this->currentDate;
215
-        }
216
-
217
-    }
218
-
219
-    /**
220
-     * This method returns the start date for the current iteration of the
221
-     * event.
222
-     *
223
-     * @return DateTimeImmutable
224
-     */
225
-    public function getDtStart() {
226
-
227
-        if ($this->currentDate) {
228
-            return clone $this->currentDate;
229
-        }
230
-
231
-    }
232
-
233
-    /**
234
-     * This method returns the end date for the current iteration of the
235
-     * event.
236
-     *
237
-     * @return DateTimeImmutable
238
-     */
239
-    public function getDtEnd() {
240
-
241
-        if (!$this->valid()) {
242
-            return;
243
-        }
244
-        $end = clone $this->currentDate;
245
-        return $end->modify('+' . $this->eventDuration . ' seconds');
246
-
247
-    }
248
-
249
-    /**
250
-     * Returns a VEVENT for the current iterations of the event.
251
-     *
252
-     * This VEVENT will have a recurrence id, and it's DTSTART and DTEND
253
-     * altered.
254
-     *
255
-     * @return VEvent
256
-     */
257
-    public function getEventObject() {
258
-
259
-        if ($this->currentOverriddenEvent) {
260
-            return $this->currentOverriddenEvent;
261
-        }
262
-
263
-        $event = clone $this->masterEvent;
264
-
265
-        // Ignoring the following block, because PHPUnit's code coverage
266
-        // ignores most of these lines, and this messes with our stats.
267
-        //
268
-        // @codeCoverageIgnoreStart
269
-        unset(
270
-            $event->RRULE,
271
-            $event->EXDATE,
272
-            $event->RDATE,
273
-            $event->EXRULE,
274
-            $event->{'RECURRENCE-ID'}
275
-        );
276
-        // @codeCoverageIgnoreEnd
277
-
278
-        $event->DTSTART->setDateTime($this->getDtStart(), $event->DTSTART->isFloating());
279
-        if (isset($event->DTEND)) {
280
-            $event->DTEND->setDateTime($this->getDtEnd(), $event->DTEND->isFloating());
281
-        }
282
-        $recurid = clone $event->DTSTART;
283
-        $recurid->name = 'RECURRENCE-ID';
284
-        $event->add($recurid);
285
-        return $event;
286
-
287
-    }
288
-
289
-    /**
290
-     * Returns the current position of the iterator.
291
-     *
292
-     * This is for us simply a 0-based index.
293
-     *
294
-     * @return int
295
-     */
296
-    public function key() {
297
-
298
-        // The counter is always 1 ahead.
299
-        return $this->counter - 1;
300
-
301
-    }
302
-
303
-    /**
304
-     * This is called after next, to see if the iterator is still at a valid
305
-     * position, or if it's at the end.
306
-     *
307
-     * @return bool
308
-     */
309
-    public function valid() {
310
-
311
-        if ($this->counter > Settings::$maxRecurrences && Settings::$maxRecurrences !== -1) {
312
-            throw new MaxInstancesExceededException('Recurring events are only allowed to generate ' . Settings::$maxRecurrences);
313
-        }
314
-        return !!$this->currentDate;
315
-
316
-    }
317
-
318
-    /**
319
-     * Sets the iterator back to the starting point.
320
-     */
321
-    public function rewind() {
322
-
323
-        $this->recurIterator->rewind();
324
-        // re-creating overridden event index.
325
-        $index = [];
326
-        foreach ($this->overriddenEvents as $key => $event) {
327
-            $stamp = $event->DTSTART->getDateTime($this->timeZone)->getTimeStamp();
328
-            $index[$stamp] = $key;
329
-        }
330
-        krsort($index);
331
-        $this->counter = 0;
332
-        $this->overriddenEventsIndex = $index;
333
-        $this->currentOverriddenEvent = null;
334
-
335
-        $this->nextDate = null;
336
-        $this->currentDate = clone $this->startDate;
337
-
338
-        $this->next();
339
-
340
-    }
341
-
342
-    /**
343
-     * Advances the iterator with one step.
344
-     *
345
-     * @return void
346
-     */
347
-    public function next() {
348
-
349
-        $this->currentOverriddenEvent = null;
350
-        $this->counter++;
351
-        if ($this->nextDate) {
352
-            // We had a stored value.
353
-            $nextDate = $this->nextDate;
354
-            $this->nextDate = null;
355
-        } else {
356
-            // We need to ask rruleparser for the next date.
357
-            // We need to do this until we find a date that's not in the
358
-            // exception list.
359
-            do {
360
-                if (!$this->recurIterator->valid()) {
361
-                    $nextDate = null;
362
-                    break;
363
-                }
364
-                $nextDate = $this->recurIterator->current();
365
-                $this->recurIterator->next();
366
-            } while (isset($this->exceptions[$nextDate->getTimeStamp()]));
367
-
368
-        }
369
-
370
-
371
-        // $nextDate now contains what rrule thinks is the next one, but an
372
-        // overridden event may cut ahead.
373
-        if ($this->overriddenEventsIndex) {
374
-
375
-            $offset = end($this->overriddenEventsIndex);
376
-            $timestamp = key($this->overriddenEventsIndex);
377
-            if (!$nextDate || $timestamp < $nextDate->getTimeStamp()) {
378
-                // Overridden event comes first.
379
-                $this->currentOverriddenEvent = $this->overriddenEvents[$offset];
380
-
381
-                // Putting the rrule next date aside.
382
-                $this->nextDate = $nextDate;
383
-                $this->currentDate = $this->currentOverriddenEvent->DTSTART->getDateTime($this->timeZone);
384
-
385
-                // Ensuring that this item will only be used once.
386
-                array_pop($this->overriddenEventsIndex);
387
-
388
-                // Exit point!
389
-                return;
390
-
391
-            }
392
-
393
-        }
394
-
395
-        $this->currentDate = $nextDate;
396
-
397
-    }
398
-
399
-    /**
400
-     * Quickly jump to a date in the future.
401
-     *
402
-     * @param DateTimeInterface $dateTime
403
-     */
404
-    public function fastForward(DateTimeInterface $dateTime) {
405
-
406
-        while ($this->valid() && $this->getDtEnd() < $dateTime) {
407
-            $this->next();
408
-        }
409
-
410
-    }
411
-
412
-    /**
413
-     * Returns true if this recurring event never ends.
414
-     *
415
-     * @return bool
416
-     */
417
-    public function isInfinite() {
418
-
419
-        return $this->recurIterator->isInfinite();
420
-
421
-    }
422
-
423
-    /**
424
-     * RRULE parser.
425
-     *
426
-     * @var RRuleIterator
427
-     */
428
-    protected $recurIterator;
429
-
430
-    /**
431
-     * The duration, in seconds, of the master event.
432
-     *
433
-     * We use this to calculate the DTEND for subsequent events.
434
-     */
435
-    protected $eventDuration;
436
-
437
-    /**
438
-     * A reference to the main (master) event.
439
-     *
440
-     * @var VEVENT
441
-     */
442
-    protected $masterEvent;
443
-
444
-    /**
445
-     * List of overridden events.
446
-     *
447
-     * @var array
448
-     */
449
-    protected $overriddenEvents = [];
450
-
451
-    /**
452
-     * Overridden event index.
453
-     *
454
-     * Key is timestamp, value is the index of the item in the $overriddenEvent
455
-     * property.
456
-     *
457
-     * @var array
458
-     */
459
-    protected $overriddenEventsIndex;
460
-
461
-    /**
462
-     * A list of recurrence-id's that are either part of EXDATE, or are
463
-     * overridden.
464
-     *
465
-     * @var array
466
-     */
467
-    protected $exceptions = [];
468
-
469
-    /**
470
-     * Internal event counter.
471
-     *
472
-     * @var int
473
-     */
474
-    protected $counter;
475
-
476
-    /**
477
-     * The very start of the iteration process.
478
-     *
479
-     * @var DateTimeImmutable
480
-     */
481
-    protected $startDate;
482
-
483
-    /**
484
-     * Where we are currently in the iteration process.
485
-     *
486
-     * @var DateTimeImmutable
487
-     */
488
-    protected $currentDate;
489
-
490
-    /**
491
-     * The next date from the rrule parser.
492
-     *
493
-     * Sometimes we need to temporary store the next date, because an
494
-     * overridden event came before.
495
-     *
496
-     * @var DateTimeImmutable
497
-     */
498
-    protected $nextDate;
499
-
500
-    /**
501
-     * The event that overwrites the current iteration
502
-     *
503
-     * @var VEVENT
504
-     */
505
-    protected $currentOverriddenEvent;
61
+	/**
62
+	 * Reference timeZone for floating dates and times.
63
+	 *
64
+	 * @var DateTimeZone
65
+	 */
66
+	protected $timeZone;
67
+
68
+	/**
69
+	 * True if we're iterating an all-day event.
70
+	 *
71
+	 * @var bool
72
+	 */
73
+	protected $allDay = false;
74
+
75
+	/**
76
+	 * Creates the iterator.
77
+	 *
78
+	 * There's three ways to set up the iterator.
79
+	 *
80
+	 * 1. You can pass a VCALENDAR component and a UID.
81
+	 * 2. You can pass an array of VEVENTs (all UIDS should match).
82
+	 * 3. You can pass a single VEVENT component.
83
+	 *
84
+	 * Only the second method is recomended. The other 1 and 3 will be removed
85
+	 * at some point in the future.
86
+	 *
87
+	 * The $uid parameter is only required for the first method.
88
+	 *
89
+	 * @param Component|array $input
90
+	 * @param string|null $uid
91
+	 * @param DateTimeZone $timeZone Reference timezone for floating dates and
92
+	 *                               times.
93
+	 */
94
+	public function __construct($input, $uid = null, DateTimeZone $timeZone = null) {
95
+
96
+		if (is_null($timeZone)) {
97
+			$timeZone = new DateTimeZone('UTC');
98
+		}
99
+		$this->timeZone = $timeZone;
100
+
101
+		if (is_array($input)) {
102
+			$events = $input;
103
+		} elseif ($input instanceof VEvent) {
104
+			// Single instance mode.
105
+			$events = [$input];
106
+		} else {
107
+			// Calendar + UID mode.
108
+			$uid = (string)$uid;
109
+			if (!$uid) {
110
+				throw new InvalidArgumentException('The UID argument is required when a VCALENDAR is passed to this constructor');
111
+			}
112
+			if (!isset($input->VEVENT)) {
113
+				throw new InvalidArgumentException('No events found in this calendar');
114
+			}
115
+			$events = $input->getByUID($uid);
116
+
117
+		}
118
+
119
+		foreach ($events as $vevent) {
120
+
121
+			if (!isset($vevent->{'RECURRENCE-ID'})) {
122
+
123
+				$this->masterEvent = $vevent;
124
+
125
+			} else {
126
+
127
+				$this->exceptions[
128
+					$vevent->{'RECURRENCE-ID'}->getDateTime($this->timeZone)->getTimeStamp()
129
+				] = true;
130
+				$this->overriddenEvents[] = $vevent;
131
+
132
+			}
133
+
134
+		}
135
+
136
+		if (!$this->masterEvent) {
137
+			// No base event was found. CalDAV does allow cases where only
138
+			// overridden instances are stored.
139
+			//
140
+			// In this particular case, we're just going to grab the first
141
+			// event and use that instead. This may not always give the
142
+			// desired result.
143
+			if (!count($this->overriddenEvents)) {
144
+				throw new InvalidArgumentException('This VCALENDAR did not have an event with UID: ' . $uid);
145
+			}
146
+			$this->masterEvent = array_shift($this->overriddenEvents);
147
+		}
148
+
149
+		$this->startDate = $this->masterEvent->DTSTART->getDateTime($this->timeZone);
150
+		$this->allDay = !$this->masterEvent->DTSTART->hasTime();
151
+
152
+		if (isset($this->masterEvent->EXDATE)) {
153
+
154
+			foreach ($this->masterEvent->EXDATE as $exDate) {
155
+
156
+				foreach ($exDate->getDateTimes($this->timeZone) as $dt) {
157
+					$this->exceptions[$dt->getTimeStamp()] = true;
158
+				}
159
+
160
+			}
161
+
162
+		}
163
+
164
+		if (isset($this->masterEvent->DTEND)) {
165
+			$this->eventDuration =
166
+				$this->masterEvent->DTEND->getDateTime($this->timeZone)->getTimeStamp() -
167
+				$this->startDate->getTimeStamp();
168
+		} elseif (isset($this->masterEvent->DURATION)) {
169
+			$duration = $this->masterEvent->DURATION->getDateInterval();
170
+			$end = clone $this->startDate;
171
+			$end = $end->add($duration);
172
+			$this->eventDuration = $end->getTimeStamp() - $this->startDate->getTimeStamp();
173
+		} elseif ($this->allDay) {
174
+			$this->eventDuration = 3600 * 24;
175
+		} else {
176
+			$this->eventDuration = 0;
177
+		}
178
+
179
+		if (isset($this->masterEvent->RDATE)) {
180
+			$this->recurIterator = new RDateIterator(
181
+				$this->masterEvent->RDATE->getParts(),
182
+				$this->startDate
183
+			);
184
+		} elseif (isset($this->masterEvent->RRULE)) {
185
+			$this->recurIterator = new RRuleIterator(
186
+				$this->masterEvent->RRULE->getParts(),
187
+				$this->startDate
188
+			);
189
+		} else {
190
+			$this->recurIterator = new RRuleIterator(
191
+				[
192
+					'FREQ'  => 'DAILY',
193
+					'COUNT' => 1,
194
+				],
195
+				$this->startDate
196
+			);
197
+		}
198
+
199
+		$this->rewind();
200
+		if (!$this->valid()) {
201
+			throw new NoInstancesException('This recurrence rule does not generate any valid instances');
202
+		}
203
+
204
+	}
205
+
206
+	/**
207
+	 * Returns the date for the current position of the iterator.
208
+	 *
209
+	 * @return DateTimeImmutable
210
+	 */
211
+	public function current() {
212
+
213
+		if ($this->currentDate) {
214
+			return clone $this->currentDate;
215
+		}
216
+
217
+	}
218
+
219
+	/**
220
+	 * This method returns the start date for the current iteration of the
221
+	 * event.
222
+	 *
223
+	 * @return DateTimeImmutable
224
+	 */
225
+	public function getDtStart() {
226
+
227
+		if ($this->currentDate) {
228
+			return clone $this->currentDate;
229
+		}
230
+
231
+	}
232
+
233
+	/**
234
+	 * This method returns the end date for the current iteration of the
235
+	 * event.
236
+	 *
237
+	 * @return DateTimeImmutable
238
+	 */
239
+	public function getDtEnd() {
240
+
241
+		if (!$this->valid()) {
242
+			return;
243
+		}
244
+		$end = clone $this->currentDate;
245
+		return $end->modify('+' . $this->eventDuration . ' seconds');
246
+
247
+	}
248
+
249
+	/**
250
+	 * Returns a VEVENT for the current iterations of the event.
251
+	 *
252
+	 * This VEVENT will have a recurrence id, and it's DTSTART and DTEND
253
+	 * altered.
254
+	 *
255
+	 * @return VEvent
256
+	 */
257
+	public function getEventObject() {
258
+
259
+		if ($this->currentOverriddenEvent) {
260
+			return $this->currentOverriddenEvent;
261
+		}
262
+
263
+		$event = clone $this->masterEvent;
264
+
265
+		// Ignoring the following block, because PHPUnit's code coverage
266
+		// ignores most of these lines, and this messes with our stats.
267
+		//
268
+		// @codeCoverageIgnoreStart
269
+		unset(
270
+			$event->RRULE,
271
+			$event->EXDATE,
272
+			$event->RDATE,
273
+			$event->EXRULE,
274
+			$event->{'RECURRENCE-ID'}
275
+		);
276
+		// @codeCoverageIgnoreEnd
277
+
278
+		$event->DTSTART->setDateTime($this->getDtStart(), $event->DTSTART->isFloating());
279
+		if (isset($event->DTEND)) {
280
+			$event->DTEND->setDateTime($this->getDtEnd(), $event->DTEND->isFloating());
281
+		}
282
+		$recurid = clone $event->DTSTART;
283
+		$recurid->name = 'RECURRENCE-ID';
284
+		$event->add($recurid);
285
+		return $event;
286
+
287
+	}
288
+
289
+	/**
290
+	 * Returns the current position of the iterator.
291
+	 *
292
+	 * This is for us simply a 0-based index.
293
+	 *
294
+	 * @return int
295
+	 */
296
+	public function key() {
297
+
298
+		// The counter is always 1 ahead.
299
+		return $this->counter - 1;
300
+
301
+	}
302
+
303
+	/**
304
+	 * This is called after next, to see if the iterator is still at a valid
305
+	 * position, or if it's at the end.
306
+	 *
307
+	 * @return bool
308
+	 */
309
+	public function valid() {
310
+
311
+		if ($this->counter > Settings::$maxRecurrences && Settings::$maxRecurrences !== -1) {
312
+			throw new MaxInstancesExceededException('Recurring events are only allowed to generate ' . Settings::$maxRecurrences);
313
+		}
314
+		return !!$this->currentDate;
315
+
316
+	}
317
+
318
+	/**
319
+	 * Sets the iterator back to the starting point.
320
+	 */
321
+	public function rewind() {
322
+
323
+		$this->recurIterator->rewind();
324
+		// re-creating overridden event index.
325
+		$index = [];
326
+		foreach ($this->overriddenEvents as $key => $event) {
327
+			$stamp = $event->DTSTART->getDateTime($this->timeZone)->getTimeStamp();
328
+			$index[$stamp] = $key;
329
+		}
330
+		krsort($index);
331
+		$this->counter = 0;
332
+		$this->overriddenEventsIndex = $index;
333
+		$this->currentOverriddenEvent = null;
334
+
335
+		$this->nextDate = null;
336
+		$this->currentDate = clone $this->startDate;
337
+
338
+		$this->next();
339
+
340
+	}
341
+
342
+	/**
343
+	 * Advances the iterator with one step.
344
+	 *
345
+	 * @return void
346
+	 */
347
+	public function next() {
348
+
349
+		$this->currentOverriddenEvent = null;
350
+		$this->counter++;
351
+		if ($this->nextDate) {
352
+			// We had a stored value.
353
+			$nextDate = $this->nextDate;
354
+			$this->nextDate = null;
355
+		} else {
356
+			// We need to ask rruleparser for the next date.
357
+			// We need to do this until we find a date that's not in the
358
+			// exception list.
359
+			do {
360
+				if (!$this->recurIterator->valid()) {
361
+					$nextDate = null;
362
+					break;
363
+				}
364
+				$nextDate = $this->recurIterator->current();
365
+				$this->recurIterator->next();
366
+			} while (isset($this->exceptions[$nextDate->getTimeStamp()]));
367
+
368
+		}
369
+
370
+
371
+		// $nextDate now contains what rrule thinks is the next one, but an
372
+		// overridden event may cut ahead.
373
+		if ($this->overriddenEventsIndex) {
374
+
375
+			$offset = end($this->overriddenEventsIndex);
376
+			$timestamp = key($this->overriddenEventsIndex);
377
+			if (!$nextDate || $timestamp < $nextDate->getTimeStamp()) {
378
+				// Overridden event comes first.
379
+				$this->currentOverriddenEvent = $this->overriddenEvents[$offset];
380
+
381
+				// Putting the rrule next date aside.
382
+				$this->nextDate = $nextDate;
383
+				$this->currentDate = $this->currentOverriddenEvent->DTSTART->getDateTime($this->timeZone);
384
+
385
+				// Ensuring that this item will only be used once.
386
+				array_pop($this->overriddenEventsIndex);
387
+
388
+				// Exit point!
389
+				return;
390
+
391
+			}
392
+
393
+		}
394
+
395
+		$this->currentDate = $nextDate;
396
+
397
+	}
398
+
399
+	/**
400
+	 * Quickly jump to a date in the future.
401
+	 *
402
+	 * @param DateTimeInterface $dateTime
403
+	 */
404
+	public function fastForward(DateTimeInterface $dateTime) {
405
+
406
+		while ($this->valid() && $this->getDtEnd() < $dateTime) {
407
+			$this->next();
408
+		}
409
+
410
+	}
411
+
412
+	/**
413
+	 * Returns true if this recurring event never ends.
414
+	 *
415
+	 * @return bool
416
+	 */
417
+	public function isInfinite() {
418
+
419
+		return $this->recurIterator->isInfinite();
420
+
421
+	}
422
+
423
+	/**
424
+	 * RRULE parser.
425
+	 *
426
+	 * @var RRuleIterator
427
+	 */
428
+	protected $recurIterator;
429
+
430
+	/**
431
+	 * The duration, in seconds, of the master event.
432
+	 *
433
+	 * We use this to calculate the DTEND for subsequent events.
434
+	 */
435
+	protected $eventDuration;
436
+
437
+	/**
438
+	 * A reference to the main (master) event.
439
+	 *
440
+	 * @var VEVENT
441
+	 */
442
+	protected $masterEvent;
443
+
444
+	/**
445
+	 * List of overridden events.
446
+	 *
447
+	 * @var array
448
+	 */
449
+	protected $overriddenEvents = [];
450
+
451
+	/**
452
+	 * Overridden event index.
453
+	 *
454
+	 * Key is timestamp, value is the index of the item in the $overriddenEvent
455
+	 * property.
456
+	 *
457
+	 * @var array
458
+	 */
459
+	protected $overriddenEventsIndex;
460
+
461
+	/**
462
+	 * A list of recurrence-id's that are either part of EXDATE, or are
463
+	 * overridden.
464
+	 *
465
+	 * @var array
466
+	 */
467
+	protected $exceptions = [];
468
+
469
+	/**
470
+	 * Internal event counter.
471
+	 *
472
+	 * @var int
473
+	 */
474
+	protected $counter;
475
+
476
+	/**
477
+	 * The very start of the iteration process.
478
+	 *
479
+	 * @var DateTimeImmutable
480
+	 */
481
+	protected $startDate;
482
+
483
+	/**
484
+	 * Where we are currently in the iteration process.
485
+	 *
486
+	 * @var DateTimeImmutable
487
+	 */
488
+	protected $currentDate;
489
+
490
+	/**
491
+	 * The next date from the rrule parser.
492
+	 *
493
+	 * Sometimes we need to temporary store the next date, because an
494
+	 * overridden event came before.
495
+	 *
496
+	 * @var DateTimeImmutable
497
+	 */
498
+	protected $nextDate;
499
+
500
+	/**
501
+	 * The event that overwrites the current iteration
502
+	 *
503
+	 * @var VEVENT
504
+	 */
505
+	protected $currentOverriddenEvent;
506 506
 
507 507
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Recur/RDateIterator.php 1 patch
Indentation   +155 added lines, -155 removed lines patch added patch discarded remove patch
@@ -21,162 +21,162 @@
 block discarded – undo
21 21
  */
22 22
 class RDateIterator implements Iterator {
23 23
 
24
-    /**
25
-     * Creates the Iterator.
26
-     *
27
-     * @param string|array $rrule
28
-     * @param DateTimeInterface $start
29
-     */
30
-    public function __construct($rrule, DateTimeInterface $start) {
24
+	/**
25
+	 * Creates the Iterator.
26
+	 *
27
+	 * @param string|array $rrule
28
+	 * @param DateTimeInterface $start
29
+	 */
30
+	public function __construct($rrule, DateTimeInterface $start) {
31 31
 
32
-        $this->startDate = $start;
33
-        $this->parseRDate($rrule);
34
-        $this->currentDate = clone $this->startDate;
35
-
36
-    }
32
+		$this->startDate = $start;
33
+		$this->parseRDate($rrule);
34
+		$this->currentDate = clone $this->startDate;
35
+
36
+	}
37 37
 
38
-    /* Implementation of the Iterator interface {{{ */
39
-
40
-    public function current() {
41
-
42
-        if (!$this->valid()) return;
43
-        return clone $this->currentDate;
44
-
45
-    }
46
-
47
-    /**
48
-     * Returns the current item number.
49
-     *
50
-     * @return int
51
-     */
52
-    public function key() {
53
-
54
-        return $this->counter;
55
-
56
-    }
57
-
58
-    /**
59
-     * Returns whether the current item is a valid item for the recurrence
60
-     * iterator.
61
-     *
62
-     * @return bool
63
-     */
64
-    public function valid() {
65
-
66
-        return ($this->counter <= count($this->dates));
67
-
68
-    }
69
-
70
-    /**
71
-     * Resets the iterator.
72
-     *
73
-     * @return void
74
-     */
75
-    public function rewind() {
76
-
77
-        $this->currentDate = clone $this->startDate;
78
-        $this->counter = 0;
79
-
80
-    }
81
-
82
-    /**
83
-     * Goes on to the next iteration.
84
-     *
85
-     * @return void
86
-     */
87
-    public function next() {
88
-
89
-        $this->counter++;
90
-        if (!$this->valid()) return;
91
-
92
-        $this->currentDate =
93
-            DateTimeParser::parse(
94
-                $this->dates[$this->counter - 1],
95
-                $this->startDate->getTimezone()
96
-            );
97
-
98
-    }
99
-
100
-    /* End of Iterator implementation }}} */
101
-
102
-    /**
103
-     * Returns true if this recurring event never ends.
104
-     *
105
-     * @return bool
106
-     */
107
-    public function isInfinite() {
108
-
109
-        return false;
110
-
111
-    }
112
-
113
-    /**
114
-     * This method allows you to quickly go to the next occurrence after the
115
-     * specified date.
116
-     *
117
-     * @param DateTimeInterface $dt
118
-     *
119
-     * @return void
120
-     */
121
-    public function fastForward(DateTimeInterface $dt) {
122
-
123
-        while ($this->valid() && $this->currentDate < $dt) {
124
-            $this->next();
125
-        }
126
-
127
-    }
128
-
129
-    /**
130
-     * The reference start date/time for the rrule.
131
-     *
132
-     * All calculations are based on this initial date.
133
-     *
134
-     * @var DateTimeInterface
135
-     */
136
-    protected $startDate;
137
-
138
-    /**
139
-     * The date of the current iteration. You can get this by calling
140
-     * ->current().
141
-     *
142
-     * @var DateTimeInterface
143
-     */
144
-    protected $currentDate;
145
-
146
-    /**
147
-     * The current item in the list.
148
-     *
149
-     * You can get this number with the key() method.
150
-     *
151
-     * @var int
152
-     */
153
-    protected $counter = 0;
154
-
155
-    /* }}} */
156
-
157
-    /**
158
-     * This method receives a string from an RRULE property, and populates this
159
-     * class with all the values.
160
-     *
161
-     * @param string|array $rrule
162
-     *
163
-     * @return void
164
-     */
165
-    protected function parseRDate($rdate) {
166
-
167
-        if (is_string($rdate)) {
168
-            $rdate = explode(',', $rdate);
169
-        }
170
-
171
-        $this->dates = $rdate;
172
-
173
-    }
174
-
175
-    /**
176
-     * Array with the RRULE dates
177
-     *
178
-     * @var array
179
-     */
180
-    protected $dates = [];
38
+	/* Implementation of the Iterator interface {{{ */
39
+
40
+	public function current() {
41
+
42
+		if (!$this->valid()) return;
43
+		return clone $this->currentDate;
44
+
45
+	}
46
+
47
+	/**
48
+	 * Returns the current item number.
49
+	 *
50
+	 * @return int
51
+	 */
52
+	public function key() {
53
+
54
+		return $this->counter;
55
+
56
+	}
57
+
58
+	/**
59
+	 * Returns whether the current item is a valid item for the recurrence
60
+	 * iterator.
61
+	 *
62
+	 * @return bool
63
+	 */
64
+	public function valid() {
65
+
66
+		return ($this->counter <= count($this->dates));
67
+
68
+	}
69
+
70
+	/**
71
+	 * Resets the iterator.
72
+	 *
73
+	 * @return void
74
+	 */
75
+	public function rewind() {
76
+
77
+		$this->currentDate = clone $this->startDate;
78
+		$this->counter = 0;
79
+
80
+	}
81
+
82
+	/**
83
+	 * Goes on to the next iteration.
84
+	 *
85
+	 * @return void
86
+	 */
87
+	public function next() {
88
+
89
+		$this->counter++;
90
+		if (!$this->valid()) return;
91
+
92
+		$this->currentDate =
93
+			DateTimeParser::parse(
94
+				$this->dates[$this->counter - 1],
95
+				$this->startDate->getTimezone()
96
+			);
97
+
98
+	}
99
+
100
+	/* End of Iterator implementation }}} */
101
+
102
+	/**
103
+	 * Returns true if this recurring event never ends.
104
+	 *
105
+	 * @return bool
106
+	 */
107
+	public function isInfinite() {
108
+
109
+		return false;
110
+
111
+	}
112
+
113
+	/**
114
+	 * This method allows you to quickly go to the next occurrence after the
115
+	 * specified date.
116
+	 *
117
+	 * @param DateTimeInterface $dt
118
+	 *
119
+	 * @return void
120
+	 */
121
+	public function fastForward(DateTimeInterface $dt) {
122
+
123
+		while ($this->valid() && $this->currentDate < $dt) {
124
+			$this->next();
125
+		}
126
+
127
+	}
128
+
129
+	/**
130
+	 * The reference start date/time for the rrule.
131
+	 *
132
+	 * All calculations are based on this initial date.
133
+	 *
134
+	 * @var DateTimeInterface
135
+	 */
136
+	protected $startDate;
137
+
138
+	/**
139
+	 * The date of the current iteration. You can get this by calling
140
+	 * ->current().
141
+	 *
142
+	 * @var DateTimeInterface
143
+	 */
144
+	protected $currentDate;
145
+
146
+	/**
147
+	 * The current item in the list.
148
+	 *
149
+	 * You can get this number with the key() method.
150
+	 *
151
+	 * @var int
152
+	 */
153
+	protected $counter = 0;
154
+
155
+	/* }}} */
156
+
157
+	/**
158
+	 * This method receives a string from an RRULE property, and populates this
159
+	 * class with all the values.
160
+	 *
161
+	 * @param string|array $rrule
162
+	 *
163
+	 * @return void
164
+	 */
165
+	protected function parseRDate($rdate) {
166
+
167
+		if (is_string($rdate)) {
168
+			$rdate = explode(',', $rdate);
169
+		}
170
+
171
+		$this->dates = $rdate;
172
+
173
+	}
174
+
175
+	/**
176
+	 * Array with the RRULE dates
177
+	 *
178
+	 * @var array
179
+	 */
180
+	protected $dates = [];
181 181
 
182 182
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/FreeBusyGenerator.php 1 patch
Indentation   +553 added lines, -553 removed lines patch added patch discarded remove patch
@@ -25,580 +25,580 @@
 block discarded – undo
25 25
  */
26 26
 class FreeBusyGenerator {
27 27
 
28
-    /**
29
-     * Input objects.
30
-     *
31
-     * @var array
32
-     */
33
-    protected $objects = [];
34
-
35
-    /**
36
-     * Start of range.
37
-     *
38
-     * @var DateTimeInterface|null
39
-     */
40
-    protected $start;
41
-
42
-    /**
43
-     * End of range.
44
-     *
45
-     * @var DateTimeInterface|null
46
-     */
47
-    protected $end;
48
-
49
-    /**
50
-     * VCALENDAR object.
51
-     *
52
-     * @var Document
53
-     */
54
-    protected $baseObject;
55
-
56
-    /**
57
-     * Reference timezone.
58
-     *
59
-     * When we are calculating busy times, and we come across so-called
60
-     * floating times (times without a timezone), we use the reference timezone
61
-     * instead.
62
-     *
63
-     * This is also used for all-day events.
64
-     *
65
-     * This defaults to UTC.
66
-     *
67
-     * @var DateTimeZone
68
-     */
69
-    protected $timeZone;
70
-
71
-    /**
72
-     * A VAVAILABILITY document.
73
-     *
74
-     * If this is set, it's information will be included when calculating
75
-     * freebusy time.
76
-     *
77
-     * @var Document
78
-     */
79
-    protected $vavailability;
80
-
81
-    /**
82
-     * Creates the generator.
83
-     *
84
-     * Check the setTimeRange and setObjects methods for details about the
85
-     * arguments.
86
-     *
87
-     * @param DateTimeInterface $start
88
-     * @param DateTimeInterface $end
89
-     * @param mixed $objects
90
-     * @param DateTimeZone $timeZone
91
-     */
92
-    public function __construct(DateTimeInterface $start = null, DateTimeInterface $end = null, $objects = null, DateTimeZone $timeZone = null) {
93
-
94
-        $this->setTimeRange($start, $end);
95
-
96
-        if ($objects) {
97
-            $this->setObjects($objects);
98
-        }
99
-        if (is_null($timeZone)) {
100
-            $timeZone = new DateTimeZone('UTC');
101
-        }
102
-        $this->setTimeZone($timeZone);
103
-
104
-    }
105
-
106
-    /**
107
-     * Sets the VCALENDAR object.
108
-     *
109
-     * If this is set, it will not be generated for you. You are responsible
110
-     * for setting things like the METHOD, CALSCALE, VERSION, etc..
111
-     *
112
-     * The VFREEBUSY object will be automatically added though.
113
-     *
114
-     * @param Document $vcalendar
115
-     * @return void
116
-     */
117
-    public function setBaseObject(Document $vcalendar) {
118
-
119
-        $this->baseObject = $vcalendar;
120
-
121
-    }
122
-
123
-    /**
124
-     * Sets a VAVAILABILITY document.
125
-     *
126
-     * @param Document $vcalendar
127
-     * @return void
128
-     */
129
-    public function setVAvailability(Document $vcalendar) {
130
-
131
-        $this->vavailability = $vcalendar;
132
-
133
-    }
134
-
135
-    /**
136
-     * Sets the input objects.
137
-     *
138
-     * You must either specify a valendar object as a string, or as the parse
139
-     * Component.
140
-     * It's also possible to specify multiple objects as an array.
141
-     *
142
-     * @param mixed $objects
143
-     *
144
-     * @return void
145
-     */
146
-    public function setObjects($objects) {
147
-
148
-        if (!is_array($objects)) {
149
-            $objects = [$objects];
150
-        }
151
-
152
-        $this->objects = [];
153
-        foreach ($objects as $object) {
154
-
155
-            if (is_string($object)) {
156
-                $this->objects[] = Reader::read($object);
157
-            } elseif ($object instanceof Component) {
158
-                $this->objects[] = $object;
159
-            } else {
160
-                throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects');
161
-            }
162
-
163
-        }
164
-
165
-    }
166
-
167
-    /**
168
-     * Sets the time range.
169
-     *
170
-     * Any freebusy object falling outside of this time range will be ignored.
171
-     *
172
-     * @param DateTimeInterface $start
173
-     * @param DateTimeInterface $end
174
-     *
175
-     * @return void
176
-     */
177
-    public function setTimeRange(DateTimeInterface $start = null, DateTimeInterface $end = null) {
178
-
179
-        if (!$start) {
180
-            $start = new DateTimeImmutable(Settings::$minDate);
181
-        }
182
-        if (!$end) {
183
-            $end = new DateTimeImmutable(Settings::$maxDate);
184
-        }
185
-        $this->start = $start;
186
-        $this->end = $end;
187
-
188
-    }
189
-
190
-    /**
191
-     * Sets the reference timezone for floating times.
192
-     *
193
-     * @param DateTimeZone $timeZone
194
-     *
195
-     * @return void
196
-     */
197
-    public function setTimeZone(DateTimeZone $timeZone) {
198
-
199
-        $this->timeZone = $timeZone;
200
-
201
-    }
202
-
203
-    /**
204
-     * Parses the input data and returns a correct VFREEBUSY object, wrapped in
205
-     * a VCALENDAR.
206
-     *
207
-     * @return Component
208
-     */
209
-    public function getResult() {
210
-
211
-        $fbData = new FreeBusyData(
212
-            $this->start->getTimeStamp(),
213
-            $this->end->getTimeStamp()
214
-        );
215
-        if ($this->vavailability) {
216
-
217
-            $this->calculateAvailability($fbData, $this->vavailability);
218
-
219
-        }
220
-
221
-        $this->calculateBusy($fbData, $this->objects);
222
-
223
-        return $this->generateFreeBusyCalendar($fbData);
224
-
225
-
226
-    }
227
-
228
-    /**
229
-     * This method takes a VAVAILABILITY component and figures out all the
230
-     * available times.
231
-     *
232
-     * @param FreeBusyData $fbData
233
-     * @param VCalendar $vavailability
234
-     * @return void
235
-     */
236
-    protected function calculateAvailability(FreeBusyData $fbData, VCalendar $vavailability) {
237
-
238
-        $vavailComps = iterator_to_array($vavailability->VAVAILABILITY);
239
-        usort(
240
-            $vavailComps,
241
-            public function($a, $b) {
242
-
243
-                // We need to order the components by priority. Priority 1
244
-                // comes first, up until priority 9. Priority 0 comes after
245
-                // priority 9. No priority implies priority 0.
246
-                //
247
-                // Yes, I'm serious.
248
-                $priorityA = isset($a->PRIORITY) ? (int)$a->PRIORITY->getValue() : 0;
249
-                $priorityB = isset($b->PRIORITY) ? (int)$b->PRIORITY->getValue() : 0;
250
-
251
-                if ($priorityA === 0) $priorityA = 10;
252
-                if ($priorityB === 0) $priorityB = 10;
253
-
254
-                return $priorityA - $priorityB;
255
-
256
-            }
257
-        );
258
-
259
-        // Now we go over all the VAVAILABILITY components and figure if
260
-        // there's any we don't need to consider.
261
-        //
262
-        // This is can be because of one of two reasons: either the
263
-        // VAVAILABILITY component falls outside the time we are interested in,
264
-        // or a different VAVAILABILITY component with a higher priority has
265
-        // already completely covered the time-range.
266
-        $old = $vavailComps;
267
-        $new = [];
268
-
269
-        foreach ($old as $vavail) {
270
-
271
-            list($compStart, $compEnd) = $vavail->getEffectiveStartEnd();
272
-
273
-            // We don't care about datetimes that are earlier or later than the
274
-            // start and end of the freebusy report, so this gets normalized
275
-            // first.
276
-            if (is_null($compStart) || $compStart < $this->start) {
277
-                $compStart = $this->start;
278
-            }
279
-            if (is_null($compEnd) || $compEnd > $this->end) {
280
-                $compEnd = $this->end;
281
-            }
282
-
283
-            // If the item fell out of the timerange, we can just skip it.
284
-            if ($compStart > $this->end || $compEnd < $this->start) {
285
-                continue;
286
-            }
287
-
288
-            // Going through our existing list of components to see if there's
289
-            // a higher priority component that already fully covers this one.
290
-            foreach ($new as $higherVavail) {
291
-
292
-                list($higherStart, $higherEnd) = $higherVavail->getEffectiveStartEnd();
293
-                if (
294
-                    (is_null($higherStart) || $higherStart < $compStart) &&
295
-                    (is_null($higherEnd)   || $higherEnd > $compEnd)
296
-                ) {
297
-
298
-                    // Component is fully covered by a higher priority
299
-                    // component. We can skip this component.
300
-                    continue 2;
301
-
302
-                }
303
-
304
-            }
305
-
306
-            // We're keeping it!
307
-            $new[] = $vavail;
28
+	/**
29
+	 * Input objects.
30
+	 *
31
+	 * @var array
32
+	 */
33
+	protected $objects = [];
34
+
35
+	/**
36
+	 * Start of range.
37
+	 *
38
+	 * @var DateTimeInterface|null
39
+	 */
40
+	protected $start;
41
+
42
+	/**
43
+	 * End of range.
44
+	 *
45
+	 * @var DateTimeInterface|null
46
+	 */
47
+	protected $end;
48
+
49
+	/**
50
+	 * VCALENDAR object.
51
+	 *
52
+	 * @var Document
53
+	 */
54
+	protected $baseObject;
55
+
56
+	/**
57
+	 * Reference timezone.
58
+	 *
59
+	 * When we are calculating busy times, and we come across so-called
60
+	 * floating times (times without a timezone), we use the reference timezone
61
+	 * instead.
62
+	 *
63
+	 * This is also used for all-day events.
64
+	 *
65
+	 * This defaults to UTC.
66
+	 *
67
+	 * @var DateTimeZone
68
+	 */
69
+	protected $timeZone;
70
+
71
+	/**
72
+	 * A VAVAILABILITY document.
73
+	 *
74
+	 * If this is set, it's information will be included when calculating
75
+	 * freebusy time.
76
+	 *
77
+	 * @var Document
78
+	 */
79
+	protected $vavailability;
80
+
81
+	/**
82
+	 * Creates the generator.
83
+	 *
84
+	 * Check the setTimeRange and setObjects methods for details about the
85
+	 * arguments.
86
+	 *
87
+	 * @param DateTimeInterface $start
88
+	 * @param DateTimeInterface $end
89
+	 * @param mixed $objects
90
+	 * @param DateTimeZone $timeZone
91
+	 */
92
+	public function __construct(DateTimeInterface $start = null, DateTimeInterface $end = null, $objects = null, DateTimeZone $timeZone = null) {
93
+
94
+		$this->setTimeRange($start, $end);
95
+
96
+		if ($objects) {
97
+			$this->setObjects($objects);
98
+		}
99
+		if (is_null($timeZone)) {
100
+			$timeZone = new DateTimeZone('UTC');
101
+		}
102
+		$this->setTimeZone($timeZone);
103
+
104
+	}
105
+
106
+	/**
107
+	 * Sets the VCALENDAR object.
108
+	 *
109
+	 * If this is set, it will not be generated for you. You are responsible
110
+	 * for setting things like the METHOD, CALSCALE, VERSION, etc..
111
+	 *
112
+	 * The VFREEBUSY object will be automatically added though.
113
+	 *
114
+	 * @param Document $vcalendar
115
+	 * @return void
116
+	 */
117
+	public function setBaseObject(Document $vcalendar) {
118
+
119
+		$this->baseObject = $vcalendar;
120
+
121
+	}
122
+
123
+	/**
124
+	 * Sets a VAVAILABILITY document.
125
+	 *
126
+	 * @param Document $vcalendar
127
+	 * @return void
128
+	 */
129
+	public function setVAvailability(Document $vcalendar) {
130
+
131
+		$this->vavailability = $vcalendar;
132
+
133
+	}
134
+
135
+	/**
136
+	 * Sets the input objects.
137
+	 *
138
+	 * You must either specify a valendar object as a string, or as the parse
139
+	 * Component.
140
+	 * It's also possible to specify multiple objects as an array.
141
+	 *
142
+	 * @param mixed $objects
143
+	 *
144
+	 * @return void
145
+	 */
146
+	public function setObjects($objects) {
147
+
148
+		if (!is_array($objects)) {
149
+			$objects = [$objects];
150
+		}
151
+
152
+		$this->objects = [];
153
+		foreach ($objects as $object) {
154
+
155
+			if (is_string($object)) {
156
+				$this->objects[] = Reader::read($object);
157
+			} elseif ($object instanceof Component) {
158
+				$this->objects[] = $object;
159
+			} else {
160
+				throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects');
161
+			}
162
+
163
+		}
164
+
165
+	}
166
+
167
+	/**
168
+	 * Sets the time range.
169
+	 *
170
+	 * Any freebusy object falling outside of this time range will be ignored.
171
+	 *
172
+	 * @param DateTimeInterface $start
173
+	 * @param DateTimeInterface $end
174
+	 *
175
+	 * @return void
176
+	 */
177
+	public function setTimeRange(DateTimeInterface $start = null, DateTimeInterface $end = null) {
178
+
179
+		if (!$start) {
180
+			$start = new DateTimeImmutable(Settings::$minDate);
181
+		}
182
+		if (!$end) {
183
+			$end = new DateTimeImmutable(Settings::$maxDate);
184
+		}
185
+		$this->start = $start;
186
+		$this->end = $end;
187
+
188
+	}
189
+
190
+	/**
191
+	 * Sets the reference timezone for floating times.
192
+	 *
193
+	 * @param DateTimeZone $timeZone
194
+	 *
195
+	 * @return void
196
+	 */
197
+	public function setTimeZone(DateTimeZone $timeZone) {
198
+
199
+		$this->timeZone = $timeZone;
200
+
201
+	}
202
+
203
+	/**
204
+	 * Parses the input data and returns a correct VFREEBUSY object, wrapped in
205
+	 * a VCALENDAR.
206
+	 *
207
+	 * @return Component
208
+	 */
209
+	public function getResult() {
210
+
211
+		$fbData = new FreeBusyData(
212
+			$this->start->getTimeStamp(),
213
+			$this->end->getTimeStamp()
214
+		);
215
+		if ($this->vavailability) {
216
+
217
+			$this->calculateAvailability($fbData, $this->vavailability);
218
+
219
+		}
220
+
221
+		$this->calculateBusy($fbData, $this->objects);
222
+
223
+		return $this->generateFreeBusyCalendar($fbData);
224
+
225
+
226
+	}
227
+
228
+	/**
229
+	 * This method takes a VAVAILABILITY component and figures out all the
230
+	 * available times.
231
+	 *
232
+	 * @param FreeBusyData $fbData
233
+	 * @param VCalendar $vavailability
234
+	 * @return void
235
+	 */
236
+	protected function calculateAvailability(FreeBusyData $fbData, VCalendar $vavailability) {
237
+
238
+		$vavailComps = iterator_to_array($vavailability->VAVAILABILITY);
239
+		usort(
240
+			$vavailComps,
241
+			public function($a, $b) {
242
+
243
+				// We need to order the components by priority. Priority 1
244
+				// comes first, up until priority 9. Priority 0 comes after
245
+				// priority 9. No priority implies priority 0.
246
+				//
247
+				// Yes, I'm serious.
248
+				$priorityA = isset($a->PRIORITY) ? (int)$a->PRIORITY->getValue() : 0;
249
+				$priorityB = isset($b->PRIORITY) ? (int)$b->PRIORITY->getValue() : 0;
250
+
251
+				if ($priorityA === 0) $priorityA = 10;
252
+				if ($priorityB === 0) $priorityB = 10;
253
+
254
+				return $priorityA - $priorityB;
255
+
256
+			}
257
+		);
258
+
259
+		// Now we go over all the VAVAILABILITY components and figure if
260
+		// there's any we don't need to consider.
261
+		//
262
+		// This is can be because of one of two reasons: either the
263
+		// VAVAILABILITY component falls outside the time we are interested in,
264
+		// or a different VAVAILABILITY component with a higher priority has
265
+		// already completely covered the time-range.
266
+		$old = $vavailComps;
267
+		$new = [];
268
+
269
+		foreach ($old as $vavail) {
270
+
271
+			list($compStart, $compEnd) = $vavail->getEffectiveStartEnd();
272
+
273
+			// We don't care about datetimes that are earlier or later than the
274
+			// start and end of the freebusy report, so this gets normalized
275
+			// first.
276
+			if (is_null($compStart) || $compStart < $this->start) {
277
+				$compStart = $this->start;
278
+			}
279
+			if (is_null($compEnd) || $compEnd > $this->end) {
280
+				$compEnd = $this->end;
281
+			}
282
+
283
+			// If the item fell out of the timerange, we can just skip it.
284
+			if ($compStart > $this->end || $compEnd < $this->start) {
285
+				continue;
286
+			}
287
+
288
+			// Going through our existing list of components to see if there's
289
+			// a higher priority component that already fully covers this one.
290
+			foreach ($new as $higherVavail) {
291
+
292
+				list($higherStart, $higherEnd) = $higherVavail->getEffectiveStartEnd();
293
+				if (
294
+					(is_null($higherStart) || $higherStart < $compStart) &&
295
+					(is_null($higherEnd)   || $higherEnd > $compEnd)
296
+				) {
297
+
298
+					// Component is fully covered by a higher priority
299
+					// component. We can skip this component.
300
+					continue 2;
301
+
302
+				}
303
+
304
+			}
305
+
306
+			// We're keeping it!
307
+			$new[] = $vavail;
308 308
 
309
-        }
309
+		}
310 310
 
311
-        // Lastly, we need to traverse the remaining components and fill in the
312
-        // freebusydata slots.
313
-        //
314
-        // We traverse the components in reverse, because we want the higher
315
-        // priority components to override the lower ones.
316
-        foreach (array_reverse($new) as $vavail) {
317
-
318
-            $busyType = isset($vavail->BUSYTYPE) ? strtoupper($vavail->BUSYTYPE) : 'BUSY-UNAVAILABLE';
319
-            list($vavailStart, $vavailEnd) = $vavail->getEffectiveStartEnd();
320
-
321
-            // Making the component size no larger than the requested free-busy
322
-            // report range.
323
-            if (!$vavailStart || $vavailStart < $this->start) {
324
-                $vavailStart = $this->start;
325
-            }
326
-            if (!$vavailEnd || $vavailEnd > $this->end) {
327
-                $vavailEnd = $this->end;
328
-            }
329
-
330
-            // Marking the entire time range of the VAVAILABILITY component as
331
-            // busy.
332
-            $fbData->add(
333
-                $vavailStart->getTimeStamp(),
334
-                $vavailEnd->getTimeStamp(),
335
-                $busyType
336
-            );
337
-
338
-            // Looping over the AVAILABLE components.
339
-            if (isset($vavail->AVAILABLE)) foreach ($vavail->AVAILABLE as $available) {
340
-
341
-                list($availStart, $availEnd) = $available->getEffectiveStartEnd();
342
-                $fbData->add(
343
-                    $availStart->getTimeStamp(),
344
-                    $availEnd->getTimeStamp(),
345
-                    'FREE'
346
-                );
347
-
348
-                if ($available->RRULE) {
349
-                    // Our favourite thing: recurrence!!
350
-
351
-                    $rruleIterator = new Recur\RRuleIterator(
352
-                        $available->RRULE->getValue(),
353
-                        $availStart
354
-                    );
355
-                    $rruleIterator->fastForward($vavailStart);
356
-
357
-                    $startEndDiff = $availStart->diff($availEnd);
358
-
359
-                    while ($rruleIterator->valid()) {
360
-
361
-                        $recurStart = $rruleIterator->current();
362
-                        $recurEnd = $recurStart->add($startEndDiff);
363
-
364
-                        if ($recurStart > $vavailEnd) {
365
-                            // We're beyond the legal timerange.
366
-                            break;
367
-                        }
368
-
369
-                        if ($recurEnd > $vavailEnd) {
370
-                            // Truncating the end if it exceeds the
371
-                            // VAVAILABILITY end.
372
-                            $recurEnd = $vavailEnd;
373
-                        }
374
-
375
-                        $fbData->add(
376
-                            $recurStart->getTimeStamp(),
377
-                            $recurEnd->getTimeStamp(),
378
-                            'FREE'
379
-                        );
380
-
381
-                        $rruleIterator->next();
382
-
383
-                    }
384
-                }
385
-
386
-            }
387
-
388
-        }
389
-
390
-    }
391
-
392
-    /**
393
-     * This method takes an array of iCalendar objects and applies its busy
394
-     * times on fbData.
395
-     *
396
-     * @param FreeBusyData $fbData
397
-     * @param VCalendar[] $objects
398
-     */
399
-    protected function calculateBusy(FreeBusyData $fbData, array $objects) {
400
-
401
-        foreach ($objects as $key => $object) {
402
-
403
-            foreach ($object->getBaseComponents() as $component) {
404
-
405
-                switch ($component->name) {
406
-
407
-                    case 'VEVENT' :
408
-
409
-                        $FBTYPE = 'BUSY';
410
-                        if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) {
411
-                            break;
412
-                        }
413
-                        if (isset($component->STATUS)) {
414
-                            $status = strtoupper($component->STATUS);
415
-                            if ($status === 'CANCELLED') {
416
-                                break;
417
-                            }
418
-                            if ($status === 'TENTATIVE') {
419
-                                $FBTYPE = 'BUSY-TENTATIVE';
420
-                            }
421
-                        }
422
-
423
-                        $times = [];
424
-
425
-                        if ($component->RRULE) {
426
-                            try {
427
-                                $iterator = new EventIterator($object, (string)$component->UID, $this->timeZone);
428
-                            } catch (NoInstancesException $e) {
429
-                                // This event is recurring, but it doesn't have a single
430
-                                // instance. We are skipping this event from the output
431
-                                // entirely.
432
-                                unset($this->objects[$key]);
433
-                                continue;
434
-                            }
435
-
436
-                            if ($this->start) {
437
-                                $iterator->fastForward($this->start);
438
-                            }
311
+		// Lastly, we need to traverse the remaining components and fill in the
312
+		// freebusydata slots.
313
+		//
314
+		// We traverse the components in reverse, because we want the higher
315
+		// priority components to override the lower ones.
316
+		foreach (array_reverse($new) as $vavail) {
317
+
318
+			$busyType = isset($vavail->BUSYTYPE) ? strtoupper($vavail->BUSYTYPE) : 'BUSY-UNAVAILABLE';
319
+			list($vavailStart, $vavailEnd) = $vavail->getEffectiveStartEnd();
320
+
321
+			// Making the component size no larger than the requested free-busy
322
+			// report range.
323
+			if (!$vavailStart || $vavailStart < $this->start) {
324
+				$vavailStart = $this->start;
325
+			}
326
+			if (!$vavailEnd || $vavailEnd > $this->end) {
327
+				$vavailEnd = $this->end;
328
+			}
329
+
330
+			// Marking the entire time range of the VAVAILABILITY component as
331
+			// busy.
332
+			$fbData->add(
333
+				$vavailStart->getTimeStamp(),
334
+				$vavailEnd->getTimeStamp(),
335
+				$busyType
336
+			);
337
+
338
+			// Looping over the AVAILABLE components.
339
+			if (isset($vavail->AVAILABLE)) foreach ($vavail->AVAILABLE as $available) {
340
+
341
+				list($availStart, $availEnd) = $available->getEffectiveStartEnd();
342
+				$fbData->add(
343
+					$availStart->getTimeStamp(),
344
+					$availEnd->getTimeStamp(),
345
+					'FREE'
346
+				);
347
+
348
+				if ($available->RRULE) {
349
+					// Our favourite thing: recurrence!!
350
+
351
+					$rruleIterator = new Recur\RRuleIterator(
352
+						$available->RRULE->getValue(),
353
+						$availStart
354
+					);
355
+					$rruleIterator->fastForward($vavailStart);
356
+
357
+					$startEndDiff = $availStart->diff($availEnd);
358
+
359
+					while ($rruleIterator->valid()) {
360
+
361
+						$recurStart = $rruleIterator->current();
362
+						$recurEnd = $recurStart->add($startEndDiff);
363
+
364
+						if ($recurStart > $vavailEnd) {
365
+							// We're beyond the legal timerange.
366
+							break;
367
+						}
368
+
369
+						if ($recurEnd > $vavailEnd) {
370
+							// Truncating the end if it exceeds the
371
+							// VAVAILABILITY end.
372
+							$recurEnd = $vavailEnd;
373
+						}
374
+
375
+						$fbData->add(
376
+							$recurStart->getTimeStamp(),
377
+							$recurEnd->getTimeStamp(),
378
+							'FREE'
379
+						);
380
+
381
+						$rruleIterator->next();
382
+
383
+					}
384
+				}
385
+
386
+			}
387
+
388
+		}
389
+
390
+	}
391
+
392
+	/**
393
+	 * This method takes an array of iCalendar objects and applies its busy
394
+	 * times on fbData.
395
+	 *
396
+	 * @param FreeBusyData $fbData
397
+	 * @param VCalendar[] $objects
398
+	 */
399
+	protected function calculateBusy(FreeBusyData $fbData, array $objects) {
400
+
401
+		foreach ($objects as $key => $object) {
402
+
403
+			foreach ($object->getBaseComponents() as $component) {
404
+
405
+				switch ($component->name) {
406
+
407
+					case 'VEVENT' :
408
+
409
+						$FBTYPE = 'BUSY';
410
+						if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) {
411
+							break;
412
+						}
413
+						if (isset($component->STATUS)) {
414
+							$status = strtoupper($component->STATUS);
415
+							if ($status === 'CANCELLED') {
416
+								break;
417
+							}
418
+							if ($status === 'TENTATIVE') {
419
+								$FBTYPE = 'BUSY-TENTATIVE';
420
+							}
421
+						}
422
+
423
+						$times = [];
424
+
425
+						if ($component->RRULE) {
426
+							try {
427
+								$iterator = new EventIterator($object, (string)$component->UID, $this->timeZone);
428
+							} catch (NoInstancesException $e) {
429
+								// This event is recurring, but it doesn't have a single
430
+								// instance. We are skipping this event from the output
431
+								// entirely.
432
+								unset($this->objects[$key]);
433
+								continue;
434
+							}
435
+
436
+							if ($this->start) {
437
+								$iterator->fastForward($this->start);
438
+							}
439 439
 
440
-                            $maxRecurrences = Settings::$maxRecurrences;
441
-
442
-                            while ($iterator->valid() && --$maxRecurrences) {
443
-
444
-                                $startTime = $iterator->getDTStart();
445
-                                if ($this->end && $startTime > $this->end) {
446
-                                    break;
447
-                                }
448
-                                $times[] = [
449
-                                    $iterator->getDTStart(),
450
-                                    $iterator->getDTEnd(),
451
-                                ];
452
-
453
-                                $iterator->next();
454
-
455
-                            }
440
+							$maxRecurrences = Settings::$maxRecurrences;
441
+
442
+							while ($iterator->valid() && --$maxRecurrences) {
443
+
444
+								$startTime = $iterator->getDTStart();
445
+								if ($this->end && $startTime > $this->end) {
446
+									break;
447
+								}
448
+								$times[] = [
449
+									$iterator->getDTStart(),
450
+									$iterator->getDTEnd(),
451
+								];
452
+
453
+								$iterator->next();
454
+
455
+							}
456 456
 
457
-                        } else {
458
-
459
-                            $startTime = $component->DTSTART->getDateTime($this->timeZone);
460
-                            if ($this->end && $startTime > $this->end) {
461
-                                break;
462
-                            }
463
-                            $endTime = null;
464
-                            if (isset($component->DTEND)) {
465
-                                $endTime = $component->DTEND->getDateTime($this->timeZone);
466
-                            } elseif (isset($component->DURATION)) {
467
-                                $duration = DateTimeParser::parseDuration((string)$component->DURATION);
468
-                                $endTime = clone $startTime;
469
-                                $endTime = $endTime->add($duration);
470
-                            } elseif (!$component->DTSTART->hasTime()) {
471
-                                $endTime = clone $startTime;
472
-                                $endTime = $endTime->modify('+1 day');
473
-                            } else {
474
-                                // The event had no duration (0 seconds)
475
-                                break;
476
-                            }
477
-
478
-                            $times[] = [$startTime, $endTime];
479
-
480
-                        }
481
-
482
-                        foreach ($times as $time) {
483
-
484
-                            if ($this->end && $time[0] > $this->end) break;
485
-                            if ($this->start && $time[1] < $this->start) break;
486
-
487
-                            $fbData->add(
488
-                                $time[0]->getTimeStamp(),
489
-                                $time[1]->getTimeStamp(),
490
-                                $FBTYPE
491
-                            );
492
-                        }
493
-                        break;
494
-
495
-                    case 'VFREEBUSY' :
496
-                        foreach ($component->FREEBUSY as $freebusy) {
497
-
498
-                            $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY';
499
-
500
-                            // Skipping intervals marked as 'free'
501
-                            if ($fbType === 'FREE')
502
-                                continue;
503
-
504
-                            $values = explode(',', $freebusy);
505
-                            foreach ($values as $value) {
506
-                                list($startTime, $endTime) = explode('/', $value);
507
-                                $startTime = DateTimeParser::parseDateTime($startTime);
508
-
509
-                                if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') {
510
-                                    $duration = DateTimeParser::parseDuration($endTime);
511
-                                    $endTime = clone $startTime;
512
-                                    $endTime = $endTime->add($duration);
513
-                                } else {
514
-                                    $endTime = DateTimeParser::parseDateTime($endTime);
515
-                                }
516
-
517
-                                if ($this->start && $this->start > $endTime) continue;
518
-                                if ($this->end && $this->end < $startTime) continue;
519
-                                $fbData->add(
520
-                                    $startTime->getTimeStamp(),
521
-                                    $endTime->getTimeStamp(),
522
-                                    $fbType
523
-                                );
524
-
525
-                            }
526
-
527
-
528
-                        }
529
-                        break;
530
-
531
-                }
457
+						} else {
458
+
459
+							$startTime = $component->DTSTART->getDateTime($this->timeZone);
460
+							if ($this->end && $startTime > $this->end) {
461
+								break;
462
+							}
463
+							$endTime = null;
464
+							if (isset($component->DTEND)) {
465
+								$endTime = $component->DTEND->getDateTime($this->timeZone);
466
+							} elseif (isset($component->DURATION)) {
467
+								$duration = DateTimeParser::parseDuration((string)$component->DURATION);
468
+								$endTime = clone $startTime;
469
+								$endTime = $endTime->add($duration);
470
+							} elseif (!$component->DTSTART->hasTime()) {
471
+								$endTime = clone $startTime;
472
+								$endTime = $endTime->modify('+1 day');
473
+							} else {
474
+								// The event had no duration (0 seconds)
475
+								break;
476
+							}
477
+
478
+							$times[] = [$startTime, $endTime];
479
+
480
+						}
481
+
482
+						foreach ($times as $time) {
483
+
484
+							if ($this->end && $time[0] > $this->end) break;
485
+							if ($this->start && $time[1] < $this->start) break;
486
+
487
+							$fbData->add(
488
+								$time[0]->getTimeStamp(),
489
+								$time[1]->getTimeStamp(),
490
+								$FBTYPE
491
+							);
492
+						}
493
+						break;
494
+
495
+					case 'VFREEBUSY' :
496
+						foreach ($component->FREEBUSY as $freebusy) {
497
+
498
+							$fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY';
499
+
500
+							// Skipping intervals marked as 'free'
501
+							if ($fbType === 'FREE')
502
+								continue;
503
+
504
+							$values = explode(',', $freebusy);
505
+							foreach ($values as $value) {
506
+								list($startTime, $endTime) = explode('/', $value);
507
+								$startTime = DateTimeParser::parseDateTime($startTime);
508
+
509
+								if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') {
510
+									$duration = DateTimeParser::parseDuration($endTime);
511
+									$endTime = clone $startTime;
512
+									$endTime = $endTime->add($duration);
513
+								} else {
514
+									$endTime = DateTimeParser::parseDateTime($endTime);
515
+								}
516
+
517
+								if ($this->start && $this->start > $endTime) continue;
518
+								if ($this->end && $this->end < $startTime) continue;
519
+								$fbData->add(
520
+									$startTime->getTimeStamp(),
521
+									$endTime->getTimeStamp(),
522
+									$fbType
523
+								);
524
+
525
+							}
526
+
527
+
528
+						}
529
+						break;
530
+
531
+				}
532 532
 
533 533
 
534
-            }
534
+			}
535 535
 
536
-        }
536
+		}
537 537
 
538
-    }
538
+	}
539 539
 
540
-    /**
541
-     * This method takes a FreeBusyData object and generates the VCALENDAR
542
-     * object associated with it.
543
-     *
544
-     * @return VCalendar
545
-     */
546
-    protected function generateFreeBusyCalendar(FreeBusyData $fbData) {
547
-
548
-        if ($this->baseObject) {
549
-            $calendar = $this->baseObject;
550
-        } else {
551
-            $calendar = new VCalendar();
552
-        }
540
+	/**
541
+	 * This method takes a FreeBusyData object and generates the VCALENDAR
542
+	 * object associated with it.
543
+	 *
544
+	 * @return VCalendar
545
+	 */
546
+	protected function generateFreeBusyCalendar(FreeBusyData $fbData) {
547
+
548
+		if ($this->baseObject) {
549
+			$calendar = $this->baseObject;
550
+		} else {
551
+			$calendar = new VCalendar();
552
+		}
553 553
 
554
-        $vfreebusy = $calendar->createComponent('VFREEBUSY');
555
-        $calendar->add($vfreebusy);
554
+		$vfreebusy = $calendar->createComponent('VFREEBUSY');
555
+		$calendar->add($vfreebusy);
556 556
 
557
-        if ($this->start) {
558
-            $dtstart = $calendar->createProperty('DTSTART');
559
-            $dtstart->setDateTime($this->start);
560
-            $vfreebusy->add($dtstart);
561
-        }
562
-        if ($this->end) {
563
-            $dtend = $calendar->createProperty('DTEND');
564
-            $dtend->setDateTime($this->end);
565
-            $vfreebusy->add($dtend);
566
-        }
557
+		if ($this->start) {
558
+			$dtstart = $calendar->createProperty('DTSTART');
559
+			$dtstart->setDateTime($this->start);
560
+			$vfreebusy->add($dtstart);
561
+		}
562
+		if ($this->end) {
563
+			$dtend = $calendar->createProperty('DTEND');
564
+			$dtend->setDateTime($this->end);
565
+			$vfreebusy->add($dtend);
566
+		}
567 567
 
568
-        $tz = new \DateTimeZone('UTC');
569
-        $dtstamp = $calendar->createProperty('DTSTAMP');
570
-        $dtstamp->setDateTime(new DateTimeImmutable('now', $tz));
571
-        $vfreebusy->add($dtstamp);
568
+		$tz = new \DateTimeZone('UTC');
569
+		$dtstamp = $calendar->createProperty('DTSTAMP');
570
+		$dtstamp->setDateTime(new DateTimeImmutable('now', $tz));
571
+		$vfreebusy->add($dtstamp);
572 572
 
573
-        foreach ($fbData->getData() as $busyTime) {
573
+		foreach ($fbData->getData() as $busyTime) {
574 574
 
575
-            $busyType = strtoupper($busyTime['type']);
575
+			$busyType = strtoupper($busyTime['type']);
576 576
 
577
-            // Ignoring all the FREE parts, because those are already assumed.
578
-            if ($busyType === 'FREE') {
579
-                continue;
580
-            }
577
+			// Ignoring all the FREE parts, because those are already assumed.
578
+			if ($busyType === 'FREE') {
579
+				continue;
580
+			}
581 581
 
582
-            $busyTime[0] = new \DateTimeImmutable('@' . $busyTime['start'], $tz);
583
-            $busyTime[1] = new \DateTimeImmutable('@' . $busyTime['end'], $tz);
582
+			$busyTime[0] = new \DateTimeImmutable('@' . $busyTime['start'], $tz);
583
+			$busyTime[1] = new \DateTimeImmutable('@' . $busyTime['end'], $tz);
584 584
 
585
-            $prop = $calendar->createProperty(
586
-                'FREEBUSY',
587
-                $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
588
-            );
585
+			$prop = $calendar->createProperty(
586
+				'FREEBUSY',
587
+				$busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
588
+			);
589 589
 
590
-            // Only setting FBTYPE if it's not BUSY, because BUSY is the
591
-            // default anyway.
592
-            if ($busyType !== 'BUSY') {
593
-                $prop['FBTYPE'] = $busyType;
594
-            }
595
-            $vfreebusy->add($prop);
590
+			// Only setting FBTYPE if it's not BUSY, because BUSY is the
591
+			// default anyway.
592
+			if ($busyType !== 'BUSY') {
593
+				$prop['FBTYPE'] = $busyType;
594
+			}
595
+			$vfreebusy->add($prop);
596 596
 
597
-        }
597
+		}
598 598
 
599
-        return $calendar;
599
+		return $calendar;
600 600
 
601 601
 
602
-    }
602
+	}
603 603
 
604 604
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/ICalendar/Period.php 1 patch
Indentation   +132 added lines, -132 removed lines patch added patch discarded remove patch
@@ -19,137 +19,137 @@
 block discarded – undo
19 19
  */
20 20
 class Period extends Property {
21 21
 
22
-    /**
23
-     * In case this is a multi-value property. This string will be used as a
24
-     * delimiter.
25
-     *
26
-     * @var string|null
27
-     */
28
-    public $delimiter = ',';
29
-
30
-    /**
31
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
32
-     *
33
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
34
-     * not yet done, but parameters are not included.
35
-     *
36
-     * @param string $val
37
-     *
38
-     * @return void
39
-     */
40
-    public function setRawMimeDirValue($val) {
41
-
42
-        $this->setValue(explode($this->delimiter, $val));
43
-
44
-    }
45
-
46
-    /**
47
-     * Returns a raw mime-dir representation of the value.
48
-     *
49
-     * @return string
50
-     */
51
-    public function getRawMimeDirValue() {
52
-
53
-        return implode($this->delimiter, $this->getParts());
54
-
55
-    }
56
-
57
-    /**
58
-     * Returns the type of value.
59
-     *
60
-     * This corresponds to the VALUE= parameter. Every property also has a
61
-     * 'default' valueType.
62
-     *
63
-     * @return string
64
-     */
65
-    public function getValueType() {
66
-
67
-        return 'PERIOD';
68
-
69
-    }
70
-
71
-    /**
72
-     * Sets the json value, as it would appear in a jCard or jCal object.
73
-     *
74
-     * The value must always be an array.
75
-     *
76
-     * @param array $value
77
-     *
78
-     * @return void
79
-     */
80
-    public function setJsonValue(array $value) {
81
-
82
-        $value = array_map(
83
-            public function($item) {
84
-
85
-                return strtr(implode('/', $item), [':' => '', '-' => '']);
86
-
87
-            },
88
-            $value
89
-        );
90
-        parent::setJsonValue($value);
91
-
92
-    }
93
-
94
-    /**
95
-     * Returns the value, in the format it should be encoded for json.
96
-     *
97
-     * This method must always return an array.
98
-     *
99
-     * @return array
100
-     */
101
-    public function getJsonValue() {
102
-
103
-        $return = [];
104
-        foreach ($this->getParts() as $item) {
105
-
106
-            list($start, $end) = explode('/', $item, 2);
107
-
108
-            $start = DateTimeParser::parseDateTime($start);
109
-
110
-            // This is a duration value.
111
-            if ($end[0] === 'P') {
112
-                $return[] = [
113
-                    $start->format('Y-m-d\\TH:i:s'),
114
-                    $end
115
-                ];
116
-            } else {
117
-                $end = DateTimeParser::parseDateTime($end);
118
-                $return[] = [
119
-                    $start->format('Y-m-d\\TH:i:s'),
120
-                    $end->format('Y-m-d\\TH:i:s'),
121
-                ];
122
-            }
123
-
124
-        }
125
-
126
-        return $return;
127
-
128
-    }
129
-
130
-    /**
131
-     * This method serializes only the value of a property. This is used to
132
-     * create xCard or xCal documents.
133
-     *
134
-     * @param Xml\Writer $writer  XML writer.
135
-     *
136
-     * @return void
137
-     */
138
-    protected function xmlSerializeValue(Xml\Writer $writer) {
139
-
140
-        $writer->startElement(strtolower($this->getValueType()));
141
-        $value = $this->getJsonValue();
142
-        $writer->writeElement('start', $value[0][0]);
143
-
144
-        if ($value[0][1][0] === 'P') {
145
-            $writer->writeElement('duration', $value[0][1]);
146
-        }
147
-        else {
148
-            $writer->writeElement('end', $value[0][1]);
149
-        }
150
-
151
-        $writer->endElement();
152
-
153
-    }
22
+	/**
23
+	 * In case this is a multi-value property. This string will be used as a
24
+	 * delimiter.
25
+	 *
26
+	 * @var string|null
27
+	 */
28
+	public $delimiter = ',';
29
+
30
+	/**
31
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
32
+	 *
33
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
34
+	 * not yet done, but parameters are not included.
35
+	 *
36
+	 * @param string $val
37
+	 *
38
+	 * @return void
39
+	 */
40
+	public function setRawMimeDirValue($val) {
41
+
42
+		$this->setValue(explode($this->delimiter, $val));
43
+
44
+	}
45
+
46
+	/**
47
+	 * Returns a raw mime-dir representation of the value.
48
+	 *
49
+	 * @return string
50
+	 */
51
+	public function getRawMimeDirValue() {
52
+
53
+		return implode($this->delimiter, $this->getParts());
54
+
55
+	}
56
+
57
+	/**
58
+	 * Returns the type of value.
59
+	 *
60
+	 * This corresponds to the VALUE= parameter. Every property also has a
61
+	 * 'default' valueType.
62
+	 *
63
+	 * @return string
64
+	 */
65
+	public function getValueType() {
66
+
67
+		return 'PERIOD';
68
+
69
+	}
70
+
71
+	/**
72
+	 * Sets the json value, as it would appear in a jCard or jCal object.
73
+	 *
74
+	 * The value must always be an array.
75
+	 *
76
+	 * @param array $value
77
+	 *
78
+	 * @return void
79
+	 */
80
+	public function setJsonValue(array $value) {
81
+
82
+		$value = array_map(
83
+			public function($item) {
84
+
85
+				return strtr(implode('/', $item), [':' => '', '-' => '']);
86
+
87
+			},
88
+			$value
89
+		);
90
+		parent::setJsonValue($value);
91
+
92
+	}
93
+
94
+	/**
95
+	 * Returns the value, in the format it should be encoded for json.
96
+	 *
97
+	 * This method must always return an array.
98
+	 *
99
+	 * @return array
100
+	 */
101
+	public function getJsonValue() {
102
+
103
+		$return = [];
104
+		foreach ($this->getParts() as $item) {
105
+
106
+			list($start, $end) = explode('/', $item, 2);
107
+
108
+			$start = DateTimeParser::parseDateTime($start);
109
+
110
+			// This is a duration value.
111
+			if ($end[0] === 'P') {
112
+				$return[] = [
113
+					$start->format('Y-m-d\\TH:i:s'),
114
+					$end
115
+				];
116
+			} else {
117
+				$end = DateTimeParser::parseDateTime($end);
118
+				$return[] = [
119
+					$start->format('Y-m-d\\TH:i:s'),
120
+					$end->format('Y-m-d\\TH:i:s'),
121
+				];
122
+			}
123
+
124
+		}
125
+
126
+		return $return;
127
+
128
+	}
129
+
130
+	/**
131
+	 * This method serializes only the value of a property. This is used to
132
+	 * create xCard or xCal documents.
133
+	 *
134
+	 * @param Xml\Writer $writer  XML writer.
135
+	 *
136
+	 * @return void
137
+	 */
138
+	protected function xmlSerializeValue(Xml\Writer $writer) {
139
+
140
+		$writer->startElement(strtolower($this->getValueType()));
141
+		$value = $this->getJsonValue();
142
+		$writer->writeElement('start', $value[0][0]);
143
+
144
+		if ($value[0][1][0] === 'P') {
145
+			$writer->writeElement('duration', $value[0][1]);
146
+		}
147
+		else {
148
+			$writer->writeElement('end', $value[0][1]);
149
+		}
150
+
151
+		$writer->endElement();
152
+
153
+	}
154 154
 
155 155
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/ICalendar/CalAddress.php 1 patch
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -3,7 +3,7 @@  discard block
 block discarded – undo
3 3
 namespace Sabre\VObject\Property\ICalendar;
4 4
 
5 5
 use
6
-    Sabre\VObject\Property\Text;
6
+	Sabre\VObject\Property\Text;
7 7
 
8 8
 /**
9 9
  * CalAddress property.
@@ -16,46 +16,46 @@  discard block
 block discarded – undo
16 16
  */
17 17
 class CalAddress extends Text {
18 18
 
19
-    /**
20
-     * In case this is a multi-value property. This string will be used as a
21
-     * delimiter.
22
-     *
23
-     * @var string|null
24
-     */
25
-    public $delimiter = null;
26
-
27
-    /**
28
-     * Returns the type of value.
29
-     *
30
-     * This corresponds to the VALUE= parameter. Every property also has a
31
-     * 'default' valueType.
32
-     *
33
-     * @return string
34
-     */
35
-    public function getValueType() {
36
-
37
-        return 'CAL-ADDRESS';
38
-
39
-    }
40
-
41
-    /**
42
-     * This returns a normalized form of the value.
43
-     *
44
-     * This is primarily used right now to turn mixed-cased schemes in user
45
-     * uris to lower-case.
46
-     *
47
-     * Evolution in particular tends to encode mailto: as MAILTO:.
48
-     *
49
-     * @return string
50
-     */
51
-    public function getNormalizedValue() {
52
-
53
-        $input = $this->getValue();
54
-        if (!strpos($input, ':')) {
55
-            return $input;
56
-        }
57
-        list($schema, $everythingElse) = explode(':', $input, 2);
58
-        return strtolower($schema) . ':' . $everythingElse;
59
-
60
-    }
19
+	/**
20
+	 * In case this is a multi-value property. This string will be used as a
21
+	 * delimiter.
22
+	 *
23
+	 * @var string|null
24
+	 */
25
+	public $delimiter = null;
26
+
27
+	/**
28
+	 * Returns the type of value.
29
+	 *
30
+	 * This corresponds to the VALUE= parameter. Every property also has a
31
+	 * 'default' valueType.
32
+	 *
33
+	 * @return string
34
+	 */
35
+	public function getValueType() {
36
+
37
+		return 'CAL-ADDRESS';
38
+
39
+	}
40
+
41
+	/**
42
+	 * This returns a normalized form of the value.
43
+	 *
44
+	 * This is primarily used right now to turn mixed-cased schemes in user
45
+	 * uris to lower-case.
46
+	 *
47
+	 * Evolution in particular tends to encode mailto: as MAILTO:.
48
+	 *
49
+	 * @return string
50
+	 */
51
+	public function getNormalizedValue() {
52
+
53
+		$input = $this->getValue();
54
+		if (!strpos($input, ':')) {
55
+			return $input;
56
+		}
57
+		list($schema, $everythingElse) = explode(':', $input, 2);
58
+		return strtolower($schema) . ':' . $everythingElse;
59
+
60
+	}
61 61
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/ICalendar/Recur.php 1 patch
Indentation   +265 added lines, -265 removed lines patch added patch discarded remove patch
@@ -24,270 +24,270 @@
 block discarded – undo
24 24
  */
25 25
 class Recur extends Property {
26 26
 
27
-    /**
28
-     * Updates the current value.
29
-     *
30
-     * This may be either a single, or multiple strings in an array.
31
-     *
32
-     * @param string|array $value
33
-     *
34
-     * @return void
35
-     */
36
-    public function setValue($value) {
37
-
38
-        // If we're getting the data from json, we'll be receiving an object
39
-        if ($value instanceof \StdClass) {
40
-            $value = (array)$value;
41
-        }
42
-
43
-        if (is_array($value)) {
44
-            $newVal = [];
45
-            foreach ($value as $k => $v) {
46
-
47
-                if (is_string($v)) {
48
-                    $v = strtoupper($v);
49
-
50
-                    // The value had multiple sub-values
51
-                    if (strpos($v, ',') !== false) {
52
-                        $v = explode(',', $v);
53
-                    }
54
-                    if (strcmp($k, 'until') === 0) {
55
-                        $v = strtr($v, [':' => '', '-' => '']);
56
-                    }
57
-                } elseif (is_array($v)) {
58
-                    $v = array_map('strtoupper', $v);
59
-                }
60
-
61
-                $newVal[strtoupper($k)] = $v;
62
-            }
63
-            $this->value = $newVal;
64
-        } elseif (is_string($value)) {
65
-            $this->value = self::stringToArray($value);
66
-        } else {
67
-            throw new \InvalidArgumentException('You must either pass a string, or a key=>value array');
68
-        }
69
-
70
-    }
71
-
72
-    /**
73
-     * Returns the current value.
74
-     *
75
-     * This method will always return a singular value. If this was a
76
-     * multi-value object, some decision will be made first on how to represent
77
-     * it as a string.
78
-     *
79
-     * To get the correct multi-value version, use getParts.
80
-     *
81
-     * @return string
82
-     */
83
-    public function getValue() {
84
-
85
-        $out = [];
86
-        foreach ($this->value as $key => $value) {
87
-            $out[] = $key . '=' . (is_array($value) ? implode(',', $value) : $value);
88
-        }
89
-        return strtoupper(implode(';', $out));
90
-
91
-    }
92
-
93
-    /**
94
-     * Sets a multi-valued property.
95
-     *
96
-     * @param array $parts
97
-     * @return void
98
-     */
99
-    public function setParts(array $parts) {
100
-
101
-        $this->setValue($parts);
102
-
103
-    }
104
-
105
-    /**
106
-     * Returns a multi-valued property.
107
-     *
108
-     * This method always returns an array, if there was only a single value,
109
-     * it will still be wrapped in an array.
110
-     *
111
-     * @return array
112
-     */
113
-    public function getParts() {
114
-
115
-        return $this->value;
116
-
117
-    }
118
-
119
-    /**
120
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
121
-     *
122
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
123
-     * not yet done, but parameters are not included.
124
-     *
125
-     * @param string $val
126
-     *
127
-     * @return void
128
-     */
129
-    public function setRawMimeDirValue($val) {
130
-
131
-        $this->setValue($val);
132
-
133
-    }
134
-
135
-    /**
136
-     * Returns a raw mime-dir representation of the value.
137
-     *
138
-     * @return string
139
-     */
140
-    public function getRawMimeDirValue() {
141
-
142
-        return $this->getValue();
143
-
144
-    }
145
-
146
-    /**
147
-     * Returns the type of value.
148
-     *
149
-     * This corresponds to the VALUE= parameter. Every property also has a
150
-     * 'default' valueType.
151
-     *
152
-     * @return string
153
-     */
154
-    public function getValueType() {
155
-
156
-        return 'RECUR';
157
-
158
-    }
159
-
160
-    /**
161
-     * Returns the value, in the format it should be encoded for json.
162
-     *
163
-     * This method must always return an array.
164
-     *
165
-     * @return array
166
-     */
167
-    public function getJsonValue() {
168
-
169
-        $values = [];
170
-        foreach ($this->getParts() as $k => $v) {
171
-            if (strcmp($k, 'UNTIL') === 0) {
172
-                $date = new DateTime($this->root, null, $v);
173
-                $values[strtolower($k)] = $date->getJsonValue()[0];
174
-            } elseif (strcmp($k, 'COUNT') === 0) {
175
-                $values[strtolower($k)] = intval($v);
176
-            } else {
177
-                $values[strtolower($k)] = $v;
178
-            }
179
-        }
180
-        return [$values];
181
-
182
-    }
183
-
184
-    /**
185
-     * This method serializes only the value of a property. This is used to
186
-     * create xCard or xCal documents.
187
-     *
188
-     * @param Xml\Writer $writer  XML writer.
189
-     *
190
-     * @return void
191
-     */
192
-    protected function xmlSerializeValue(Xml\Writer $writer) {
193
-
194
-        $valueType = strtolower($this->getValueType());
195
-
196
-        foreach ($this->getJsonValue() as $value) {
197
-            $writer->writeElement($valueType, $value);
198
-        }
199
-
200
-    }
201
-
202
-    /**
203
-     * Parses an RRULE value string, and turns it into a struct-ish array.
204
-     *
205
-     * @param string $value
206
-     *
207
-     * @return array
208
-     */
209
-    static function stringToArray($value) {
210
-
211
-        $value = strtoupper($value);
212
-        $newValue = [];
213
-        foreach (explode(';', $value) as $part) {
214
-
215
-            // Skipping empty parts.
216
-            if (empty($part)) {
217
-                continue;
218
-            }
219
-            list($partName, $partValue) = explode('=', $part);
220
-
221
-            // The value itself had multiple values..
222
-            if (strpos($partValue, ',') !== false) {
223
-                $partValue = explode(',', $partValue);
224
-            }
225
-            $newValue[$partName] = $partValue;
226
-
227
-        }
228
-
229
-        return $newValue;
230
-    }
231
-
232
-    /**
233
-     * Validates the node for correctness.
234
-     *
235
-     * The following options are supported:
236
-     *   Node::REPAIR - May attempt to automatically repair the problem.
237
-     *
238
-     * This method returns an array with detected problems.
239
-     * Every element has the following properties:
240
-     *
241
-     *  * level - problem level.
242
-     *  * message - A human-readable string describing the issue.
243
-     *  * node - A reference to the problematic node.
244
-     *
245
-     * The level means:
246
-     *   1 - The issue was repaired (only happens if REPAIR was turned on)
247
-     *   2 - An inconsequential issue
248
-     *   3 - A severe issue.
249
-     *
250
-     * @param int $options
251
-     *
252
-     * @return array
253
-     */
254
-    public function validate($options = 0) {
255
-
256
-        $repair = ($options & self::REPAIR);
257
-
258
-        $warnings = parent::validate($options);
259
-        $values = $this->getParts();
260
-
261
-        foreach ($values as $key => $value) {
262
-
263
-            if (empty($value)) {
264
-                $warnings[] = [
265
-                    'level'   => $repair ? 3 : 1,
266
-                    'message' => 'Invalid value for ' . $key . ' in ' . $this->name,
267
-                    'node'    => $this
268
-                ];
269
-                if ($repair) {
270
-                    unset($values[$key]);
271
-                }
272
-            }
273
-
274
-        }
275
-        if (!isset($values['FREQ'])) {
276
-            $warnings[] = [
277
-                'level'   => $repair ? 3 : 1,
278
-                'message' => 'FREQ is required in ' . $this->name,
279
-                'node'    => $this
280
-            ];
281
-            if ($repair) {
282
-                $this->parent->remove($this);
283
-            }
284
-        }
285
-        if ($repair) {
286
-            $this->setValue($values);
287
-        }
288
-
289
-        return $warnings;
290
-
291
-    }
27
+	/**
28
+	 * Updates the current value.
29
+	 *
30
+	 * This may be either a single, or multiple strings in an array.
31
+	 *
32
+	 * @param string|array $value
33
+	 *
34
+	 * @return void
35
+	 */
36
+	public function setValue($value) {
37
+
38
+		// If we're getting the data from json, we'll be receiving an object
39
+		if ($value instanceof \StdClass) {
40
+			$value = (array)$value;
41
+		}
42
+
43
+		if (is_array($value)) {
44
+			$newVal = [];
45
+			foreach ($value as $k => $v) {
46
+
47
+				if (is_string($v)) {
48
+					$v = strtoupper($v);
49
+
50
+					// The value had multiple sub-values
51
+					if (strpos($v, ',') !== false) {
52
+						$v = explode(',', $v);
53
+					}
54
+					if (strcmp($k, 'until') === 0) {
55
+						$v = strtr($v, [':' => '', '-' => '']);
56
+					}
57
+				} elseif (is_array($v)) {
58
+					$v = array_map('strtoupper', $v);
59
+				}
60
+
61
+				$newVal[strtoupper($k)] = $v;
62
+			}
63
+			$this->value = $newVal;
64
+		} elseif (is_string($value)) {
65
+			$this->value = self::stringToArray($value);
66
+		} else {
67
+			throw new \InvalidArgumentException('You must either pass a string, or a key=>value array');
68
+		}
69
+
70
+	}
71
+
72
+	/**
73
+	 * Returns the current value.
74
+	 *
75
+	 * This method will always return a singular value. If this was a
76
+	 * multi-value object, some decision will be made first on how to represent
77
+	 * it as a string.
78
+	 *
79
+	 * To get the correct multi-value version, use getParts.
80
+	 *
81
+	 * @return string
82
+	 */
83
+	public function getValue() {
84
+
85
+		$out = [];
86
+		foreach ($this->value as $key => $value) {
87
+			$out[] = $key . '=' . (is_array($value) ? implode(',', $value) : $value);
88
+		}
89
+		return strtoupper(implode(';', $out));
90
+
91
+	}
92
+
93
+	/**
94
+	 * Sets a multi-valued property.
95
+	 *
96
+	 * @param array $parts
97
+	 * @return void
98
+	 */
99
+	public function setParts(array $parts) {
100
+
101
+		$this->setValue($parts);
102
+
103
+	}
104
+
105
+	/**
106
+	 * Returns a multi-valued property.
107
+	 *
108
+	 * This method always returns an array, if there was only a single value,
109
+	 * it will still be wrapped in an array.
110
+	 *
111
+	 * @return array
112
+	 */
113
+	public function getParts() {
114
+
115
+		return $this->value;
116
+
117
+	}
118
+
119
+	/**
120
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
121
+	 *
122
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
123
+	 * not yet done, but parameters are not included.
124
+	 *
125
+	 * @param string $val
126
+	 *
127
+	 * @return void
128
+	 */
129
+	public function setRawMimeDirValue($val) {
130
+
131
+		$this->setValue($val);
132
+
133
+	}
134
+
135
+	/**
136
+	 * Returns a raw mime-dir representation of the value.
137
+	 *
138
+	 * @return string
139
+	 */
140
+	public function getRawMimeDirValue() {
141
+
142
+		return $this->getValue();
143
+
144
+	}
145
+
146
+	/**
147
+	 * Returns the type of value.
148
+	 *
149
+	 * This corresponds to the VALUE= parameter. Every property also has a
150
+	 * 'default' valueType.
151
+	 *
152
+	 * @return string
153
+	 */
154
+	public function getValueType() {
155
+
156
+		return 'RECUR';
157
+
158
+	}
159
+
160
+	/**
161
+	 * Returns the value, in the format it should be encoded for json.
162
+	 *
163
+	 * This method must always return an array.
164
+	 *
165
+	 * @return array
166
+	 */
167
+	public function getJsonValue() {
168
+
169
+		$values = [];
170
+		foreach ($this->getParts() as $k => $v) {
171
+			if (strcmp($k, 'UNTIL') === 0) {
172
+				$date = new DateTime($this->root, null, $v);
173
+				$values[strtolower($k)] = $date->getJsonValue()[0];
174
+			} elseif (strcmp($k, 'COUNT') === 0) {
175
+				$values[strtolower($k)] = intval($v);
176
+			} else {
177
+				$values[strtolower($k)] = $v;
178
+			}
179
+		}
180
+		return [$values];
181
+
182
+	}
183
+
184
+	/**
185
+	 * This method serializes only the value of a property. This is used to
186
+	 * create xCard or xCal documents.
187
+	 *
188
+	 * @param Xml\Writer $writer  XML writer.
189
+	 *
190
+	 * @return void
191
+	 */
192
+	protected function xmlSerializeValue(Xml\Writer $writer) {
193
+
194
+		$valueType = strtolower($this->getValueType());
195
+
196
+		foreach ($this->getJsonValue() as $value) {
197
+			$writer->writeElement($valueType, $value);
198
+		}
199
+
200
+	}
201
+
202
+	/**
203
+	 * Parses an RRULE value string, and turns it into a struct-ish array.
204
+	 *
205
+	 * @param string $value
206
+	 *
207
+	 * @return array
208
+	 */
209
+	static function stringToArray($value) {
210
+
211
+		$value = strtoupper($value);
212
+		$newValue = [];
213
+		foreach (explode(';', $value) as $part) {
214
+
215
+			// Skipping empty parts.
216
+			if (empty($part)) {
217
+				continue;
218
+			}
219
+			list($partName, $partValue) = explode('=', $part);
220
+
221
+			// The value itself had multiple values..
222
+			if (strpos($partValue, ',') !== false) {
223
+				$partValue = explode(',', $partValue);
224
+			}
225
+			$newValue[$partName] = $partValue;
226
+
227
+		}
228
+
229
+		return $newValue;
230
+	}
231
+
232
+	/**
233
+	 * Validates the node for correctness.
234
+	 *
235
+	 * The following options are supported:
236
+	 *   Node::REPAIR - May attempt to automatically repair the problem.
237
+	 *
238
+	 * This method returns an array with detected problems.
239
+	 * Every element has the following properties:
240
+	 *
241
+	 *  * level - problem level.
242
+	 *  * message - A human-readable string describing the issue.
243
+	 *  * node - A reference to the problematic node.
244
+	 *
245
+	 * The level means:
246
+	 *   1 - The issue was repaired (only happens if REPAIR was turned on)
247
+	 *   2 - An inconsequential issue
248
+	 *   3 - A severe issue.
249
+	 *
250
+	 * @param int $options
251
+	 *
252
+	 * @return array
253
+	 */
254
+	public function validate($options = 0) {
255
+
256
+		$repair = ($options & self::REPAIR);
257
+
258
+		$warnings = parent::validate($options);
259
+		$values = $this->getParts();
260
+
261
+		foreach ($values as $key => $value) {
262
+
263
+			if (empty($value)) {
264
+				$warnings[] = [
265
+					'level'   => $repair ? 3 : 1,
266
+					'message' => 'Invalid value for ' . $key . ' in ' . $this->name,
267
+					'node'    => $this
268
+				];
269
+				if ($repair) {
270
+					unset($values[$key]);
271
+				}
272
+			}
273
+
274
+		}
275
+		if (!isset($values['FREQ'])) {
276
+			$warnings[] = [
277
+				'level'   => $repair ? 3 : 1,
278
+				'message' => 'FREQ is required in ' . $this->name,
279
+				'node'    => $this
280
+			];
281
+			if ($repair) {
282
+				$this->parent->remove($this);
283
+			}
284
+		}
285
+		if ($repair) {
286
+			$this->setValue($values);
287
+		}
288
+
289
+		return $warnings;
290
+
291
+	}
292 292
 
293 293
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/ICalendar/Duration.php 1 patch
Indentation   +63 added lines, -63 removed lines patch added patch discarded remove patch
@@ -18,68 +18,68 @@
 block discarded – undo
18 18
  */
19 19
 class Duration extends Property {
20 20
 
21
-    /**
22
-     * In case this is a multi-value property. This string will be used as a
23
-     * delimiter.
24
-     *
25
-     * @var string|null
26
-     */
27
-    public $delimiter = ',';
28
-
29
-    /**
30
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
31
-     *
32
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
33
-     * not yet done, but parameters are not included.
34
-     *
35
-     * @param string $val
36
-     *
37
-     * @return void
38
-     */
39
-    public function setRawMimeDirValue($val) {
40
-
41
-        $this->setValue(explode($this->delimiter, $val));
42
-
43
-    }
44
-
45
-    /**
46
-     * Returns a raw mime-dir representation of the value.
47
-     *
48
-     * @return string
49
-     */
50
-    public function getRawMimeDirValue() {
51
-
52
-        return implode($this->delimiter, $this->getParts());
53
-
54
-    }
55
-
56
-    /**
57
-     * Returns the type of value.
58
-     *
59
-     * This corresponds to the VALUE= parameter. Every property also has a
60
-     * 'default' valueType.
61
-     *
62
-     * @return string
63
-     */
64
-    public function getValueType() {
65
-
66
-        return 'DURATION';
67
-
68
-    }
69
-
70
-    /**
71
-     * Returns a DateInterval representation of the Duration property.
72
-     *
73
-     * If the property has more than one value, only the first is returned.
74
-     *
75
-     * @return \DateInterval
76
-     */
77
-    public function getDateInterval() {
78
-
79
-        $parts = $this->getParts();
80
-        $value = $parts[0];
81
-        return DateTimeParser::parseDuration($value);
82
-
83
-    }
21
+	/**
22
+	 * In case this is a multi-value property. This string will be used as a
23
+	 * delimiter.
24
+	 *
25
+	 * @var string|null
26
+	 */
27
+	public $delimiter = ',';
28
+
29
+	/**
30
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
31
+	 *
32
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
33
+	 * not yet done, but parameters are not included.
34
+	 *
35
+	 * @param string $val
36
+	 *
37
+	 * @return void
38
+	 */
39
+	public function setRawMimeDirValue($val) {
40
+
41
+		$this->setValue(explode($this->delimiter, $val));
42
+
43
+	}
44
+
45
+	/**
46
+	 * Returns a raw mime-dir representation of the value.
47
+	 *
48
+	 * @return string
49
+	 */
50
+	public function getRawMimeDirValue() {
51
+
52
+		return implode($this->delimiter, $this->getParts());
53
+
54
+	}
55
+
56
+	/**
57
+	 * Returns the type of value.
58
+	 *
59
+	 * This corresponds to the VALUE= parameter. Every property also has a
60
+	 * 'default' valueType.
61
+	 *
62
+	 * @return string
63
+	 */
64
+	public function getValueType() {
65
+
66
+		return 'DURATION';
67
+
68
+	}
69
+
70
+	/**
71
+	 * Returns a DateInterval representation of the Duration property.
72
+	 *
73
+	 * If the property has more than one value, only the first is returned.
74
+	 *
75
+	 * @return \DateInterval
76
+	 */
77
+	public function getDateInterval() {
78
+
79
+		$parts = $this->getParts();
80
+		$value = $parts[0];
81
+		return DateTimeParser::parseDuration($value);
82
+
83
+	}
84 84
 
85 85
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/ICalendar/DateTime.php 1 patch
Indentation   +375 added lines, -375 removed lines patch added patch discarded remove patch
@@ -26,379 +26,379 @@
 block discarded – undo
26 26
  */
27 27
 class DateTime extends Property {
28 28
 
29
-    /**
30
-     * In case this is a multi-value property. This string will be used as a
31
-     * delimiter.
32
-     *
33
-     * @var string|null
34
-     */
35
-    public $delimiter = ',';
36
-
37
-    /**
38
-     * Sets a multi-valued property.
39
-     *
40
-     * You may also specify DateTime objects here.
41
-     *
42
-     * @param array $parts
43
-     *
44
-     * @return void
45
-     */
46
-    public function setParts(array $parts) {
47
-
48
-        if (isset($parts[0]) && $parts[0] instanceof DateTimeInterface) {
49
-            $this->setDateTimes($parts);
50
-        } else {
51
-            parent::setParts($parts);
52
-        }
53
-
54
-    }
55
-
56
-    /**
57
-     * Updates the current value.
58
-     *
59
-     * This may be either a single, or multiple strings in an array.
60
-     *
61
-     * Instead of strings, you may also use DateTime here.
62
-     *
63
-     * @param string|array|DateTimeInterface $value
64
-     *
65
-     * @return void
66
-     */
67
-    public function setValue($value) {
68
-
69
-        if (is_array($value) && isset($value[0]) && $value[0] instanceof DateTimeInterface) {
70
-            $this->setDateTimes($value);
71
-        } elseif ($value instanceof DateTimeInterface) {
72
-            $this->setDateTimes([$value]);
73
-        } else {
74
-            parent::setValue($value);
75
-        }
76
-
77
-    }
78
-
79
-    /**
80
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
81
-     *
82
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
83
-     * not yet done, but parameters are not included.
84
-     *
85
-     * @param string $val
86
-     *
87
-     * @return void
88
-     */
89
-    public function setRawMimeDirValue($val) {
90
-
91
-        $this->setValue(explode($this->delimiter, $val));
92
-
93
-    }
94
-
95
-    /**
96
-     * Returns a raw mime-dir representation of the value.
97
-     *
98
-     * @return string
99
-     */
100
-    public function getRawMimeDirValue() {
101
-
102
-        return implode($this->delimiter, $this->getParts());
103
-
104
-    }
105
-
106
-    /**
107
-     * Returns true if this is a DATE-TIME value, false if it's a DATE.
108
-     *
109
-     * @return bool
110
-     */
111
-    public function hasTime() {
112
-
113
-        return strtoupper((string)$this['VALUE']) !== 'DATE';
114
-
115
-    }
116
-
117
-    /**
118
-     * Returns true if this is a floating DATE or DATE-TIME.
119
-     *
120
-     * Note that DATE is always floating.
121
-     */
122
-    public function isFloating() {
123
-
124
-        return
125
-            !$this->hasTime() ||
126
-            (
127
-                !isset($this['TZID']) &&
128
-                strpos($this->getValue(), 'Z') === false
129
-            );
130
-
131
-    }
132
-
133
-    /**
134
-     * Returns a date-time value.
135
-     *
136
-     * Note that if this property contained more than 1 date-time, only the
137
-     * first will be returned. To get an array with multiple values, call
138
-     * getDateTimes.
139
-     *
140
-     * If no timezone information is known, because it's either an all-day
141
-     * property or floating time, we will use the DateTimeZone argument to
142
-     * figure out the exact date.
143
-     *
144
-     * @param DateTimeZone $timeZone
145
-     *
146
-     * @return DateTimeImmutable
147
-     */
148
-    public function getDateTime(DateTimeZone $timeZone = null) {
149
-
150
-        $dt = $this->getDateTimes($timeZone);
151
-        if (!$dt) return;
152
-
153
-        return $dt[0];
154
-
155
-    }
156
-
157
-    /**
158
-     * Returns multiple date-time values.
159
-     *
160
-     * If no timezone information is known, because it's either an all-day
161
-     * property or floating time, we will use the DateTimeZone argument to
162
-     * figure out the exact date.
163
-     *
164
-     * @param DateTimeZone $timeZone
165
-     *
166
-     * @return DateTimeImmutable[]
167
-     * @return \DateTime[]
168
-     */
169
-    public function getDateTimes(DateTimeZone $timeZone = null) {
170
-
171
-        // Does the property have a TZID?
172
-        $tzid = $this['TZID'];
173
-
174
-        if ($tzid) {
175
-            $timeZone = TimeZoneUtil::getTimeZone((string)$tzid, $this->root);
176
-        }
177
-
178
-        $dts = [];
179
-        foreach ($this->getParts() as $part) {
180
-            $dts[] = DateTimeParser::parse($part, $timeZone);
181
-        }
182
-        return $dts;
183
-
184
-    }
185
-
186
-    /**
187
-     * Sets the property as a DateTime object.
188
-     *
189
-     * @param DateTimeInterface $dt
190
-     * @param bool isFloating If set to true, timezones will be ignored.
191
-     *
192
-     * @return void
193
-     */
194
-    public function setDateTime(DateTimeInterface $dt, $isFloating = false) {
195
-
196
-        $this->setDateTimes([$dt], $isFloating);
197
-
198
-    }
199
-
200
-    /**
201
-     * Sets the property as multiple date-time objects.
202
-     *
203
-     * The first value will be used as a reference for the timezones, and all
204
-     * the otehr values will be adjusted for that timezone
205
-     *
206
-     * @param DateTimeInterface[] $dt
207
-     * @param bool isFloating If set to true, timezones will be ignored.
208
-     *
209
-     * @return void
210
-     */
211
-    public function setDateTimes(array $dt, $isFloating = false) {
212
-
213
-        $values = [];
214
-
215
-        if ($this->hasTime()) {
216
-
217
-            $tz = null;
218
-            $isUtc = false;
219
-
220
-            foreach ($dt as $d) {
221
-
222
-                if ($isFloating) {
223
-                    $values[] = $d->format('Ymd\\THis');
224
-                    continue;
225
-                }
226
-                if (is_null($tz)) {
227
-                    $tz = $d->getTimeZone();
228
-                    $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00']);
229
-                    if (!$isUtc) {
230
-                        $this->offsetSet('TZID', $tz->getName());
231
-                    }
232
-                } else {
233
-                    $d = $d->setTimeZone($tz);
234
-                }
235
-
236
-                if ($isUtc) {
237
-                    $values[] = $d->format('Ymd\\THis\\Z');
238
-                } else {
239
-                    $values[] = $d->format('Ymd\\THis');
240
-                }
241
-
242
-            }
243
-            if ($isUtc || $isFloating) {
244
-                $this->offsetUnset('TZID');
245
-            }
246
-
247
-        } else {
248
-
249
-            foreach ($dt as $d) {
250
-
251
-                $values[] = $d->format('Ymd');
252
-
253
-            }
254
-            $this->offsetUnset('TZID');
255
-
256
-        }
257
-
258
-        $this->value = $values;
259
-
260
-    }
261
-
262
-    /**
263
-     * Returns the type of value.
264
-     *
265
-     * This corresponds to the VALUE= parameter. Every property also has a
266
-     * 'default' valueType.
267
-     *
268
-     * @return string
269
-     */
270
-    public function getValueType() {
271
-
272
-        return $this->hasTime() ? 'DATE-TIME' : 'DATE';
273
-
274
-    }
275
-
276
-    /**
277
-     * Returns the value, in the format it should be encoded for JSON.
278
-     *
279
-     * This method must always return an array.
280
-     *
281
-     * @return array
282
-     */
283
-    public function getJsonValue() {
284
-
285
-        $dts = $this->getDateTimes();
286
-        $hasTime = $this->hasTime();
287
-        $isFloating = $this->isFloating();
288
-
289
-        $tz = $dts[0]->getTimeZone();
290
-        $isUtc = $isFloating ? false : in_array($tz->getName(), ['UTC', 'GMT', 'Z']);
291
-
292
-        return array_map(
293
-            public function(DateTimeInterface $dt) use ($hasTime, $isUtc) {
294
-
295
-                if ($hasTime) {
296
-                    return $dt->format('Y-m-d\\TH:i:s') . ($isUtc ? 'Z' : '');
297
-                } else {
298
-                    return $dt->format('Y-m-d');
299
-                }
300
-
301
-            },
302
-            $dts
303
-        );
304
-
305
-    }
306
-
307
-    /**
308
-     * Sets the json value, as it would appear in a jCard or jCal object.
309
-     *
310
-     * The value must always be an array.
311
-     *
312
-     * @param array $value
313
-     *
314
-     * @return void
315
-     */
316
-    public function setJsonValue(array $value) {
317
-
318
-        // dates and times in jCal have one difference to dates and times in
319
-        // iCalendar. In jCal date-parts are separated by dashes, and
320
-        // time-parts are separated by colons. It makes sense to just remove
321
-        // those.
322
-        $this->setValue(
323
-            array_map(
324
-                public function($item) {
325
-
326
-                    return strtr($item, [':' => '', '-' => '']);
327
-
328
-                },
329
-                $value
330
-            )
331
-        );
332
-
333
-    }
334
-
335
-    /**
336
-     * We need to intercept offsetSet, because it may be used to alter the
337
-     * VALUE from DATE-TIME to DATE or vice-versa.
338
-     *
339
-     * @param string $name
340
-     * @param mixed $value
341
-     *
342
-     * @return void
343
-     */
344
-    public function offsetSet($name, $value) {
345
-
346
-        parent::offsetSet($name, $value);
347
-        if (strtoupper($name) !== 'VALUE') {
348
-            return;
349
-        }
350
-
351
-        // This will ensure that dates are correctly encoded.
352
-        $this->setDateTimes($this->getDateTimes());
353
-
354
-    }
355
-
356
-    /**
357
-     * Validates the node for correctness.
358
-     *
359
-     * The following options are supported:
360
-     *   Node::REPAIR - May attempt to automatically repair the problem.
361
-     *
362
-     * This method returns an array with detected problems.
363
-     * Every element has the following properties:
364
-     *
365
-     *  * level - problem level.
366
-     *  * message - A human-readable string describing the issue.
367
-     *  * node - A reference to the problematic node.
368
-     *
369
-     * The level means:
370
-     *   1 - The issue was repaired (only happens if REPAIR was turned on)
371
-     *   2 - An inconsequential issue
372
-     *   3 - A severe issue.
373
-     *
374
-     * @param int $options
375
-     *
376
-     * @return array
377
-     */
378
-    public function validate($options = 0) {
379
-
380
-        $messages = parent::validate($options);
381
-        $valueType = $this->getValueType();
382
-        $values = $this->getParts();
383
-        try {
384
-            foreach ($values as $value) {
385
-                switch ($valueType) {
386
-                    case 'DATE' :
387
-                        DateTimeParser::parseDate($value);
388
-                        break;
389
-                    case 'DATE-TIME' :
390
-                        DateTimeParser::parseDateTime($value);
391
-                        break;
392
-                }
393
-            }
394
-        } catch (InvalidDataException $e) {
395
-            $messages[] = [
396
-                'level'   => 3,
397
-                'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType,
398
-                'node'    => $this,
399
-            ];
400
-        }
401
-        return $messages;
402
-
403
-    }
29
+	/**
30
+	 * In case this is a multi-value property. This string will be used as a
31
+	 * delimiter.
32
+	 *
33
+	 * @var string|null
34
+	 */
35
+	public $delimiter = ',';
36
+
37
+	/**
38
+	 * Sets a multi-valued property.
39
+	 *
40
+	 * You may also specify DateTime objects here.
41
+	 *
42
+	 * @param array $parts
43
+	 *
44
+	 * @return void
45
+	 */
46
+	public function setParts(array $parts) {
47
+
48
+		if (isset($parts[0]) && $parts[0] instanceof DateTimeInterface) {
49
+			$this->setDateTimes($parts);
50
+		} else {
51
+			parent::setParts($parts);
52
+		}
53
+
54
+	}
55
+
56
+	/**
57
+	 * Updates the current value.
58
+	 *
59
+	 * This may be either a single, or multiple strings in an array.
60
+	 *
61
+	 * Instead of strings, you may also use DateTime here.
62
+	 *
63
+	 * @param string|array|DateTimeInterface $value
64
+	 *
65
+	 * @return void
66
+	 */
67
+	public function setValue($value) {
68
+
69
+		if (is_array($value) && isset($value[0]) && $value[0] instanceof DateTimeInterface) {
70
+			$this->setDateTimes($value);
71
+		} elseif ($value instanceof DateTimeInterface) {
72
+			$this->setDateTimes([$value]);
73
+		} else {
74
+			parent::setValue($value);
75
+		}
76
+
77
+	}
78
+
79
+	/**
80
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
81
+	 *
82
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
83
+	 * not yet done, but parameters are not included.
84
+	 *
85
+	 * @param string $val
86
+	 *
87
+	 * @return void
88
+	 */
89
+	public function setRawMimeDirValue($val) {
90
+
91
+		$this->setValue(explode($this->delimiter, $val));
92
+
93
+	}
94
+
95
+	/**
96
+	 * Returns a raw mime-dir representation of the value.
97
+	 *
98
+	 * @return string
99
+	 */
100
+	public function getRawMimeDirValue() {
101
+
102
+		return implode($this->delimiter, $this->getParts());
103
+
104
+	}
105
+
106
+	/**
107
+	 * Returns true if this is a DATE-TIME value, false if it's a DATE.
108
+	 *
109
+	 * @return bool
110
+	 */
111
+	public function hasTime() {
112
+
113
+		return strtoupper((string)$this['VALUE']) !== 'DATE';
114
+
115
+	}
116
+
117
+	/**
118
+	 * Returns true if this is a floating DATE or DATE-TIME.
119
+	 *
120
+	 * Note that DATE is always floating.
121
+	 */
122
+	public function isFloating() {
123
+
124
+		return
125
+			!$this->hasTime() ||
126
+			(
127
+				!isset($this['TZID']) &&
128
+				strpos($this->getValue(), 'Z') === false
129
+			);
130
+
131
+	}
132
+
133
+	/**
134
+	 * Returns a date-time value.
135
+	 *
136
+	 * Note that if this property contained more than 1 date-time, only the
137
+	 * first will be returned. To get an array with multiple values, call
138
+	 * getDateTimes.
139
+	 *
140
+	 * If no timezone information is known, because it's either an all-day
141
+	 * property or floating time, we will use the DateTimeZone argument to
142
+	 * figure out the exact date.
143
+	 *
144
+	 * @param DateTimeZone $timeZone
145
+	 *
146
+	 * @return DateTimeImmutable
147
+	 */
148
+	public function getDateTime(DateTimeZone $timeZone = null) {
149
+
150
+		$dt = $this->getDateTimes($timeZone);
151
+		if (!$dt) return;
152
+
153
+		return $dt[0];
154
+
155
+	}
156
+
157
+	/**
158
+	 * Returns multiple date-time values.
159
+	 *
160
+	 * If no timezone information is known, because it's either an all-day
161
+	 * property or floating time, we will use the DateTimeZone argument to
162
+	 * figure out the exact date.
163
+	 *
164
+	 * @param DateTimeZone $timeZone
165
+	 *
166
+	 * @return DateTimeImmutable[]
167
+	 * @return \DateTime[]
168
+	 */
169
+	public function getDateTimes(DateTimeZone $timeZone = null) {
170
+
171
+		// Does the property have a TZID?
172
+		$tzid = $this['TZID'];
173
+
174
+		if ($tzid) {
175
+			$timeZone = TimeZoneUtil::getTimeZone((string)$tzid, $this->root);
176
+		}
177
+
178
+		$dts = [];
179
+		foreach ($this->getParts() as $part) {
180
+			$dts[] = DateTimeParser::parse($part, $timeZone);
181
+		}
182
+		return $dts;
183
+
184
+	}
185
+
186
+	/**
187
+	 * Sets the property as a DateTime object.
188
+	 *
189
+	 * @param DateTimeInterface $dt
190
+	 * @param bool isFloating If set to true, timezones will be ignored.
191
+	 *
192
+	 * @return void
193
+	 */
194
+	public function setDateTime(DateTimeInterface $dt, $isFloating = false) {
195
+
196
+		$this->setDateTimes([$dt], $isFloating);
197
+
198
+	}
199
+
200
+	/**
201
+	 * Sets the property as multiple date-time objects.
202
+	 *
203
+	 * The first value will be used as a reference for the timezones, and all
204
+	 * the otehr values will be adjusted for that timezone
205
+	 *
206
+	 * @param DateTimeInterface[] $dt
207
+	 * @param bool isFloating If set to true, timezones will be ignored.
208
+	 *
209
+	 * @return void
210
+	 */
211
+	public function setDateTimes(array $dt, $isFloating = false) {
212
+
213
+		$values = [];
214
+
215
+		if ($this->hasTime()) {
216
+
217
+			$tz = null;
218
+			$isUtc = false;
219
+
220
+			foreach ($dt as $d) {
221
+
222
+				if ($isFloating) {
223
+					$values[] = $d->format('Ymd\\THis');
224
+					continue;
225
+				}
226
+				if (is_null($tz)) {
227
+					$tz = $d->getTimeZone();
228
+					$isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00']);
229
+					if (!$isUtc) {
230
+						$this->offsetSet('TZID', $tz->getName());
231
+					}
232
+				} else {
233
+					$d = $d->setTimeZone($tz);
234
+				}
235
+
236
+				if ($isUtc) {
237
+					$values[] = $d->format('Ymd\\THis\\Z');
238
+				} else {
239
+					$values[] = $d->format('Ymd\\THis');
240
+				}
241
+
242
+			}
243
+			if ($isUtc || $isFloating) {
244
+				$this->offsetUnset('TZID');
245
+			}
246
+
247
+		} else {
248
+
249
+			foreach ($dt as $d) {
250
+
251
+				$values[] = $d->format('Ymd');
252
+
253
+			}
254
+			$this->offsetUnset('TZID');
255
+
256
+		}
257
+
258
+		$this->value = $values;
259
+
260
+	}
261
+
262
+	/**
263
+	 * Returns the type of value.
264
+	 *
265
+	 * This corresponds to the VALUE= parameter. Every property also has a
266
+	 * 'default' valueType.
267
+	 *
268
+	 * @return string
269
+	 */
270
+	public function getValueType() {
271
+
272
+		return $this->hasTime() ? 'DATE-TIME' : 'DATE';
273
+
274
+	}
275
+
276
+	/**
277
+	 * Returns the value, in the format it should be encoded for JSON.
278
+	 *
279
+	 * This method must always return an array.
280
+	 *
281
+	 * @return array
282
+	 */
283
+	public function getJsonValue() {
284
+
285
+		$dts = $this->getDateTimes();
286
+		$hasTime = $this->hasTime();
287
+		$isFloating = $this->isFloating();
288
+
289
+		$tz = $dts[0]->getTimeZone();
290
+		$isUtc = $isFloating ? false : in_array($tz->getName(), ['UTC', 'GMT', 'Z']);
291
+
292
+		return array_map(
293
+			public function(DateTimeInterface $dt) use ($hasTime, $isUtc) {
294
+
295
+				if ($hasTime) {
296
+					return $dt->format('Y-m-d\\TH:i:s') . ($isUtc ? 'Z' : '');
297
+				} else {
298
+					return $dt->format('Y-m-d');
299
+				}
300
+
301
+			},
302
+			$dts
303
+		);
304
+
305
+	}
306
+
307
+	/**
308
+	 * Sets the json value, as it would appear in a jCard or jCal object.
309
+	 *
310
+	 * The value must always be an array.
311
+	 *
312
+	 * @param array $value
313
+	 *
314
+	 * @return void
315
+	 */
316
+	public function setJsonValue(array $value) {
317
+
318
+		// dates and times in jCal have one difference to dates and times in
319
+		// iCalendar. In jCal date-parts are separated by dashes, and
320
+		// time-parts are separated by colons. It makes sense to just remove
321
+		// those.
322
+		$this->setValue(
323
+			array_map(
324
+				public function($item) {
325
+
326
+					return strtr($item, [':' => '', '-' => '']);
327
+
328
+				},
329
+				$value
330
+			)
331
+		);
332
+
333
+	}
334
+
335
+	/**
336
+	 * We need to intercept offsetSet, because it may be used to alter the
337
+	 * VALUE from DATE-TIME to DATE or vice-versa.
338
+	 *
339
+	 * @param string $name
340
+	 * @param mixed $value
341
+	 *
342
+	 * @return void
343
+	 */
344
+	public function offsetSet($name, $value) {
345
+
346
+		parent::offsetSet($name, $value);
347
+		if (strtoupper($name) !== 'VALUE') {
348
+			return;
349
+		}
350
+
351
+		// This will ensure that dates are correctly encoded.
352
+		$this->setDateTimes($this->getDateTimes());
353
+
354
+	}
355
+
356
+	/**
357
+	 * Validates the node for correctness.
358
+	 *
359
+	 * The following options are supported:
360
+	 *   Node::REPAIR - May attempt to automatically repair the problem.
361
+	 *
362
+	 * This method returns an array with detected problems.
363
+	 * Every element has the following properties:
364
+	 *
365
+	 *  * level - problem level.
366
+	 *  * message - A human-readable string describing the issue.
367
+	 *  * node - A reference to the problematic node.
368
+	 *
369
+	 * The level means:
370
+	 *   1 - The issue was repaired (only happens if REPAIR was turned on)
371
+	 *   2 - An inconsequential issue
372
+	 *   3 - A severe issue.
373
+	 *
374
+	 * @param int $options
375
+	 *
376
+	 * @return array
377
+	 */
378
+	public function validate($options = 0) {
379
+
380
+		$messages = parent::validate($options);
381
+		$valueType = $this->getValueType();
382
+		$values = $this->getParts();
383
+		try {
384
+			foreach ($values as $value) {
385
+				switch ($valueType) {
386
+					case 'DATE' :
387
+						DateTimeParser::parseDate($value);
388
+						break;
389
+					case 'DATE-TIME' :
390
+						DateTimeParser::parseDateTime($value);
391
+						break;
392
+				}
393
+			}
394
+		} catch (InvalidDataException $e) {
395
+			$messages[] = [
396
+				'level'   => 3,
397
+				'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType,
398
+				'node'    => $this,
399
+			];
400
+		}
401
+		return $messages;
402
+
403
+	}
404 404
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/Unknown.php 1 patch
Indentation   +26 added lines, -26 removed lines patch added patch discarded remove patch
@@ -14,31 +14,31 @@
 block discarded – undo
14 14
  */
15 15
 class Unknown extends Text {
16 16
 
17
-    /**
18
-     * Returns the value, in the format it should be encoded for json.
19
-     *
20
-     * This method must always return an array.
21
-     *
22
-     * @return array
23
-     */
24
-    public function getJsonValue() {
25
-
26
-        return [$this->getRawMimeDirValue()];
27
-
28
-    }
29
-
30
-    /**
31
-     * Returns the type of value.
32
-     *
33
-     * This corresponds to the VALUE= parameter. Every property also has a
34
-     * 'default' valueType.
35
-     *
36
-     * @return string
37
-     */
38
-    public function getValueType() {
39
-
40
-        return 'UNKNOWN';
41
-
42
-    }
17
+	/**
18
+	 * Returns the value, in the format it should be encoded for json.
19
+	 *
20
+	 * This method must always return an array.
21
+	 *
22
+	 * @return array
23
+	 */
24
+	public function getJsonValue() {
25
+
26
+		return [$this->getRawMimeDirValue()];
27
+
28
+	}
29
+
30
+	/**
31
+	 * Returns the type of value.
32
+	 *
33
+	 * This corresponds to the VALUE= parameter. Every property also has a
34
+	 * 'default' valueType.
35
+	 *
36
+	 * @return string
37
+	 */
38
+	public function getValueType() {
39
+
40
+		return 'UNKNOWN';
41
+
42
+	}
43 43
 
44 44
 }
Please login to merge, or discard this patch.