Completed
Pull Request — developer (#4001)
by Thom
542:26 queued 508:45
created
libraries/SabreDAV/VObject/Component/VCalendar.php 3 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -6,10 +6,10 @@
 block discarded – undo
6 6
 use DateTimeZone;
7 7
 use Sabre\VObject;
8 8
 use Sabre\VObject\Component;
9
+use Sabre\VObject\InvalidDataException;
9 10
 use Sabre\VObject\Property;
10 11
 use Sabre\VObject\Recur\EventIterator;
11 12
 use Sabre\VObject\Recur\NoInstancesException;
12
-use Sabre\VObject\InvalidDataException;
13 13
 
14 14
 /**
15 15
  * The VCalendar component.
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
                 // converting everything to UTC.
332 332
                 if ($child->name === 'VEVENT' && (isset($child->{'RECURRENCE-ID'}) || isset($child->RRULE) || isset($child->RDATE))) {
333 333
                     // Handle these a bit later.
334
-                    $uid = (string)$child->UID;
334
+                    $uid = (string) $child->UID;
335 335
                     if (!$uid) {
336 336
                         throw new InvalidDataException('Every VEVENT object must have a UID property');
337 337
                     }
@@ -449,7 +449,7 @@  discard block
 block discarded – undo
449 449
         $warnings = parent::validate($options);
450 450
 
451 451
         if ($ver = $this->VERSION) {
452
-            if ((string)$ver !== '2.0') {
452
+            if ((string) $ver !== '2.0') {
453 453
                 $warnings[] = [
454 454
                     'level'   => 3,
455 455
                     'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
@@ -472,7 +472,7 @@  discard block
 block discarded – undo
472 472
                 }
473 473
                 $componentTypes[] = $child->name;
474 474
 
475
-                $uid = (string)$child->UID;
475
+                $uid = (string) $child->UID;
476 476
                 $isMaster = isset($child->{'RECURRENCE-ID'}) ? 0 : 1;
477 477
                 if (isset($uidList[$uid])) {
478 478
                     $uidList[$uid]['count']++;
Please login to merge, or discard this patch.
Indentation   +534 added lines, -534 removed lines patch added patch discarded remove patch
@@ -22,540 +22,540 @@
 block discarded – undo
22 22
  */
23 23
 class VCalendar extends VObject\Document {
24 24
 
25
-    /**
26
-     * The default name for this component.
27
-     *
28
-     * This should be 'VCALENDAR' or 'VCARD'.
29
-     *
30
-     * @var string
31
-     */
32
-    static $defaultName = 'VCALENDAR';
33
-
34
-    /**
35
-     * This is a list of components, and which classes they should map to.
36
-     *
37
-     * @var array
38
-     */
39
-    static $componentMap = [
40
-        'VCALENDAR'     => 'Sabre\\VObject\\Component\\VCalendar',
41
-        'VALARM'        => 'Sabre\\VObject\\Component\\VAlarm',
42
-        'VEVENT'        => 'Sabre\\VObject\\Component\\VEvent',
43
-        'VFREEBUSY'     => 'Sabre\\VObject\\Component\\VFreeBusy',
44
-        'VAVAILABILITY' => 'Sabre\\VObject\\Component\\VAvailability',
45
-        'AVAILABLE'     => 'Sabre\\VObject\\Component\\Available',
46
-        'VJOURNAL'      => 'Sabre\\VObject\\Component\\VJournal',
47
-        'VTIMEZONE'     => 'Sabre\\VObject\\Component\\VTimeZone',
48
-        'VTODO'         => 'Sabre\\VObject\\Component\\VTodo',
49
-    ];
50
-
51
-    /**
52
-     * List of value-types, and which classes they map to.
53
-     *
54
-     * @var array
55
-     */
56
-    static $valueMap = [
57
-        'BINARY'           => 'Sabre\\VObject\\Property\\Binary',
58
-        'BOOLEAN'          => 'Sabre\\VObject\\Property\\Boolean',
59
-        'CAL-ADDRESS'      => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
60
-        'DATE'             => 'Sabre\\VObject\\Property\\ICalendar\\Date',
61
-        'DATE-TIME'        => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
62
-        'DURATION'         => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
63
-        'FLOAT'            => 'Sabre\\VObject\\Property\\FloatValue',
64
-        'INTEGER'          => 'Sabre\\VObject\\Property\\IntegerValue',
65
-        'PERIOD'           => 'Sabre\\VObject\\Property\\ICalendar\\Period',
66
-        'RECUR'            => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
67
-        'TEXT'             => 'Sabre\\VObject\\Property\\Text',
68
-        'TIME'             => 'Sabre\\VObject\\Property\\Time',
69
-        'UNKNOWN'          => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
70
-        'URI'              => 'Sabre\\VObject\\Property\\Uri',
71
-        'UTC-OFFSET'       => 'Sabre\\VObject\\Property\\UtcOffset',
72
-    ];
73
-
74
-    /**
75
-     * List of properties, and which classes they map to.
76
-     *
77
-     * @var array
78
-     */
79
-    static $propertyMap = [
80
-        // Calendar properties
81
-        'CALSCALE'      => 'Sabre\\VObject\\Property\\FlatText',
82
-        'METHOD'        => 'Sabre\\VObject\\Property\\FlatText',
83
-        'PRODID'        => 'Sabre\\VObject\\Property\\FlatText',
84
-        'VERSION'       => 'Sabre\\VObject\\Property\\FlatText',
85
-
86
-        // Component properties
87
-        'ATTACH'            => 'Sabre\\VObject\\Property\\Uri',
88
-        'CATEGORIES'        => 'Sabre\\VObject\\Property\\Text',
89
-        'CLASS'             => 'Sabre\\VObject\\Property\\FlatText',
90
-        'COMMENT'           => 'Sabre\\VObject\\Property\\FlatText',
91
-        'DESCRIPTION'       => 'Sabre\\VObject\\Property\\FlatText',
92
-        'GEO'               => 'Sabre\\VObject\\Property\\FloatValue',
93
-        'LOCATION'          => 'Sabre\\VObject\\Property\\FlatText',
94
-        'PERCENT-COMPLETE'  => 'Sabre\\VObject\\Property\\IntegerValue',
95
-        'PRIORITY'          => 'Sabre\\VObject\\Property\\IntegerValue',
96
-        'RESOURCES'         => 'Sabre\\VObject\\Property\\Text',
97
-        'STATUS'            => 'Sabre\\VObject\\Property\\FlatText',
98
-        'SUMMARY'           => 'Sabre\\VObject\\Property\\FlatText',
99
-
100
-        // Date and Time Component Properties
101
-        'COMPLETED'     => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
102
-        'DTEND'         => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
103
-        'DUE'           => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
104
-        'DTSTART'       => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
105
-        'DURATION'      => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
106
-        'FREEBUSY'      => 'Sabre\\VObject\\Property\\ICalendar\\Period',
107
-        'TRANSP'        => 'Sabre\\VObject\\Property\\FlatText',
108
-
109
-        // Time Zone Component Properties
110
-        'TZID'          => 'Sabre\\VObject\\Property\\FlatText',
111
-        'TZNAME'        => 'Sabre\\VObject\\Property\\FlatText',
112
-        'TZOFFSETFROM'  => 'Sabre\\VObject\\Property\\UtcOffset',
113
-        'TZOFFSETTO'    => 'Sabre\\VObject\\Property\\UtcOffset',
114
-        'TZURL'         => 'Sabre\\VObject\\Property\\Uri',
115
-
116
-        // Relationship Component Properties
117
-        'ATTENDEE'      => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
118
-        'CONTACT'       => 'Sabre\\VObject\\Property\\FlatText',
119
-        'ORGANIZER'     => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
120
-        'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
121
-        'RELATED-TO'    => 'Sabre\\VObject\\Property\\FlatText',
122
-        'URL'           => 'Sabre\\VObject\\Property\\Uri',
123
-        'UID'           => 'Sabre\\VObject\\Property\\FlatText',
124
-
125
-        // Recurrence Component Properties
126
-        'EXDATE'        => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
127
-        'RDATE'         => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
128
-        'RRULE'         => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
129
-        'EXRULE'        => 'Sabre\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545
130
-
131
-        // Alarm Component Properties
132
-        'ACTION'        => 'Sabre\\VObject\\Property\\FlatText',
133
-        'REPEAT'        => 'Sabre\\VObject\\Property\\IntegerValue',
134
-        'TRIGGER'       => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
135
-
136
-        // Change Management Component Properties
137
-        'CREATED'       => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
138
-        'DTSTAMP'       => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
139
-        'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
140
-        'SEQUENCE'      => 'Sabre\\VObject\\Property\\IntegerValue',
141
-
142
-        // Request Status
143
-        'REQUEST-STATUS' => 'Sabre\\VObject\\Property\\Text',
144
-
145
-        // Additions from draft-daboo-valarm-extensions-04
146
-        'ALARM-AGENT'    => 'Sabre\\VObject\\Property\\Text',
147
-        'ACKNOWLEDGED'   => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
148
-        'PROXIMITY'      => 'Sabre\\VObject\\Property\\Text',
149
-        'DEFAULT-ALARM'  => 'Sabre\\VObject\\Property\\Boolean',
150
-
151
-        // Additions from draft-daboo-calendar-availability-05
152
-        'BUSYTYPE'       => 'Sabre\\VObject\\Property\\Text',
153
-
154
-    ];
155
-
156
-    /**
157
-     * Returns the current document type.
158
-     *
159
-     * @return int
160
-     */
161
-    public function getDocumentType() {
162
-
163
-        return self::ICALENDAR20;
164
-
165
-    }
166
-
167
-    /**
168
-     * Returns a list of all 'base components'. For instance, if an Event has
169
-     * a recurrence rule, and one instance is overridden, the overridden event
170
-     * will have the same UID, but will be excluded from this list.
171
-     *
172
-     * VTIMEZONE components will always be excluded.
173
-     *
174
-     * @param string $componentName filter by component name
175
-     *
176
-     * @return VObject\Component[]
177
-     */
178
-    public function getBaseComponents($componentName = null) {
179
-
180
-        $isBaseComponent = function($component) {
181
-
182
-            if (!$component instanceof VObject\Component) {
183
-                return false;
184
-            }
185
-            if ($component->name === 'VTIMEZONE') {
186
-                return false;
187
-            }
188
-            if (isset($component->{'RECURRENCE-ID'})) {
189
-                return false;
190
-            }
191
-            return true;
192
-
193
-        };
194
-
195
-        if ($componentName) {
196
-            // Early exit
197
-            return array_filter(
198
-                $this->select($componentName),
199
-                $isBaseComponent
200
-            );
201
-        }
202
-
203
-        $components = [];
204
-        foreach ($this->children as $childGroup) {
205
-
206
-            foreach ($childGroup as $child) {
207
-
208
-                if (!$child instanceof Component) {
209
-                    // If one child is not a component, they all are so we skip
210
-                    // the entire group.
211
-                    continue 2;
212
-                }
213
-                if ($isBaseComponent($child)) {
214
-                    $components[] = $child;
215
-                }
216
-
217
-            }
218
-
219
-        }
220
-        return $components;
221
-
222
-    }
223
-
224
-    /**
225
-     * Returns the first component that is not a VTIMEZONE, and does not have
226
-     * an RECURRENCE-ID.
227
-     *
228
-     * If there is no such component, null will be returned.
229
-     *
230
-     * @param string $componentName filter by component name
231
-     *
232
-     * @return VObject\Component|null
233
-     */
234
-    public function getBaseComponent($componentName = null) {
235
-
236
-        $isBaseComponent = function($component) {
237
-
238
-            if (!$component instanceof VObject\Component) {
239
-                return false;
240
-            }
241
-            if ($component->name === 'VTIMEZONE') {
242
-                return false;
243
-            }
244
-            if (isset($component->{'RECURRENCE-ID'})) {
245
-                return false;
246
-            }
247
-            return true;
248
-
249
-        };
250
-
251
-        if ($componentName) {
252
-            foreach ($this->select($componentName) as $child) {
253
-                if ($isBaseComponent($child)) {
254
-                    return $child;
255
-                }
256
-            }
257
-            return null;
258
-        }
259
-
260
-        // Searching all components
261
-        foreach ($this->children as $childGroup) {
262
-            foreach ($childGroup as $child) {
263
-                if ($isBaseComponent($child)) {
264
-                    return $child;
265
-                }
266
-            }
267
-
268
-        }
269
-        return null;
270
-
271
-    }
272
-
273
-    /**
274
-     * Expand all events in this VCalendar object and return a new VCalendar
275
-     * with the expanded events.
276
-     *
277
-     * If this calendar object, has events with recurrence rules, this method
278
-     * can be used to expand the event into multiple sub-events.
279
-     *
280
-     * Each event will be stripped from it's recurrence information, and only
281
-     * the instances of the event in the specified timerange will be left
282
-     * alone.
283
-     *
284
-     * In addition, this method will cause timezone information to be stripped,
285
-     * and normalized to UTC.
286
-     *
287
-     * @param DateTimeInterface $start
288
-     * @param DateTimeInterface $end
289
-     * @param DateTimeZone $timeZone reference timezone for floating dates and
290
-     *                     times.
291
-     * @return VCalendar
292
-     */
293
-    public function expand(DateTimeInterface $start, DateTimeInterface $end, DateTimeZone $timeZone = null) {
294
-
295
-        $newChildren = [];
296
-        $recurringEvents = [];
297
-
298
-        if (!$timeZone) {
299
-            $timeZone = new DateTimeZone('UTC');
300
-        }
301
-
302
-        $stripTimezones = function(Component $component) use ($timeZone, &$stripTimezones) {
303
-
304
-            foreach ($component->children() as $componentChild) {
305
-                if ($componentChild instanceof Property\ICalendar\DateTime && $componentChild->hasTime()) {
306
-
307
-                    $dt = $componentChild->getDateTimes($timeZone);
308
-                    // We only need to update the first timezone, because
309
-                    // setDateTimes will match all other timezones to the
310
-                    // first.
311
-                    $dt[0] = $dt[0]->setTimeZone(new DateTimeZone('UTC'));
312
-                    $componentChild->setDateTimes($dt);
313
-                } elseif ($componentChild instanceof Component) {
314
-                    $stripTimezones($componentChild);
315
-                }
316
-
317
-            }
318
-            return $component;
319
-
320
-        };
321
-
322
-        foreach ($this->children() as $child) {
323
-
324
-            if ($child instanceof Property && $child->name !== 'PRODID') {
325
-                // We explictly want to ignore PRODID, because we want to
326
-                // overwrite it with our own.
327
-                $newChildren[] = clone $child;
328
-            } elseif ($child instanceof Component && $child->name !== 'VTIMEZONE') {
329
-
330
-                // We're also stripping all VTIMEZONE objects because we're
331
-                // converting everything to UTC.
332
-                if ($child->name === 'VEVENT' && (isset($child->{'RECURRENCE-ID'}) || isset($child->RRULE) || isset($child->RDATE))) {
333
-                    // Handle these a bit later.
334
-                    $uid = (string)$child->UID;
335
-                    if (!$uid) {
336
-                        throw new InvalidDataException('Every VEVENT object must have a UID property');
337
-                    }
338
-                    if (isset($recurringEvents[$uid])) {
339
-                        $recurringEvents[$uid][] = clone $child;
340
-                    } else {
341
-                        $recurringEvents[$uid] = [clone $child];
342
-                    }
343
-                } elseif ($child->name === 'VEVENT' && $child->isInTimeRange($start, $end)) {
344
-                    $newChildren[] = $stripTimezones(clone $child);
345
-                }
346
-
347
-            }
348
-
349
-        }
350
-
351
-        foreach ($recurringEvents as $events) {
352
-
353
-            try {
354
-                $it = new EventIterator($events, $timeZone);
355
-
356
-            } catch (NoInstancesException $e) {
357
-                // This event is recurring, but it doesn't have a single
358
-                // instance. We are skipping this event from the output
359
-                // entirely.
360
-                continue;
361
-            }
362
-            $it->fastForward($start);
363
-
364
-            while ($it->valid() && $it->getDTStart() < $end) {
365
-
366
-                if ($it->getDTEnd() > $start) {
367
-
368
-                    $newChildren[] = $stripTimezones($it->getEventObject());
369
-
370
-                }
371
-                $it->next();
372
-
373
-            }
374
-
375
-        }
376
-
377
-        return new self($newChildren);
378
-
379
-    }
380
-
381
-    /**
382
-     * This method should return a list of default property values.
383
-     *
384
-     * @return array
385
-     */
386
-    protected function getDefaults() {
387
-
388
-        return [
389
-            'VERSION'  => '2.0',
390
-            'PRODID'   => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
391
-            'CALSCALE' => 'GREGORIAN',
392
-        ];
393
-
394
-    }
395
-
396
-    /**
397
-     * A simple list of validation rules.
398
-     *
399
-     * This is simply a list of properties, and how many times they either
400
-     * must or must not appear.
401
-     *
402
-     * Possible values per property:
403
-     *   * 0 - Must not appear.
404
-     *   * 1 - Must appear exactly once.
405
-     *   * + - Must appear at least once.
406
-     *   * * - Can appear any number of times.
407
-     *   * ? - May appear, but not more than once.
408
-     *
409
-     * @var array
410
-     */
411
-    public function getValidationRules() {
412
-
413
-        return [
414
-            'PRODID'  => 1,
415
-            'VERSION' => 1,
416
-
417
-            'CALSCALE' => '?',
418
-            'METHOD'   => '?',
419
-        ];
420
-
421
-    }
422
-
423
-    /**
424
-     * Validates the node for correctness.
425
-     *
426
-     * The following options are supported:
427
-     *   Node::REPAIR - May attempt to automatically repair the problem.
428
-     *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
429
-     *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
430
-     *
431
-     * This method returns an array with detected problems.
432
-     * Every element has the following properties:
433
-     *
434
-     *  * level - problem level.
435
-     *  * message - A human-readable string describing the issue.
436
-     *  * node - A reference to the problematic node.
437
-     *
438
-     * The level means:
439
-     *   1 - The issue was repaired (only happens if REPAIR was turned on).
440
-     *   2 - A warning.
441
-     *   3 - An error.
442
-     *
443
-     * @param int $options
444
-     *
445
-     * @return array
446
-     */
447
-    public function validate($options = 0) {
448
-
449
-        $warnings = parent::validate($options);
450
-
451
-        if ($ver = $this->VERSION) {
452
-            if ((string)$ver !== '2.0') {
453
-                $warnings[] = [
454
-                    'level'   => 3,
455
-                    'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
456
-                    'node'    => $this,
457
-                ];
458
-            }
459
-
460
-        }
461
-
462
-        $uidList = [];
463
-        $componentsFound = 0;
464
-        $componentTypes = [];
465
-
466
-        foreach ($this->children() as $child) {
467
-            if ($child instanceof Component) {
468
-                $componentsFound++;
469
-
470
-                if (!in_array($child->name, ['VEVENT', 'VTODO', 'VJOURNAL'])) {
471
-                    continue;
472
-                }
473
-                $componentTypes[] = $child->name;
474
-
475
-                $uid = (string)$child->UID;
476
-                $isMaster = isset($child->{'RECURRENCE-ID'}) ? 0 : 1;
477
-                if (isset($uidList[$uid])) {
478
-                    $uidList[$uid]['count']++;
479
-                    if ($isMaster && $uidList[$uid]['hasMaster']) {
480
-                        $warnings[] = [
481
-                            'level'   => 3,
482
-                            'message' => 'More than one master object was found for the object with UID ' . $uid,
483
-                            'node'    => $this,
484
-                        ];
485
-                    }
486
-                    $uidList[$uid]['hasMaster'] += $isMaster;
487
-                } else {
488
-                    $uidList[$uid] = [
489
-                        'count'     => 1,
490
-                        'hasMaster' => $isMaster,
491
-                    ];
492
-                }
493
-
494
-            }
495
-        }
496
-
497
-        if ($componentsFound === 0) {
498
-            $warnings[] = [
499
-                'level'   => 3,
500
-                'message' => 'An iCalendar object must have at least 1 component.',
501
-                'node'    => $this,
502
-            ];
503
-        }
504
-
505
-        if ($options & self::PROFILE_CALDAV) {
506
-            if (count($uidList) > 1) {
507
-                $warnings[] = [
508
-                    'level'   => 3,
509
-                    'message' => 'A calendar object on a CalDAV server may only have components with the same UID.',
510
-                    'node'    => $this,
511
-                ];
512
-            }
513
-            if (count($componentTypes) === 0) {
514
-                $warnings[] = [
515
-                    'level'   => 3,
516
-                    'message' => 'A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL).',
517
-                    'node'    => $this,
518
-                ];
519
-            }
520
-            if (count(array_unique($componentTypes)) > 1) {
521
-                $warnings[] = [
522
-                    'level'   => 3,
523
-                    'message' => 'A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL).',
524
-                    'node'    => $this,
525
-                ];
526
-            }
527
-
528
-            if (isset($this->METHOD)) {
529
-                $warnings[] = [
530
-                    'level'   => 3,
531
-                    'message' => 'A calendar object on a CalDAV server MUST NOT have a METHOD property.',
532
-                    'node'    => $this,
533
-                ];
534
-            }
535
-        }
536
-
537
-        return $warnings;
538
-
539
-    }
540
-
541
-    /**
542
-     * Returns all components with a specific UID value.
543
-     *
544
-     * @return array
545
-     */
546
-    public function getByUID($uid) {
547
-
548
-        return array_filter($this->getComponents(), function($item) use ($uid) {
549
-
550
-            if (!$itemUid = $item->select('UID')) {
551
-                return false;
552
-            }
553
-            $itemUid = current($itemUid)->getValue();
554
-            return $uid === $itemUid;
555
-
556
-        });
557
-
558
-    }
25
+	/**
26
+	 * The default name for this component.
27
+	 *
28
+	 * This should be 'VCALENDAR' or 'VCARD'.
29
+	 *
30
+	 * @var string
31
+	 */
32
+	static $defaultName = 'VCALENDAR';
33
+
34
+	/**
35
+	 * This is a list of components, and which classes they should map to.
36
+	 *
37
+	 * @var array
38
+	 */
39
+	static $componentMap = [
40
+		'VCALENDAR'     => 'Sabre\\VObject\\Component\\VCalendar',
41
+		'VALARM'        => 'Sabre\\VObject\\Component\\VAlarm',
42
+		'VEVENT'        => 'Sabre\\VObject\\Component\\VEvent',
43
+		'VFREEBUSY'     => 'Sabre\\VObject\\Component\\VFreeBusy',
44
+		'VAVAILABILITY' => 'Sabre\\VObject\\Component\\VAvailability',
45
+		'AVAILABLE'     => 'Sabre\\VObject\\Component\\Available',
46
+		'VJOURNAL'      => 'Sabre\\VObject\\Component\\VJournal',
47
+		'VTIMEZONE'     => 'Sabre\\VObject\\Component\\VTimeZone',
48
+		'VTODO'         => 'Sabre\\VObject\\Component\\VTodo',
49
+	];
50
+
51
+	/**
52
+	 * List of value-types, and which classes they map to.
53
+	 *
54
+	 * @var array
55
+	 */
56
+	static $valueMap = [
57
+		'BINARY'           => 'Sabre\\VObject\\Property\\Binary',
58
+		'BOOLEAN'          => 'Sabre\\VObject\\Property\\Boolean',
59
+		'CAL-ADDRESS'      => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
60
+		'DATE'             => 'Sabre\\VObject\\Property\\ICalendar\\Date',
61
+		'DATE-TIME'        => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
62
+		'DURATION'         => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
63
+		'FLOAT'            => 'Sabre\\VObject\\Property\\FloatValue',
64
+		'INTEGER'          => 'Sabre\\VObject\\Property\\IntegerValue',
65
+		'PERIOD'           => 'Sabre\\VObject\\Property\\ICalendar\\Period',
66
+		'RECUR'            => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
67
+		'TEXT'             => 'Sabre\\VObject\\Property\\Text',
68
+		'TIME'             => 'Sabre\\VObject\\Property\\Time',
69
+		'UNKNOWN'          => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
70
+		'URI'              => 'Sabre\\VObject\\Property\\Uri',
71
+		'UTC-OFFSET'       => 'Sabre\\VObject\\Property\\UtcOffset',
72
+	];
73
+
74
+	/**
75
+	 * List of properties, and which classes they map to.
76
+	 *
77
+	 * @var array
78
+	 */
79
+	static $propertyMap = [
80
+		// Calendar properties
81
+		'CALSCALE'      => 'Sabre\\VObject\\Property\\FlatText',
82
+		'METHOD'        => 'Sabre\\VObject\\Property\\FlatText',
83
+		'PRODID'        => 'Sabre\\VObject\\Property\\FlatText',
84
+		'VERSION'       => 'Sabre\\VObject\\Property\\FlatText',
85
+
86
+		// Component properties
87
+		'ATTACH'            => 'Sabre\\VObject\\Property\\Uri',
88
+		'CATEGORIES'        => 'Sabre\\VObject\\Property\\Text',
89
+		'CLASS'             => 'Sabre\\VObject\\Property\\FlatText',
90
+		'COMMENT'           => 'Sabre\\VObject\\Property\\FlatText',
91
+		'DESCRIPTION'       => 'Sabre\\VObject\\Property\\FlatText',
92
+		'GEO'               => 'Sabre\\VObject\\Property\\FloatValue',
93
+		'LOCATION'          => 'Sabre\\VObject\\Property\\FlatText',
94
+		'PERCENT-COMPLETE'  => 'Sabre\\VObject\\Property\\IntegerValue',
95
+		'PRIORITY'          => 'Sabre\\VObject\\Property\\IntegerValue',
96
+		'RESOURCES'         => 'Sabre\\VObject\\Property\\Text',
97
+		'STATUS'            => 'Sabre\\VObject\\Property\\FlatText',
98
+		'SUMMARY'           => 'Sabre\\VObject\\Property\\FlatText',
99
+
100
+		// Date and Time Component Properties
101
+		'COMPLETED'     => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
102
+		'DTEND'         => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
103
+		'DUE'           => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
104
+		'DTSTART'       => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
105
+		'DURATION'      => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
106
+		'FREEBUSY'      => 'Sabre\\VObject\\Property\\ICalendar\\Period',
107
+		'TRANSP'        => 'Sabre\\VObject\\Property\\FlatText',
108
+
109
+		// Time Zone Component Properties
110
+		'TZID'          => 'Sabre\\VObject\\Property\\FlatText',
111
+		'TZNAME'        => 'Sabre\\VObject\\Property\\FlatText',
112
+		'TZOFFSETFROM'  => 'Sabre\\VObject\\Property\\UtcOffset',
113
+		'TZOFFSETTO'    => 'Sabre\\VObject\\Property\\UtcOffset',
114
+		'TZURL'         => 'Sabre\\VObject\\Property\\Uri',
115
+
116
+		// Relationship Component Properties
117
+		'ATTENDEE'      => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
118
+		'CONTACT'       => 'Sabre\\VObject\\Property\\FlatText',
119
+		'ORGANIZER'     => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
120
+		'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
121
+		'RELATED-TO'    => 'Sabre\\VObject\\Property\\FlatText',
122
+		'URL'           => 'Sabre\\VObject\\Property\\Uri',
123
+		'UID'           => 'Sabre\\VObject\\Property\\FlatText',
124
+
125
+		// Recurrence Component Properties
126
+		'EXDATE'        => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
127
+		'RDATE'         => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
128
+		'RRULE'         => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
129
+		'EXRULE'        => 'Sabre\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545
130
+
131
+		// Alarm Component Properties
132
+		'ACTION'        => 'Sabre\\VObject\\Property\\FlatText',
133
+		'REPEAT'        => 'Sabre\\VObject\\Property\\IntegerValue',
134
+		'TRIGGER'       => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
135
+
136
+		// Change Management Component Properties
137
+		'CREATED'       => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
138
+		'DTSTAMP'       => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
139
+		'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
140
+		'SEQUENCE'      => 'Sabre\\VObject\\Property\\IntegerValue',
141
+
142
+		// Request Status
143
+		'REQUEST-STATUS' => 'Sabre\\VObject\\Property\\Text',
144
+
145
+		// Additions from draft-daboo-valarm-extensions-04
146
+		'ALARM-AGENT'    => 'Sabre\\VObject\\Property\\Text',
147
+		'ACKNOWLEDGED'   => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
148
+		'PROXIMITY'      => 'Sabre\\VObject\\Property\\Text',
149
+		'DEFAULT-ALARM'  => 'Sabre\\VObject\\Property\\Boolean',
150
+
151
+		// Additions from draft-daboo-calendar-availability-05
152
+		'BUSYTYPE'       => 'Sabre\\VObject\\Property\\Text',
153
+
154
+	];
155
+
156
+	/**
157
+	 * Returns the current document type.
158
+	 *
159
+	 * @return int
160
+	 */
161
+	public function getDocumentType() {
162
+
163
+		return self::ICALENDAR20;
164
+
165
+	}
166
+
167
+	/**
168
+	 * Returns a list of all 'base components'. For instance, if an Event has
169
+	 * a recurrence rule, and one instance is overridden, the overridden event
170
+	 * will have the same UID, but will be excluded from this list.
171
+	 *
172
+	 * VTIMEZONE components will always be excluded.
173
+	 *
174
+	 * @param string $componentName filter by component name
175
+	 *
176
+	 * @return VObject\Component[]
177
+	 */
178
+	public function getBaseComponents($componentName = null) {
179
+
180
+		$isBaseComponent = function($component) {
181
+
182
+			if (!$component instanceof VObject\Component) {
183
+				return false;
184
+			}
185
+			if ($component->name === 'VTIMEZONE') {
186
+				return false;
187
+			}
188
+			if (isset($component->{'RECURRENCE-ID'})) {
189
+				return false;
190
+			}
191
+			return true;
192
+
193
+		};
194
+
195
+		if ($componentName) {
196
+			// Early exit
197
+			return array_filter(
198
+				$this->select($componentName),
199
+				$isBaseComponent
200
+			);
201
+		}
202
+
203
+		$components = [];
204
+		foreach ($this->children as $childGroup) {
205
+
206
+			foreach ($childGroup as $child) {
207
+
208
+				if (!$child instanceof Component) {
209
+					// If one child is not a component, they all are so we skip
210
+					// the entire group.
211
+					continue 2;
212
+				}
213
+				if ($isBaseComponent($child)) {
214
+					$components[] = $child;
215
+				}
216
+
217
+			}
218
+
219
+		}
220
+		return $components;
221
+
222
+	}
223
+
224
+	/**
225
+	 * Returns the first component that is not a VTIMEZONE, and does not have
226
+	 * an RECURRENCE-ID.
227
+	 *
228
+	 * If there is no such component, null will be returned.
229
+	 *
230
+	 * @param string $componentName filter by component name
231
+	 *
232
+	 * @return VObject\Component|null
233
+	 */
234
+	public function getBaseComponent($componentName = null) {
235
+
236
+		$isBaseComponent = function($component) {
237
+
238
+			if (!$component instanceof VObject\Component) {
239
+				return false;
240
+			}
241
+			if ($component->name === 'VTIMEZONE') {
242
+				return false;
243
+			}
244
+			if (isset($component->{'RECURRENCE-ID'})) {
245
+				return false;
246
+			}
247
+			return true;
248
+
249
+		};
250
+
251
+		if ($componentName) {
252
+			foreach ($this->select($componentName) as $child) {
253
+				if ($isBaseComponent($child)) {
254
+					return $child;
255
+				}
256
+			}
257
+			return null;
258
+		}
259
+
260
+		// Searching all components
261
+		foreach ($this->children as $childGroup) {
262
+			foreach ($childGroup as $child) {
263
+				if ($isBaseComponent($child)) {
264
+					return $child;
265
+				}
266
+			}
267
+
268
+		}
269
+		return null;
270
+
271
+	}
272
+
273
+	/**
274
+	 * Expand all events in this VCalendar object and return a new VCalendar
275
+	 * with the expanded events.
276
+	 *
277
+	 * If this calendar object, has events with recurrence rules, this method
278
+	 * can be used to expand the event into multiple sub-events.
279
+	 *
280
+	 * Each event will be stripped from it's recurrence information, and only
281
+	 * the instances of the event in the specified timerange will be left
282
+	 * alone.
283
+	 *
284
+	 * In addition, this method will cause timezone information to be stripped,
285
+	 * and normalized to UTC.
286
+	 *
287
+	 * @param DateTimeInterface $start
288
+	 * @param DateTimeInterface $end
289
+	 * @param DateTimeZone $timeZone reference timezone for floating dates and
290
+	 *                     times.
291
+	 * @return VCalendar
292
+	 */
293
+	public function expand(DateTimeInterface $start, DateTimeInterface $end, DateTimeZone $timeZone = null) {
294
+
295
+		$newChildren = [];
296
+		$recurringEvents = [];
297
+
298
+		if (!$timeZone) {
299
+			$timeZone = new DateTimeZone('UTC');
300
+		}
301
+
302
+		$stripTimezones = function(Component $component) use ($timeZone, &$stripTimezones) {
303
+
304
+			foreach ($component->children() as $componentChild) {
305
+				if ($componentChild instanceof Property\ICalendar\DateTime && $componentChild->hasTime()) {
306
+
307
+					$dt = $componentChild->getDateTimes($timeZone);
308
+					// We only need to update the first timezone, because
309
+					// setDateTimes will match all other timezones to the
310
+					// first.
311
+					$dt[0] = $dt[0]->setTimeZone(new DateTimeZone('UTC'));
312
+					$componentChild->setDateTimes($dt);
313
+				} elseif ($componentChild instanceof Component) {
314
+					$stripTimezones($componentChild);
315
+				}
316
+
317
+			}
318
+			return $component;
319
+
320
+		};
321
+
322
+		foreach ($this->children() as $child) {
323
+
324
+			if ($child instanceof Property && $child->name !== 'PRODID') {
325
+				// We explictly want to ignore PRODID, because we want to
326
+				// overwrite it with our own.
327
+				$newChildren[] = clone $child;
328
+			} elseif ($child instanceof Component && $child->name !== 'VTIMEZONE') {
329
+
330
+				// We're also stripping all VTIMEZONE objects because we're
331
+				// converting everything to UTC.
332
+				if ($child->name === 'VEVENT' && (isset($child->{'RECURRENCE-ID'}) || isset($child->RRULE) || isset($child->RDATE))) {
333
+					// Handle these a bit later.
334
+					$uid = (string)$child->UID;
335
+					if (!$uid) {
336
+						throw new InvalidDataException('Every VEVENT object must have a UID property');
337
+					}
338
+					if (isset($recurringEvents[$uid])) {
339
+						$recurringEvents[$uid][] = clone $child;
340
+					} else {
341
+						$recurringEvents[$uid] = [clone $child];
342
+					}
343
+				} elseif ($child->name === 'VEVENT' && $child->isInTimeRange($start, $end)) {
344
+					$newChildren[] = $stripTimezones(clone $child);
345
+				}
346
+
347
+			}
348
+
349
+		}
350
+
351
+		foreach ($recurringEvents as $events) {
352
+
353
+			try {
354
+				$it = new EventIterator($events, $timeZone);
355
+
356
+			} catch (NoInstancesException $e) {
357
+				// This event is recurring, but it doesn't have a single
358
+				// instance. We are skipping this event from the output
359
+				// entirely.
360
+				continue;
361
+			}
362
+			$it->fastForward($start);
363
+
364
+			while ($it->valid() && $it->getDTStart() < $end) {
365
+
366
+				if ($it->getDTEnd() > $start) {
367
+
368
+					$newChildren[] = $stripTimezones($it->getEventObject());
369
+
370
+				}
371
+				$it->next();
372
+
373
+			}
374
+
375
+		}
376
+
377
+		return new self($newChildren);
378
+
379
+	}
380
+
381
+	/**
382
+	 * This method should return a list of default property values.
383
+	 *
384
+	 * @return array
385
+	 */
386
+	protected function getDefaults() {
387
+
388
+		return [
389
+			'VERSION'  => '2.0',
390
+			'PRODID'   => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
391
+			'CALSCALE' => 'GREGORIAN',
392
+		];
393
+
394
+	}
395
+
396
+	/**
397
+	 * A simple list of validation rules.
398
+	 *
399
+	 * This is simply a list of properties, and how many times they either
400
+	 * must or must not appear.
401
+	 *
402
+	 * Possible values per property:
403
+	 *   * 0 - Must not appear.
404
+	 *   * 1 - Must appear exactly once.
405
+	 *   * + - Must appear at least once.
406
+	 *   * * - Can appear any number of times.
407
+	 *   * ? - May appear, but not more than once.
408
+	 *
409
+	 * @var array
410
+	 */
411
+	public function getValidationRules() {
412
+
413
+		return [
414
+			'PRODID'  => 1,
415
+			'VERSION' => 1,
416
+
417
+			'CALSCALE' => '?',
418
+			'METHOD'   => '?',
419
+		];
420
+
421
+	}
422
+
423
+	/**
424
+	 * Validates the node for correctness.
425
+	 *
426
+	 * The following options are supported:
427
+	 *   Node::REPAIR - May attempt to automatically repair the problem.
428
+	 *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
429
+	 *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
430
+	 *
431
+	 * This method returns an array with detected problems.
432
+	 * Every element has the following properties:
433
+	 *
434
+	 *  * level - problem level.
435
+	 *  * message - A human-readable string describing the issue.
436
+	 *  * node - A reference to the problematic node.
437
+	 *
438
+	 * The level means:
439
+	 *   1 - The issue was repaired (only happens if REPAIR was turned on).
440
+	 *   2 - A warning.
441
+	 *   3 - An error.
442
+	 *
443
+	 * @param int $options
444
+	 *
445
+	 * @return array
446
+	 */
447
+	public function validate($options = 0) {
448
+
449
+		$warnings = parent::validate($options);
450
+
451
+		if ($ver = $this->VERSION) {
452
+			if ((string)$ver !== '2.0') {
453
+				$warnings[] = [
454
+					'level'   => 3,
455
+					'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
456
+					'node'    => $this,
457
+				];
458
+			}
459
+
460
+		}
461
+
462
+		$uidList = [];
463
+		$componentsFound = 0;
464
+		$componentTypes = [];
465
+
466
+		foreach ($this->children() as $child) {
467
+			if ($child instanceof Component) {
468
+				$componentsFound++;
469
+
470
+				if (!in_array($child->name, ['VEVENT', 'VTODO', 'VJOURNAL'])) {
471
+					continue;
472
+				}
473
+				$componentTypes[] = $child->name;
474
+
475
+				$uid = (string)$child->UID;
476
+				$isMaster = isset($child->{'RECURRENCE-ID'}) ? 0 : 1;
477
+				if (isset($uidList[$uid])) {
478
+					$uidList[$uid]['count']++;
479
+					if ($isMaster && $uidList[$uid]['hasMaster']) {
480
+						$warnings[] = [
481
+							'level'   => 3,
482
+							'message' => 'More than one master object was found for the object with UID ' . $uid,
483
+							'node'    => $this,
484
+						];
485
+					}
486
+					$uidList[$uid]['hasMaster'] += $isMaster;
487
+				} else {
488
+					$uidList[$uid] = [
489
+						'count'     => 1,
490
+						'hasMaster' => $isMaster,
491
+					];
492
+				}
493
+
494
+			}
495
+		}
496
+
497
+		if ($componentsFound === 0) {
498
+			$warnings[] = [
499
+				'level'   => 3,
500
+				'message' => 'An iCalendar object must have at least 1 component.',
501
+				'node'    => $this,
502
+			];
503
+		}
504
+
505
+		if ($options & self::PROFILE_CALDAV) {
506
+			if (count($uidList) > 1) {
507
+				$warnings[] = [
508
+					'level'   => 3,
509
+					'message' => 'A calendar object on a CalDAV server may only have components with the same UID.',
510
+					'node'    => $this,
511
+				];
512
+			}
513
+			if (count($componentTypes) === 0) {
514
+				$warnings[] = [
515
+					'level'   => 3,
516
+					'message' => 'A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL).',
517
+					'node'    => $this,
518
+				];
519
+			}
520
+			if (count(array_unique($componentTypes)) > 1) {
521
+				$warnings[] = [
522
+					'level'   => 3,
523
+					'message' => 'A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL).',
524
+					'node'    => $this,
525
+				];
526
+			}
527
+
528
+			if (isset($this->METHOD)) {
529
+				$warnings[] = [
530
+					'level'   => 3,
531
+					'message' => 'A calendar object on a CalDAV server MUST NOT have a METHOD property.',
532
+					'node'    => $this,
533
+				];
534
+			}
535
+		}
536
+
537
+		return $warnings;
538
+
539
+	}
540
+
541
+	/**
542
+	 * Returns all components with a specific UID value.
543
+	 *
544
+	 * @return array
545
+	 */
546
+	public function getByUID($uid) {
547
+
548
+		return array_filter($this->getComponents(), function($item) use ($uid) {
549
+
550
+			if (!$itemUid = $item->select('UID')) {
551
+				return false;
552
+			}
553
+			$itemUid = current($itemUid)->getValue();
554
+			return $uid === $itemUid;
555
+
556
+		});
557
+
558
+	}
559 559
 
560 560
 
561 561
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Component/VCard.php 3 patches
Doc Comments   -1 removed lines patch added patch discarded remove patch
@@ -383,7 +383,6 @@
 block discarded – undo
383 383
      * If neither of those parameters are specified, the first is returned, if
384 384
      * a field with that name does not exist, null is returned.
385 385
      *
386
-     * @param string $fieldName
387 386
      *
388 387
      * @return VObject\Property|null
389 388
      */
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -50,7 +50,7 @@  discard block
 block discarded – undo
50 50
     static $valueMap = [
51 51
         'BINARY'           => 'Sabre\\VObject\\Property\\Binary',
52 52
         'BOOLEAN'          => 'Sabre\\VObject\\Property\\Boolean',
53
-        'CONTENT-ID'       => 'Sabre\\VObject\\Property\\FlatText',   // vCard 2.1 only
53
+        'CONTENT-ID'       => 'Sabre\\VObject\\Property\\FlatText', // vCard 2.1 only
54 54
         'DATE'             => 'Sabre\\VObject\\Property\\VCard\\Date',
55 55
         'DATE-TIME'        => 'Sabre\\VObject\\Property\\VCard\\DateTime',
56 56
         'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
 
149 149
         if (!$this->version) {
150 150
 
151
-            $version = (string)$this->VERSION;
151
+            $version = (string) $this->VERSION;
152 152
 
153 153
             switch ($version) {
154 154
                 case '2.1' :
@@ -233,7 +233,7 @@  discard block
 block discarded – undo
233 233
 
234 234
         $version = $this->select('VERSION');
235 235
         if (count($version) === 1) {
236
-            $version = (string)$this->VERSION;
236
+            $version = (string) $this->VERSION;
237 237
             if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') {
238 238
                 $warnings[] = [
239 239
                     'level'   => 3,
@@ -283,7 +283,7 @@  discard block
 block discarded – undo
283 283
                 // We're going to try to see if we can use the contents of the
284 284
                 // N property.
285 285
                 if (isset($this->N)) {
286
-                    $value = explode(';', (string)$this->N);
286
+                    $value = explode(';', (string) $this->N);
287 287
                     if (isset($value[1]) && $value[1]) {
288 288
                         $this->FN = $value[1] . ' ' . $value[0];
289 289
                     } else {
@@ -293,7 +293,7 @@  discard block
 block discarded – undo
293 293
 
294 294
                 // Otherwise, the ORG property may work
295 295
                 } elseif (isset($this->ORG)) {
296
-                    $this->FN = (string)$this->ORG;
296
+                    $this->FN = (string) $this->ORG;
297 297
                     $repaired = true;
298 298
                 }
299 299
 
Please login to merge, or discard this patch.
Indentation   +520 added lines, -520 removed lines patch added patch discarded remove patch
@@ -17,537 +17,537 @@
 block discarded – undo
17 17
  */
18 18
 class VCard extends VObject\Document {
19 19
 
20
-    /**
21
-     * The default name for this component.
22
-     *
23
-     * This should be 'VCALENDAR' or 'VCARD'.
24
-     *
25
-     * @var string
26
-     */
27
-    static $defaultName = 'VCARD';
28
-
29
-    /**
30
-     * Caching the version number.
31
-     *
32
-     * @var int
33
-     */
34
-    private $version = null;
35
-
36
-    /**
37
-     * This is a list of components, and which classes they should map to.
38
-     *
39
-     * @var array
40
-     */
41
-    static $componentMap = [
42
-        'VCARD' => 'Sabre\\VObject\\Component\\VCard',
43
-    ];
44
-
45
-    /**
46
-     * List of value-types, and which classes they map to.
47
-     *
48
-     * @var array
49
-     */
50
-    static $valueMap = [
51
-        'BINARY'           => 'Sabre\\VObject\\Property\\Binary',
52
-        'BOOLEAN'          => 'Sabre\\VObject\\Property\\Boolean',
53
-        'CONTENT-ID'       => 'Sabre\\VObject\\Property\\FlatText',   // vCard 2.1 only
54
-        'DATE'             => 'Sabre\\VObject\\Property\\VCard\\Date',
55
-        'DATE-TIME'        => 'Sabre\\VObject\\Property\\VCard\\DateTime',
56
-        'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only
57
-        'FLOAT'            => 'Sabre\\VObject\\Property\\FloatValue',
58
-        'INTEGER'          => 'Sabre\\VObject\\Property\\IntegerValue',
59
-        'LANGUAGE-TAG'     => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
60
-        'TIMESTAMP'        => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
61
-        'TEXT'             => 'Sabre\\VObject\\Property\\Text',
62
-        'TIME'             => 'Sabre\\VObject\\Property\\Time',
63
-        'UNKNOWN'          => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
64
-        'URI'              => 'Sabre\\VObject\\Property\\Uri',
65
-        'URL'              => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only
66
-        'UTC-OFFSET'       => 'Sabre\\VObject\\Property\\UtcOffset',
67
-    ];
68
-
69
-    /**
70
-     * List of properties, and which classes they map to.
71
-     *
72
-     * @var array
73
-     */
74
-    static $propertyMap = [
75
-
76
-        // vCard 2.1 properties and up
77
-        'N'       => 'Sabre\\VObject\\Property\\Text',
78
-        'FN'      => 'Sabre\\VObject\\Property\\FlatText',
79
-        'PHOTO'   => 'Sabre\\VObject\\Property\\Binary',
80
-        'BDAY'    => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
81
-        'ADR'     => 'Sabre\\VObject\\Property\\Text',
82
-        'LABEL'   => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
83
-        'TEL'     => 'Sabre\\VObject\\Property\\FlatText',
84
-        'EMAIL'   => 'Sabre\\VObject\\Property\\FlatText',
85
-        'MAILER'  => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
86
-        'GEO'     => 'Sabre\\VObject\\Property\\FlatText',
87
-        'TITLE'   => 'Sabre\\VObject\\Property\\FlatText',
88
-        'ROLE'    => 'Sabre\\VObject\\Property\\FlatText',
89
-        'LOGO'    => 'Sabre\\VObject\\Property\\Binary',
90
-        // 'AGENT'   => 'Sabre\\VObject\\Property\\',      // Todo: is an embedded vCard. Probably rare, so
91
-                                 // not supported at the moment
92
-        'ORG'     => 'Sabre\\VObject\\Property\\Text',
93
-        'NOTE'    => 'Sabre\\VObject\\Property\\FlatText',
94
-        'REV'     => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
95
-        'SOUND'   => 'Sabre\\VObject\\Property\\FlatText',
96
-        'URL'     => 'Sabre\\VObject\\Property\\Uri',
97
-        'UID'     => 'Sabre\\VObject\\Property\\FlatText',
98
-        'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
99
-        'KEY'     => 'Sabre\\VObject\\Property\\FlatText',
100
-        'TZ'      => 'Sabre\\VObject\\Property\\Text',
101
-
102
-        // vCard 3.0 properties
103
-        'CATEGORIES'  => 'Sabre\\VObject\\Property\\Text',
104
-        'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText',
105
-        'PRODID'      => 'Sabre\\VObject\\Property\\FlatText',
106
-        'NICKNAME'    => 'Sabre\\VObject\\Property\\Text',
107
-        'CLASS'       => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
108
-
109
-        // rfc2739 properties
110
-        'FBURL'        => 'Sabre\\VObject\\Property\\Uri',
111
-        'CAPURI'       => 'Sabre\\VObject\\Property\\Uri',
112
-        'CALURI'       => 'Sabre\\VObject\\Property\\Uri',
113
-        'CALADRURI'    => 'Sabre\\VObject\\Property\\Uri',
114
-
115
-        // rfc4770 properties
116
-        'IMPP'         => 'Sabre\\VObject\\Property\\Uri',
117
-
118
-        // vCard 4.0 properties
119
-        'SOURCE'       => 'Sabre\\VObject\\Property\\Uri',
120
-        'XML'          => 'Sabre\\VObject\\Property\\FlatText',
121
-        'ANNIVERSARY'  => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
122
-        'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text',
123
-        'LANG'         => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
124
-        'GENDER'       => 'Sabre\\VObject\\Property\\Text',
125
-        'KIND'         => 'Sabre\\VObject\\Property\\FlatText',
126
-        'MEMBER'       => 'Sabre\\VObject\\Property\\Uri',
127
-        'RELATED'      => 'Sabre\\VObject\\Property\\Uri',
128
-
129
-        // rfc6474 properties
130
-        'BIRTHPLACE'    => 'Sabre\\VObject\\Property\\FlatText',
131
-        'DEATHPLACE'    => 'Sabre\\VObject\\Property\\FlatText',
132
-        'DEATHDATE'     => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
133
-
134
-        // rfc6715 properties
135
-        'EXPERTISE'     => 'Sabre\\VObject\\Property\\FlatText',
136
-        'HOBBY'         => 'Sabre\\VObject\\Property\\FlatText',
137
-        'INTEREST'      => 'Sabre\\VObject\\Property\\FlatText',
138
-        'ORG-DIRECTORY' => 'Sabre\\VObject\\Property\\FlatText',
139
-
140
-    ];
141
-
142
-    /**
143
-     * Returns the current document type.
144
-     *
145
-     * @return int
146
-     */
147
-    public function getDocumentType() {
148
-
149
-        if (!$this->version) {
150
-
151
-            $version = (string)$this->VERSION;
152
-
153
-            switch ($version) {
154
-                case '2.1' :
155
-                    $this->version = self::VCARD21;
156
-                    break;
157
-                case '3.0' :
158
-                    $this->version = self::VCARD30;
159
-                    break;
160
-                case '4.0' :
161
-                    $this->version = self::VCARD40;
162
-                    break;
163
-                default :
164
-                    // We don't want to cache the version if it's unknown,
165
-                    // because we might get a version property in a bit.
166
-                    return self::UNKNOWN;
167
-            }
168
-        }
169
-
170
-        return $this->version;
171
-
172
-    }
173
-
174
-    /**
175
-     * Converts the document to a different vcard version.
176
-     *
177
-     * Use one of the VCARD constants for the target. This method will return
178
-     * a copy of the vcard in the new version.
179
-     *
180
-     * At the moment the only supported conversion is from 3.0 to 4.0.
181
-     *
182
-     * If input and output version are identical, a clone is returned.
183
-     *
184
-     * @param int $target
185
-     *
186
-     * @return VCard
187
-     */
188
-    public function convert($target) {
189
-
190
-        $converter = new VObject\VCardConverter();
191
-        return $converter->convert($this, $target);
192
-
193
-    }
194
-
195
-    /**
196
-     * VCards with version 2.1, 3.0 and 4.0 are found.
197
-     *
198
-     * If the VCARD doesn't know its version, 2.1 is assumed.
199
-     */
200
-    const DEFAULT_VERSION = self::VCARD21;
201
-
202
-    /**
203
-     * Validates the node for correctness.
204
-     *
205
-     * The following options are supported:
206
-     *   Node::REPAIR - May attempt to automatically repair the problem.
207
-     *
208
-     * This method returns an array with detected problems.
209
-     * Every element has the following properties:
210
-     *
211
-     *  * level - problem level.
212
-     *  * message - A human-readable string describing the issue.
213
-     *  * node - A reference to the problematic node.
214
-     *
215
-     * The level means:
216
-     *   1 - The issue was repaired (only happens if REPAIR was turned on)
217
-     *   2 - An inconsequential issue
218
-     *   3 - A severe issue.
219
-     *
220
-     * @param int $options
221
-     *
222
-     * @return array
223
-     */
224
-    public function validate($options = 0) {
225
-
226
-        $warnings = [];
227
-
228
-        $versionMap = [
229
-            self::VCARD21 => '2.1',
230
-            self::VCARD30 => '3.0',
231
-            self::VCARD40 => '4.0',
232
-        ];
233
-
234
-        $version = $this->select('VERSION');
235
-        if (count($version) === 1) {
236
-            $version = (string)$this->VERSION;
237
-            if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') {
238
-                $warnings[] = [
239
-                    'level'   => 3,
240
-                    'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
241
-                    'node'    => $this,
242
-                ];
243
-                if ($options & self::REPAIR) {
244
-                    $this->VERSION = $versionMap[self::DEFAULT_VERSION];
245
-                }
246
-            }
247
-            if ($version === '2.1' && ($options & self::PROFILE_CARDDAV)) {
248
-                $warnings[] = [
249
-                    'level'   => 3,
250
-                    'message' => 'CardDAV servers are not allowed to accept vCard 2.1.',
251
-                    'node'    => $this,
252
-                ];
253
-            }
254
-
255
-        }
256
-        $uid = $this->select('UID');
257
-        if (count($uid) === 0) {
258
-            if ($options & self::PROFILE_CARDDAV) {
259
-                // Required for CardDAV
260
-                $warningLevel = 3;
261
-                $message = 'vCards on CardDAV servers MUST have a UID property.';
262
-            } else {
263
-                // Not required for regular vcards
264
-                $warningLevel = 2;
265
-                $message = 'Adding a UID to a vCard property is recommended.';
266
-            }
267
-            if ($options & self::REPAIR) {
268
-                $this->UID = VObject\UUIDUtil::getUUID();
269
-                $warningLevel = 1;
270
-            }
271
-            $warnings[] = [
272
-                'level'   => $warningLevel,
273
-                'message' => $message,
274
-                'node'    => $this,
275
-            ];
276
-        }
277
-
278
-        $fn = $this->select('FN');
279
-        if (count($fn) !== 1) {
280
-
281
-            $repaired = false;
282
-            if (($options & self::REPAIR) && count($fn) === 0) {
283
-                // We're going to try to see if we can use the contents of the
284
-                // N property.
285
-                if (isset($this->N)) {
286
-                    $value = explode(';', (string)$this->N);
287
-                    if (isset($value[1]) && $value[1]) {
288
-                        $this->FN = $value[1] . ' ' . $value[0];
289
-                    } else {
290
-                        $this->FN = $value[0];
291
-                    }
292
-                    $repaired = true;
293
-
294
-                // Otherwise, the ORG property may work
295
-                } elseif (isset($this->ORG)) {
296
-                    $this->FN = (string)$this->ORG;
297
-                    $repaired = true;
298
-                }
299
-
300
-            }
301
-            $warnings[] = [
302
-                'level'   => $repaired ? 1 : 3,
303
-                'message' => 'The FN property must appear in the VCARD component exactly 1 time',
304
-                'node'    => $this,
305
-            ];
306
-        }
307
-
308
-        return array_merge(
309
-            parent::validate($options),
310
-            $warnings
311
-        );
312
-
313
-    }
314
-
315
-    /**
316
-     * A simple list of validation rules.
317
-     *
318
-     * This is simply a list of properties, and how many times they either
319
-     * must or must not appear.
320
-     *
321
-     * Possible values per property:
322
-     *   * 0 - Must not appear.
323
-     *   * 1 - Must appear exactly once.
324
-     *   * + - Must appear at least once.
325
-     *   * * - Can appear any number of times.
326
-     *   * ? - May appear, but not more than once.
327
-     *
328
-     * @var array
329
-     */
330
-    public function getValidationRules() {
331
-
332
-        return [
333
-            'ADR'          => '*',
334
-            'ANNIVERSARY'  => '?',
335
-            'BDAY'         => '?',
336
-            'CALADRURI'    => '*',
337
-            'CALURI'       => '*',
338
-            'CATEGORIES'   => '*',
339
-            'CLIENTPIDMAP' => '*',
340
-            'EMAIL'        => '*',
341
-            'FBURL'        => '*',
342
-            'IMPP'         => '*',
343
-            'GENDER'       => '?',
344
-            'GEO'          => '*',
345
-            'KEY'          => '*',
346
-            'KIND'         => '?',
347
-            'LANG'         => '*',
348
-            'LOGO'         => '*',
349
-            'MEMBER'       => '*',
350
-            'N'            => '?',
351
-            'NICKNAME'     => '*',
352
-            'NOTE'         => '*',
353
-            'ORG'          => '*',
354
-            'PHOTO'        => '*',
355
-            'PRODID'       => '?',
356
-            'RELATED'      => '*',
357
-            'REV'          => '?',
358
-            'ROLE'         => '*',
359
-            'SOUND'        => '*',
360
-            'SOURCE'       => '*',
361
-            'TEL'          => '*',
362
-            'TITLE'        => '*',
363
-            'TZ'           => '*',
364
-            'URL'          => '*',
365
-            'VERSION'      => '1',
366
-            'XML'          => '*',
367
-
368
-            // FN is commented out, because it's already handled by the
369
-            // validate function, which may also try to repair it.
370
-            // 'FN'           => '+',
371
-            'UID'          => '?',
372
-        ];
373
-
374
-    }
375
-
376
-    /**
377
-     * Returns a preferred field.
378
-     *
379
-     * VCards can indicate wether a field such as ADR, TEL or EMAIL is
380
-     * preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x
381
-     * being a number between 1 and 100).
382
-     *
383
-     * If neither of those parameters are specified, the first is returned, if
384
-     * a field with that name does not exist, null is returned.
385
-     *
386
-     * @param string $fieldName
387
-     *
388
-     * @return VObject\Property|null
389
-     */
390
-    public function preferred($propertyName) {
391
-
392
-        $preferred = null;
393
-        $lastPref = 101;
394
-        foreach ($this->select($propertyName) as $field) {
395
-
396
-            $pref = 101;
397
-            if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) {
398
-                $pref = 1;
399
-            } elseif (isset($field['PREF'])) {
400
-                $pref = $field['PREF']->getValue();
401
-            }
402
-
403
-            if ($pref < $lastPref || is_null($preferred)) {
404
-                $preferred = $field;
405
-                $lastPref = $pref;
406
-            }
407
-
408
-        }
409
-        return $preferred;
410
-
411
-    }
412
-
413
-    /**
414
-     * Returns a property with a specific TYPE value (ADR, TEL, or EMAIL).
415
-     *
416
-     * This function will return null if the property does not exist. If there are
417
-     * multiple properties with the same TYPE value, only one will be returned.
418
-     *
419
-     * @param string $propertyName
420
-     * @param string $type
421
-     *
422
-     * @return VObject\Property|null
423
-     */
424
-    public function getByType($propertyName, $type) {
425
-        foreach ($this->select($propertyName) as $field) {
426
-            if (isset($field['TYPE']) && $field['TYPE']->has($type)) {
427
-                return $field;
428
-            }
429
-        }
430
-    }
431
-
432
-    /**
433
-     * This method should return a list of default property values.
434
-     *
435
-     * @return array
436
-     */
437
-    protected function getDefaults() {
438
-
439
-        return [
440
-            'VERSION' => '4.0',
441
-            'PRODID'  => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
442
-            'UID'     => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
443
-        ];
444
-
445
-    }
446
-
447
-    /**
448
-     * This method returns an array, with the representation as it should be
449
-     * encoded in json. This is used to create jCard or jCal documents.
450
-     *
451
-     * @return array
452
-     */
453
-    public function jsonSerialize() {
454
-
455
-        // A vcard does not have sub-components, so we're overriding this
456
-        // method to remove that array element.
457
-        $properties = [];
458
-
459
-        foreach ($this->children() as $child) {
460
-            $properties[] = $child->jsonSerialize();
461
-        }
462
-
463
-        return [
464
-            strtolower($this->name),
465
-            $properties,
466
-        ];
467
-
468
-    }
469
-
470
-    /**
471
-     * This method serializes the data into XML. This is used to create xCard or
472
-     * xCal documents.
473
-     *
474
-     * @param Xml\Writer $writer  XML writer.
475
-     *
476
-     * @return void
477
-     */
478
-    public function xmlSerialize(Xml\Writer $writer) {
479
-
480
-        $propertiesByGroup = [];
481
-
482
-        foreach ($this->children() as $property) {
483
-
484
-            $group = $property->group;
485
-
486
-            if (!isset($propertiesByGroup[$group])) {
487
-                $propertiesByGroup[$group] = [];
488
-            }
489
-
490
-            $propertiesByGroup[$group][] = $property;
491
-
492
-        }
493
-
494
-        $writer->startElement(strtolower($this->name));
495
-
496
-        foreach ($propertiesByGroup as $group => $properties) {
497
-
498
-            if (!empty($group)) {
499
-
500
-                $writer->startElement('group');
501
-                $writer->writeAttribute('name', strtolower($group));
20
+	/**
21
+	 * The default name for this component.
22
+	 *
23
+	 * This should be 'VCALENDAR' or 'VCARD'.
24
+	 *
25
+	 * @var string
26
+	 */
27
+	static $defaultName = 'VCARD';
28
+
29
+	/**
30
+	 * Caching the version number.
31
+	 *
32
+	 * @var int
33
+	 */
34
+	private $version = null;
35
+
36
+	/**
37
+	 * This is a list of components, and which classes they should map to.
38
+	 *
39
+	 * @var array
40
+	 */
41
+	static $componentMap = [
42
+		'VCARD' => 'Sabre\\VObject\\Component\\VCard',
43
+	];
44
+
45
+	/**
46
+	 * List of value-types, and which classes they map to.
47
+	 *
48
+	 * @var array
49
+	 */
50
+	static $valueMap = [
51
+		'BINARY'           => 'Sabre\\VObject\\Property\\Binary',
52
+		'BOOLEAN'          => 'Sabre\\VObject\\Property\\Boolean',
53
+		'CONTENT-ID'       => 'Sabre\\VObject\\Property\\FlatText',   // vCard 2.1 only
54
+		'DATE'             => 'Sabre\\VObject\\Property\\VCard\\Date',
55
+		'DATE-TIME'        => 'Sabre\\VObject\\Property\\VCard\\DateTime',
56
+		'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only
57
+		'FLOAT'            => 'Sabre\\VObject\\Property\\FloatValue',
58
+		'INTEGER'          => 'Sabre\\VObject\\Property\\IntegerValue',
59
+		'LANGUAGE-TAG'     => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
60
+		'TIMESTAMP'        => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
61
+		'TEXT'             => 'Sabre\\VObject\\Property\\Text',
62
+		'TIME'             => 'Sabre\\VObject\\Property\\Time',
63
+		'UNKNOWN'          => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
64
+		'URI'              => 'Sabre\\VObject\\Property\\Uri',
65
+		'URL'              => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only
66
+		'UTC-OFFSET'       => 'Sabre\\VObject\\Property\\UtcOffset',
67
+	];
68
+
69
+	/**
70
+	 * List of properties, and which classes they map to.
71
+	 *
72
+	 * @var array
73
+	 */
74
+	static $propertyMap = [
75
+
76
+		// vCard 2.1 properties and up
77
+		'N'       => 'Sabre\\VObject\\Property\\Text',
78
+		'FN'      => 'Sabre\\VObject\\Property\\FlatText',
79
+		'PHOTO'   => 'Sabre\\VObject\\Property\\Binary',
80
+		'BDAY'    => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
81
+		'ADR'     => 'Sabre\\VObject\\Property\\Text',
82
+		'LABEL'   => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
83
+		'TEL'     => 'Sabre\\VObject\\Property\\FlatText',
84
+		'EMAIL'   => 'Sabre\\VObject\\Property\\FlatText',
85
+		'MAILER'  => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
86
+		'GEO'     => 'Sabre\\VObject\\Property\\FlatText',
87
+		'TITLE'   => 'Sabre\\VObject\\Property\\FlatText',
88
+		'ROLE'    => 'Sabre\\VObject\\Property\\FlatText',
89
+		'LOGO'    => 'Sabre\\VObject\\Property\\Binary',
90
+		// 'AGENT'   => 'Sabre\\VObject\\Property\\',      // Todo: is an embedded vCard. Probably rare, so
91
+								 // not supported at the moment
92
+		'ORG'     => 'Sabre\\VObject\\Property\\Text',
93
+		'NOTE'    => 'Sabre\\VObject\\Property\\FlatText',
94
+		'REV'     => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
95
+		'SOUND'   => 'Sabre\\VObject\\Property\\FlatText',
96
+		'URL'     => 'Sabre\\VObject\\Property\\Uri',
97
+		'UID'     => 'Sabre\\VObject\\Property\\FlatText',
98
+		'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
99
+		'KEY'     => 'Sabre\\VObject\\Property\\FlatText',
100
+		'TZ'      => 'Sabre\\VObject\\Property\\Text',
101
+
102
+		// vCard 3.0 properties
103
+		'CATEGORIES'  => 'Sabre\\VObject\\Property\\Text',
104
+		'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText',
105
+		'PRODID'      => 'Sabre\\VObject\\Property\\FlatText',
106
+		'NICKNAME'    => 'Sabre\\VObject\\Property\\Text',
107
+		'CLASS'       => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
108
+
109
+		// rfc2739 properties
110
+		'FBURL'        => 'Sabre\\VObject\\Property\\Uri',
111
+		'CAPURI'       => 'Sabre\\VObject\\Property\\Uri',
112
+		'CALURI'       => 'Sabre\\VObject\\Property\\Uri',
113
+		'CALADRURI'    => 'Sabre\\VObject\\Property\\Uri',
114
+
115
+		// rfc4770 properties
116
+		'IMPP'         => 'Sabre\\VObject\\Property\\Uri',
117
+
118
+		// vCard 4.0 properties
119
+		'SOURCE'       => 'Sabre\\VObject\\Property\\Uri',
120
+		'XML'          => 'Sabre\\VObject\\Property\\FlatText',
121
+		'ANNIVERSARY'  => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
122
+		'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text',
123
+		'LANG'         => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
124
+		'GENDER'       => 'Sabre\\VObject\\Property\\Text',
125
+		'KIND'         => 'Sabre\\VObject\\Property\\FlatText',
126
+		'MEMBER'       => 'Sabre\\VObject\\Property\\Uri',
127
+		'RELATED'      => 'Sabre\\VObject\\Property\\Uri',
128
+
129
+		// rfc6474 properties
130
+		'BIRTHPLACE'    => 'Sabre\\VObject\\Property\\FlatText',
131
+		'DEATHPLACE'    => 'Sabre\\VObject\\Property\\FlatText',
132
+		'DEATHDATE'     => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
133
+
134
+		// rfc6715 properties
135
+		'EXPERTISE'     => 'Sabre\\VObject\\Property\\FlatText',
136
+		'HOBBY'         => 'Sabre\\VObject\\Property\\FlatText',
137
+		'INTEREST'      => 'Sabre\\VObject\\Property\\FlatText',
138
+		'ORG-DIRECTORY' => 'Sabre\\VObject\\Property\\FlatText',
139
+
140
+	];
141
+
142
+	/**
143
+	 * Returns the current document type.
144
+	 *
145
+	 * @return int
146
+	 */
147
+	public function getDocumentType() {
148
+
149
+		if (!$this->version) {
150
+
151
+			$version = (string)$this->VERSION;
152
+
153
+			switch ($version) {
154
+				case '2.1' :
155
+					$this->version = self::VCARD21;
156
+					break;
157
+				case '3.0' :
158
+					$this->version = self::VCARD30;
159
+					break;
160
+				case '4.0' :
161
+					$this->version = self::VCARD40;
162
+					break;
163
+				default :
164
+					// We don't want to cache the version if it's unknown,
165
+					// because we might get a version property in a bit.
166
+					return self::UNKNOWN;
167
+			}
168
+		}
169
+
170
+		return $this->version;
171
+
172
+	}
173
+
174
+	/**
175
+	 * Converts the document to a different vcard version.
176
+	 *
177
+	 * Use one of the VCARD constants for the target. This method will return
178
+	 * a copy of the vcard in the new version.
179
+	 *
180
+	 * At the moment the only supported conversion is from 3.0 to 4.0.
181
+	 *
182
+	 * If input and output version are identical, a clone is returned.
183
+	 *
184
+	 * @param int $target
185
+	 *
186
+	 * @return VCard
187
+	 */
188
+	public function convert($target) {
189
+
190
+		$converter = new VObject\VCardConverter();
191
+		return $converter->convert($this, $target);
192
+
193
+	}
194
+
195
+	/**
196
+	 * VCards with version 2.1, 3.0 and 4.0 are found.
197
+	 *
198
+	 * If the VCARD doesn't know its version, 2.1 is assumed.
199
+	 */
200
+	const DEFAULT_VERSION = self::VCARD21;
201
+
202
+	/**
203
+	 * Validates the node for correctness.
204
+	 *
205
+	 * The following options are supported:
206
+	 *   Node::REPAIR - May attempt to automatically repair the problem.
207
+	 *
208
+	 * This method returns an array with detected problems.
209
+	 * Every element has the following properties:
210
+	 *
211
+	 *  * level - problem level.
212
+	 *  * message - A human-readable string describing the issue.
213
+	 *  * node - A reference to the problematic node.
214
+	 *
215
+	 * The level means:
216
+	 *   1 - The issue was repaired (only happens if REPAIR was turned on)
217
+	 *   2 - An inconsequential issue
218
+	 *   3 - A severe issue.
219
+	 *
220
+	 * @param int $options
221
+	 *
222
+	 * @return array
223
+	 */
224
+	public function validate($options = 0) {
225
+
226
+		$warnings = [];
227
+
228
+		$versionMap = [
229
+			self::VCARD21 => '2.1',
230
+			self::VCARD30 => '3.0',
231
+			self::VCARD40 => '4.0',
232
+		];
233
+
234
+		$version = $this->select('VERSION');
235
+		if (count($version) === 1) {
236
+			$version = (string)$this->VERSION;
237
+			if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') {
238
+				$warnings[] = [
239
+					'level'   => 3,
240
+					'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
241
+					'node'    => $this,
242
+				];
243
+				if ($options & self::REPAIR) {
244
+					$this->VERSION = $versionMap[self::DEFAULT_VERSION];
245
+				}
246
+			}
247
+			if ($version === '2.1' && ($options & self::PROFILE_CARDDAV)) {
248
+				$warnings[] = [
249
+					'level'   => 3,
250
+					'message' => 'CardDAV servers are not allowed to accept vCard 2.1.',
251
+					'node'    => $this,
252
+				];
253
+			}
254
+
255
+		}
256
+		$uid = $this->select('UID');
257
+		if (count($uid) === 0) {
258
+			if ($options & self::PROFILE_CARDDAV) {
259
+				// Required for CardDAV
260
+				$warningLevel = 3;
261
+				$message = 'vCards on CardDAV servers MUST have a UID property.';
262
+			} else {
263
+				// Not required for regular vcards
264
+				$warningLevel = 2;
265
+				$message = 'Adding a UID to a vCard property is recommended.';
266
+			}
267
+			if ($options & self::REPAIR) {
268
+				$this->UID = VObject\UUIDUtil::getUUID();
269
+				$warningLevel = 1;
270
+			}
271
+			$warnings[] = [
272
+				'level'   => $warningLevel,
273
+				'message' => $message,
274
+				'node'    => $this,
275
+			];
276
+		}
277
+
278
+		$fn = $this->select('FN');
279
+		if (count($fn) !== 1) {
280
+
281
+			$repaired = false;
282
+			if (($options & self::REPAIR) && count($fn) === 0) {
283
+				// We're going to try to see if we can use the contents of the
284
+				// N property.
285
+				if (isset($this->N)) {
286
+					$value = explode(';', (string)$this->N);
287
+					if (isset($value[1]) && $value[1]) {
288
+						$this->FN = $value[1] . ' ' . $value[0];
289
+					} else {
290
+						$this->FN = $value[0];
291
+					}
292
+					$repaired = true;
293
+
294
+				// Otherwise, the ORG property may work
295
+				} elseif (isset($this->ORG)) {
296
+					$this->FN = (string)$this->ORG;
297
+					$repaired = true;
298
+				}
299
+
300
+			}
301
+			$warnings[] = [
302
+				'level'   => $repaired ? 1 : 3,
303
+				'message' => 'The FN property must appear in the VCARD component exactly 1 time',
304
+				'node'    => $this,
305
+			];
306
+		}
307
+
308
+		return array_merge(
309
+			parent::validate($options),
310
+			$warnings
311
+		);
312
+
313
+	}
314
+
315
+	/**
316
+	 * A simple list of validation rules.
317
+	 *
318
+	 * This is simply a list of properties, and how many times they either
319
+	 * must or must not appear.
320
+	 *
321
+	 * Possible values per property:
322
+	 *   * 0 - Must not appear.
323
+	 *   * 1 - Must appear exactly once.
324
+	 *   * + - Must appear at least once.
325
+	 *   * * - Can appear any number of times.
326
+	 *   * ? - May appear, but not more than once.
327
+	 *
328
+	 * @var array
329
+	 */
330
+	public function getValidationRules() {
331
+
332
+		return [
333
+			'ADR'          => '*',
334
+			'ANNIVERSARY'  => '?',
335
+			'BDAY'         => '?',
336
+			'CALADRURI'    => '*',
337
+			'CALURI'       => '*',
338
+			'CATEGORIES'   => '*',
339
+			'CLIENTPIDMAP' => '*',
340
+			'EMAIL'        => '*',
341
+			'FBURL'        => '*',
342
+			'IMPP'         => '*',
343
+			'GENDER'       => '?',
344
+			'GEO'          => '*',
345
+			'KEY'          => '*',
346
+			'KIND'         => '?',
347
+			'LANG'         => '*',
348
+			'LOGO'         => '*',
349
+			'MEMBER'       => '*',
350
+			'N'            => '?',
351
+			'NICKNAME'     => '*',
352
+			'NOTE'         => '*',
353
+			'ORG'          => '*',
354
+			'PHOTO'        => '*',
355
+			'PRODID'       => '?',
356
+			'RELATED'      => '*',
357
+			'REV'          => '?',
358
+			'ROLE'         => '*',
359
+			'SOUND'        => '*',
360
+			'SOURCE'       => '*',
361
+			'TEL'          => '*',
362
+			'TITLE'        => '*',
363
+			'TZ'           => '*',
364
+			'URL'          => '*',
365
+			'VERSION'      => '1',
366
+			'XML'          => '*',
367
+
368
+			// FN is commented out, because it's already handled by the
369
+			// validate function, which may also try to repair it.
370
+			// 'FN'           => '+',
371
+			'UID'          => '?',
372
+		];
373
+
374
+	}
375
+
376
+	/**
377
+	 * Returns a preferred field.
378
+	 *
379
+	 * VCards can indicate wether a field such as ADR, TEL or EMAIL is
380
+	 * preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x
381
+	 * being a number between 1 and 100).
382
+	 *
383
+	 * If neither of those parameters are specified, the first is returned, if
384
+	 * a field with that name does not exist, null is returned.
385
+	 *
386
+	 * @param string $fieldName
387
+	 *
388
+	 * @return VObject\Property|null
389
+	 */
390
+	public function preferred($propertyName) {
391
+
392
+		$preferred = null;
393
+		$lastPref = 101;
394
+		foreach ($this->select($propertyName) as $field) {
395
+
396
+			$pref = 101;
397
+			if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) {
398
+				$pref = 1;
399
+			} elseif (isset($field['PREF'])) {
400
+				$pref = $field['PREF']->getValue();
401
+			}
402
+
403
+			if ($pref < $lastPref || is_null($preferred)) {
404
+				$preferred = $field;
405
+				$lastPref = $pref;
406
+			}
407
+
408
+		}
409
+		return $preferred;
410
+
411
+	}
412
+
413
+	/**
414
+	 * Returns a property with a specific TYPE value (ADR, TEL, or EMAIL).
415
+	 *
416
+	 * This function will return null if the property does not exist. If there are
417
+	 * multiple properties with the same TYPE value, only one will be returned.
418
+	 *
419
+	 * @param string $propertyName
420
+	 * @param string $type
421
+	 *
422
+	 * @return VObject\Property|null
423
+	 */
424
+	public function getByType($propertyName, $type) {
425
+		foreach ($this->select($propertyName) as $field) {
426
+			if (isset($field['TYPE']) && $field['TYPE']->has($type)) {
427
+				return $field;
428
+			}
429
+		}
430
+	}
431
+
432
+	/**
433
+	 * This method should return a list of default property values.
434
+	 *
435
+	 * @return array
436
+	 */
437
+	protected function getDefaults() {
438
+
439
+		return [
440
+			'VERSION' => '4.0',
441
+			'PRODID'  => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
442
+			'UID'     => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
443
+		];
444
+
445
+	}
446
+
447
+	/**
448
+	 * This method returns an array, with the representation as it should be
449
+	 * encoded in json. This is used to create jCard or jCal documents.
450
+	 *
451
+	 * @return array
452
+	 */
453
+	public function jsonSerialize() {
454
+
455
+		// A vcard does not have sub-components, so we're overriding this
456
+		// method to remove that array element.
457
+		$properties = [];
458
+
459
+		foreach ($this->children() as $child) {
460
+			$properties[] = $child->jsonSerialize();
461
+		}
462
+
463
+		return [
464
+			strtolower($this->name),
465
+			$properties,
466
+		];
467
+
468
+	}
469
+
470
+	/**
471
+	 * This method serializes the data into XML. This is used to create xCard or
472
+	 * xCal documents.
473
+	 *
474
+	 * @param Xml\Writer $writer  XML writer.
475
+	 *
476
+	 * @return void
477
+	 */
478
+	public function xmlSerialize(Xml\Writer $writer) {
479
+
480
+		$propertiesByGroup = [];
481
+
482
+		foreach ($this->children() as $property) {
483
+
484
+			$group = $property->group;
485
+
486
+			if (!isset($propertiesByGroup[$group])) {
487
+				$propertiesByGroup[$group] = [];
488
+			}
489
+
490
+			$propertiesByGroup[$group][] = $property;
491
+
492
+		}
493
+
494
+		$writer->startElement(strtolower($this->name));
495
+
496
+		foreach ($propertiesByGroup as $group => $properties) {
497
+
498
+			if (!empty($group)) {
499
+
500
+				$writer->startElement('group');
501
+				$writer->writeAttribute('name', strtolower($group));
502 502
 
503
-            }
504
-
505
-            foreach ($properties as $property) {
506
-                switch ($property->name) {
503
+			}
504
+
505
+			foreach ($properties as $property) {
506
+				switch ($property->name) {
507 507
 
508
-                    case 'VERSION':
509
-                        continue;
508
+					case 'VERSION':
509
+						continue;
510 510
 
511
-                    case 'XML':
512
-                        $value = $property->getParts();
513
-                        $fragment = new Xml\Element\XmlFragment($value[0]);
514
-                        $writer->write($fragment);
515
-                        break;
516
-
517
-                    default:
518
-                        $property->xmlSerialize($writer);
519
-                        break;
511
+					case 'XML':
512
+						$value = $property->getParts();
513
+						$fragment = new Xml\Element\XmlFragment($value[0]);
514
+						$writer->write($fragment);
515
+						break;
516
+
517
+					default:
518
+						$property->xmlSerialize($writer);
519
+						break;
520 520
 
521
-                }
522
-            }
521
+				}
522
+			}
523 523
 
524
-            if (!empty($group)) {
525
-                $writer->endElement();
526
-            }
524
+			if (!empty($group)) {
525
+				$writer->endElement();
526
+			}
527 527
 
528
-        }
528
+		}
529 529
 
530
-        $writer->endElement();
530
+		$writer->endElement();
531 531
 
532
-    }
532
+	}
533 533
 
534
-    /**
535
-     * Returns the default class for a property name.
536
-     *
537
-     * @param string $propertyName
538
-     *
539
-     * @return string
540
-     */
541
-    public function getClassNameForPropertyName($propertyName) {
534
+	/**
535
+	 * Returns the default class for a property name.
536
+	 *
537
+	 * @param string $propertyName
538
+	 *
539
+	 * @return string
540
+	 */
541
+	public function getClassNameForPropertyName($propertyName) {
542 542
 
543
-        $className = parent::getClassNameForPropertyName($propertyName);
543
+		$className = parent::getClassNameForPropertyName($propertyName);
544 544
 
545
-        // In vCard 4, BINARY no longer exists, and we need URI instead.
546
-        if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType() === self::VCARD40) {
547
-            return 'Sabre\\VObject\\Property\\Uri';
548
-        }
549
-        return $className;
545
+		// In vCard 4, BINARY no longer exists, and we need URI instead.
546
+		if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType() === self::VCARD40) {
547
+			return 'Sabre\\VObject\\Property\\Uri';
548
+		}
549
+		return $className;
550 550
 
551
-    }
551
+	}
552 552
 
553 553
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/DateTimeParser.php 4 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,9 +2,9 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\VObject;
4 4
 
5
+use DateInterval;
5 6
 use DateTimeImmutable;
6 7
 use DateTimeZone;
7
-use DateInterval;
8 8
 
9 9
 /**
10 10
  * DateTimeParser.
Please login to merge, or discard this patch.
Indentation   +480 added lines, -480 removed lines patch added patch discarded remove patch
@@ -18,252 +18,252 @@  discard block
 block discarded – undo
18 18
  */
19 19
 class DateTimeParser {
20 20
 
21
-    /**
22
-     * Parses an iCalendar (rfc5545) formatted datetime and returns a
23
-     * DateTimeImmutable object.
24
-     *
25
-     * Specifying a reference timezone is optional. It will only be used
26
-     * if the non-UTC format is used. The argument is used as a reference, the
27
-     * returned DateTimeImmutable object will still be in the UTC timezone.
28
-     *
29
-     * @param string $dt
30
-     * @param DateTimeZone $tz
31
-     *
32
-     * @return DateTimeImmutable
33
-     */
34
-    static function parseDateTime($dt, DateTimeZone $tz = null) {
35
-
36
-        // Format is YYYYMMDD + "T" + hhmmss
37
-        $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/', $dt, $matches);
38
-
39
-        if (!$result) {
40
-            throw new InvalidDataException('The supplied iCalendar datetime value is incorrect: ' . $dt);
41
-        }
42
-
43
-        if ($matches[7] === 'Z' || is_null($tz)) {
44
-            $tz = new DateTimeZone('UTC');
45
-        }
46
-        $date = new DateTimeImmutable($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] . ':' . $matches[6], $tz);
47
-
48
-        return $date;
49
-
50
-    }
51
-
52
-    /**
53
-     * Parses an iCalendar (rfc5545) formatted date and returns a DateTimeImmutable object.
54
-     *
55
-     * @param string $date
56
-     * @param DateTimeZone $tz
57
-     *
58
-     * @return DateTimeImmutable
59
-     */
60
-    static function parseDate($date, DateTimeZone $tz = null) {
61
-
62
-        // Format is YYYYMMDD
63
-        $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])$/', $date, $matches);
64
-
65
-        if (!$result) {
66
-            throw new InvalidDataException('The supplied iCalendar date value is incorrect: ' . $date);
67
-        }
68
-
69
-        if (is_null($tz)) {
70
-            $tz = new DateTimeZone('UTC');
71
-        }
72
-
73
-        $date = new DateTimeImmutable($matches[1] . '-' . $matches[2] . '-' . $matches[3], $tz);
74
-
75
-        return $date;
76
-
77
-    }
78
-
79
-    /**
80
-     * Parses an iCalendar (RFC5545) formatted duration value.
81
-     *
82
-     * This method will either return a DateTimeInterval object, or a string
83
-     * suitable for strtotime or DateTime::modify.
84
-     *
85
-     * @param string $duration
86
-     * @param bool $asString
87
-     *
88
-     * @return DateInterval|string
89
-     */
90
-    static function parseDuration($duration, $asString = false) {
91
-
92
-        $result = preg_match('/^(?<plusminus>\+|-)?P((?<week>\d+)W)?((?<day>\d+)D)?(T((?<hour>\d+)H)?((?<minute>\d+)M)?((?<second>\d+)S)?)?$/', $duration, $matches);
93
-        if (!$result) {
94
-            throw new InvalidDataException('The supplied iCalendar duration value is incorrect: ' . $duration);
95
-        }
96
-
97
-        if (!$asString) {
98
-
99
-            $invert = false;
100
-
101
-            if ($matches['plusminus'] === '-') {
102
-                $invert = true;
103
-            }
104
-
105
-            $parts = [
106
-                'week',
107
-                'day',
108
-                'hour',
109
-                'minute',
110
-                'second',
111
-            ];
112
-
113
-            foreach ($parts as $part) {
114
-                $matches[$part] = isset($matches[$part]) && $matches[$part] ? (int)$matches[$part] : 0;
115
-            }
116
-
117
-            // We need to re-construct the $duration string, because weeks and
118
-            // days are not supported by DateInterval in the same string.
119
-            $duration = 'P';
120
-            $days = $matches['day'];
121
-
122
-            if ($matches['week']) {
123
-                $days += $matches['week'] * 7;
124
-            }
125
-
126
-            if ($days) {
127
-                $duration .= $days . 'D';
128
-            }
129
-
130
-            if ($matches['minute'] || $matches['second'] || $matches['hour']) {
131
-
132
-                $duration .= 'T';
133
-
134
-                if ($matches['hour']) {
135
-                    $duration .= $matches['hour'] . 'H';
136
-                }
137
-
138
-                if ($matches['minute']) {
139
-                    $duration .= $matches['minute'] . 'M';
140
-                }
141
-
142
-                if ($matches['second']) {
143
-                    $duration .= $matches['second'] . 'S';
144
-                }
145
-
146
-            }
147
-
148
-            if ($duration === 'P') {
149
-                $duration = 'PT0S';
150
-            }
151
-
152
-            $iv = new DateInterval($duration);
153
-
154
-            if ($invert) {
155
-                $iv->invert = true;
156
-            }
157
-
158
-            return $iv;
159
-
160
-        }
161
-
162
-        $parts = [
163
-            'week',
164
-            'day',
165
-            'hour',
166
-            'minute',
167
-            'second',
168
-        ];
169
-
170
-        $newDur = '';
171
-
172
-        foreach ($parts as $part) {
173
-            if (isset($matches[$part]) && $matches[$part]) {
174
-                $newDur .= ' ' . $matches[$part] . ' ' . $part . 's';
175
-            }
176
-        }
177
-
178
-        $newDur = ($matches['plusminus'] === '-' ? '-' : '+') . trim($newDur);
179
-
180
-        if ($newDur === '+') {
181
-            $newDur = '+0 seconds';
182
-        };
183
-
184
-        return $newDur;
185
-
186
-    }
187
-
188
-    /**
189
-     * Parses either a Date or DateTime, or Duration value.
190
-     *
191
-     * @param string $date
192
-     * @param DateTimeZone|string $referenceTz
193
-     *
194
-     * @return DateTimeImmutable|DateInterval
195
-     */
196
-    static function parse($date, $referenceTz = null) {
197
-
198
-        if ($date[0] === 'P' || ($date[0] === '-' && $date[1] === 'P')) {
199
-            return self::parseDuration($date);
200
-        } elseif (strlen($date) === 8) {
201
-            return self::parseDate($date, $referenceTz);
202
-        } else {
203
-            return self::parseDateTime($date, $referenceTz);
204
-        }
205
-
206
-    }
207
-
208
-    /**
209
-     * This method parses a vCard date and or time value.
210
-     *
211
-     * This can be used for the DATE, DATE-TIME, TIMESTAMP and
212
-     * DATE-AND-OR-TIME value.
213
-     *
214
-     * This method returns an array, not a DateTime value.
215
-     *
216
-     * The elements in the array are in the following order:
217
-     * year, month, date, hour, minute, second, timezone
218
-     *
219
-     * Almost any part of the string may be omitted. It's for example legal to
220
-     * just specify seconds, leave out the year, etc.
221
-     *
222
-     * Timezone is either returned as 'Z' or as '+0800'
223
-     *
224
-     * For any non-specified values null is returned.
225
-     *
226
-     * List of date formats that are supported:
227
-     * YYYY
228
-     * YYYY-MM
229
-     * YYYYMMDD
230
-     * --MMDD
231
-     * ---DD
232
-     *
233
-     * YYYY-MM-DD
234
-     * --MM-DD
235
-     * ---DD
236
-     *
237
-     * List of supported time formats:
238
-     *
239
-     * HH
240
-     * HHMM
241
-     * HHMMSS
242
-     * -MMSS
243
-     * --SS
244
-     *
245
-     * HH
246
-     * HH:MM
247
-     * HH:MM:SS
248
-     * -MM:SS
249
-     * --SS
250
-     *
251
-     * A full basic-format date-time string looks like :
252
-     * 20130603T133901
253
-     *
254
-     * A full extended-format date-time string looks like :
255
-     * 2013-06-03T13:39:01
256
-     *
257
-     * Times may be postfixed by a timezone offset. This can be either 'Z' for
258
-     * UTC, or a string like -0500 or +1100.
259
-     *
260
-     * @param string $date
261
-     *
262
-     * @return array
263
-     */
264
-    static function parseVCardDateTime($date) {
265
-
266
-        $regex = '/^
21
+	/**
22
+	 * Parses an iCalendar (rfc5545) formatted datetime and returns a
23
+	 * DateTimeImmutable object.
24
+	 *
25
+	 * Specifying a reference timezone is optional. It will only be used
26
+	 * if the non-UTC format is used. The argument is used as a reference, the
27
+	 * returned DateTimeImmutable object will still be in the UTC timezone.
28
+	 *
29
+	 * @param string $dt
30
+	 * @param DateTimeZone $tz
31
+	 *
32
+	 * @return DateTimeImmutable
33
+	 */
34
+	static function parseDateTime($dt, DateTimeZone $tz = null) {
35
+
36
+		// Format is YYYYMMDD + "T" + hhmmss
37
+		$result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/', $dt, $matches);
38
+
39
+		if (!$result) {
40
+			throw new InvalidDataException('The supplied iCalendar datetime value is incorrect: ' . $dt);
41
+		}
42
+
43
+		if ($matches[7] === 'Z' || is_null($tz)) {
44
+			$tz = new DateTimeZone('UTC');
45
+		}
46
+		$date = new DateTimeImmutable($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] . ':' . $matches[6], $tz);
47
+
48
+		return $date;
49
+
50
+	}
51
+
52
+	/**
53
+	 * Parses an iCalendar (rfc5545) formatted date and returns a DateTimeImmutable object.
54
+	 *
55
+	 * @param string $date
56
+	 * @param DateTimeZone $tz
57
+	 *
58
+	 * @return DateTimeImmutable
59
+	 */
60
+	static function parseDate($date, DateTimeZone $tz = null) {
61
+
62
+		// Format is YYYYMMDD
63
+		$result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])$/', $date, $matches);
64
+
65
+		if (!$result) {
66
+			throw new InvalidDataException('The supplied iCalendar date value is incorrect: ' . $date);
67
+		}
68
+
69
+		if (is_null($tz)) {
70
+			$tz = new DateTimeZone('UTC');
71
+		}
72
+
73
+		$date = new DateTimeImmutable($matches[1] . '-' . $matches[2] . '-' . $matches[3], $tz);
74
+
75
+		return $date;
76
+
77
+	}
78
+
79
+	/**
80
+	 * Parses an iCalendar (RFC5545) formatted duration value.
81
+	 *
82
+	 * This method will either return a DateTimeInterval object, or a string
83
+	 * suitable for strtotime or DateTime::modify.
84
+	 *
85
+	 * @param string $duration
86
+	 * @param bool $asString
87
+	 *
88
+	 * @return DateInterval|string
89
+	 */
90
+	static function parseDuration($duration, $asString = false) {
91
+
92
+		$result = preg_match('/^(?<plusminus>\+|-)?P((?<week>\d+)W)?((?<day>\d+)D)?(T((?<hour>\d+)H)?((?<minute>\d+)M)?((?<second>\d+)S)?)?$/', $duration, $matches);
93
+		if (!$result) {
94
+			throw new InvalidDataException('The supplied iCalendar duration value is incorrect: ' . $duration);
95
+		}
96
+
97
+		if (!$asString) {
98
+
99
+			$invert = false;
100
+
101
+			if ($matches['plusminus'] === '-') {
102
+				$invert = true;
103
+			}
104
+
105
+			$parts = [
106
+				'week',
107
+				'day',
108
+				'hour',
109
+				'minute',
110
+				'second',
111
+			];
112
+
113
+			foreach ($parts as $part) {
114
+				$matches[$part] = isset($matches[$part]) && $matches[$part] ? (int)$matches[$part] : 0;
115
+			}
116
+
117
+			// We need to re-construct the $duration string, because weeks and
118
+			// days are not supported by DateInterval in the same string.
119
+			$duration = 'P';
120
+			$days = $matches['day'];
121
+
122
+			if ($matches['week']) {
123
+				$days += $matches['week'] * 7;
124
+			}
125
+
126
+			if ($days) {
127
+				$duration .= $days . 'D';
128
+			}
129
+
130
+			if ($matches['minute'] || $matches['second'] || $matches['hour']) {
131
+
132
+				$duration .= 'T';
133
+
134
+				if ($matches['hour']) {
135
+					$duration .= $matches['hour'] . 'H';
136
+				}
137
+
138
+				if ($matches['minute']) {
139
+					$duration .= $matches['minute'] . 'M';
140
+				}
141
+
142
+				if ($matches['second']) {
143
+					$duration .= $matches['second'] . 'S';
144
+				}
145
+
146
+			}
147
+
148
+			if ($duration === 'P') {
149
+				$duration = 'PT0S';
150
+			}
151
+
152
+			$iv = new DateInterval($duration);
153
+
154
+			if ($invert) {
155
+				$iv->invert = true;
156
+			}
157
+
158
+			return $iv;
159
+
160
+		}
161
+
162
+		$parts = [
163
+			'week',
164
+			'day',
165
+			'hour',
166
+			'minute',
167
+			'second',
168
+		];
169
+
170
+		$newDur = '';
171
+
172
+		foreach ($parts as $part) {
173
+			if (isset($matches[$part]) && $matches[$part]) {
174
+				$newDur .= ' ' . $matches[$part] . ' ' . $part . 's';
175
+			}
176
+		}
177
+
178
+		$newDur = ($matches['plusminus'] === '-' ? '-' : '+') . trim($newDur);
179
+
180
+		if ($newDur === '+') {
181
+			$newDur = '+0 seconds';
182
+		};
183
+
184
+		return $newDur;
185
+
186
+	}
187
+
188
+	/**
189
+	 * Parses either a Date or DateTime, or Duration value.
190
+	 *
191
+	 * @param string $date
192
+	 * @param DateTimeZone|string $referenceTz
193
+	 *
194
+	 * @return DateTimeImmutable|DateInterval
195
+	 */
196
+	static function parse($date, $referenceTz = null) {
197
+
198
+		if ($date[0] === 'P' || ($date[0] === '-' && $date[1] === 'P')) {
199
+			return self::parseDuration($date);
200
+		} elseif (strlen($date) === 8) {
201
+			return self::parseDate($date, $referenceTz);
202
+		} else {
203
+			return self::parseDateTime($date, $referenceTz);
204
+		}
205
+
206
+	}
207
+
208
+	/**
209
+	 * This method parses a vCard date and or time value.
210
+	 *
211
+	 * This can be used for the DATE, DATE-TIME, TIMESTAMP and
212
+	 * DATE-AND-OR-TIME value.
213
+	 *
214
+	 * This method returns an array, not a DateTime value.
215
+	 *
216
+	 * The elements in the array are in the following order:
217
+	 * year, month, date, hour, minute, second, timezone
218
+	 *
219
+	 * Almost any part of the string may be omitted. It's for example legal to
220
+	 * just specify seconds, leave out the year, etc.
221
+	 *
222
+	 * Timezone is either returned as 'Z' or as '+0800'
223
+	 *
224
+	 * For any non-specified values null is returned.
225
+	 *
226
+	 * List of date formats that are supported:
227
+	 * YYYY
228
+	 * YYYY-MM
229
+	 * YYYYMMDD
230
+	 * --MMDD
231
+	 * ---DD
232
+	 *
233
+	 * YYYY-MM-DD
234
+	 * --MM-DD
235
+	 * ---DD
236
+	 *
237
+	 * List of supported time formats:
238
+	 *
239
+	 * HH
240
+	 * HHMM
241
+	 * HHMMSS
242
+	 * -MMSS
243
+	 * --SS
244
+	 *
245
+	 * HH
246
+	 * HH:MM
247
+	 * HH:MM:SS
248
+	 * -MM:SS
249
+	 * --SS
250
+	 *
251
+	 * A full basic-format date-time string looks like :
252
+	 * 20130603T133901
253
+	 *
254
+	 * A full extended-format date-time string looks like :
255
+	 * 2013-06-03T13:39:01
256
+	 *
257
+	 * Times may be postfixed by a timezone offset. This can be either 'Z' for
258
+	 * UTC, or a string like -0500 or +1100.
259
+	 *
260
+	 * @param string $date
261
+	 *
262
+	 * @return array
263
+	 */
264
+	static function parseVCardDateTime($date) {
265
+
266
+		$regex = '/^
267 267
             (?:  # date part
268 268
                 (?:
269 269
                     (?: (?<year> [0-9]{4}) (?: -)?| --)
@@ -286,10 +286,10 @@  discard block
 block discarded – undo
286 286
             )?
287 287
             $/x';
288 288
 
289
-        if (!preg_match($regex, $date, $matches)) {
289
+		if (!preg_match($regex, $date, $matches)) {
290 290
 
291
-            // Attempting to parse the extended format.
292
-            $regex = '/^
291
+			// Attempting to parse the extended format.
292
+			$regex = '/^
293 293
                 (?: # date part
294 294
                     (?: (?<year> [0-9]{4}) - | -- )
295 295
                     (?<month> [0-9]{2}) -
@@ -311,83 +311,83 @@  discard block
 block discarded – undo
311 311
                 )?
312 312
                 $/x';
313 313
 
314
-            if (!preg_match($regex, $date, $matches)) {
315
-                throw new InvalidDataException('Invalid vCard date-time string: ' . $date);
316
-            }
317
-
318
-        }
319
-        $parts = [
320
-            'year',
321
-            'month',
322
-            'date',
323
-            'hour',
324
-            'minute',
325
-            'second',
326
-            'timezone'
327
-        ];
328
-
329
-        $result = [];
330
-        foreach ($parts as $part) {
331
-
332
-            if (empty($matches[$part])) {
333
-                $result[$part] = null;
334
-            } elseif ($matches[$part] === '-' || $matches[$part] === '--') {
335
-                $result[$part] = null;
336
-            } else {
337
-                $result[$part] = $matches[$part];
338
-            }
339
-
340
-        }
341
-
342
-        return $result;
343
-
344
-    }
345
-
346
-    /**
347
-     * This method parses a vCard TIME value.
348
-     *
349
-     * This method returns an array, not a DateTime value.
350
-     *
351
-     * The elements in the array are in the following order:
352
-     * hour, minute, second, timezone
353
-     *
354
-     * Almost any part of the string may be omitted. It's for example legal to
355
-     * just specify seconds, leave out the hour etc.
356
-     *
357
-     * Timezone is either returned as 'Z' or as '+08:00'
358
-     *
359
-     * For any non-specified values null is returned.
360
-     *
361
-     * List of supported time formats:
362
-     *
363
-     * HH
364
-     * HHMM
365
-     * HHMMSS
366
-     * -MMSS
367
-     * --SS
368
-     *
369
-     * HH
370
-     * HH:MM
371
-     * HH:MM:SS
372
-     * -MM:SS
373
-     * --SS
374
-     *
375
-     * A full basic-format time string looks like :
376
-     * 133901
377
-     *
378
-     * A full extended-format time string looks like :
379
-     * 13:39:01
380
-     *
381
-     * Times may be postfixed by a timezone offset. This can be either 'Z' for
382
-     * UTC, or a string like -0500 or +11:00.
383
-     *
384
-     * @param string $date
385
-     *
386
-     * @return array
387
-     */
388
-    static function parseVCardTime($date) {
389
-
390
-        $regex = '/^
314
+			if (!preg_match($regex, $date, $matches)) {
315
+				throw new InvalidDataException('Invalid vCard date-time string: ' . $date);
316
+			}
317
+
318
+		}
319
+		$parts = [
320
+			'year',
321
+			'month',
322
+			'date',
323
+			'hour',
324
+			'minute',
325
+			'second',
326
+			'timezone'
327
+		];
328
+
329
+		$result = [];
330
+		foreach ($parts as $part) {
331
+
332
+			if (empty($matches[$part])) {
333
+				$result[$part] = null;
334
+			} elseif ($matches[$part] === '-' || $matches[$part] === '--') {
335
+				$result[$part] = null;
336
+			} else {
337
+				$result[$part] = $matches[$part];
338
+			}
339
+
340
+		}
341
+
342
+		return $result;
343
+
344
+	}
345
+
346
+	/**
347
+	 * This method parses a vCard TIME value.
348
+	 *
349
+	 * This method returns an array, not a DateTime value.
350
+	 *
351
+	 * The elements in the array are in the following order:
352
+	 * hour, minute, second, timezone
353
+	 *
354
+	 * Almost any part of the string may be omitted. It's for example legal to
355
+	 * just specify seconds, leave out the hour etc.
356
+	 *
357
+	 * Timezone is either returned as 'Z' or as '+08:00'
358
+	 *
359
+	 * For any non-specified values null is returned.
360
+	 *
361
+	 * List of supported time formats:
362
+	 *
363
+	 * HH
364
+	 * HHMM
365
+	 * HHMMSS
366
+	 * -MMSS
367
+	 * --SS
368
+	 *
369
+	 * HH
370
+	 * HH:MM
371
+	 * HH:MM:SS
372
+	 * -MM:SS
373
+	 * --SS
374
+	 *
375
+	 * A full basic-format time string looks like :
376
+	 * 133901
377
+	 *
378
+	 * A full extended-format time string looks like :
379
+	 * 13:39:01
380
+	 *
381
+	 * Times may be postfixed by a timezone offset. This can be either 'Z' for
382
+	 * UTC, or a string like -0500 or +11:00.
383
+	 *
384
+	 * @param string $date
385
+	 *
386
+	 * @return array
387
+	 */
388
+	static function parseVCardTime($date) {
389
+
390
+		$regex = '/^
391 391
             (?<hour> [0-9]{2} | -)
392 392
             (?<minute> [0-9]{2} | -)?
393 393
             (?<second> [0-9]{2})?
@@ -401,10 +401,10 @@  discard block
 block discarded – undo
401 401
             $/x';
402 402
 
403 403
 
404
-        if (!preg_match($regex, $date, $matches)) {
404
+		if (!preg_match($regex, $date, $matches)) {
405 405
 
406
-            // Attempting to parse the extended format.
407
-            $regex = '/^
406
+			// Attempting to parse the extended format.
407
+			$regex = '/^
408 408
                 (?: (?<hour> [0-9]{2}) : | -)
409 409
                 (?: (?<minute> [0-9]{2}) : | -)?
410 410
                 (?<second> [0-9]{2})?
@@ -417,155 +417,155 @@  discard block
 block discarded – undo
417 417
                 )?
418 418
                 $/x';
419 419
 
420
-            if (!preg_match($regex, $date, $matches)) {
421
-                throw new InvalidDataException('Invalid vCard time string: ' . $date);
422
-            }
423
-
424
-        }
425
-        $parts = [
426
-            'hour',
427
-            'minute',
428
-            'second',
429
-            'timezone'
430
-        ];
431
-
432
-        $result = [];
433
-        foreach ($parts as $part) {
434
-
435
-            if (empty($matches[$part])) {
436
-                $result[$part] = null;
437
-            } elseif ($matches[$part] === '-') {
438
-                $result[$part] = null;
439
-            } else {
440
-                $result[$part] = $matches[$part];
441
-            }
442
-
443
-        }
444
-
445
-        return $result;
446
-
447
-    }
448
-
449
-    /**
450
-     * This method parses a vCard date and or time value.
451
-     *
452
-     * This can be used for the DATE, DATE-TIME and
453
-     * DATE-AND-OR-TIME value.
454
-     *
455
-     * This method returns an array, not a DateTime value.
456
-     * The elements in the array are in the following order:
457
-     *     year, month, date, hour, minute, second, timezone
458
-     * Almost any part of the string may be omitted. It's for example legal to
459
-     * just specify seconds, leave out the year, etc.
460
-     *
461
-     * Timezone is either returned as 'Z' or as '+0800'
462
-     *
463
-     * For any non-specified values null is returned.
464
-     *
465
-     * List of date formats that are supported:
466
-     *     20150128
467
-     *     2015-01
468
-     *     --01
469
-     *     --0128
470
-     *     ---28
471
-     *
472
-     * List of supported time formats:
473
-     *     13
474
-     *     1353
475
-     *     135301
476
-     *     -53
477
-     *     -5301
478
-     *     --01 (unreachable, see the tests)
479
-     *     --01Z
480
-     *     --01+1234
481
-     *
482
-     * List of supported date-time formats:
483
-     *     20150128T13
484
-     *     --0128T13
485
-     *     ---28T13
486
-     *     ---28T1353
487
-     *     ---28T135301
488
-     *     ---28T13Z
489
-     *     ---28T13+1234
490
-     *
491
-     * See the regular expressions for all the possible patterns.
492
-     *
493
-     * Times may be postfixed by a timezone offset. This can be either 'Z' for
494
-     * UTC, or a string like -0500 or +1100.
495
-     *
496
-     * @param string $date
497
-     *
498
-     * @return array
499
-     */
500
-    static function parseVCardDateAndOrTime($date) {
501
-
502
-        // \d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d
503
-        $valueDate     = '/^(?J)(?:' .
504
-                         '(?<year>\d{4})(?<month>\d\d)(?<date>\d\d)' .
505
-                         '|(?<year>\d{4})-(?<month>\d\d)' .
506
-                         '|--(?<month>\d\d)(?<date>\d\d)?' .
507
-                         '|---(?<date>\d\d)' .
508
-                         ')$/';
509
-
510
-        // (\d\d(\d\d(\d\d)?)?|-\d\d(\d\d)?|--\d\d)(Z|[+\-]\d\d(\d\d)?)?
511
-        $valueTime     = '/^(?J)(?:' .
512
-                         '((?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?' .
513
-                         '|-(?<minute>\d\d)(?<second>\d\d)?' .
514
-                         '|--(?<second>\d\d))' .
515
-                         '(?<timezone>(Z|[+\-]\d\d(\d\d)?))?' .
516
-                         ')$/';
517
-
518
-        // (\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?(Z|[+\-]\d\d(\d\d?)?
519
-        $valueDateTime = '/^(?:' .
520
-                         '((?<year0>\d{4})(?<month0>\d\d)(?<date0>\d\d)' .
521
-                         '|--(?<month1>\d\d)(?<date1>\d\d)' .
522
-                         '|---(?<date2>\d\d))' .
523
-                         'T' .
524
-                         '(?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?' .
525
-                         '(?<timezone>(Z|[+\-]\d\d(\d\d?)))?' .
526
-                         ')$/';
527
-
528
-        // date-and-or-time is date | date-time | time
529
-        // in this strict order.
530
-
531
-        if (0 === preg_match($valueDate, $date, $matches)
532
-            && 0 === preg_match($valueDateTime, $date, $matches)
533
-            && 0 === preg_match($valueTime, $date, $matches)) {
534
-            throw new InvalidDataException('Invalid vCard date-time string: ' . $date);
535
-        }
536
-
537
-        $parts = [
538
-            'year'     => null,
539
-            'month'    => null,
540
-            'date'     => null,
541
-            'hour'     => null,
542
-            'minute'   => null,
543
-            'second'   => null,
544
-            'timezone' => null
545
-        ];
546
-
547
-        // The $valueDateTime expression has a bug with (?J) so we simulate it.
548
-        $parts['date0']  = &$parts['date'];
549
-        $parts['date1']  = &$parts['date'];
550
-        $parts['date2']  = &$parts['date'];
551
-        $parts['month0'] = &$parts['month'];
552
-        $parts['month1'] = &$parts['month'];
553
-        $parts['year0']  = &$parts['year'];
554
-
555
-        foreach ($parts as $part => &$value) {
556
-            if (!empty($matches[$part])) {
557
-                $value = $matches[$part];
558
-            }
559
-        }
560
-
561
-        unset($parts['date0']);
562
-        unset($parts['date1']);
563
-        unset($parts['date2']);
564
-        unset($parts['month0']);
565
-        unset($parts['month1']);
566
-        unset($parts['year0']);
567
-
568
-        return $parts;
569
-
570
-    }
420
+			if (!preg_match($regex, $date, $matches)) {
421
+				throw new InvalidDataException('Invalid vCard time string: ' . $date);
422
+			}
423
+
424
+		}
425
+		$parts = [
426
+			'hour',
427
+			'minute',
428
+			'second',
429
+			'timezone'
430
+		];
431
+
432
+		$result = [];
433
+		foreach ($parts as $part) {
434
+
435
+			if (empty($matches[$part])) {
436
+				$result[$part] = null;
437
+			} elseif ($matches[$part] === '-') {
438
+				$result[$part] = null;
439
+			} else {
440
+				$result[$part] = $matches[$part];
441
+			}
442
+
443
+		}
444
+
445
+		return $result;
446
+
447
+	}
448
+
449
+	/**
450
+	 * This method parses a vCard date and or time value.
451
+	 *
452
+	 * This can be used for the DATE, DATE-TIME and
453
+	 * DATE-AND-OR-TIME value.
454
+	 *
455
+	 * This method returns an array, not a DateTime value.
456
+	 * The elements in the array are in the following order:
457
+	 *     year, month, date, hour, minute, second, timezone
458
+	 * Almost any part of the string may be omitted. It's for example legal to
459
+	 * just specify seconds, leave out the year, etc.
460
+	 *
461
+	 * Timezone is either returned as 'Z' or as '+0800'
462
+	 *
463
+	 * For any non-specified values null is returned.
464
+	 *
465
+	 * List of date formats that are supported:
466
+	 *     20150128
467
+	 *     2015-01
468
+	 *     --01
469
+	 *     --0128
470
+	 *     ---28
471
+	 *
472
+	 * List of supported time formats:
473
+	 *     13
474
+	 *     1353
475
+	 *     135301
476
+	 *     -53
477
+	 *     -5301
478
+	 *     --01 (unreachable, see the tests)
479
+	 *     --01Z
480
+	 *     --01+1234
481
+	 *
482
+	 * List of supported date-time formats:
483
+	 *     20150128T13
484
+	 *     --0128T13
485
+	 *     ---28T13
486
+	 *     ---28T1353
487
+	 *     ---28T135301
488
+	 *     ---28T13Z
489
+	 *     ---28T13+1234
490
+	 *
491
+	 * See the regular expressions for all the possible patterns.
492
+	 *
493
+	 * Times may be postfixed by a timezone offset. This can be either 'Z' for
494
+	 * UTC, or a string like -0500 or +1100.
495
+	 *
496
+	 * @param string $date
497
+	 *
498
+	 * @return array
499
+	 */
500
+	static function parseVCardDateAndOrTime($date) {
501
+
502
+		// \d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d
503
+		$valueDate     = '/^(?J)(?:' .
504
+						 '(?<year>\d{4})(?<month>\d\d)(?<date>\d\d)' .
505
+						 '|(?<year>\d{4})-(?<month>\d\d)' .
506
+						 '|--(?<month>\d\d)(?<date>\d\d)?' .
507
+						 '|---(?<date>\d\d)' .
508
+						 ')$/';
509
+
510
+		// (\d\d(\d\d(\d\d)?)?|-\d\d(\d\d)?|--\d\d)(Z|[+\-]\d\d(\d\d)?)?
511
+		$valueTime     = '/^(?J)(?:' .
512
+						 '((?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?' .
513
+						 '|-(?<minute>\d\d)(?<second>\d\d)?' .
514
+						 '|--(?<second>\d\d))' .
515
+						 '(?<timezone>(Z|[+\-]\d\d(\d\d)?))?' .
516
+						 ')$/';
517
+
518
+		// (\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?(Z|[+\-]\d\d(\d\d?)?
519
+		$valueDateTime = '/^(?:' .
520
+						 '((?<year0>\d{4})(?<month0>\d\d)(?<date0>\d\d)' .
521
+						 '|--(?<month1>\d\d)(?<date1>\d\d)' .
522
+						 '|---(?<date2>\d\d))' .
523
+						 'T' .
524
+						 '(?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?' .
525
+						 '(?<timezone>(Z|[+\-]\d\d(\d\d?)))?' .
526
+						 ')$/';
527
+
528
+		// date-and-or-time is date | date-time | time
529
+		// in this strict order.
530
+
531
+		if (0 === preg_match($valueDate, $date, $matches)
532
+			&& 0 === preg_match($valueDateTime, $date, $matches)
533
+			&& 0 === preg_match($valueTime, $date, $matches)) {
534
+			throw new InvalidDataException('Invalid vCard date-time string: ' . $date);
535
+		}
536
+
537
+		$parts = [
538
+			'year'     => null,
539
+			'month'    => null,
540
+			'date'     => null,
541
+			'hour'     => null,
542
+			'minute'   => null,
543
+			'second'   => null,
544
+			'timezone' => null
545
+		];
546
+
547
+		// The $valueDateTime expression has a bug with (?J) so we simulate it.
548
+		$parts['date0']  = &$parts['date'];
549
+		$parts['date1']  = &$parts['date'];
550
+		$parts['date2']  = &$parts['date'];
551
+		$parts['month0'] = &$parts['month'];
552
+		$parts['month1'] = &$parts['month'];
553
+		$parts['year0']  = &$parts['year'];
554
+
555
+		foreach ($parts as $part => &$value) {
556
+			if (!empty($matches[$part])) {
557
+				$value = $matches[$part];
558
+			}
559
+		}
560
+
561
+		unset($parts['date0']);
562
+		unset($parts['date1']);
563
+		unset($parts['date2']);
564
+		unset($parts['month0']);
565
+		unset($parts['month1']);
566
+		unset($parts['year0']);
567
+
568
+		return $parts;
569
+
570
+	}
571 571
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -111,7 +111,7 @@  discard block
 block discarded – undo
111 111
             ];
112 112
 
113 113
             foreach ($parts as $part) {
114
-                $matches[$part] = isset($matches[$part]) && $matches[$part] ? (int)$matches[$part] : 0;
114
+                $matches[$part] = isset($matches[$part]) && $matches[$part] ? (int) $matches[$part] : 0;
115 115
             }
116 116
 
117 117
             // We need to re-construct the $duration string, because weeks and
@@ -500,7 +500,7 @@  discard block
 block discarded – undo
500 500
     static function parseVCardDateAndOrTime($date) {
501 501
 
502 502
         // \d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d
503
-        $valueDate     = '/^(?J)(?:' .
503
+        $valueDate = '/^(?J)(?:' .
504 504
                          '(?<year>\d{4})(?<month>\d\d)(?<date>\d\d)' .
505 505
                          '|(?<year>\d{4})-(?<month>\d\d)' .
506 506
                          '|--(?<month>\d\d)(?<date>\d\d)?' .
@@ -508,7 +508,7 @@  discard block
 block discarded – undo
508 508
                          ')$/';
509 509
 
510 510
         // (\d\d(\d\d(\d\d)?)?|-\d\d(\d\d)?|--\d\d)(Z|[+\-]\d\d(\d\d)?)?
511
-        $valueTime     = '/^(?J)(?:' .
511
+        $valueTime = '/^(?J)(?:' .
512 512
                          '((?<hour>\d\d)((?<minute>\d\d)(?<second>\d\d)?)?' .
513 513
                          '|-(?<minute>\d\d)(?<second>\d\d)?' .
514 514
                          '|--(?<second>\d\d))' .
Please login to merge, or discard this patch.
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -85,7 +85,7 @@
 block discarded – undo
85 85
      * @param string $duration
86 86
      * @param bool $asString
87 87
      *
88
-     * @return DateInterval|string
88
+     * @return DateInterval
89 89
      */
90 90
     static function parseDuration($duration, $asString = false) {
91 91
 
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Document.php 3 patches
Doc Comments   -1 removed lines patch added patch discarded remove patch
@@ -125,7 +125,6 @@
 block discarded – undo
125 125
      * otherwise, we'll assume it's a property and call createProperty instead.
126 126
      *
127 127
      * @param string $name
128
-     * @param string $arg1,... Unlimited number of args
129 128
      *
130 129
      * @return mixed
131 130
      */
Please login to merge, or discard this patch.
Braces   +7 added lines, -4 removed lines patch added patch discarded remove patch
@@ -171,7 +171,9 @@  discard block
 block discarded – undo
171 171
         if (isset(static::$componentMap[$name])) {
172 172
             $class = static::$componentMap[$name];
173 173
         }
174
-        if (is_null($children)) $children = [];
174
+        if (is_null($children)) {
175
+        	$children = [];
176
+        }
175 177
         return new $class($this, $name, $children, $defaults);
176 178
 
177 179
     }
@@ -219,12 +221,13 @@  discard block
 block discarded – undo
219 221
                 if (is_null($class)) {
220 222
                     throw new InvalidDataException('Unsupported VALUE parameter for ' . $name . ' property. You supplied "' . $parameters['VALUE'] . '"');
221 223
                 }
222
-            }
223
-            else {
224
+            } else {
224 225
                 $class = $this->getClassNameForPropertyName($name);
225 226
             }
226 227
         }
227
-        if (is_null($parameters)) $parameters = [];
228
+        if (is_null($parameters)) {
229
+        	$parameters = [];
230
+        }
228 231
 
229 232
         return new $class($this, $name, $value, $parameters, $group);
230 233
 
Please login to merge, or discard this patch.
Indentation   +248 added lines, -248 removed lines patch added patch discarded remove patch
@@ -18,253 +18,253 @@
 block discarded – undo
18 18
  */
19 19
 abstract class Document extends Component {
20 20
 
21
-    /**
22
-     * Unknown document type.
23
-     */
24
-    const UNKNOWN = 1;
25
-
26
-    /**
27
-     * vCalendar 1.0.
28
-     */
29
-    const VCALENDAR10 = 2;
30
-
31
-    /**
32
-     * iCalendar 2.0.
33
-     */
34
-    const ICALENDAR20 = 3;
35
-
36
-    /**
37
-     * vCard 2.1.
38
-     */
39
-    const VCARD21 = 4;
40
-
41
-    /**
42
-     * vCard 3.0.
43
-     */
44
-    const VCARD30 = 5;
45
-
46
-    /**
47
-     * vCard 4.0.
48
-     */
49
-    const VCARD40 = 6;
50
-
51
-    /**
52
-     * The default name for this component.
53
-     *
54
-     * This should be 'VCALENDAR' or 'VCARD'.
55
-     *
56
-     * @var string
57
-     */
58
-    static $defaultName;
59
-
60
-    /**
61
-     * List of properties, and which classes they map to.
62
-     *
63
-     * @var array
64
-     */
65
-    static $propertyMap = [];
66
-
67
-    /**
68
-     * List of components, along with which classes they map to.
69
-     *
70
-     * @var array
71
-     */
72
-    static $componentMap = [];
73
-
74
-    /**
75
-     * List of value-types, and which classes they map to.
76
-     *
77
-     * @var array
78
-     */
79
-    static $valueMap = [];
80
-
81
-    /**
82
-     * Creates a new document.
83
-     *
84
-     * We're changing the default behavior slightly here. First, we don't want
85
-     * to have to specify a name (we already know it), and we want to allow
86
-     * children to be specified in the first argument.
87
-     *
88
-     * But, the default behavior also works.
89
-     *
90
-     * So the two sigs:
91
-     *
92
-     * new Document(array $children = [], $defaults = true);
93
-     * new Document(string $name, array $children = [], $defaults = true)
94
-     *
95
-     * @return void
96
-     */
97
-    public function __construct() {
98
-
99
-        $args = func_get_args();
100
-        if (count($args) === 0 || is_array($args[0])) {
101
-            array_unshift($args, $this, static::$defaultName);
102
-            call_user_func_array(['parent', '__construct'], $args);
103
-        } else {
104
-            array_unshift($args, $this);
105
-            call_user_func_array(['parent', '__construct'], $args);
106
-        }
107
-
108
-    }
109
-
110
-    /**
111
-     * Returns the current document type.
112
-     *
113
-     * @return int
114
-     */
115
-    public function getDocumentType() {
116
-
117
-        return self::UNKNOWN;
118
-
119
-    }
120
-
121
-    /**
122
-     * Creates a new component or property.
123
-     *
124
-     * If it's a known component, we will automatically call createComponent.
125
-     * otherwise, we'll assume it's a property and call createProperty instead.
126
-     *
127
-     * @param string $name
128
-     * @param string $arg1,... Unlimited number of args
129
-     *
130
-     * @return mixed
131
-     */
132
-    public function create($name) {
133
-
134
-        if (isset(static::$componentMap[strtoupper($name)])) {
135
-
136
-            return call_user_func_array([$this, 'createComponent'], func_get_args());
137
-
138
-        } else {
139
-
140
-            return call_user_func_array([$this, 'createProperty'], func_get_args());
141
-
142
-        }
143
-
144
-    }
145
-
146
-    /**
147
-     * Creates a new component.
148
-     *
149
-     * This method automatically searches for the correct component class, based
150
-     * on its name.
151
-     *
152
-     * You can specify the children either in key=>value syntax, in which case
153
-     * properties will automatically be created, or you can just pass a list of
154
-     * Component and Property object.
155
-     *
156
-     * By default, a set of sensible values will be added to the component. For
157
-     * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
158
-     * ensure that this does not happen, set $defaults to false.
159
-     *
160
-     * @param string $name
161
-     * @param array $children
162
-     * @param bool $defaults
163
-     *
164
-     * @return Component
165
-     */
166
-    public function createComponent($name, array $children = null, $defaults = true) {
167
-
168
-        $name = strtoupper($name);
169
-        $class = 'Sabre\\VObject\\Component';
170
-
171
-        if (isset(static::$componentMap[$name])) {
172
-            $class = static::$componentMap[$name];
173
-        }
174
-        if (is_null($children)) $children = [];
175
-        return new $class($this, $name, $children, $defaults);
176
-
177
-    }
178
-
179
-    /**
180
-     * Factory method for creating new properties.
181
-     *
182
-     * This method automatically searches for the correct property class, based
183
-     * on its name.
184
-     *
185
-     * You can specify the parameters either in key=>value syntax, in which case
186
-     * parameters will automatically be created, or you can just pass a list of
187
-     * Parameter objects.
188
-     *
189
-     * @param string $name
190
-     * @param mixed $value
191
-     * @param array $parameters
192
-     * @param string $valueType Force a specific valuetype, such as URI or TEXT
193
-     *
194
-     * @return Property
195
-     */
196
-    public function createProperty($name, $value = null, array $parameters = null, $valueType = null) {
197
-
198
-        // If there's a . in the name, it means it's prefixed by a groupname.
199
-        if (($i = strpos($name, '.')) !== false) {
200
-            $group = substr($name, 0, $i);
201
-            $name = strtoupper(substr($name, $i + 1));
202
-        } else {
203
-            $name = strtoupper($name);
204
-            $group = null;
205
-        }
206
-
207
-        $class = null;
208
-
209
-        if ($valueType) {
210
-            // The valueType argument comes first to figure out the correct
211
-            // class.
212
-            $class = $this->getClassNameForPropertyValue($valueType);
213
-        }
214
-
215
-        if (is_null($class)) {
216
-            // If a VALUE parameter is supplied, we should use that.
217
-            if (isset($parameters['VALUE'])) {
218
-                $class = $this->getClassNameForPropertyValue($parameters['VALUE']);
219
-                if (is_null($class)) {
220
-                    throw new InvalidDataException('Unsupported VALUE parameter for ' . $name . ' property. You supplied "' . $parameters['VALUE'] . '"');
221
-                }
222
-            }
223
-            else {
224
-                $class = $this->getClassNameForPropertyName($name);
225
-            }
226
-        }
227
-        if (is_null($parameters)) $parameters = [];
228
-
229
-        return new $class($this, $name, $value, $parameters, $group);
230
-
231
-    }
232
-
233
-    /**
234
-     * This method returns a full class-name for a value parameter.
235
-     *
236
-     * For instance, DTSTART may have VALUE=DATE. In that case we will look in
237
-     * our valueMap table and return the appropriate class name.
238
-     *
239
-     * This method returns null if we don't have a specialized class.
240
-     *
241
-     * @param string $valueParam
242
-     * @return string|null
243
-     */
244
-    public function getClassNameForPropertyValue($valueParam) {
245
-
246
-        $valueParam = strtoupper($valueParam);
247
-        if (isset(static::$valueMap[$valueParam])) {
248
-            return static::$valueMap[$valueParam];
249
-        }
250
-
251
-    }
252
-
253
-    /**
254
-     * Returns the default class for a property name.
255
-     *
256
-     * @param string $propertyName
257
-     *
258
-     * @return string
259
-     */
260
-    public function getClassNameForPropertyName($propertyName) {
261
-
262
-        if (isset(static::$propertyMap[$propertyName])) {
263
-            return static::$propertyMap[$propertyName];
264
-        } else {
265
-            return 'Sabre\\VObject\\Property\\Unknown';
266
-        }
267
-
268
-    }
21
+	/**
22
+	 * Unknown document type.
23
+	 */
24
+	const UNKNOWN = 1;
25
+
26
+	/**
27
+	 * vCalendar 1.0.
28
+	 */
29
+	const VCALENDAR10 = 2;
30
+
31
+	/**
32
+	 * iCalendar 2.0.
33
+	 */
34
+	const ICALENDAR20 = 3;
35
+
36
+	/**
37
+	 * vCard 2.1.
38
+	 */
39
+	const VCARD21 = 4;
40
+
41
+	/**
42
+	 * vCard 3.0.
43
+	 */
44
+	const VCARD30 = 5;
45
+
46
+	/**
47
+	 * vCard 4.0.
48
+	 */
49
+	const VCARD40 = 6;
50
+
51
+	/**
52
+	 * The default name for this component.
53
+	 *
54
+	 * This should be 'VCALENDAR' or 'VCARD'.
55
+	 *
56
+	 * @var string
57
+	 */
58
+	static $defaultName;
59
+
60
+	/**
61
+	 * List of properties, and which classes they map to.
62
+	 *
63
+	 * @var array
64
+	 */
65
+	static $propertyMap = [];
66
+
67
+	/**
68
+	 * List of components, along with which classes they map to.
69
+	 *
70
+	 * @var array
71
+	 */
72
+	static $componentMap = [];
73
+
74
+	/**
75
+	 * List of value-types, and which classes they map to.
76
+	 *
77
+	 * @var array
78
+	 */
79
+	static $valueMap = [];
80
+
81
+	/**
82
+	 * Creates a new document.
83
+	 *
84
+	 * We're changing the default behavior slightly here. First, we don't want
85
+	 * to have to specify a name (we already know it), and we want to allow
86
+	 * children to be specified in the first argument.
87
+	 *
88
+	 * But, the default behavior also works.
89
+	 *
90
+	 * So the two sigs:
91
+	 *
92
+	 * new Document(array $children = [], $defaults = true);
93
+	 * new Document(string $name, array $children = [], $defaults = true)
94
+	 *
95
+	 * @return void
96
+	 */
97
+	public function __construct() {
98
+
99
+		$args = func_get_args();
100
+		if (count($args) === 0 || is_array($args[0])) {
101
+			array_unshift($args, $this, static::$defaultName);
102
+			call_user_func_array(['parent', '__construct'], $args);
103
+		} else {
104
+			array_unshift($args, $this);
105
+			call_user_func_array(['parent', '__construct'], $args);
106
+		}
107
+
108
+	}
109
+
110
+	/**
111
+	 * Returns the current document type.
112
+	 *
113
+	 * @return int
114
+	 */
115
+	public function getDocumentType() {
116
+
117
+		return self::UNKNOWN;
118
+
119
+	}
120
+
121
+	/**
122
+	 * Creates a new component or property.
123
+	 *
124
+	 * If it's a known component, we will automatically call createComponent.
125
+	 * otherwise, we'll assume it's a property and call createProperty instead.
126
+	 *
127
+	 * @param string $name
128
+	 * @param string $arg1,... Unlimited number of args
129
+	 *
130
+	 * @return mixed
131
+	 */
132
+	public function create($name) {
133
+
134
+		if (isset(static::$componentMap[strtoupper($name)])) {
135
+
136
+			return call_user_func_array([$this, 'createComponent'], func_get_args());
137
+
138
+		} else {
139
+
140
+			return call_user_func_array([$this, 'createProperty'], func_get_args());
141
+
142
+		}
143
+
144
+	}
145
+
146
+	/**
147
+	 * Creates a new component.
148
+	 *
149
+	 * This method automatically searches for the correct component class, based
150
+	 * on its name.
151
+	 *
152
+	 * You can specify the children either in key=>value syntax, in which case
153
+	 * properties will automatically be created, or you can just pass a list of
154
+	 * Component and Property object.
155
+	 *
156
+	 * By default, a set of sensible values will be added to the component. For
157
+	 * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
158
+	 * ensure that this does not happen, set $defaults to false.
159
+	 *
160
+	 * @param string $name
161
+	 * @param array $children
162
+	 * @param bool $defaults
163
+	 *
164
+	 * @return Component
165
+	 */
166
+	public function createComponent($name, array $children = null, $defaults = true) {
167
+
168
+		$name = strtoupper($name);
169
+		$class = 'Sabre\\VObject\\Component';
170
+
171
+		if (isset(static::$componentMap[$name])) {
172
+			$class = static::$componentMap[$name];
173
+		}
174
+		if (is_null($children)) $children = [];
175
+		return new $class($this, $name, $children, $defaults);
176
+
177
+	}
178
+
179
+	/**
180
+	 * Factory method for creating new properties.
181
+	 *
182
+	 * This method automatically searches for the correct property class, based
183
+	 * on its name.
184
+	 *
185
+	 * You can specify the parameters either in key=>value syntax, in which case
186
+	 * parameters will automatically be created, or you can just pass a list of
187
+	 * Parameter objects.
188
+	 *
189
+	 * @param string $name
190
+	 * @param mixed $value
191
+	 * @param array $parameters
192
+	 * @param string $valueType Force a specific valuetype, such as URI or TEXT
193
+	 *
194
+	 * @return Property
195
+	 */
196
+	public function createProperty($name, $value = null, array $parameters = null, $valueType = null) {
197
+
198
+		// If there's a . in the name, it means it's prefixed by a groupname.
199
+		if (($i = strpos($name, '.')) !== false) {
200
+			$group = substr($name, 0, $i);
201
+			$name = strtoupper(substr($name, $i + 1));
202
+		} else {
203
+			$name = strtoupper($name);
204
+			$group = null;
205
+		}
206
+
207
+		$class = null;
208
+
209
+		if ($valueType) {
210
+			// The valueType argument comes first to figure out the correct
211
+			// class.
212
+			$class = $this->getClassNameForPropertyValue($valueType);
213
+		}
214
+
215
+		if (is_null($class)) {
216
+			// If a VALUE parameter is supplied, we should use that.
217
+			if (isset($parameters['VALUE'])) {
218
+				$class = $this->getClassNameForPropertyValue($parameters['VALUE']);
219
+				if (is_null($class)) {
220
+					throw new InvalidDataException('Unsupported VALUE parameter for ' . $name . ' property. You supplied "' . $parameters['VALUE'] . '"');
221
+				}
222
+			}
223
+			else {
224
+				$class = $this->getClassNameForPropertyName($name);
225
+			}
226
+		}
227
+		if (is_null($parameters)) $parameters = [];
228
+
229
+		return new $class($this, $name, $value, $parameters, $group);
230
+
231
+	}
232
+
233
+	/**
234
+	 * This method returns a full class-name for a value parameter.
235
+	 *
236
+	 * For instance, DTSTART may have VALUE=DATE. In that case we will look in
237
+	 * our valueMap table and return the appropriate class name.
238
+	 *
239
+	 * This method returns null if we don't have a specialized class.
240
+	 *
241
+	 * @param string $valueParam
242
+	 * @return string|null
243
+	 */
244
+	public function getClassNameForPropertyValue($valueParam) {
245
+
246
+		$valueParam = strtoupper($valueParam);
247
+		if (isset(static::$valueMap[$valueParam])) {
248
+			return static::$valueMap[$valueParam];
249
+		}
250
+
251
+	}
252
+
253
+	/**
254
+	 * Returns the default class for a property name.
255
+	 *
256
+	 * @param string $propertyName
257
+	 *
258
+	 * @return string
259
+	 */
260
+	public function getClassNameForPropertyName($propertyName) {
261
+
262
+		if (isset(static::$propertyMap[$propertyName])) {
263
+			return static::$propertyMap[$propertyName];
264
+		} else {
265
+			return 'Sabre\\VObject\\Property\\Unknown';
266
+		}
267
+
268
+	}
269 269
 
270 270
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/FreeBusyGenerator.php 4 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,8 +2,8 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\VObject;
4 4
 
5
-use DateTimeInterface;
6 5
 use DateTimeImmutable;
6
+use DateTimeInterface;
7 7
 use DateTimeZone;
8 8
 use Sabre\VObject\Component\VCalendar;
9 9
 use Sabre\VObject\Recur\EventIterator;
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -245,8 +245,8 @@  discard block
 block discarded – undo
245 245
                 // priority 9. No priority implies priority 0.
246 246
                 //
247 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;
248
+                $priorityA = isset($a->PRIORITY) ? (int) $a->PRIORITY->getValue() : 0;
249
+                $priorityB = isset($b->PRIORITY) ? (int) $b->PRIORITY->getValue() : 0;
250 250
 
251 251
                 if ($priorityA === 0) $priorityA = 10;
252 252
                 if ($priorityB === 0) $priorityB = 10;
@@ -292,7 +292,7 @@  discard block
 block discarded – undo
292 292
                 list($higherStart, $higherEnd) = $higherVavail->getEffectiveStartEnd();
293 293
                 if (
294 294
                     (is_null($higherStart) || $higherStart < $compStart) &&
295
-                    (is_null($higherEnd)   || $higherEnd > $compEnd)
295
+                    (is_null($higherEnd) || $higherEnd > $compEnd)
296 296
                 ) {
297 297
 
298 298
                     // Component is fully covered by a higher priority
@@ -424,7 +424,7 @@  discard block
 block discarded – undo
424 424
 
425 425
                         if ($component->RRULE) {
426 426
                             try {
427
-                                $iterator = new EventIterator($object, (string)$component->UID, $this->timeZone);
427
+                                $iterator = new EventIterator($object, (string) $component->UID, $this->timeZone);
428 428
                             } catch (NoInstancesException $e) {
429 429
                                 // This event is recurring, but it doesn't have a single
430 430
                                 // instance. We are skipping this event from the output
@@ -464,7 +464,7 @@  discard block
 block discarded – undo
464 464
                             if (isset($component->DTEND)) {
465 465
                                 $endTime = $component->DTEND->getDateTime($this->timeZone);
466 466
                             } elseif (isset($component->DURATION)) {
467
-                                $duration = DateTimeParser::parseDuration((string)$component->DURATION);
467
+                                $duration = DateTimeParser::parseDuration((string) $component->DURATION);
468 468
                                 $endTime = clone $startTime;
469 469
                                 $endTime = $endTime->add($duration);
470 470
                             } elseif (!$component->DTSTART->hasTime()) {
Please login to merge, or discard this patch.
Braces   +24 added lines, -9 removed lines patch added patch discarded remove patch
@@ -248,8 +248,12 @@  discard block
 block discarded – undo
248 248
                 $priorityA = isset($a->PRIORITY) ? (int)$a->PRIORITY->getValue() : 0;
249 249
                 $priorityB = isset($b->PRIORITY) ? (int)$b->PRIORITY->getValue() : 0;
250 250
 
251
-                if ($priorityA === 0) $priorityA = 10;
252
-                if ($priorityB === 0) $priorityB = 10;
251
+                if ($priorityA === 0) {
252
+                	$priorityA = 10;
253
+                }
254
+                if ($priorityB === 0) {
255
+                	$priorityB = 10;
256
+                }
253 257
 
254 258
                 return $priorityA - $priorityB;
255 259
 
@@ -336,9 +340,11 @@  discard block
 block discarded – undo
336 340
             );
337 341
 
338 342
             // Looping over the AVAILABLE components.
339
-            if (isset($vavail->AVAILABLE)) foreach ($vavail->AVAILABLE as $available) {
343
+            if (isset($vavail->AVAILABLE)) {
344
+            	foreach ($vavail->AVAILABLE as $available) {
340 345
 
341 346
                 list($availStart, $availEnd) = $available->getEffectiveStartEnd();
347
+            }
342 348
                 $fbData->add(
343 349
                     $availStart->getTimeStamp(),
344 350
                     $availEnd->getTimeStamp(),
@@ -481,8 +487,12 @@  discard block
 block discarded – undo
481 487
 
482 488
                         foreach ($times as $time) {
483 489
 
484
-                            if ($this->end && $time[0] > $this->end) break;
485
-                            if ($this->start && $time[1] < $this->start) break;
490
+                            if ($this->end && $time[0] > $this->end) {
491
+                            	break;
492
+                            }
493
+                            if ($this->start && $time[1] < $this->start) {
494
+                            	break;
495
+                            }
486 496
 
487 497
                             $fbData->add(
488 498
                                 $time[0]->getTimeStamp(),
@@ -498,8 +508,9 @@  discard block
 block discarded – undo
498 508
                             $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY';
499 509
 
500 510
                             // Skipping intervals marked as 'free'
501
-                            if ($fbType === 'FREE')
502
-                                continue;
511
+                            if ($fbType === 'FREE') {
512
+                                                            continue;
513
+                            }
503 514
 
504 515
                             $values = explode(',', $freebusy);
505 516
                             foreach ($values as $value) {
@@ -514,8 +525,12 @@  discard block
 block discarded – undo
514 525
                                     $endTime = DateTimeParser::parseDateTime($endTime);
515 526
                                 }
516 527
 
517
-                                if ($this->start && $this->start > $endTime) continue;
518
-                                if ($this->end && $this->end < $startTime) continue;
528
+                                if ($this->start && $this->start > $endTime) {
529
+                                	continue;
530
+                                }
531
+                                if ($this->end && $this->end < $startTime) {
532
+                                	continue;
533
+                                }
519 534
                                 $fbData->add(
520 535
                                     $startTime->getTimeStamp(),
521 536
                                     $endTime->getTimeStamp(),
Please login to merge, or discard this 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
-            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
+			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/Parameter.php 5 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,8 +2,8 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\VObject;
4 4
 
5
-use Sabre\Xml;
6 5
 use ArrayIterator;
6
+use Sabre\Xml;
7 7
 
8 8
 /**
9 9
  * VObject Parameter.
Please login to merge, or discard this patch.
Indentation   +371 added lines, -371 removed lines patch added patch discarded remove patch
@@ -19,376 +19,376 @@
 block discarded – undo
19 19
  */
20 20
 class Parameter extends Node {
21 21
 
22
-    /**
23
-     * Parameter name.
24
-     *
25
-     * @var string
26
-     */
27
-    public $name;
28
-
29
-    /**
30
-     * vCard 2.1 allows parameters to be encoded without a name.
31
-     *
32
-     * We can deduce the parameter name based on it's value.
33
-     *
34
-     * @var bool
35
-     */
36
-    public $noName = false;
37
-
38
-    /**
39
-     * Parameter value.
40
-     *
41
-     * @var string
42
-     */
43
-    protected $value;
44
-
45
-    /**
46
-     * Sets up the object.
47
-     *
48
-     * It's recommended to use the create:: factory method instead.
49
-     *
50
-     * @param string $name
51
-     * @param string $value
52
-     */
53
-    public function __construct(Document $root, $name, $value = null) {
54
-
55
-        $this->name = strtoupper($name);
56
-        $this->root = $root;
57
-        if (is_null($name)) {
58
-            $this->noName = true;
59
-            $this->name = static::guessParameterNameByValue($value);
60
-        }
61
-
62
-        // If guessParameterNameByValue() returns an empty string
63
-        // above, we're actually dealing with a parameter that has no value.
64
-        // In that case we have to move the value to the name.
65
-        if ($this->name === '') {
66
-            $this->noName = false;
67
-            $this->name = strtoupper($value);
68
-        } else {
69
-            $this->setValue($value);
70
-        }
71
-
72
-    }
73
-
74
-    /**
75
-     * Try to guess property name by value, can be used for vCard 2.1 nameless parameters.
76
-     *
77
-     * Figuring out what the name should have been. Note that a ton of
78
-     * these are rather silly in 2014 and would probably rarely be
79
-     * used, but we like to be complete.
80
-     *
81
-     * @param string $value
82
-     *
83
-     * @return string
84
-     */
85
-    static function guessParameterNameByValue($value) {
86
-        switch (strtoupper($value)) {
87
-
88
-            // Encodings
89
-            case '7-BIT' :
90
-            case 'QUOTED-PRINTABLE' :
91
-            case 'BASE64' :
92
-                $name = 'ENCODING';
93
-                break;
94
-
95
-            // Common types
96
-            case 'WORK' :
97
-            case 'HOME' :
98
-            case 'PREF' :
99
-
100
-            // Delivery Label Type
101
-            case 'DOM' :
102
-            case 'INTL' :
103
-            case 'POSTAL' :
104
-            case 'PARCEL' :
105
-
106
-            // Telephone types
107
-            case 'VOICE' :
108
-            case 'FAX' :
109
-            case 'MSG' :
110
-            case 'CELL' :
111
-            case 'PAGER' :
112
-            case 'BBS' :
113
-            case 'MODEM' :
114
-            case 'CAR' :
115
-            case 'ISDN' :
116
-            case 'VIDEO' :
117
-
118
-            // EMAIL types (lol)
119
-            case 'AOL' :
120
-            case 'APPLELINK' :
121
-            case 'ATTMAIL' :
122
-            case 'CIS' :
123
-            case 'EWORLD' :
124
-            case 'INTERNET' :
125
-            case 'IBMMAIL' :
126
-            case 'MCIMAIL' :
127
-            case 'POWERSHARE' :
128
-            case 'PRODIGY' :
129
-            case 'TLX' :
130
-            case 'X400' :
131
-
132
-            // Photo / Logo format types
133
-            case 'GIF' :
134
-            case 'CGM' :
135
-            case 'WMF' :
136
-            case 'BMP' :
137
-            case 'DIB' :
138
-            case 'PICT' :
139
-            case 'TIFF' :
140
-            case 'PDF' :
141
-            case 'PS' :
142
-            case 'JPEG' :
143
-            case 'MPEG' :
144
-            case 'MPEG2' :
145
-            case 'AVI' :
146
-            case 'QTIME' :
147
-
148
-            // Sound Digital Audio Type
149
-            case 'WAVE' :
150
-            case 'PCM' :
151
-            case 'AIFF' :
152
-
153
-            // Key types
154
-            case 'X509' :
155
-            case 'PGP' :
156
-                $name = 'TYPE';
157
-                break;
158
-
159
-            // Value types
160
-            case 'INLINE' :
161
-            case 'URL' :
162
-            case 'CONTENT-ID' :
163
-            case 'CID' :
164
-                $name = 'VALUE';
165
-                break;
166
-
167
-            default:
168
-                $name = '';
169
-        }
170
-
171
-        return $name;
172
-    }
173
-
174
-    /**
175
-     * Updates the current value.
176
-     *
177
-     * This may be either a single, or multiple strings in an array.
178
-     *
179
-     * @param string|array $value
180
-     *
181
-     * @return void
182
-     */
183
-    public function setValue($value) {
184
-
185
-        $this->value = $value;
186
-
187
-    }
188
-
189
-    /**
190
-     * Returns the current value.
191
-     *
192
-     * This method will always return a string, or null. If there were multiple
193
-     * values, it will automatically concatenate them (separated by comma).
194
-     *
195
-     * @return string|null
196
-     */
197
-    public function getValue() {
198
-
199
-        if (is_array($this->value)) {
200
-            return implode(',', $this->value);
201
-        } else {
202
-            return $this->value;
203
-        }
204
-
205
-    }
206
-
207
-    /**
208
-     * Sets multiple values for this parameter.
209
-     *
210
-     * @param array $value
211
-     *
212
-     * @return void
213
-     */
214
-    public function setParts(array $value) {
215
-
216
-        $this->value = $value;
217
-
218
-    }
219
-
220
-    /**
221
-     * Returns all values for this parameter.
222
-     *
223
-     * If there were no values, an empty array will be returned.
224
-     *
225
-     * @return array
226
-     */
227
-    public function getParts() {
228
-
229
-        if (is_array($this->value)) {
230
-            return $this->value;
231
-        } elseif (is_null($this->value)) {
232
-            return [];
233
-        } else {
234
-            return [$this->value];
235
-        }
236
-
237
-    }
238
-
239
-    /**
240
-     * Adds a value to this parameter.
241
-     *
242
-     * If the argument is specified as an array, all items will be added to the
243
-     * parameter value list.
244
-     *
245
-     * @param string|array $part
246
-     *
247
-     * @return void
248
-     */
249
-    public function addValue($part) {
250
-
251
-        if (is_null($this->value)) {
252
-            $this->value = $part;
253
-        } else {
254
-            $this->value = array_merge((array)$this->value, (array)$part);
255
-        }
256
-
257
-    }
258
-
259
-    /**
260
-     * Checks if this parameter contains the specified value.
261
-     *
262
-     * This is a case-insensitive match. It makes sense to call this for for
263
-     * instance the TYPE parameter, to see if it contains a keyword such as
264
-     * 'WORK' or 'FAX'.
265
-     *
266
-     * @param string $value
267
-     *
268
-     * @return bool
269
-     */
270
-    public function has($value) {
271
-
272
-        return in_array(
273
-            strtolower($value),
274
-            array_map('strtolower', (array)$this->value)
275
-        );
276
-
277
-    }
278
-
279
-    /**
280
-     * Turns the object back into a serialized blob.
281
-     *
282
-     * @return string
283
-     */
284
-    public function serialize() {
285
-
286
-        $value = $this->getParts();
287
-
288
-        if (count($value) === 0) {
289
-            return $this->name . '=';
290
-        }
291
-
292
-        if ($this->root->getDocumentType() === Document::VCARD21 && $this->noName) {
293
-
294
-            return implode(';', $value);
295
-
296
-        }
297
-
298
-        return $this->name . '=' . array_reduce(
299
-            $value,
300
-            function($out, $item) {
301
-
302
-                if (!is_null($out)) $out .= ',';
303
-
304
-                // If there's no special characters in the string, we'll use the simple
305
-                // format.
306
-                //
307
-                // The list of special characters is defined as:
308
-                //
309
-                // Any character except CONTROL, DQUOTE, ";", ":", ","
310
-                //
311
-                // by the iCalendar spec:
312
-                // https://tools.ietf.org/html/rfc5545#section-3.1
313
-                //
314
-                // And we add ^ to that because of:
315
-                // https://tools.ietf.org/html/rfc6868
316
-                //
317
-                // But we've found that iCal (7.0, shipped with OSX 10.9)
318
-                // severaly trips on + characters not being quoted, so we
319
-                // added + as well.
320
-                if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) {
321
-                    return $out . $item;
322
-                } else {
323
-                    // Enclosing in double-quotes, and using RFC6868 for encoding any
324
-                    // special characters
325
-                    $out .= '"' . strtr(
326
-                        $item,
327
-                        [
328
-                            '^'  => '^^',
329
-                            "\n" => '^n',
330
-                            '"'  => '^\'',
331
-                        ]
332
-                    ) . '"';
333
-                    return $out;
334
-                }
335
-
336
-            }
337
-        );
338
-
339
-    }
340
-
341
-    /**
342
-     * This method returns an array, with the representation as it should be
343
-     * encoded in JSON. This is used to create jCard or jCal documents.
344
-     *
345
-     * @return array
346
-     */
347
-    public function jsonSerialize() {
348
-
349
-        return $this->value;
350
-
351
-    }
352
-
353
-    /**
354
-     * This method serializes the data into XML. This is used to create xCard or
355
-     * xCal documents.
356
-     *
357
-     * @param Xml\Writer $writer  XML writer.
358
-     *
359
-     * @return void
360
-     */
361
-    public function xmlSerialize(Xml\Writer $writer) {
362
-
363
-        foreach (explode(',', $this->value) as $value) {
364
-            $writer->writeElement('text', $value);
365
-        }
366
-
367
-    }
368
-
369
-    /**
370
-     * Called when this object is being cast to a string.
371
-     *
372
-     * @return string
373
-     */
374
-    public function __toString() {
375
-
376
-        return (string)$this->getValue();
377
-
378
-    }
379
-
380
-    /**
381
-     * Returns the iterator for this object.
382
-     *
383
-     * @return ElementList
384
-     */
385
-    public function getIterator() {
386
-
387
-        if (!is_null($this->iterator))
388
-            return $this->iterator;
389
-
390
-        return $this->iterator = new ArrayIterator((array)$this->value);
391
-
392
-    }
22
+	/**
23
+	 * Parameter name.
24
+	 *
25
+	 * @var string
26
+	 */
27
+	public $name;
28
+
29
+	/**
30
+	 * vCard 2.1 allows parameters to be encoded without a name.
31
+	 *
32
+	 * We can deduce the parameter name based on it's value.
33
+	 *
34
+	 * @var bool
35
+	 */
36
+	public $noName = false;
37
+
38
+	/**
39
+	 * Parameter value.
40
+	 *
41
+	 * @var string
42
+	 */
43
+	protected $value;
44
+
45
+	/**
46
+	 * Sets up the object.
47
+	 *
48
+	 * It's recommended to use the create:: factory method instead.
49
+	 *
50
+	 * @param string $name
51
+	 * @param string $value
52
+	 */
53
+	public function __construct(Document $root, $name, $value = null) {
54
+
55
+		$this->name = strtoupper($name);
56
+		$this->root = $root;
57
+		if (is_null($name)) {
58
+			$this->noName = true;
59
+			$this->name = static::guessParameterNameByValue($value);
60
+		}
61
+
62
+		// If guessParameterNameByValue() returns an empty string
63
+		// above, we're actually dealing with a parameter that has no value.
64
+		// In that case we have to move the value to the name.
65
+		if ($this->name === '') {
66
+			$this->noName = false;
67
+			$this->name = strtoupper($value);
68
+		} else {
69
+			$this->setValue($value);
70
+		}
71
+
72
+	}
73
+
74
+	/**
75
+	 * Try to guess property name by value, can be used for vCard 2.1 nameless parameters.
76
+	 *
77
+	 * Figuring out what the name should have been. Note that a ton of
78
+	 * these are rather silly in 2014 and would probably rarely be
79
+	 * used, but we like to be complete.
80
+	 *
81
+	 * @param string $value
82
+	 *
83
+	 * @return string
84
+	 */
85
+	static function guessParameterNameByValue($value) {
86
+		switch (strtoupper($value)) {
87
+
88
+			// Encodings
89
+			case '7-BIT' :
90
+			case 'QUOTED-PRINTABLE' :
91
+			case 'BASE64' :
92
+				$name = 'ENCODING';
93
+				break;
94
+
95
+			// Common types
96
+			case 'WORK' :
97
+			case 'HOME' :
98
+			case 'PREF' :
99
+
100
+			// Delivery Label Type
101
+			case 'DOM' :
102
+			case 'INTL' :
103
+			case 'POSTAL' :
104
+			case 'PARCEL' :
105
+
106
+			// Telephone types
107
+			case 'VOICE' :
108
+			case 'FAX' :
109
+			case 'MSG' :
110
+			case 'CELL' :
111
+			case 'PAGER' :
112
+			case 'BBS' :
113
+			case 'MODEM' :
114
+			case 'CAR' :
115
+			case 'ISDN' :
116
+			case 'VIDEO' :
117
+
118
+			// EMAIL types (lol)
119
+			case 'AOL' :
120
+			case 'APPLELINK' :
121
+			case 'ATTMAIL' :
122
+			case 'CIS' :
123
+			case 'EWORLD' :
124
+			case 'INTERNET' :
125
+			case 'IBMMAIL' :
126
+			case 'MCIMAIL' :
127
+			case 'POWERSHARE' :
128
+			case 'PRODIGY' :
129
+			case 'TLX' :
130
+			case 'X400' :
131
+
132
+			// Photo / Logo format types
133
+			case 'GIF' :
134
+			case 'CGM' :
135
+			case 'WMF' :
136
+			case 'BMP' :
137
+			case 'DIB' :
138
+			case 'PICT' :
139
+			case 'TIFF' :
140
+			case 'PDF' :
141
+			case 'PS' :
142
+			case 'JPEG' :
143
+			case 'MPEG' :
144
+			case 'MPEG2' :
145
+			case 'AVI' :
146
+			case 'QTIME' :
147
+
148
+			// Sound Digital Audio Type
149
+			case 'WAVE' :
150
+			case 'PCM' :
151
+			case 'AIFF' :
152
+
153
+			// Key types
154
+			case 'X509' :
155
+			case 'PGP' :
156
+				$name = 'TYPE';
157
+				break;
158
+
159
+			// Value types
160
+			case 'INLINE' :
161
+			case 'URL' :
162
+			case 'CONTENT-ID' :
163
+			case 'CID' :
164
+				$name = 'VALUE';
165
+				break;
166
+
167
+			default:
168
+				$name = '';
169
+		}
170
+
171
+		return $name;
172
+	}
173
+
174
+	/**
175
+	 * Updates the current value.
176
+	 *
177
+	 * This may be either a single, or multiple strings in an array.
178
+	 *
179
+	 * @param string|array $value
180
+	 *
181
+	 * @return void
182
+	 */
183
+	public function setValue($value) {
184
+
185
+		$this->value = $value;
186
+
187
+	}
188
+
189
+	/**
190
+	 * Returns the current value.
191
+	 *
192
+	 * This method will always return a string, or null. If there were multiple
193
+	 * values, it will automatically concatenate them (separated by comma).
194
+	 *
195
+	 * @return string|null
196
+	 */
197
+	public function getValue() {
198
+
199
+		if (is_array($this->value)) {
200
+			return implode(',', $this->value);
201
+		} else {
202
+			return $this->value;
203
+		}
204
+
205
+	}
206
+
207
+	/**
208
+	 * Sets multiple values for this parameter.
209
+	 *
210
+	 * @param array $value
211
+	 *
212
+	 * @return void
213
+	 */
214
+	public function setParts(array $value) {
215
+
216
+		$this->value = $value;
217
+
218
+	}
219
+
220
+	/**
221
+	 * Returns all values for this parameter.
222
+	 *
223
+	 * If there were no values, an empty array will be returned.
224
+	 *
225
+	 * @return array
226
+	 */
227
+	public function getParts() {
228
+
229
+		if (is_array($this->value)) {
230
+			return $this->value;
231
+		} elseif (is_null($this->value)) {
232
+			return [];
233
+		} else {
234
+			return [$this->value];
235
+		}
236
+
237
+	}
238
+
239
+	/**
240
+	 * Adds a value to this parameter.
241
+	 *
242
+	 * If the argument is specified as an array, all items will be added to the
243
+	 * parameter value list.
244
+	 *
245
+	 * @param string|array $part
246
+	 *
247
+	 * @return void
248
+	 */
249
+	public function addValue($part) {
250
+
251
+		if (is_null($this->value)) {
252
+			$this->value = $part;
253
+		} else {
254
+			$this->value = array_merge((array)$this->value, (array)$part);
255
+		}
256
+
257
+	}
258
+
259
+	/**
260
+	 * Checks if this parameter contains the specified value.
261
+	 *
262
+	 * This is a case-insensitive match. It makes sense to call this for for
263
+	 * instance the TYPE parameter, to see if it contains a keyword such as
264
+	 * 'WORK' or 'FAX'.
265
+	 *
266
+	 * @param string $value
267
+	 *
268
+	 * @return bool
269
+	 */
270
+	public function has($value) {
271
+
272
+		return in_array(
273
+			strtolower($value),
274
+			array_map('strtolower', (array)$this->value)
275
+		);
276
+
277
+	}
278
+
279
+	/**
280
+	 * Turns the object back into a serialized blob.
281
+	 *
282
+	 * @return string
283
+	 */
284
+	public function serialize() {
285
+
286
+		$value = $this->getParts();
287
+
288
+		if (count($value) === 0) {
289
+			return $this->name . '=';
290
+		}
291
+
292
+		if ($this->root->getDocumentType() === Document::VCARD21 && $this->noName) {
293
+
294
+			return implode(';', $value);
295
+
296
+		}
297
+
298
+		return $this->name . '=' . array_reduce(
299
+			$value,
300
+			function($out, $item) {
301
+
302
+				if (!is_null($out)) $out .= ',';
303
+
304
+				// If there's no special characters in the string, we'll use the simple
305
+				// format.
306
+				//
307
+				// The list of special characters is defined as:
308
+				//
309
+				// Any character except CONTROL, DQUOTE, ";", ":", ","
310
+				//
311
+				// by the iCalendar spec:
312
+				// https://tools.ietf.org/html/rfc5545#section-3.1
313
+				//
314
+				// And we add ^ to that because of:
315
+				// https://tools.ietf.org/html/rfc6868
316
+				//
317
+				// But we've found that iCal (7.0, shipped with OSX 10.9)
318
+				// severaly trips on + characters not being quoted, so we
319
+				// added + as well.
320
+				if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) {
321
+					return $out . $item;
322
+				} else {
323
+					// Enclosing in double-quotes, and using RFC6868 for encoding any
324
+					// special characters
325
+					$out .= '"' . strtr(
326
+						$item,
327
+						[
328
+							'^'  => '^^',
329
+							"\n" => '^n',
330
+							'"'  => '^\'',
331
+						]
332
+					) . '"';
333
+					return $out;
334
+				}
335
+
336
+			}
337
+		);
338
+
339
+	}
340
+
341
+	/**
342
+	 * This method returns an array, with the representation as it should be
343
+	 * encoded in JSON. This is used to create jCard or jCal documents.
344
+	 *
345
+	 * @return array
346
+	 */
347
+	public function jsonSerialize() {
348
+
349
+		return $this->value;
350
+
351
+	}
352
+
353
+	/**
354
+	 * This method serializes the data into XML. This is used to create xCard or
355
+	 * xCal documents.
356
+	 *
357
+	 * @param Xml\Writer $writer  XML writer.
358
+	 *
359
+	 * @return void
360
+	 */
361
+	public function xmlSerialize(Xml\Writer $writer) {
362
+
363
+		foreach (explode(',', $this->value) as $value) {
364
+			$writer->writeElement('text', $value);
365
+		}
366
+
367
+	}
368
+
369
+	/**
370
+	 * Called when this object is being cast to a string.
371
+	 *
372
+	 * @return string
373
+	 */
374
+	public function __toString() {
375
+
376
+		return (string)$this->getValue();
377
+
378
+	}
379
+
380
+	/**
381
+	 * Returns the iterator for this object.
382
+	 *
383
+	 * @return ElementList
384
+	 */
385
+	public function getIterator() {
386
+
387
+		if (!is_null($this->iterator))
388
+			return $this->iterator;
389
+
390
+		return $this->iterator = new ArrayIterator((array)$this->value);
391
+
392
+	}
393 393
 
394 394
 }
Please login to merge, or discard this patch.
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
      *
177 177
      * This may be either a single, or multiple strings in an array.
178 178
      *
179
-     * @param string|array $value
179
+     * @param string|null $value
180 180
      *
181 181
      * @return void
182 182
      */
@@ -342,7 +342,7 @@  discard block
 block discarded – undo
342 342
      * This method returns an array, with the representation as it should be
343 343
      * encoded in JSON. This is used to create jCard or jCal documents.
344 344
      *
345
-     * @return array
345
+     * @return string
346 346
      */
347 347
     public function jsonSerialize() {
348 348
 
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -251,7 +251,7 @@  discard block
 block discarded – undo
251 251
         if (is_null($this->value)) {
252 252
             $this->value = $part;
253 253
         } else {
254
-            $this->value = array_merge((array)$this->value, (array)$part);
254
+            $this->value = array_merge((array) $this->value, (array) $part);
255 255
         }
256 256
 
257 257
     }
@@ -271,7 +271,7 @@  discard block
 block discarded – undo
271 271
 
272 272
         return in_array(
273 273
             strtolower($value),
274
-            array_map('strtolower', (array)$this->value)
274
+            array_map('strtolower', (array) $this->value)
275 275
         );
276 276
 
277 277
     }
@@ -373,7 +373,7 @@  discard block
 block discarded – undo
373 373
      */
374 374
     public function __toString() {
375 375
 
376
-        return (string)$this->getValue();
376
+        return (string) $this->getValue();
377 377
 
378 378
     }
379 379
 
@@ -387,7 +387,7 @@  discard block
 block discarded – undo
387 387
         if (!is_null($this->iterator))
388 388
             return $this->iterator;
389 389
 
390
-        return $this->iterator = new ArrayIterator((array)$this->value);
390
+        return $this->iterator = new ArrayIterator((array) $this->value);
391 391
 
392 392
     }
393 393
 
Please login to merge, or discard this patch.
Braces   +6 added lines, -3 removed lines patch added patch discarded remove patch
@@ -299,7 +299,9 @@  discard block
 block discarded – undo
299 299
             $value,
300 300
             function($out, $item) {
301 301
 
302
-                if (!is_null($out)) $out .= ',';
302
+                if (!is_null($out)) {
303
+                	$out .= ',';
304
+                }
303 305
 
304 306
                 // If there's no special characters in the string, we'll use the simple
305 307
                 // format.
@@ -384,8 +386,9 @@  discard block
 block discarded – undo
384 386
      */
385 387
     public function getIterator() {
386 388
 
387
-        if (!is_null($this->iterator))
388
-            return $this->iterator;
389
+        if (!is_null($this->iterator)) {
390
+                    return $this->iterator;
391
+        }
389 392
 
390 393
         return $this->iterator = new ArrayIterator((array)$this->value);
391 394
 
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Parser/Json.php 4 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -4,8 +4,8 @@
 block discarded – undo
4 4
 
5 5
 use Sabre\VObject\Component\VCalendar;
6 6
 use Sabre\VObject\Component\VCard;
7
-use Sabre\VObject\ParseException;
8 7
 use Sabre\VObject\EofException;
8
+use Sabre\VObject\ParseException;
9 9
 
10 10
 /**
11 11
  * Json Parser.
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -142,7 +142,7 @@
 block discarded – undo
142 142
         // value type. We're using this value later in this function.
143 143
         $defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName);
144 144
 
145
-        $parameters = (array)$parameters;
145
+        $parameters = (array) $parameters;
146 146
 
147 147
         $value = array_slice($jProp, 3);
148 148
 
Please login to merge, or discard this patch.
Braces   +6 added lines, -2 removed lines patch added patch discarded remove patch
@@ -72,9 +72,11 @@  discard block
 block discarded – undo
72 72
         foreach ($this->input[1] as $prop) {
73 73
             $this->root->add($this->parseProperty($prop));
74 74
         }
75
-        if (isset($this->input[2])) foreach ($this->input[2] as $comp) {
75
+        if (isset($this->input[2])) {
76
+        	foreach ($this->input[2] as $comp) {
76 77
             $this->root->add($this->parseComponent($comp));
77 78
         }
79
+        }
78 80
 
79 81
         // Resetting the input so we can throw an feof exception the next time.
80 82
         $this->input = null;
@@ -111,7 +113,9 @@  discard block
 block discarded – undo
111 113
                 $jComp[2]
112 114
             );
113 115
 
114
-        } else $components = [];
116
+        } else {
117
+        	$components = [];
118
+        }
115 119
 
116 120
         return $this->root->createComponent(
117 121
             $jComp[0],
Please login to merge, or discard this patch.
Indentation   +175 added lines, -175 removed lines patch added patch discarded remove patch
@@ -18,180 +18,180 @@
 block discarded – undo
18 18
  */
19 19
 class Json extends Parser {
20 20
 
21
-    /**
22
-     * The input data.
23
-     *
24
-     * @var array
25
-     */
26
-    protected $input;
27
-
28
-    /**
29
-     * Root component.
30
-     *
31
-     * @var Document
32
-     */
33
-    protected $root;
34
-
35
-    /**
36
-     * This method starts the parsing process.
37
-     *
38
-     * If the input was not supplied during construction, it's possible to pass
39
-     * it here instead.
40
-     *
41
-     * If either input or options are not supplied, the defaults will be used.
42
-     *
43
-     * @param resource|string|array|null $input
44
-     * @param int $options
45
-     *
46
-     * @return Sabre\VObject\Document
47
-     */
48
-    public function parse($input = null, $options = 0) {
49
-
50
-        if (!is_null($input)) {
51
-            $this->setInput($input);
52
-        }
53
-        if (is_null($this->input)) {
54
-            throw new EofException('End of input stream, or no input supplied');
55
-        }
56
-
57
-        if (0 !== $options) {
58
-            $this->options = $options;
59
-        }
60
-
61
-        switch ($this->input[0]) {
62
-            case 'vcalendar' :
63
-                $this->root = new VCalendar([], false);
64
-                break;
65
-            case 'vcard' :
66
-                $this->root = new VCard([], false);
67
-                break;
68
-            default :
69
-                throw new ParseException('The root component must either be a vcalendar, or a vcard');
70
-
71
-        }
72
-        foreach ($this->input[1] as $prop) {
73
-            $this->root->add($this->parseProperty($prop));
74
-        }
75
-        if (isset($this->input[2])) foreach ($this->input[2] as $comp) {
76
-            $this->root->add($this->parseComponent($comp));
77
-        }
78
-
79
-        // Resetting the input so we can throw an feof exception the next time.
80
-        $this->input = null;
81
-
82
-        return $this->root;
83
-
84
-    }
85
-
86
-    /**
87
-     * Parses a component.
88
-     *
89
-     * @param array $jComp
90
-     *
91
-     * @return \Sabre\VObject\Component
92
-     */
93
-    public function parseComponent(array $jComp) {
94
-
95
-        // We can remove $self from PHP 5.4 onward.
96
-        $self = $this;
97
-
98
-        $properties = array_map(
99
-            function($jProp) use ($self) {
100
-                return $self->parseProperty($jProp);
101
-            },
102
-            $jComp[1]
103
-        );
104
-
105
-        if (isset($jComp[2])) {
106
-
107
-            $components = array_map(
108
-                function($jComp) use ($self) {
109
-                    return $self->parseComponent($jComp);
110
-                },
111
-                $jComp[2]
112
-            );
113
-
114
-        } else $components = [];
115
-
116
-        return $this->root->createComponent(
117
-            $jComp[0],
118
-            array_merge($properties, $components),
119
-            $defaults = false
120
-        );
121
-
122
-    }
123
-
124
-    /**
125
-     * Parses properties.
126
-     *
127
-     * @param array $jProp
128
-     *
129
-     * @return \Sabre\VObject\Property
130
-     */
131
-    public function parseProperty(array $jProp) {
132
-
133
-        list(
134
-            $propertyName,
135
-            $parameters,
136
-            $valueType
137
-        ) = $jProp;
138
-
139
-        $propertyName = strtoupper($propertyName);
140
-
141
-        // This is the default class we would be using if we didn't know the
142
-        // value type. We're using this value later in this function.
143
-        $defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName);
144
-
145
-        $parameters = (array)$parameters;
146
-
147
-        $value = array_slice($jProp, 3);
148
-
149
-        $valueType = strtoupper($valueType);
150
-
151
-        if (isset($parameters['group'])) {
152
-            $propertyName = $parameters['group'] . '.' . $propertyName;
153
-            unset($parameters['group']);
154
-        }
155
-
156
-        $prop = $this->root->createProperty($propertyName, null, $parameters, $valueType);
157
-        $prop->setJsonValue($value);
158
-
159
-        // We have to do something awkward here. FlatText as well as Text
160
-        // represents TEXT values. We have to normalize these here. In the
161
-        // future we can get rid of FlatText once we're allowed to break BC
162
-        // again.
163
-        if ($defaultPropertyClass === 'Sabre\VObject\Property\FlatText') {
164
-            $defaultPropertyClass = 'Sabre\VObject\Property\Text';
165
-        }
166
-
167
-        // If the value type we received (e.g.: TEXT) was not the default value
168
-        // type for the given property (e.g.: BDAY), we need to add a VALUE=
169
-        // parameter.
170
-        if ($defaultPropertyClass !== get_class($prop)) {
171
-            $prop["VALUE"] = $valueType;
172
-        }
173
-
174
-        return $prop;
175
-
176
-    }
177
-
178
-    /**
179
-     * Sets the input data.
180
-     *
181
-     * @param resource|string|array $input
182
-     *
183
-     * @return void
184
-     */
185
-    public function setInput($input) {
186
-
187
-        if (is_resource($input)) {
188
-            $input = stream_get_contents($input);
189
-        }
190
-        if (is_string($input)) {
191
-            $input = json_decode($input);
192
-        }
193
-        $this->input = $input;
194
-
195
-    }
21
+	/**
22
+	 * The input data.
23
+	 *
24
+	 * @var array
25
+	 */
26
+	protected $input;
27
+
28
+	/**
29
+	 * Root component.
30
+	 *
31
+	 * @var Document
32
+	 */
33
+	protected $root;
34
+
35
+	/**
36
+	 * This method starts the parsing process.
37
+	 *
38
+	 * If the input was not supplied during construction, it's possible to pass
39
+	 * it here instead.
40
+	 *
41
+	 * If either input or options are not supplied, the defaults will be used.
42
+	 *
43
+	 * @param resource|string|array|null $input
44
+	 * @param int $options
45
+	 *
46
+	 * @return Sabre\VObject\Document
47
+	 */
48
+	public function parse($input = null, $options = 0) {
49
+
50
+		if (!is_null($input)) {
51
+			$this->setInput($input);
52
+		}
53
+		if (is_null($this->input)) {
54
+			throw new EofException('End of input stream, or no input supplied');
55
+		}
56
+
57
+		if (0 !== $options) {
58
+			$this->options = $options;
59
+		}
60
+
61
+		switch ($this->input[0]) {
62
+			case 'vcalendar' :
63
+				$this->root = new VCalendar([], false);
64
+				break;
65
+			case 'vcard' :
66
+				$this->root = new VCard([], false);
67
+				break;
68
+			default :
69
+				throw new ParseException('The root component must either be a vcalendar, or a vcard');
70
+
71
+		}
72
+		foreach ($this->input[1] as $prop) {
73
+			$this->root->add($this->parseProperty($prop));
74
+		}
75
+		if (isset($this->input[2])) foreach ($this->input[2] as $comp) {
76
+			$this->root->add($this->parseComponent($comp));
77
+		}
78
+
79
+		// Resetting the input so we can throw an feof exception the next time.
80
+		$this->input = null;
81
+
82
+		return $this->root;
83
+
84
+	}
85
+
86
+	/**
87
+	 * Parses a component.
88
+	 *
89
+	 * @param array $jComp
90
+	 *
91
+	 * @return \Sabre\VObject\Component
92
+	 */
93
+	public function parseComponent(array $jComp) {
94
+
95
+		// We can remove $self from PHP 5.4 onward.
96
+		$self = $this;
97
+
98
+		$properties = array_map(
99
+			function($jProp) use ($self) {
100
+				return $self->parseProperty($jProp);
101
+			},
102
+			$jComp[1]
103
+		);
104
+
105
+		if (isset($jComp[2])) {
106
+
107
+			$components = array_map(
108
+				function($jComp) use ($self) {
109
+					return $self->parseComponent($jComp);
110
+				},
111
+				$jComp[2]
112
+			);
113
+
114
+		} else $components = [];
115
+
116
+		return $this->root->createComponent(
117
+			$jComp[0],
118
+			array_merge($properties, $components),
119
+			$defaults = false
120
+		);
121
+
122
+	}
123
+
124
+	/**
125
+	 * Parses properties.
126
+	 *
127
+	 * @param array $jProp
128
+	 *
129
+	 * @return \Sabre\VObject\Property
130
+	 */
131
+	public function parseProperty(array $jProp) {
132
+
133
+		list(
134
+			$propertyName,
135
+			$parameters,
136
+			$valueType
137
+		) = $jProp;
138
+
139
+		$propertyName = strtoupper($propertyName);
140
+
141
+		// This is the default class we would be using if we didn't know the
142
+		// value type. We're using this value later in this function.
143
+		$defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName);
144
+
145
+		$parameters = (array)$parameters;
146
+
147
+		$value = array_slice($jProp, 3);
148
+
149
+		$valueType = strtoupper($valueType);
150
+
151
+		if (isset($parameters['group'])) {
152
+			$propertyName = $parameters['group'] . '.' . $propertyName;
153
+			unset($parameters['group']);
154
+		}
155
+
156
+		$prop = $this->root->createProperty($propertyName, null, $parameters, $valueType);
157
+		$prop->setJsonValue($value);
158
+
159
+		// We have to do something awkward here. FlatText as well as Text
160
+		// represents TEXT values. We have to normalize these here. In the
161
+		// future we can get rid of FlatText once we're allowed to break BC
162
+		// again.
163
+		if ($defaultPropertyClass === 'Sabre\VObject\Property\FlatText') {
164
+			$defaultPropertyClass = 'Sabre\VObject\Property\Text';
165
+		}
166
+
167
+		// If the value type we received (e.g.: TEXT) was not the default value
168
+		// type for the given property (e.g.: BDAY), we need to add a VALUE=
169
+		// parameter.
170
+		if ($defaultPropertyClass !== get_class($prop)) {
171
+			$prop["VALUE"] = $valueType;
172
+		}
173
+
174
+		return $prop;
175
+
176
+	}
177
+
178
+	/**
179
+	 * Sets the input data.
180
+	 *
181
+	 * @param resource|string|array $input
182
+	 *
183
+	 * @return void
184
+	 */
185
+	public function setInput($input) {
186
+
187
+		if (is_resource($input)) {
188
+			$input = stream_get_contents($input);
189
+		}
190
+		if (is_string($input)) {
191
+			$input = json_decode($input);
192
+		}
193
+		$this->input = $input;
194
+
195
+	}
196 196
 
197 197
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Parser/MimeDir.php 4 patches
Doc Comments   +2 added lines, -1 removed lines patch added patch discarded remove patch
@@ -331,6 +331,7 @@  discard block
 block discarded – undo
331 331
     /**
332 332
      * Reads a property or component from a line.
333 333
      *
334
+     * @param string $line
334 335
      * @return void
335 336
      */
336 337
     protected function readProperty($line) {
@@ -623,7 +624,7 @@  discard block
 block discarded – undo
623 624
      *
624 625
      * @param string $input
625 626
      *
626
-     * @return void
627
+     * @return string
627 628
      */
628 629
     private function unescapeParam($input) {
629 630
 
Please login to merge, or discard this patch.
Unused Use Statements   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -2,12 +2,12 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\VObject\Parser;
4 4
 
5
-use Sabre\VObject\ParseException;
6
-use Sabre\VObject\EofException;
7 5
 use Sabre\VObject\Component;
8 6
 use Sabre\VObject\Component\VCalendar;
9 7
 use Sabre\VObject\Component\VCard;
10 8
 use Sabre\VObject\Document;
9
+use Sabre\VObject\EofException;
10
+use Sabre\VObject\ParseException;
11 11
 
12 12
 /**
13 13
  * MimeDir parser.
Please login to merge, or discard this patch.
Indentation   +645 added lines, -645 removed lines patch added patch discarded remove patch
@@ -24,328 +24,328 @@  discard block
 block discarded – undo
24 24
  */
25 25
 class MimeDir extends Parser {
26 26
 
27
-    /**
28
-     * The input stream.
29
-     *
30
-     * @var resource
31
-     */
32
-    protected $input;
33
-
34
-    /**
35
-     * Root component.
36
-     *
37
-     * @var Component
38
-     */
39
-    protected $root;
40
-
41
-    /**
42
-     * By default all input will be assumed to be UTF-8.
43
-     *
44
-     * However, both iCalendar and vCard might be encoded using different
45
-     * character sets. The character set is usually set in the mime-type.
46
-     *
47
-     * If this is the case, use setEncoding to specify that a different
48
-     * encoding will be used. If this is set, the parser will automatically
49
-     * convert all incoming data to UTF-8.
50
-     *
51
-     * @var string
52
-     */
53
-    protected $charset = 'UTF-8';
54
-
55
-    /**
56
-     * The list of character sets we support when decoding.
57
-     *
58
-     * This would be a const expression but for now we need to support PHP 5.5
59
-     */
60
-    protected static $SUPPORTED_CHARSETS = [
61
-        'UTF-8',
62
-        'ISO-8859-1',
63
-        'Windows-1252',
64
-    ];
65
-
66
-    /**
67
-     * Parses an iCalendar or vCard file.
68
-     *
69
-     * Pass a stream or a string. If null is parsed, the existing buffer is
70
-     * used.
71
-     *
72
-     * @param string|resource|null $input
73
-     * @param int $options
74
-     *
75
-     * @return Sabre\VObject\Document
76
-     */
77
-    public function parse($input = null, $options = 0) {
78
-
79
-        $this->root = null;
80
-
81
-        if (!is_null($input)) {
82
-            $this->setInput($input);
83
-        }
84
-
85
-        if (0 !== $options) {
86
-            $this->options = $options;
87
-        }
88
-
89
-        $this->parseDocument();
90
-
91
-        return $this->root;
92
-
93
-    }
94
-
95
-    /**
96
-     * By default all input will be assumed to be UTF-8.
97
-     *
98
-     * However, both iCalendar and vCard might be encoded using different
99
-     * character sets. The character set is usually set in the mime-type.
100
-     *
101
-     * If this is the case, use setEncoding to specify that a different
102
-     * encoding will be used. If this is set, the parser will automatically
103
-     * convert all incoming data to UTF-8.
104
-     *
105
-     * @param string $charset
106
-     */
107
-    public function setCharset($charset) {
108
-
109
-        if (!in_array($charset, self::$SUPPORTED_CHARSETS)) {
110
-            throw new \InvalidArgumentException('Unsupported encoding. (Supported encodings: ' . implode(', ', self::$SUPPORTED_CHARSETS) . ')');
111
-        }
112
-        $this->charset = $charset;
113
-
114
-    }
115
-
116
-    /**
117
-     * Sets the input buffer. Must be a string or stream.
118
-     *
119
-     * @param resource|string $input
120
-     *
121
-     * @return void
122
-     */
123
-    public function setInput($input) {
124
-
125
-        // Resetting the parser
126
-        $this->lineIndex = 0;
127
-        $this->startLine = 0;
128
-
129
-        if (is_string($input)) {
130
-            // Convering to a stream.
131
-            $stream = fopen('php://temp', 'r+');
132
-            fwrite($stream, $input);
133
-            rewind($stream);
134
-            $this->input = $stream;
135
-        } elseif (is_resource($input)) {
136
-            $this->input = $input;
137
-        } else {
138
-            throw new \InvalidArgumentException('This parser can only read from strings or streams.');
139
-        }
140
-
141
-    }
142
-
143
-    /**
144
-     * Parses an entire document.
145
-     *
146
-     * @return void
147
-     */
148
-    protected function parseDocument() {
149
-
150
-        $line = $this->readLine();
151
-
152
-        // BOM is ZERO WIDTH NO-BREAK SPACE (U+FEFF).
153
-        // It's 0xEF 0xBB 0xBF in UTF-8 hex.
154
-        if (3 <= strlen($line)
155
-            && ord($line[0]) === 0xef
156
-            && ord($line[1]) === 0xbb
157
-            && ord($line[2]) === 0xbf) {
158
-            $line = substr($line, 3);
159
-        }
160
-
161
-        switch (strtoupper($line)) {
162
-            case 'BEGIN:VCALENDAR' :
163
-                $class = VCalendar::$componentMap['VCALENDAR'];
164
-                break;
165
-            case 'BEGIN:VCARD' :
166
-                $class = VCard::$componentMap['VCARD'];
167
-                break;
168
-            default :
169
-                throw new ParseException('This parser only supports VCARD and VCALENDAR files');
170
-        }
171
-
172
-        $this->root = new $class([], false);
173
-
174
-        while (true) {
175
-
176
-            // Reading until we hit END:
177
-            $line = $this->readLine();
178
-            if (strtoupper(substr($line, 0, 4)) === 'END:') {
179
-                break;
180
-            }
181
-            $result = $this->parseLine($line);
182
-            if ($result) {
183
-                $this->root->add($result);
184
-            }
185
-
186
-        }
187
-
188
-        $name = strtoupper(substr($line, 4));
189
-        if ($name !== $this->root->name) {
190
-            throw new ParseException('Invalid MimeDir file. expected: "END:' . $this->root->name . '" got: "END:' . $name . '"');
191
-        }
192
-
193
-    }
194
-
195
-    /**
196
-     * Parses a line, and if it hits a component, it will also attempt to parse
197
-     * the entire component.
198
-     *
199
-     * @param string $line Unfolded line
200
-     *
201
-     * @return Node
202
-     */
203
-    protected function parseLine($line) {
204
-
205
-        // Start of a new component
206
-        if (strtoupper(substr($line, 0, 6)) === 'BEGIN:') {
207
-
208
-            $component = $this->root->createComponent(substr($line, 6), [], false);
209
-
210
-            while (true) {
211
-
212
-                // Reading until we hit END:
213
-                $line = $this->readLine();
214
-                if (strtoupper(substr($line, 0, 4)) === 'END:') {
215
-                    break;
216
-                }
217
-                $result = $this->parseLine($line);
218
-                if ($result) {
219
-                    $component->add($result);
220
-                }
221
-
222
-            }
223
-
224
-            $name = strtoupper(substr($line, 4));
225
-            if ($name !== $component->name) {
226
-                throw new ParseException('Invalid MimeDir file. expected: "END:' . $component->name . '" got: "END:' . $name . '"');
227
-            }
228
-
229
-            return $component;
230
-
231
-        } else {
232
-
233
-            // Property reader
234
-            $property = $this->readProperty($line);
235
-            if (!$property) {
236
-                // Ignored line
237
-                return false;
238
-            }
239
-            return $property;
240
-
241
-        }
242
-
243
-    }
244
-
245
-    /**
246
-     * We need to look ahead 1 line every time to see if we need to 'unfold'
247
-     * the next line.
248
-     *
249
-     * If that was not the case, we store it here.
250
-     *
251
-     * @var null|string
252
-     */
253
-    protected $lineBuffer;
254
-
255
-    /**
256
-     * The real current line number.
257
-     */
258
-    protected $lineIndex = 0;
259
-
260
-    /**
261
-     * In the case of unfolded lines, this property holds the line number for
262
-     * the start of the line.
263
-     *
264
-     * @var int
265
-     */
266
-    protected $startLine = 0;
267
-
268
-    /**
269
-     * Contains a 'raw' representation of the current line.
270
-     *
271
-     * @var string
272
-     */
273
-    protected $rawLine;
274
-
275
-    /**
276
-     * Reads a single line from the buffer.
277
-     *
278
-     * This method strips any newlines and also takes care of unfolding.
279
-     *
280
-     * @throws \Sabre\VObject\EofException
281
-     *
282
-     * @return string
283
-     */
284
-    protected function readLine() {
285
-
286
-        if (!is_null($this->lineBuffer)) {
287
-            $rawLine = $this->lineBuffer;
288
-            $this->lineBuffer = null;
289
-        } else {
290
-            do {
291
-                $eof = feof($this->input);
292
-
293
-                $rawLine = fgets($this->input);
294
-
295
-                if ($eof || (feof($this->input) && $rawLine === false)) {
296
-                    throw new EofException('End of document reached prematurely');
297
-                }
298
-                if ($rawLine === false) {
299
-                    throw new ParseException('Error reading from input stream');
300
-                }
301
-                $rawLine = rtrim($rawLine, "\r\n");
302
-            } while ($rawLine === ''); // Skipping empty lines
303
-            $this->lineIndex++;
304
-        }
305
-        $line = $rawLine;
306
-
307
-        $this->startLine = $this->lineIndex;
308
-
309
-        // Looking ahead for folded lines.
310
-        while (true) {
311
-
312
-            $nextLine = rtrim(fgets($this->input), "\r\n");
313
-            $this->lineIndex++;
314
-            if (!$nextLine) {
315
-                break;
316
-            }
317
-            if ($nextLine[0] === "\t" || $nextLine[0] === " ") {
318
-                $line .= substr($nextLine, 1);
319
-                $rawLine .= "\n " . substr($nextLine, 1);
320
-            } else {
321
-                $this->lineBuffer = $nextLine;
322
-                break;
323
-            }
324
-
325
-        }
326
-        $this->rawLine = $rawLine;
327
-        return $line;
328
-
329
-    }
330
-
331
-    /**
332
-     * Reads a property or component from a line.
333
-     *
334
-     * @return void
335
-     */
336
-    protected function readProperty($line) {
337
-
338
-        if ($this->options & self::OPTION_FORGIVING) {
339
-            $propNameToken = 'A-Z0-9\-\._\\/';
340
-        } else {
341
-            $propNameToken = 'A-Z0-9\-\.';
342
-        }
343
-
344
-        $paramNameToken = 'A-Z0-9\-';
345
-        $safeChar = '^";:,';
346
-        $qSafeChar = '^"';
347
-
348
-        $regex = "/
27
+	/**
28
+	 * The input stream.
29
+	 *
30
+	 * @var resource
31
+	 */
32
+	protected $input;
33
+
34
+	/**
35
+	 * Root component.
36
+	 *
37
+	 * @var Component
38
+	 */
39
+	protected $root;
40
+
41
+	/**
42
+	 * By default all input will be assumed to be UTF-8.
43
+	 *
44
+	 * However, both iCalendar and vCard might be encoded using different
45
+	 * character sets. The character set is usually set in the mime-type.
46
+	 *
47
+	 * If this is the case, use setEncoding to specify that a different
48
+	 * encoding will be used. If this is set, the parser will automatically
49
+	 * convert all incoming data to UTF-8.
50
+	 *
51
+	 * @var string
52
+	 */
53
+	protected $charset = 'UTF-8';
54
+
55
+	/**
56
+	 * The list of character sets we support when decoding.
57
+	 *
58
+	 * This would be a const expression but for now we need to support PHP 5.5
59
+	 */
60
+	protected static $SUPPORTED_CHARSETS = [
61
+		'UTF-8',
62
+		'ISO-8859-1',
63
+		'Windows-1252',
64
+	];
65
+
66
+	/**
67
+	 * Parses an iCalendar or vCard file.
68
+	 *
69
+	 * Pass a stream or a string. If null is parsed, the existing buffer is
70
+	 * used.
71
+	 *
72
+	 * @param string|resource|null $input
73
+	 * @param int $options
74
+	 *
75
+	 * @return Sabre\VObject\Document
76
+	 */
77
+	public function parse($input = null, $options = 0) {
78
+
79
+		$this->root = null;
80
+
81
+		if (!is_null($input)) {
82
+			$this->setInput($input);
83
+		}
84
+
85
+		if (0 !== $options) {
86
+			$this->options = $options;
87
+		}
88
+
89
+		$this->parseDocument();
90
+
91
+		return $this->root;
92
+
93
+	}
94
+
95
+	/**
96
+	 * By default all input will be assumed to be UTF-8.
97
+	 *
98
+	 * However, both iCalendar and vCard might be encoded using different
99
+	 * character sets. The character set is usually set in the mime-type.
100
+	 *
101
+	 * If this is the case, use setEncoding to specify that a different
102
+	 * encoding will be used. If this is set, the parser will automatically
103
+	 * convert all incoming data to UTF-8.
104
+	 *
105
+	 * @param string $charset
106
+	 */
107
+	public function setCharset($charset) {
108
+
109
+		if (!in_array($charset, self::$SUPPORTED_CHARSETS)) {
110
+			throw new \InvalidArgumentException('Unsupported encoding. (Supported encodings: ' . implode(', ', self::$SUPPORTED_CHARSETS) . ')');
111
+		}
112
+		$this->charset = $charset;
113
+
114
+	}
115
+
116
+	/**
117
+	 * Sets the input buffer. Must be a string or stream.
118
+	 *
119
+	 * @param resource|string $input
120
+	 *
121
+	 * @return void
122
+	 */
123
+	public function setInput($input) {
124
+
125
+		// Resetting the parser
126
+		$this->lineIndex = 0;
127
+		$this->startLine = 0;
128
+
129
+		if (is_string($input)) {
130
+			// Convering to a stream.
131
+			$stream = fopen('php://temp', 'r+');
132
+			fwrite($stream, $input);
133
+			rewind($stream);
134
+			$this->input = $stream;
135
+		} elseif (is_resource($input)) {
136
+			$this->input = $input;
137
+		} else {
138
+			throw new \InvalidArgumentException('This parser can only read from strings or streams.');
139
+		}
140
+
141
+	}
142
+
143
+	/**
144
+	 * Parses an entire document.
145
+	 *
146
+	 * @return void
147
+	 */
148
+	protected function parseDocument() {
149
+
150
+		$line = $this->readLine();
151
+
152
+		// BOM is ZERO WIDTH NO-BREAK SPACE (U+FEFF).
153
+		// It's 0xEF 0xBB 0xBF in UTF-8 hex.
154
+		if (3 <= strlen($line)
155
+			&& ord($line[0]) === 0xef
156
+			&& ord($line[1]) === 0xbb
157
+			&& ord($line[2]) === 0xbf) {
158
+			$line = substr($line, 3);
159
+		}
160
+
161
+		switch (strtoupper($line)) {
162
+			case 'BEGIN:VCALENDAR' :
163
+				$class = VCalendar::$componentMap['VCALENDAR'];
164
+				break;
165
+			case 'BEGIN:VCARD' :
166
+				$class = VCard::$componentMap['VCARD'];
167
+				break;
168
+			default :
169
+				throw new ParseException('This parser only supports VCARD and VCALENDAR files');
170
+		}
171
+
172
+		$this->root = new $class([], false);
173
+
174
+		while (true) {
175
+
176
+			// Reading until we hit END:
177
+			$line = $this->readLine();
178
+			if (strtoupper(substr($line, 0, 4)) === 'END:') {
179
+				break;
180
+			}
181
+			$result = $this->parseLine($line);
182
+			if ($result) {
183
+				$this->root->add($result);
184
+			}
185
+
186
+		}
187
+
188
+		$name = strtoupper(substr($line, 4));
189
+		if ($name !== $this->root->name) {
190
+			throw new ParseException('Invalid MimeDir file. expected: "END:' . $this->root->name . '" got: "END:' . $name . '"');
191
+		}
192
+
193
+	}
194
+
195
+	/**
196
+	 * Parses a line, and if it hits a component, it will also attempt to parse
197
+	 * the entire component.
198
+	 *
199
+	 * @param string $line Unfolded line
200
+	 *
201
+	 * @return Node
202
+	 */
203
+	protected function parseLine($line) {
204
+
205
+		// Start of a new component
206
+		if (strtoupper(substr($line, 0, 6)) === 'BEGIN:') {
207
+
208
+			$component = $this->root->createComponent(substr($line, 6), [], false);
209
+
210
+			while (true) {
211
+
212
+				// Reading until we hit END:
213
+				$line = $this->readLine();
214
+				if (strtoupper(substr($line, 0, 4)) === 'END:') {
215
+					break;
216
+				}
217
+				$result = $this->parseLine($line);
218
+				if ($result) {
219
+					$component->add($result);
220
+				}
221
+
222
+			}
223
+
224
+			$name = strtoupper(substr($line, 4));
225
+			if ($name !== $component->name) {
226
+				throw new ParseException('Invalid MimeDir file. expected: "END:' . $component->name . '" got: "END:' . $name . '"');
227
+			}
228
+
229
+			return $component;
230
+
231
+		} else {
232
+
233
+			// Property reader
234
+			$property = $this->readProperty($line);
235
+			if (!$property) {
236
+				// Ignored line
237
+				return false;
238
+			}
239
+			return $property;
240
+
241
+		}
242
+
243
+	}
244
+
245
+	/**
246
+	 * We need to look ahead 1 line every time to see if we need to 'unfold'
247
+	 * the next line.
248
+	 *
249
+	 * If that was not the case, we store it here.
250
+	 *
251
+	 * @var null|string
252
+	 */
253
+	protected $lineBuffer;
254
+
255
+	/**
256
+	 * The real current line number.
257
+	 */
258
+	protected $lineIndex = 0;
259
+
260
+	/**
261
+	 * In the case of unfolded lines, this property holds the line number for
262
+	 * the start of the line.
263
+	 *
264
+	 * @var int
265
+	 */
266
+	protected $startLine = 0;
267
+
268
+	/**
269
+	 * Contains a 'raw' representation of the current line.
270
+	 *
271
+	 * @var string
272
+	 */
273
+	protected $rawLine;
274
+
275
+	/**
276
+	 * Reads a single line from the buffer.
277
+	 *
278
+	 * This method strips any newlines and also takes care of unfolding.
279
+	 *
280
+	 * @throws \Sabre\VObject\EofException
281
+	 *
282
+	 * @return string
283
+	 */
284
+	protected function readLine() {
285
+
286
+		if (!is_null($this->lineBuffer)) {
287
+			$rawLine = $this->lineBuffer;
288
+			$this->lineBuffer = null;
289
+		} else {
290
+			do {
291
+				$eof = feof($this->input);
292
+
293
+				$rawLine = fgets($this->input);
294
+
295
+				if ($eof || (feof($this->input) && $rawLine === false)) {
296
+					throw new EofException('End of document reached prematurely');
297
+				}
298
+				if ($rawLine === false) {
299
+					throw new ParseException('Error reading from input stream');
300
+				}
301
+				$rawLine = rtrim($rawLine, "\r\n");
302
+			} while ($rawLine === ''); // Skipping empty lines
303
+			$this->lineIndex++;
304
+		}
305
+		$line = $rawLine;
306
+
307
+		$this->startLine = $this->lineIndex;
308
+
309
+		// Looking ahead for folded lines.
310
+		while (true) {
311
+
312
+			$nextLine = rtrim(fgets($this->input), "\r\n");
313
+			$this->lineIndex++;
314
+			if (!$nextLine) {
315
+				break;
316
+			}
317
+			if ($nextLine[0] === "\t" || $nextLine[0] === " ") {
318
+				$line .= substr($nextLine, 1);
319
+				$rawLine .= "\n " . substr($nextLine, 1);
320
+			} else {
321
+				$this->lineBuffer = $nextLine;
322
+				break;
323
+			}
324
+
325
+		}
326
+		$this->rawLine = $rawLine;
327
+		return $line;
328
+
329
+	}
330
+
331
+	/**
332
+	 * Reads a property or component from a line.
333
+	 *
334
+	 * @return void
335
+	 */
336
+	protected function readProperty($line) {
337
+
338
+		if ($this->options & self::OPTION_FORGIVING) {
339
+			$propNameToken = 'A-Z0-9\-\._\\/';
340
+		} else {
341
+			$propNameToken = 'A-Z0-9\-\.';
342
+		}
343
+
344
+		$paramNameToken = 'A-Z0-9\-';
345
+		$safeChar = '^";:,';
346
+		$qSafeChar = '^"';
347
+
348
+		$regex = "/
349 349
             ^(?P<name> [$propNameToken]+ ) (?=[;:])        # property name
350 350
             |
351 351
             (?<=:)(?P<propValue> .+)$                      # property value
@@ -358,338 +358,338 @@  discard block
 block discarded – undo
358 358
             ) (?=[;:,])
359 359
             /xi";
360 360
 
361
-        preg_match_all($regex, $line, $matches,  PREG_SET_ORDER);
362
-
363
-        $property = [
364
-            'name'       => null,
365
-            'parameters' => [],
366
-            'value'      => null
367
-        ];
368
-
369
-        $lastParam = null;
370
-
371
-        /**
372
-         * Looping through all the tokens.
373
-         *
374
-         * Note that we are looping through them in reverse order, because if a
375
-         * sub-pattern matched, the subsequent named patterns will not show up
376
-         * in the result.
377
-         */
378
-        foreach ($matches as $match) {
379
-
380
-            if (isset($match['paramValue'])) {
381
-                if ($match['paramValue'] && $match['paramValue'][0] === '"') {
382
-                    $value = substr($match['paramValue'], 1, -1);
383
-                } else {
384
-                    $value = $match['paramValue'];
385
-                }
386
-
387
-                $value = $this->unescapeParam($value);
388
-
389
-                if (is_null($lastParam)) {
390
-                    throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions');
391
-                }
392
-                if (is_null($property['parameters'][$lastParam])) {
393
-                    $property['parameters'][$lastParam] = $value;
394
-                } elseif (is_array($property['parameters'][$lastParam])) {
395
-                    $property['parameters'][$lastParam][] = $value;
396
-                } else {
397
-                    $property['parameters'][$lastParam] = [
398
-                        $property['parameters'][$lastParam],
399
-                        $value
400
-                    ];
401
-                }
402
-                continue;
403
-            }
404
-            if (isset($match['paramName'])) {
405
-                $lastParam = strtoupper($match['paramName']);
406
-                if (!isset($property['parameters'][$lastParam])) {
407
-                    $property['parameters'][$lastParam] = null;
408
-                }
409
-                continue;
410
-            }
411
-            if (isset($match['propValue'])) {
412
-                $property['value'] = $match['propValue'];
413
-                continue;
414
-            }
415
-            if (isset($match['name']) && $match['name']) {
416
-                $property['name'] = strtoupper($match['name']);
417
-                continue;
418
-            }
419
-
420
-            // @codeCoverageIgnoreStart
421
-            throw new \LogicException('This code should not be reachable');
422
-            // @codeCoverageIgnoreEnd
423
-
424
-        }
425
-
426
-        if (is_null($property['value'])) {
427
-            $property['value'] = '';
428
-        }
429
-        if (!$property['name']) {
430
-            if ($this->options & self::OPTION_IGNORE_INVALID_LINES) {
431
-                return false;
432
-            }
433
-            throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions');
434
-        }
435
-
436
-        // vCard 2.1 states that parameters may appear without a name, and only
437
-        // a value. We can deduce the value based on it's name.
438
-        //
439
-        // Our parser will get those as parameters without a value instead, so
440
-        // we're filtering these parameters out first.
441
-        $namedParameters = [];
442
-        $namelessParameters = [];
443
-
444
-        foreach ($property['parameters'] as $name => $value) {
445
-            if (!is_null($value)) {
446
-                $namedParameters[$name] = $value;
447
-            } else {
448
-                $namelessParameters[] = $name;
449
-            }
450
-        }
451
-
452
-        $propObj = $this->root->createProperty($property['name'], null, $namedParameters);
453
-
454
-        foreach ($namelessParameters as $namelessParameter) {
455
-            $propObj->add(null, $namelessParameter);
456
-        }
457
-
458
-        if (strtoupper($propObj['ENCODING']) === 'QUOTED-PRINTABLE') {
459
-            $propObj->setQuotedPrintableValue($this->extractQuotedPrintableValue());
460
-        } else {
461
-            $charset = $this->charset;
462
-            if ($this->root->getDocumentType() === Document::VCARD21 && isset($propObj['CHARSET'])) {
463
-                // vCard 2.1 allows the character set to be specified per property.
464
-                $charset = (string)$propObj['CHARSET'];
465
-            }
466
-            switch ($charset) {
467
-                case 'UTF-8' :
468
-                    break;
469
-                case 'ISO-8859-1' :
470
-                    $property['value'] = utf8_encode($property['value']);
471
-                    break;
472
-                case 'Windows-1252' :
473
-                    $property['value'] = mb_convert_encoding($property['value'], 'UTF-8', $charset);
474
-                    break;
475
-                default :
476
-                    throw new ParseException('Unsupported CHARSET: ' . $propObj['CHARSET']);
477
-            }
478
-            $propObj->setRawMimeDirValue($property['value']);
479
-        }
480
-
481
-        return $propObj;
482
-
483
-    }
484
-
485
-    /**
486
-     * Unescapes a property value.
487
-     *
488
-     * vCard 2.1 says:
489
-     *   * Semi-colons must be escaped in some property values, specifically
490
-     *     ADR, ORG and N.
491
-     *   * Semi-colons must be escaped in parameter values, because semi-colons
492
-     *     are also use to separate values.
493
-     *   * No mention of escaping backslashes with another backslash.
494
-     *   * newlines are not escaped either, instead QUOTED-PRINTABLE is used to
495
-     *     span values over more than 1 line.
496
-     *
497
-     * vCard 3.0 says:
498
-     *   * (rfc2425) Backslashes, newlines (\n or \N) and comma's must be
499
-     *     escaped, all time time.
500
-     *   * Comma's are used for delimeters in multiple values
501
-     *   * (rfc2426) Adds to to this that the semi-colon MUST also be escaped,
502
-     *     as in some properties semi-colon is used for separators.
503
-     *   * Properties using semi-colons: N, ADR, GEO, ORG
504
-     *   * Both ADR and N's individual parts may be broken up further with a
505
-     *     comma.
506
-     *   * Properties using commas: NICKNAME, CATEGORIES
507
-     *
508
-     * vCard 4.0 (rfc6350) says:
509
-     *   * Commas must be escaped.
510
-     *   * Semi-colons may be escaped, an unescaped semi-colon _may_ be a
511
-     *     delimiter, depending on the property.
512
-     *   * Backslashes must be escaped
513
-     *   * Newlines must be escaped as either \N or \n.
514
-     *   * Some compound properties may contain multiple parts themselves, so a
515
-     *     comma within a semi-colon delimited property may also be unescaped
516
-     *     to denote multiple parts _within_ the compound property.
517
-     *   * Text-properties using semi-colons: N, ADR, ORG, CLIENTPIDMAP.
518
-     *   * Text-properties using commas: NICKNAME, RELATED, CATEGORIES, PID.
519
-     *
520
-     * Even though the spec says that commas must always be escaped, the
521
-     * example for GEO in Section 6.5.2 seems to violate this.
522
-     *
523
-     * iCalendar 2.0 (rfc5545) says:
524
-     *   * Commas or semi-colons may be used as delimiters, depending on the
525
-     *     property.
526
-     *   * Commas, semi-colons, backslashes, newline (\N or \n) are always
527
-     *     escaped, unless they are delimiters.
528
-     *   * Colons shall not be escaped.
529
-     *   * Commas can be considered the 'default delimiter' and is described as
530
-     *     the delimiter in cases where the order of the multiple values is
531
-     *     insignificant.
532
-     *   * Semi-colons are described as the delimiter for 'structured values'.
533
-     *     They are specifically used in Semi-colons are used as a delimiter in
534
-     *     REQUEST-STATUS, RRULE, GEO and EXRULE. EXRULE is deprecated however.
535
-     *
536
-     * Now for the parameters
537
-     *
538
-     * If delimiter is not set (null) this method will just return a string.
539
-     * If it's a comma or a semi-colon the string will be split on those
540
-     * characters, and always return an array.
541
-     *
542
-     * @param string $input
543
-     * @param string $delimiter
544
-     *
545
-     * @return string|string[]
546
-     */
547
-    static function unescapeValue($input, $delimiter = ';') {
548
-
549
-        $regex = '#  (?: (\\\\ (?: \\\\ | N | n | ; | , ) )';
550
-        if ($delimiter) {
551
-            $regex .= ' | (' . $delimiter . ')';
552
-        }
553
-        $regex .= ') #x';
554
-
555
-        $matches = preg_split($regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
556
-
557
-        $resultArray = [];
558
-        $result = '';
559
-
560
-        foreach ($matches as $match) {
561
-
562
-            switch ($match) {
563
-                case '\\\\' :
564
-                    $result .= '\\';
565
-                    break;
566
-                case '\N' :
567
-                case '\n' :
568
-                    $result .= "\n";
569
-                    break;
570
-                case '\;' :
571
-                    $result .= ';';
572
-                    break;
573
-                case '\,' :
574
-                    $result .= ',';
575
-                    break;
576
-                case $delimiter :
577
-                    $resultArray[] = $result;
578
-                    $result = '';
579
-                    break;
580
-                default :
581
-                    $result .= $match;
582
-                    break;
583
-
584
-            }
585
-
586
-        }
587
-
588
-        $resultArray[] = $result;
589
-        return $delimiter ? $resultArray : $result;
590
-
591
-    }
592
-
593
-    /**
594
-     * Unescapes a parameter value.
595
-     *
596
-     * vCard 2.1:
597
-     *   * Does not mention a mechanism for this. In addition, double quotes
598
-     *     are never used to wrap values.
599
-     *   * This means that parameters can simply not contain colons or
600
-     *     semi-colons.
601
-     *
602
-     * vCard 3.0 (rfc2425, rfc2426):
603
-     *   * Parameters _may_ be surrounded by double quotes.
604
-     *   * If this is not the case, semi-colon, colon and comma may simply not
605
-     *     occur (the comma used for multiple parameter values though).
606
-     *   * If it is surrounded by double-quotes, it may simply not contain
607
-     *     double-quotes.
608
-     *   * This means that a parameter can in no case encode double-quotes, or
609
-     *     newlines.
610
-     *
611
-     * vCard 4.0 (rfc6350)
612
-     *   * Behavior seems to be identical to vCard 3.0
613
-     *
614
-     * iCalendar 2.0 (rfc5545)
615
-     *   * Behavior seems to be identical to vCard 3.0
616
-     *
617
-     * Parameter escaping mechanism (rfc6868) :
618
-     *   * This rfc describes a new way to escape parameter values.
619
-     *   * New-line is encoded as ^n
620
-     *   * ^ is encoded as ^^.
621
-     *   * " is encoded as ^'
622
-     *
623
-     * @param string $input
624
-     *
625
-     * @return void
626
-     */
627
-    private function unescapeParam($input) {
628
-
629
-        return
630
-            preg_replace_callback(
631
-                '#(\^(\^|n|\'))#',
632
-                function($matches) {
633
-                    switch ($matches[2]) {
634
-                        case 'n' :
635
-                            return "\n";
636
-                        case '^' :
637
-                            return '^';
638
-                        case '\'' :
639
-                            return '"';
640
-
641
-                    // @codeCoverageIgnoreStart
642
-                    }
643
-                    // @codeCoverageIgnoreEnd
644
-                },
645
-                $input
646
-            );
647
-    }
648
-
649
-    /**
650
-     * Gets the full quoted printable value.
651
-     *
652
-     * We need a special method for this, because newlines have both a meaning
653
-     * in vCards, and in QuotedPrintable.
654
-     *
655
-     * This method does not do any decoding.
656
-     *
657
-     * @return string
658
-     */
659
-    private function extractQuotedPrintableValue() {
660
-
661
-        // We need to parse the raw line again to get the start of the value.
662
-        //
663
-        // We are basically looking for the first colon (:), but we need to
664
-        // skip over the parameters first, as they may contain one.
665
-        $regex = '/^
361
+		preg_match_all($regex, $line, $matches,  PREG_SET_ORDER);
362
+
363
+		$property = [
364
+			'name'       => null,
365
+			'parameters' => [],
366
+			'value'      => null
367
+		];
368
+
369
+		$lastParam = null;
370
+
371
+		/**
372
+		 * Looping through all the tokens.
373
+		 *
374
+		 * Note that we are looping through them in reverse order, because if a
375
+		 * sub-pattern matched, the subsequent named patterns will not show up
376
+		 * in the result.
377
+		 */
378
+		foreach ($matches as $match) {
379
+
380
+			if (isset($match['paramValue'])) {
381
+				if ($match['paramValue'] && $match['paramValue'][0] === '"') {
382
+					$value = substr($match['paramValue'], 1, -1);
383
+				} else {
384
+					$value = $match['paramValue'];
385
+				}
386
+
387
+				$value = $this->unescapeParam($value);
388
+
389
+				if (is_null($lastParam)) {
390
+					throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions');
391
+				}
392
+				if (is_null($property['parameters'][$lastParam])) {
393
+					$property['parameters'][$lastParam] = $value;
394
+				} elseif (is_array($property['parameters'][$lastParam])) {
395
+					$property['parameters'][$lastParam][] = $value;
396
+				} else {
397
+					$property['parameters'][$lastParam] = [
398
+						$property['parameters'][$lastParam],
399
+						$value
400
+					];
401
+				}
402
+				continue;
403
+			}
404
+			if (isset($match['paramName'])) {
405
+				$lastParam = strtoupper($match['paramName']);
406
+				if (!isset($property['parameters'][$lastParam])) {
407
+					$property['parameters'][$lastParam] = null;
408
+				}
409
+				continue;
410
+			}
411
+			if (isset($match['propValue'])) {
412
+				$property['value'] = $match['propValue'];
413
+				continue;
414
+			}
415
+			if (isset($match['name']) && $match['name']) {
416
+				$property['name'] = strtoupper($match['name']);
417
+				continue;
418
+			}
419
+
420
+			// @codeCoverageIgnoreStart
421
+			throw new \LogicException('This code should not be reachable');
422
+			// @codeCoverageIgnoreEnd
423
+
424
+		}
425
+
426
+		if (is_null($property['value'])) {
427
+			$property['value'] = '';
428
+		}
429
+		if (!$property['name']) {
430
+			if ($this->options & self::OPTION_IGNORE_INVALID_LINES) {
431
+				return false;
432
+			}
433
+			throw new ParseException('Invalid Mimedir file. Line starting at ' . $this->startLine . ' did not follow iCalendar/vCard conventions');
434
+		}
435
+
436
+		// vCard 2.1 states that parameters may appear without a name, and only
437
+		// a value. We can deduce the value based on it's name.
438
+		//
439
+		// Our parser will get those as parameters without a value instead, so
440
+		// we're filtering these parameters out first.
441
+		$namedParameters = [];
442
+		$namelessParameters = [];
443
+
444
+		foreach ($property['parameters'] as $name => $value) {
445
+			if (!is_null($value)) {
446
+				$namedParameters[$name] = $value;
447
+			} else {
448
+				$namelessParameters[] = $name;
449
+			}
450
+		}
451
+
452
+		$propObj = $this->root->createProperty($property['name'], null, $namedParameters);
453
+
454
+		foreach ($namelessParameters as $namelessParameter) {
455
+			$propObj->add(null, $namelessParameter);
456
+		}
457
+
458
+		if (strtoupper($propObj['ENCODING']) === 'QUOTED-PRINTABLE') {
459
+			$propObj->setQuotedPrintableValue($this->extractQuotedPrintableValue());
460
+		} else {
461
+			$charset = $this->charset;
462
+			if ($this->root->getDocumentType() === Document::VCARD21 && isset($propObj['CHARSET'])) {
463
+				// vCard 2.1 allows the character set to be specified per property.
464
+				$charset = (string)$propObj['CHARSET'];
465
+			}
466
+			switch ($charset) {
467
+				case 'UTF-8' :
468
+					break;
469
+				case 'ISO-8859-1' :
470
+					$property['value'] = utf8_encode($property['value']);
471
+					break;
472
+				case 'Windows-1252' :
473
+					$property['value'] = mb_convert_encoding($property['value'], 'UTF-8', $charset);
474
+					break;
475
+				default :
476
+					throw new ParseException('Unsupported CHARSET: ' . $propObj['CHARSET']);
477
+			}
478
+			$propObj->setRawMimeDirValue($property['value']);
479
+		}
480
+
481
+		return $propObj;
482
+
483
+	}
484
+
485
+	/**
486
+	 * Unescapes a property value.
487
+	 *
488
+	 * vCard 2.1 says:
489
+	 *   * Semi-colons must be escaped in some property values, specifically
490
+	 *     ADR, ORG and N.
491
+	 *   * Semi-colons must be escaped in parameter values, because semi-colons
492
+	 *     are also use to separate values.
493
+	 *   * No mention of escaping backslashes with another backslash.
494
+	 *   * newlines are not escaped either, instead QUOTED-PRINTABLE is used to
495
+	 *     span values over more than 1 line.
496
+	 *
497
+	 * vCard 3.0 says:
498
+	 *   * (rfc2425) Backslashes, newlines (\n or \N) and comma's must be
499
+	 *     escaped, all time time.
500
+	 *   * Comma's are used for delimeters in multiple values
501
+	 *   * (rfc2426) Adds to to this that the semi-colon MUST also be escaped,
502
+	 *     as in some properties semi-colon is used for separators.
503
+	 *   * Properties using semi-colons: N, ADR, GEO, ORG
504
+	 *   * Both ADR and N's individual parts may be broken up further with a
505
+	 *     comma.
506
+	 *   * Properties using commas: NICKNAME, CATEGORIES
507
+	 *
508
+	 * vCard 4.0 (rfc6350) says:
509
+	 *   * Commas must be escaped.
510
+	 *   * Semi-colons may be escaped, an unescaped semi-colon _may_ be a
511
+	 *     delimiter, depending on the property.
512
+	 *   * Backslashes must be escaped
513
+	 *   * Newlines must be escaped as either \N or \n.
514
+	 *   * Some compound properties may contain multiple parts themselves, so a
515
+	 *     comma within a semi-colon delimited property may also be unescaped
516
+	 *     to denote multiple parts _within_ the compound property.
517
+	 *   * Text-properties using semi-colons: N, ADR, ORG, CLIENTPIDMAP.
518
+	 *   * Text-properties using commas: NICKNAME, RELATED, CATEGORIES, PID.
519
+	 *
520
+	 * Even though the spec says that commas must always be escaped, the
521
+	 * example for GEO in Section 6.5.2 seems to violate this.
522
+	 *
523
+	 * iCalendar 2.0 (rfc5545) says:
524
+	 *   * Commas or semi-colons may be used as delimiters, depending on the
525
+	 *     property.
526
+	 *   * Commas, semi-colons, backslashes, newline (\N or \n) are always
527
+	 *     escaped, unless they are delimiters.
528
+	 *   * Colons shall not be escaped.
529
+	 *   * Commas can be considered the 'default delimiter' and is described as
530
+	 *     the delimiter in cases where the order of the multiple values is
531
+	 *     insignificant.
532
+	 *   * Semi-colons are described as the delimiter for 'structured values'.
533
+	 *     They are specifically used in Semi-colons are used as a delimiter in
534
+	 *     REQUEST-STATUS, RRULE, GEO and EXRULE. EXRULE is deprecated however.
535
+	 *
536
+	 * Now for the parameters
537
+	 *
538
+	 * If delimiter is not set (null) this method will just return a string.
539
+	 * If it's a comma or a semi-colon the string will be split on those
540
+	 * characters, and always return an array.
541
+	 *
542
+	 * @param string $input
543
+	 * @param string $delimiter
544
+	 *
545
+	 * @return string|string[]
546
+	 */
547
+	static function unescapeValue($input, $delimiter = ';') {
548
+
549
+		$regex = '#  (?: (\\\\ (?: \\\\ | N | n | ; | , ) )';
550
+		if ($delimiter) {
551
+			$regex .= ' | (' . $delimiter . ')';
552
+		}
553
+		$regex .= ') #x';
554
+
555
+		$matches = preg_split($regex, $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
556
+
557
+		$resultArray = [];
558
+		$result = '';
559
+
560
+		foreach ($matches as $match) {
561
+
562
+			switch ($match) {
563
+				case '\\\\' :
564
+					$result .= '\\';
565
+					break;
566
+				case '\N' :
567
+				case '\n' :
568
+					$result .= "\n";
569
+					break;
570
+				case '\;' :
571
+					$result .= ';';
572
+					break;
573
+				case '\,' :
574
+					$result .= ',';
575
+					break;
576
+				case $delimiter :
577
+					$resultArray[] = $result;
578
+					$result = '';
579
+					break;
580
+				default :
581
+					$result .= $match;
582
+					break;
583
+
584
+			}
585
+
586
+		}
587
+
588
+		$resultArray[] = $result;
589
+		return $delimiter ? $resultArray : $result;
590
+
591
+	}
592
+
593
+	/**
594
+	 * Unescapes a parameter value.
595
+	 *
596
+	 * vCard 2.1:
597
+	 *   * Does not mention a mechanism for this. In addition, double quotes
598
+	 *     are never used to wrap values.
599
+	 *   * This means that parameters can simply not contain colons or
600
+	 *     semi-colons.
601
+	 *
602
+	 * vCard 3.0 (rfc2425, rfc2426):
603
+	 *   * Parameters _may_ be surrounded by double quotes.
604
+	 *   * If this is not the case, semi-colon, colon and comma may simply not
605
+	 *     occur (the comma used for multiple parameter values though).
606
+	 *   * If it is surrounded by double-quotes, it may simply not contain
607
+	 *     double-quotes.
608
+	 *   * This means that a parameter can in no case encode double-quotes, or
609
+	 *     newlines.
610
+	 *
611
+	 * vCard 4.0 (rfc6350)
612
+	 *   * Behavior seems to be identical to vCard 3.0
613
+	 *
614
+	 * iCalendar 2.0 (rfc5545)
615
+	 *   * Behavior seems to be identical to vCard 3.0
616
+	 *
617
+	 * Parameter escaping mechanism (rfc6868) :
618
+	 *   * This rfc describes a new way to escape parameter values.
619
+	 *   * New-line is encoded as ^n
620
+	 *   * ^ is encoded as ^^.
621
+	 *   * " is encoded as ^'
622
+	 *
623
+	 * @param string $input
624
+	 *
625
+	 * @return void
626
+	 */
627
+	private function unescapeParam($input) {
628
+
629
+		return
630
+			preg_replace_callback(
631
+				'#(\^(\^|n|\'))#',
632
+				function($matches) {
633
+					switch ($matches[2]) {
634
+						case 'n' :
635
+							return "\n";
636
+						case '^' :
637
+							return '^';
638
+						case '\'' :
639
+							return '"';
640
+
641
+					// @codeCoverageIgnoreStart
642
+					}
643
+					// @codeCoverageIgnoreEnd
644
+				},
645
+				$input
646
+			);
647
+	}
648
+
649
+	/**
650
+	 * Gets the full quoted printable value.
651
+	 *
652
+	 * We need a special method for this, because newlines have both a meaning
653
+	 * in vCards, and in QuotedPrintable.
654
+	 *
655
+	 * This method does not do any decoding.
656
+	 *
657
+	 * @return string
658
+	 */
659
+	private function extractQuotedPrintableValue() {
660
+
661
+		// We need to parse the raw line again to get the start of the value.
662
+		//
663
+		// We are basically looking for the first colon (:), but we need to
664
+		// skip over the parameters first, as they may contain one.
665
+		$regex = '/^
666 666
             (?: [^:])+ # Anything but a colon
667 667
             (?: "[^"]")* # A parameter in double quotes
668 668
             : # start of the value we really care about
669 669
             (.*)$
670 670
         /xs';
671 671
 
672
-        preg_match($regex, $this->rawLine, $matches);
672
+		preg_match($regex, $this->rawLine, $matches);
673 673
 
674
-        $value = $matches[1];
675
-        // Removing the first whitespace character from every line. Kind of
676
-        // like unfolding, but we keep the newline.
677
-        $value = str_replace("\n ", "\n", $value);
674
+		$value = $matches[1];
675
+		// Removing the first whitespace character from every line. Kind of
676
+		// like unfolding, but we keep the newline.
677
+		$value = str_replace("\n ", "\n", $value);
678 678
 
679
-        // Microsoft products don't always correctly fold lines, they may be
680
-        // missing a whitespace. So if 'forgiving' is turned on, we will take
681
-        // those as well.
682
-        if ($this->options & self::OPTION_FORGIVING) {
683
-            while (substr($value, -1) === '=') {
684
-                // Reading the line
685
-                $this->readLine();
686
-                // Grabbing the raw form
687
-                $value .= "\n" . $this->rawLine;
688
-            }
689
-        }
679
+		// Microsoft products don't always correctly fold lines, they may be
680
+		// missing a whitespace. So if 'forgiving' is turned on, we will take
681
+		// those as well.
682
+		if ($this->options & self::OPTION_FORGIVING) {
683
+			while (substr($value, -1) === '=') {
684
+				// Reading the line
685
+				$this->readLine();
686
+				// Grabbing the raw form
687
+				$value .= "\n" . $this->rawLine;
688
+			}
689
+		}
690 690
 
691
-        return $value;
691
+		return $value;
692 692
 
693
-    }
693
+	}
694 694
 
695 695
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -358,7 +358,7 @@  discard block
 block discarded – undo
358 358
             ) (?=[;:,])
359 359
             /xi";
360 360
 
361
-        preg_match_all($regex, $line, $matches,  PREG_SET_ORDER);
361
+        preg_match_all($regex, $line, $matches, PREG_SET_ORDER);
362 362
 
363 363
         $property = [
364 364
             'name'       => null,
@@ -461,7 +461,7 @@  discard block
 block discarded – undo
461 461
             $charset = $this->charset;
462 462
             if ($this->root->getDocumentType() === Document::VCARD21 && isset($propObj['CHARSET'])) {
463 463
                 // vCard 2.1 allows the character set to be specified per property.
464
-                $charset = (string)$propObj['CHARSET'];
464
+                $charset = (string) $propObj['CHARSET'];
465 465
             }
466 466
             switch ($charset) {
467 467
                 case 'UTF-8' :
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Parser/XML/Element/KeyValue.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -33,7 +33,7 @@
 block discarded – undo
33 33
      * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
34 34
      * the next element.
35 35
      *
36
-     * @param XML\Reader $reader
36
+     * @param SabreXml\Reader $reader
37 37
      *
38 38
      * @return mixed
39 39
      */
Please login to merge, or discard this patch.
Indentation   +41 added lines, -41 removed lines patch added patch discarded remove patch
@@ -15,56 +15,56 @@
 block discarded – undo
15 15
  */
16 16
 class KeyValue extends SabreXml\Element\KeyValue {
17 17
 
18
-    /**
19
-     * The deserialize method is called during xml parsing.
20
-     *
21
-     * This method is called staticly, this is because in theory this method
22
-     * may be used as a type of constructor, or factory method.
23
-     *
24
-     * Often you want to return an instance of the current class, but you are
25
-     * free to return other data as well.
26
-     *
27
-     * Important note 2: You are responsible for advancing the reader to the
28
-     * next element. Not doing anything will result in a never-ending loop.
29
-     *
30
-     * If you just want to skip parsing for this element altogether, you can
31
-     * just call $reader->next();
32
-     *
33
-     * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
34
-     * the next element.
35
-     *
36
-     * @param XML\Reader $reader
37
-     *
38
-     * @return mixed
39
-     */
40
-    static function xmlDeserialize(SabreXml\Reader $reader) {
18
+	/**
19
+	 * The deserialize method is called during xml parsing.
20
+	 *
21
+	 * This method is called staticly, this is because in theory this method
22
+	 * may be used as a type of constructor, or factory method.
23
+	 *
24
+	 * Often you want to return an instance of the current class, but you are
25
+	 * free to return other data as well.
26
+	 *
27
+	 * Important note 2: You are responsible for advancing the reader to the
28
+	 * next element. Not doing anything will result in a never-ending loop.
29
+	 *
30
+	 * If you just want to skip parsing for this element altogether, you can
31
+	 * just call $reader->next();
32
+	 *
33
+	 * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
34
+	 * the next element.
35
+	 *
36
+	 * @param XML\Reader $reader
37
+	 *
38
+	 * @return mixed
39
+	 */
40
+	static function xmlDeserialize(SabreXml\Reader $reader) {
41 41
 
42
-        // If there's no children, we don't do anything.
43
-        if ($reader->isEmptyElement) {
44
-            $reader->next();
45
-            return [];
46
-        }
42
+		// If there's no children, we don't do anything.
43
+		if ($reader->isEmptyElement) {
44
+			$reader->next();
45
+			return [];
46
+		}
47 47
 
48
-        $values = [];
49
-        $reader->read();
48
+		$values = [];
49
+		$reader->read();
50 50
 
51
-        do {
51
+		do {
52 52
 
53
-            if ($reader->nodeType === SabreXml\Reader::ELEMENT) {
53
+			if ($reader->nodeType === SabreXml\Reader::ELEMENT) {
54 54
 
55
-                $name = $reader->localName;
56
-                $values[$name] = $reader->parseCurrentElement()['value'];
55
+				$name = $reader->localName;
56
+				$values[$name] = $reader->parseCurrentElement()['value'];
57 57
 
58
-            } else {
59
-                $reader->read();
60
-            }
58
+			} else {
59
+				$reader->read();
60
+			}
61 61
 
62
-        } while ($reader->nodeType !== SabreXml\Reader::END_ELEMENT);
62
+		} while ($reader->nodeType !== SabreXml\Reader::END_ELEMENT);
63 63
 
64
-        $reader->read();
64
+		$reader->read();
65 65
 
66
-        return $values;
66
+		return $values;
67 67
 
68
-    }
68
+	}
69 69
 
70 70
 }
Please login to merge, or discard this patch.