Completed
Pull Request — developer (#4001)
by Thom
542:26 queued 508:45
created
libraries/SabreDAV/VObject/Property/ICalendar/Duration.php 2 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\Property\ICalendar;
4 4
 
5
-use Sabre\VObject\Property;
6 5
 use Sabre\VObject\DateTimeParser;
6
+use Sabre\VObject\Property;
7 7
 
8 8
 /**
9 9
  * Duration property.
Please login to merge, or discard this patch.
Indentation   +63 added lines, -63 removed lines patch added patch discarded remove patch
@@ -18,68 +18,68 @@
 block discarded – undo
18 18
  */
19 19
 class Duration extends Property {
20 20
 
21
-    /**
22
-     * In case this is a multi-value property. This string will be used as a
23
-     * delimiter.
24
-     *
25
-     * @var string|null
26
-     */
27
-    public $delimiter = ',';
28
-
29
-    /**
30
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
31
-     *
32
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
33
-     * not yet done, but parameters are not included.
34
-     *
35
-     * @param string $val
36
-     *
37
-     * @return void
38
-     */
39
-    public function setRawMimeDirValue($val) {
40
-
41
-        $this->setValue(explode($this->delimiter, $val));
42
-
43
-    }
44
-
45
-    /**
46
-     * Returns a raw mime-dir representation of the value.
47
-     *
48
-     * @return string
49
-     */
50
-    public function getRawMimeDirValue() {
51
-
52
-        return implode($this->delimiter, $this->getParts());
53
-
54
-    }
55
-
56
-    /**
57
-     * Returns the type of value.
58
-     *
59
-     * This corresponds to the VALUE= parameter. Every property also has a
60
-     * 'default' valueType.
61
-     *
62
-     * @return string
63
-     */
64
-    public function getValueType() {
65
-
66
-        return 'DURATION';
67
-
68
-    }
69
-
70
-    /**
71
-     * Returns a DateInterval representation of the Duration property.
72
-     *
73
-     * If the property has more than one value, only the first is returned.
74
-     *
75
-     * @return \DateInterval
76
-     */
77
-    public function getDateInterval() {
78
-
79
-        $parts = $this->getParts();
80
-        $value = $parts[0];
81
-        return DateTimeParser::parseDuration($value);
82
-
83
-    }
21
+	/**
22
+	 * In case this is a multi-value property. This string will be used as a
23
+	 * delimiter.
24
+	 *
25
+	 * @var string|null
26
+	 */
27
+	public $delimiter = ',';
28
+
29
+	/**
30
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
31
+	 *
32
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
33
+	 * not yet done, but parameters are not included.
34
+	 *
35
+	 * @param string $val
36
+	 *
37
+	 * @return void
38
+	 */
39
+	public function setRawMimeDirValue($val) {
40
+
41
+		$this->setValue(explode($this->delimiter, $val));
42
+
43
+	}
44
+
45
+	/**
46
+	 * Returns a raw mime-dir representation of the value.
47
+	 *
48
+	 * @return string
49
+	 */
50
+	public function getRawMimeDirValue() {
51
+
52
+		return implode($this->delimiter, $this->getParts());
53
+
54
+	}
55
+
56
+	/**
57
+	 * Returns the type of value.
58
+	 *
59
+	 * This corresponds to the VALUE= parameter. Every property also has a
60
+	 * 'default' valueType.
61
+	 *
62
+	 * @return string
63
+	 */
64
+	public function getValueType() {
65
+
66
+		return 'DURATION';
67
+
68
+	}
69
+
70
+	/**
71
+	 * Returns a DateInterval representation of the Duration property.
72
+	 *
73
+	 * If the property has more than one value, only the first is returned.
74
+	 *
75
+	 * @return \DateInterval
76
+	 */
77
+	public function getDateInterval() {
78
+
79
+		$parts = $this->getParts();
80
+		$value = $parts[0];
81
+		return DateTimeParser::parseDuration($value);
82
+
83
+	}
84 84
 
85 85
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/ICalendar/Period.php 3 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\Property\ICalendar;
4 4
 
5
-use Sabre\VObject\Property;
6 5
 use Sabre\VObject\DateTimeParser;
6
+use Sabre\VObject\Property;
7 7
 use Sabre\Xml;
8 8
 
9 9
 /**
Please login to merge, or discard this patch.
Braces   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -143,8 +143,7 @@
 block discarded – undo
143 143
 
144 144
         if ($value[0][1][0] === 'P') {
145 145
             $writer->writeElement('duration', $value[0][1]);
146
-        }
147
-        else {
146
+        } else {
148 147
             $writer->writeElement('end', $value[0][1]);
149 148
         }
150 149
 
Please login to merge, or discard this patch.
Indentation   +132 added lines, -132 removed lines patch added patch discarded remove patch
@@ -19,137 +19,137 @@
 block discarded – undo
19 19
  */
20 20
 class Period extends Property {
21 21
 
22
-    /**
23
-     * In case this is a multi-value property. This string will be used as a
24
-     * delimiter.
25
-     *
26
-     * @var string|null
27
-     */
28
-    public $delimiter = ',';
29
-
30
-    /**
31
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
32
-     *
33
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
34
-     * not yet done, but parameters are not included.
35
-     *
36
-     * @param string $val
37
-     *
38
-     * @return void
39
-     */
40
-    public function setRawMimeDirValue($val) {
41
-
42
-        $this->setValue(explode($this->delimiter, $val));
43
-
44
-    }
45
-
46
-    /**
47
-     * Returns a raw mime-dir representation of the value.
48
-     *
49
-     * @return string
50
-     */
51
-    public function getRawMimeDirValue() {
52
-
53
-        return implode($this->delimiter, $this->getParts());
54
-
55
-    }
56
-
57
-    /**
58
-     * Returns the type of value.
59
-     *
60
-     * This corresponds to the VALUE= parameter. Every property also has a
61
-     * 'default' valueType.
62
-     *
63
-     * @return string
64
-     */
65
-    public function getValueType() {
66
-
67
-        return 'PERIOD';
68
-
69
-    }
70
-
71
-    /**
72
-     * Sets the json value, as it would appear in a jCard or jCal object.
73
-     *
74
-     * The value must always be an array.
75
-     *
76
-     * @param array $value
77
-     *
78
-     * @return void
79
-     */
80
-    public function setJsonValue(array $value) {
81
-
82
-        $value = array_map(
83
-            function($item) {
84
-
85
-                return strtr(implode('/', $item), [':' => '', '-' => '']);
86
-
87
-            },
88
-            $value
89
-        );
90
-        parent::setJsonValue($value);
91
-
92
-    }
93
-
94
-    /**
95
-     * Returns the value, in the format it should be encoded for json.
96
-     *
97
-     * This method must always return an array.
98
-     *
99
-     * @return array
100
-     */
101
-    public function getJsonValue() {
102
-
103
-        $return = [];
104
-        foreach ($this->getParts() as $item) {
105
-
106
-            list($start, $end) = explode('/', $item, 2);
107
-
108
-            $start = DateTimeParser::parseDateTime($start);
109
-
110
-            // This is a duration value.
111
-            if ($end[0] === 'P') {
112
-                $return[] = [
113
-                    $start->format('Y-m-d\\TH:i:s'),
114
-                    $end
115
-                ];
116
-            } else {
117
-                $end = DateTimeParser::parseDateTime($end);
118
-                $return[] = [
119
-                    $start->format('Y-m-d\\TH:i:s'),
120
-                    $end->format('Y-m-d\\TH:i:s'),
121
-                ];
122
-            }
123
-
124
-        }
125
-
126
-        return $return;
127
-
128
-    }
129
-
130
-    /**
131
-     * This method serializes only the value of a property. This is used to
132
-     * create xCard or xCal documents.
133
-     *
134
-     * @param Xml\Writer $writer  XML writer.
135
-     *
136
-     * @return void
137
-     */
138
-    protected function xmlSerializeValue(Xml\Writer $writer) {
139
-
140
-        $writer->startElement(strtolower($this->getValueType()));
141
-        $value = $this->getJsonValue();
142
-        $writer->writeElement('start', $value[0][0]);
143
-
144
-        if ($value[0][1][0] === 'P') {
145
-            $writer->writeElement('duration', $value[0][1]);
146
-        }
147
-        else {
148
-            $writer->writeElement('end', $value[0][1]);
149
-        }
150
-
151
-        $writer->endElement();
152
-
153
-    }
22
+	/**
23
+	 * In case this is a multi-value property. This string will be used as a
24
+	 * delimiter.
25
+	 *
26
+	 * @var string|null
27
+	 */
28
+	public $delimiter = ',';
29
+
30
+	/**
31
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
32
+	 *
33
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
34
+	 * not yet done, but parameters are not included.
35
+	 *
36
+	 * @param string $val
37
+	 *
38
+	 * @return void
39
+	 */
40
+	public function setRawMimeDirValue($val) {
41
+
42
+		$this->setValue(explode($this->delimiter, $val));
43
+
44
+	}
45
+
46
+	/**
47
+	 * Returns a raw mime-dir representation of the value.
48
+	 *
49
+	 * @return string
50
+	 */
51
+	public function getRawMimeDirValue() {
52
+
53
+		return implode($this->delimiter, $this->getParts());
54
+
55
+	}
56
+
57
+	/**
58
+	 * Returns the type of value.
59
+	 *
60
+	 * This corresponds to the VALUE= parameter. Every property also has a
61
+	 * 'default' valueType.
62
+	 *
63
+	 * @return string
64
+	 */
65
+	public function getValueType() {
66
+
67
+		return 'PERIOD';
68
+
69
+	}
70
+
71
+	/**
72
+	 * Sets the json value, as it would appear in a jCard or jCal object.
73
+	 *
74
+	 * The value must always be an array.
75
+	 *
76
+	 * @param array $value
77
+	 *
78
+	 * @return void
79
+	 */
80
+	public function setJsonValue(array $value) {
81
+
82
+		$value = array_map(
83
+			function($item) {
84
+
85
+				return strtr(implode('/', $item), [':' => '', '-' => '']);
86
+
87
+			},
88
+			$value
89
+		);
90
+		parent::setJsonValue($value);
91
+
92
+	}
93
+
94
+	/**
95
+	 * Returns the value, in the format it should be encoded for json.
96
+	 *
97
+	 * This method must always return an array.
98
+	 *
99
+	 * @return array
100
+	 */
101
+	public function getJsonValue() {
102
+
103
+		$return = [];
104
+		foreach ($this->getParts() as $item) {
105
+
106
+			list($start, $end) = explode('/', $item, 2);
107
+
108
+			$start = DateTimeParser::parseDateTime($start);
109
+
110
+			// This is a duration value.
111
+			if ($end[0] === 'P') {
112
+				$return[] = [
113
+					$start->format('Y-m-d\\TH:i:s'),
114
+					$end
115
+				];
116
+			} else {
117
+				$end = DateTimeParser::parseDateTime($end);
118
+				$return[] = [
119
+					$start->format('Y-m-d\\TH:i:s'),
120
+					$end->format('Y-m-d\\TH:i:s'),
121
+				];
122
+			}
123
+
124
+		}
125
+
126
+		return $return;
127
+
128
+	}
129
+
130
+	/**
131
+	 * This method serializes only the value of a property. This is used to
132
+	 * create xCard or xCal documents.
133
+	 *
134
+	 * @param Xml\Writer $writer  XML writer.
135
+	 *
136
+	 * @return void
137
+	 */
138
+	protected function xmlSerializeValue(Xml\Writer $writer) {
139
+
140
+		$writer->startElement(strtolower($this->getValueType()));
141
+		$value = $this->getJsonValue();
142
+		$writer->writeElement('start', $value[0][0]);
143
+
144
+		if ($value[0][1][0] === 'P') {
145
+			$writer->writeElement('duration', $value[0][1]);
146
+		}
147
+		else {
148
+			$writer->writeElement('end', $value[0][1]);
149
+		}
150
+
151
+		$writer->endElement();
152
+
153
+	}
154 154
 
155 155
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/IntegerValue.php 4 patches
Unused Use Statements   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -2,8 +2,7 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\VObject\Property;
4 4
 
5
-use
6
-    Sabre\VObject\Property;
5
+use Sabre\VObject\Property;
7 6
 
8 7
 /**
9 8
  * Integer property.
Please login to merge, or discard this patch.
Indentation   +69 added lines, -69 removed lines patch added patch discarded remove patch
@@ -3,7 +3,7 @@  discard block
 block discarded – undo
3 3
 namespace Sabre\VObject\Property;
4 4
 
5 5
 use
6
-    Sabre\VObject\Property;
6
+	Sabre\VObject\Property;
7 7
 
8 8
 /**
9 9
  * Integer property.
@@ -17,72 +17,72 @@  discard block
 block discarded – undo
17 17
  */
18 18
 class IntegerValue extends Property {
19 19
 
20
-    /**
21
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
22
-     *
23
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
24
-     * not yet done, but parameters are not included.
25
-     *
26
-     * @param string $val
27
-     *
28
-     * @return void
29
-     */
30
-    public function setRawMimeDirValue($val) {
31
-
32
-        $this->setValue((int)$val);
33
-
34
-    }
35
-
36
-    /**
37
-     * Returns a raw mime-dir representation of the value.
38
-     *
39
-     * @return string
40
-     */
41
-    public function getRawMimeDirValue() {
42
-
43
-        return $this->value;
44
-
45
-    }
46
-
47
-    /**
48
-     * Returns the type of value.
49
-     *
50
-     * This corresponds to the VALUE= parameter. Every property also has a
51
-     * 'default' valueType.
52
-     *
53
-     * @return string
54
-     */
55
-    public function getValueType() {
56
-
57
-        return 'INTEGER';
58
-
59
-    }
60
-
61
-    /**
62
-     * Returns the value, in the format it should be encoded for json.
63
-     *
64
-     * This method must always return an array.
65
-     *
66
-     * @return array
67
-     */
68
-    public function getJsonValue() {
69
-
70
-        return [(int)$this->getValue()];
71
-
72
-    }
73
-
74
-    /**
75
-     * Hydrate data from a XML subtree, as it would appear in a xCard or xCal
76
-     * object.
77
-     *
78
-     * @param array $value
79
-     *
80
-     * @return void
81
-     */
82
-    public function setXmlValue(array $value) {
83
-
84
-        $value = array_map('intval', $value);
85
-        parent::setXmlValue($value);
86
-
87
-    }
20
+	/**
21
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
22
+	 *
23
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
24
+	 * not yet done, but parameters are not included.
25
+	 *
26
+	 * @param string $val
27
+	 *
28
+	 * @return void
29
+	 */
30
+	public function setRawMimeDirValue($val) {
31
+
32
+		$this->setValue((int)$val);
33
+
34
+	}
35
+
36
+	/**
37
+	 * Returns a raw mime-dir representation of the value.
38
+	 *
39
+	 * @return string
40
+	 */
41
+	public function getRawMimeDirValue() {
42
+
43
+		return $this->value;
44
+
45
+	}
46
+
47
+	/**
48
+	 * Returns the type of value.
49
+	 *
50
+	 * This corresponds to the VALUE= parameter. Every property also has a
51
+	 * 'default' valueType.
52
+	 *
53
+	 * @return string
54
+	 */
55
+	public function getValueType() {
56
+
57
+		return 'INTEGER';
58
+
59
+	}
60
+
61
+	/**
62
+	 * Returns the value, in the format it should be encoded for json.
63
+	 *
64
+	 * This method must always return an array.
65
+	 *
66
+	 * @return array
67
+	 */
68
+	public function getJsonValue() {
69
+
70
+		return [(int)$this->getValue()];
71
+
72
+	}
73
+
74
+	/**
75
+	 * Hydrate data from a XML subtree, as it would appear in a xCard or xCal
76
+	 * object.
77
+	 *
78
+	 * @param array $value
79
+	 *
80
+	 * @return void
81
+	 */
82
+	public function setXmlValue(array $value) {
83
+
84
+		$value = array_map('intval', $value);
85
+		parent::setXmlValue($value);
86
+
87
+	}
88 88
 }
Please login to merge, or discard this patch.
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -63,7 +63,7 @@
 block discarded – undo
63 63
      *
64 64
      * This method must always return an array.
65 65
      *
66
-     * @return array
66
+     * @return integer[]
67 67
      */
68 68
     public function getJsonValue() {
69 69
 
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -29,7 +29,7 @@  discard block
 block discarded – undo
29 29
      */
30 30
     public function setRawMimeDirValue($val) {
31 31
 
32
-        $this->setValue((int)$val);
32
+        $this->setValue((int) $val);
33 33
 
34 34
     }
35 35
 
@@ -67,7 +67,7 @@  discard block
 block discarded – undo
67 67
      */
68 68
     public function getJsonValue() {
69 69
 
70
-        return [(int)$this->getValue()];
70
+        return [(int) $this->getValue()];
71 71
 
72 72
     }
73 73
 
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/Text.php 4 patches
Unused Use Statements   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -2,10 +2,10 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\VObject\Property;
4 4
 
5
-use Sabre\VObject\Property;
6 5
 use Sabre\VObject\Component;
7
-use Sabre\VObject\Parser\MimeDir;
8 6
 use Sabre\VObject\Document;
7
+use Sabre\VObject\Parser\MimeDir;
8
+use Sabre\VObject\Property;
9 9
 use Sabre\Xml;
10 10
 
11 11
 /**
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -252,7 +252,7 @@  discard block
 block discarded – undo
252 252
             // encode newlines for us. Specifically, the \r\n sequence must in
253 253
             // vcards be encoded as =0D=OA and we must insert soft-newlines
254 254
             // every 75 bytes.
255
-            for ($ii = 0;$ii < strlen($val);$ii++) {
255
+            for ($ii = 0; $ii < strlen($val); $ii++) {
256 256
                 $ord = ord($val[$ii]);
257 257
                 // These characters are encoded as themselves.
258 258
                 if ($ord >= 32 && $ord <= 126) {
@@ -397,7 +397,7 @@  discard block
 block discarded – undo
397 397
             if (count($parts) < $minimum) {
398 398
                 $warnings[] = [
399 399
                     'level'   => $options & self::REPAIR ? 1 : 3,
400
-                    'message' => 'The ' . $this->name  . ' property must have at least ' . $minimum . ' values. It only has ' . count($parts),
400
+                    'message' => 'The ' . $this->name . ' property must have at least ' . $minimum . ' values. It only has ' . count($parts),
401 401
                     'node'    => $this,
402 402
                 ];
403 403
                 if ($options & self::REPAIR) {
Please login to merge, or discard this patch.
Braces   +6 added lines, -2 removed lines patch added patch discarded remove patch
@@ -228,7 +228,9 @@  discard block
 block discarded – undo
228 228
         }
229 229
 
230 230
         $str = $this->name;
231
-        if ($this->group) $str = $this->group . '.' . $this->name;
231
+        if ($this->group) {
232
+        	$str = $this->group . '.' . $this->name;
233
+        }
232 234
         foreach ($this->parameters as $param) {
233 235
 
234 236
             if ($param->getValue() === 'QUOTED-PRINTABLE') {
@@ -267,7 +269,9 @@  discard block
 block discarded – undo
267 269
                 }
268 270
 
269 271
             }
270
-            if (!is_null($lastLine)) $out .= $lastLine . "\r\n";
272
+            if (!is_null($lastLine)) {
273
+            	$out .= $lastLine . "\r\n";
274
+            }
271 275
             return $out;
272 276
 
273 277
         } else {
Please login to merge, or discard this patch.
Indentation   +391 added lines, -391 removed lines patch added patch discarded remove patch
@@ -19,395 +19,395 @@
 block discarded – undo
19 19
  */
20 20
 class Text extends Property {
21 21
 
22
-    /**
23
-     * In case this is a multi-value property. This string will be used as a
24
-     * delimiter.
25
-     *
26
-     * @var string
27
-     */
28
-    public $delimiter = ',';
29
-
30
-    /**
31
-     * List of properties that are considered 'structured'.
32
-     *
33
-     * @var array
34
-     */
35
-    protected $structuredValues = [
36
-        // vCard
37
-        'N',
38
-        'ADR',
39
-        'ORG',
40
-        'GENDER',
41
-        'CLIENTPIDMAP',
42
-
43
-        // iCalendar
44
-        'REQUEST-STATUS',
45
-    ];
46
-
47
-    /**
48
-     * Some text components have a minimum number of components.
49
-     *
50
-     * N must for instance be represented as 5 components, separated by ;, even
51
-     * if the last few components are unused.
52
-     *
53
-     * @var array
54
-     */
55
-    protected $minimumPropertyValues = [
56
-        'N'   => 5,
57
-        'ADR' => 7,
58
-    ];
59
-
60
-    /**
61
-     * Creates the property.
62
-     *
63
-     * You can specify the parameters either in key=>value syntax, in which case
64
-     * parameters will automatically be created, or you can just pass a list of
65
-     * Parameter objects.
66
-     *
67
-     * @param Component $root The root document
68
-     * @param string $name
69
-     * @param string|array|null $value
70
-     * @param array $parameters List of parameters
71
-     * @param string $group The vcard property group
72
-     *
73
-     * @return void
74
-     */
75
-    public function __construct(Component $root, $name, $value = null, array $parameters = [], $group = null) {
76
-
77
-        // There's two types of multi-valued text properties:
78
-        // 1. multivalue properties.
79
-        // 2. structured value properties
80
-        //
81
-        // The former is always separated by a comma, the latter by semi-colon.
82
-        if (in_array($name, $this->structuredValues)) {
83
-            $this->delimiter = ';';
84
-        }
85
-
86
-        parent::__construct($root, $name, $value, $parameters, $group);
87
-
88
-    }
89
-
90
-    /**
91
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
92
-     *
93
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
94
-     * not yet done, but parameters are not included.
95
-     *
96
-     * @param string $val
97
-     *
98
-     * @return void
99
-     */
100
-    public function setRawMimeDirValue($val) {
101
-
102
-        $this->setValue(MimeDir::unescapeValue($val, $this->delimiter));
103
-
104
-    }
105
-
106
-    /**
107
-     * Sets the value as a quoted-printable encoded string.
108
-     *
109
-     * @param string $val
110
-     *
111
-     * @return void
112
-     */
113
-    public function setQuotedPrintableValue($val) {
114
-
115
-        $val = quoted_printable_decode($val);
116
-
117
-        // Quoted printable only appears in vCard 2.1, and the only character
118
-        // that may be escaped there is ;. So we are simply splitting on just
119
-        // that.
120
-        //
121
-        // We also don't have to unescape \\, so all we need to look for is a ;
122
-        // that's not preceeded with a \.
123
-        $regex = '# (?<!\\\\) ; #x';
124
-        $matches = preg_split($regex, $val);
125
-        $this->setValue($matches);
126
-
127
-    }
128
-
129
-    /**
130
-     * Returns a raw mime-dir representation of the value.
131
-     *
132
-     * @return string
133
-     */
134
-    public function getRawMimeDirValue() {
135
-
136
-        $val = $this->getParts();
137
-
138
-        if (isset($this->minimumPropertyValues[$this->name])) {
139
-            $val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
140
-        }
141
-
142
-        foreach ($val as &$item) {
143
-
144
-            if (!is_array($item)) {
145
-                $item = [$item];
146
-            }
147
-
148
-            foreach ($item as &$subItem) {
149
-                $subItem = strtr(
150
-                    $subItem,
151
-                    [
152
-                        '\\' => '\\\\',
153
-                        ';'  => '\;',
154
-                        ','  => '\,',
155
-                        "\n" => '\n',
156
-                        "\r" => "",
157
-                    ]
158
-                );
159
-            }
160
-            $item = implode(',', $item);
161
-
162
-        }
163
-
164
-        return implode($this->delimiter, $val);
165
-
166
-    }
167
-
168
-    /**
169
-     * Returns the value, in the format it should be encoded for json.
170
-     *
171
-     * This method must always return an array.
172
-     *
173
-     * @return array
174
-     */
175
-    public function getJsonValue() {
176
-
177
-        // Structured text values should always be returned as a single
178
-        // array-item. Multi-value text should be returned as multiple items in
179
-        // the top-array.
180
-        if (in_array($this->name, $this->structuredValues)) {
181
-            return [$this->getParts()];
182
-        }
183
-        return $this->getParts();
184
-
185
-    }
186
-
187
-    /**
188
-     * Returns the type of value.
189
-     *
190
-     * This corresponds to the VALUE= parameter. Every property also has a
191
-     * 'default' valueType.
192
-     *
193
-     * @return string
194
-     */
195
-    public function getValueType() {
196
-
197
-        return 'TEXT';
198
-
199
-    }
200
-
201
-    /**
202
-     * Turns the object back into a serialized blob.
203
-     *
204
-     * @return string
205
-     */
206
-    public function serialize() {
207
-
208
-        // We need to kick in a special type of encoding, if it's a 2.1 vcard.
209
-        if ($this->root->getDocumentType() !== Document::VCARD21) {
210
-            return parent::serialize();
211
-        }
212
-
213
-        $val = $this->getParts();
214
-
215
-        if (isset($this->minimumPropertyValues[$this->name])) {
216
-            $val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
217
-        }
218
-
219
-        // Imploding multiple parts into a single value, and splitting the
220
-        // values with ;.
221
-        if (count($val) > 1) {
222
-            foreach ($val as $k => $v) {
223
-                $val[$k] = str_replace(';', '\;', $v);
224
-            }
225
-            $val = implode(';', $val);
226
-        } else {
227
-            $val = $val[0];
228
-        }
229
-
230
-        $str = $this->name;
231
-        if ($this->group) $str = $this->group . '.' . $this->name;
232
-        foreach ($this->parameters as $param) {
233
-
234
-            if ($param->getValue() === 'QUOTED-PRINTABLE') {
235
-                continue;
236
-            }
237
-            $str .= ';' . $param->serialize();
238
-
239
-        }
240
-
241
-
242
-
243
-        // If the resulting value contains a \n, we must encode it as
244
-        // quoted-printable.
245
-        if (strpos($val, "\n") !== false) {
246
-
247
-            $str .= ';ENCODING=QUOTED-PRINTABLE:';
248
-            $lastLine = $str;
249
-            $out = null;
250
-
251
-            // The PHP built-in quoted-printable-encode does not correctly
252
-            // encode newlines for us. Specifically, the \r\n sequence must in
253
-            // vcards be encoded as =0D=OA and we must insert soft-newlines
254
-            // every 75 bytes.
255
-            for ($ii = 0;$ii < strlen($val);$ii++) {
256
-                $ord = ord($val[$ii]);
257
-                // These characters are encoded as themselves.
258
-                if ($ord >= 32 && $ord <= 126) {
259
-                    $lastLine .= $val[$ii];
260
-                } else {
261
-                    $lastLine .= '=' . strtoupper(bin2hex($val[$ii]));
262
-                }
263
-                if (strlen($lastLine) >= 75) {
264
-                    // Soft line break
265
-                    $out .= $lastLine . "=\r\n ";
266
-                    $lastLine = null;
267
-                }
268
-
269
-            }
270
-            if (!is_null($lastLine)) $out .= $lastLine . "\r\n";
271
-            return $out;
272
-
273
-        } else {
274
-            $str .= ':' . $val;
275
-            $out = '';
276
-            while (strlen($str) > 0) {
277
-                if (strlen($str) > 75) {
278
-                    $out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n";
279
-                    $str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8');
280
-                } else {
281
-                    $out .= $str . "\r\n";
282
-                    $str = '';
283
-                    break;
284
-                }
285
-            }
286
-
287
-            return $out;
288
-
289
-        }
290
-
291
-    }
292
-
293
-    /**
294
-     * This method serializes only the value of a property. This is used to
295
-     * create xCard or xCal documents.
296
-     *
297
-     * @param Xml\Writer $writer  XML writer.
298
-     *
299
-     * @return void
300
-     */
301
-    protected function xmlSerializeValue(Xml\Writer $writer) {
302
-
303
-        $values = $this->getParts();
304
-
305
-        $map = function($items) use ($values, $writer) {
306
-            foreach ($items as $i => $item) {
307
-                $writer->writeElement(
308
-                    $item,
309
-                    !empty($values[$i]) ? $values[$i] : null
310
-                );
311
-            }
312
-        };
313
-
314
-        switch ($this->name) {
315
-
316
-            // Special-casing the REQUEST-STATUS property.
317
-            //
318
-            // See:
319
-            // http://tools.ietf.org/html/rfc6321#section-3.4.1.3
320
-            case 'REQUEST-STATUS':
321
-                $writer->writeElement('code', $values[0]);
322
-                $writer->writeElement('description', $values[1]);
323
-
324
-                if (isset($values[2])) {
325
-                    $writer->writeElement('data', $values[2]);
326
-                }
327
-                break;
328
-
329
-            case 'N':
330
-                $map([
331
-                    'surname',
332
-                    'given',
333
-                    'additional',
334
-                    'prefix',
335
-                    'suffix'
336
-                ]);
337
-                break;
338
-
339
-            case 'GENDER':
340
-                $map([
341
-                    'sex',
342
-                    'text'
343
-                ]);
344
-                break;
345
-
346
-            case 'ADR':
347
-                $map([
348
-                    'pobox',
349
-                    'ext',
350
-                    'street',
351
-                    'locality',
352
-                    'region',
353
-                    'code',
354
-                    'country'
355
-                ]);
356
-                break;
357
-
358
-            case 'CLIENTPIDMAP':
359
-                $map([
360
-                    'sourceid',
361
-                    'uri'
362
-                ]);
363
-                break;
364
-
365
-            default:
366
-                parent::xmlSerializeValue($writer);
367
-        }
368
-
369
-    }
370
-
371
-    /**
372
-     * Validates the node for correctness.
373
-     *
374
-     * The following options are supported:
375
-     *   - Node::REPAIR - If something is broken, and automatic repair may
376
-     *                    be attempted.
377
-     *
378
-     * An array is returned with warnings.
379
-     *
380
-     * Every item in the array has the following properties:
381
-     *    * level - (number between 1 and 3 with severity information)
382
-     *    * message - (human readable message)
383
-     *    * node - (reference to the offending node)
384
-     *
385
-     * @param int $options
386
-     *
387
-     * @return array
388
-     */
389
-    public function validate($options = 0) {
390
-
391
-        $warnings = parent::validate($options);
392
-
393
-        if (isset($this->minimumPropertyValues[$this->name])) {
394
-
395
-            $minimum = $this->minimumPropertyValues[$this->name];
396
-            $parts = $this->getParts();
397
-            if (count($parts) < $minimum) {
398
-                $warnings[] = [
399
-                    'level'   => $options & self::REPAIR ? 1 : 3,
400
-                    'message' => 'The ' . $this->name  . ' property must have at least ' . $minimum . ' values. It only has ' . count($parts),
401
-                    'node'    => $this,
402
-                ];
403
-                if ($options & self::REPAIR) {
404
-                    $parts = array_pad($parts, $minimum, '');
405
-                    $this->setParts($parts);
406
-                }
407
-            }
408
-
409
-        }
410
-        return $warnings;
411
-
412
-    }
22
+	/**
23
+	 * In case this is a multi-value property. This string will be used as a
24
+	 * delimiter.
25
+	 *
26
+	 * @var string
27
+	 */
28
+	public $delimiter = ',';
29
+
30
+	/**
31
+	 * List of properties that are considered 'structured'.
32
+	 *
33
+	 * @var array
34
+	 */
35
+	protected $structuredValues = [
36
+		// vCard
37
+		'N',
38
+		'ADR',
39
+		'ORG',
40
+		'GENDER',
41
+		'CLIENTPIDMAP',
42
+
43
+		// iCalendar
44
+		'REQUEST-STATUS',
45
+	];
46
+
47
+	/**
48
+	 * Some text components have a minimum number of components.
49
+	 *
50
+	 * N must for instance be represented as 5 components, separated by ;, even
51
+	 * if the last few components are unused.
52
+	 *
53
+	 * @var array
54
+	 */
55
+	protected $minimumPropertyValues = [
56
+		'N'   => 5,
57
+		'ADR' => 7,
58
+	];
59
+
60
+	/**
61
+	 * Creates the property.
62
+	 *
63
+	 * You can specify the parameters either in key=>value syntax, in which case
64
+	 * parameters will automatically be created, or you can just pass a list of
65
+	 * Parameter objects.
66
+	 *
67
+	 * @param Component $root The root document
68
+	 * @param string $name
69
+	 * @param string|array|null $value
70
+	 * @param array $parameters List of parameters
71
+	 * @param string $group The vcard property group
72
+	 *
73
+	 * @return void
74
+	 */
75
+	public function __construct(Component $root, $name, $value = null, array $parameters = [], $group = null) {
76
+
77
+		// There's two types of multi-valued text properties:
78
+		// 1. multivalue properties.
79
+		// 2. structured value properties
80
+		//
81
+		// The former is always separated by a comma, the latter by semi-colon.
82
+		if (in_array($name, $this->structuredValues)) {
83
+			$this->delimiter = ';';
84
+		}
85
+
86
+		parent::__construct($root, $name, $value, $parameters, $group);
87
+
88
+	}
89
+
90
+	/**
91
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
92
+	 *
93
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
94
+	 * not yet done, but parameters are not included.
95
+	 *
96
+	 * @param string $val
97
+	 *
98
+	 * @return void
99
+	 */
100
+	public function setRawMimeDirValue($val) {
101
+
102
+		$this->setValue(MimeDir::unescapeValue($val, $this->delimiter));
103
+
104
+	}
105
+
106
+	/**
107
+	 * Sets the value as a quoted-printable encoded string.
108
+	 *
109
+	 * @param string $val
110
+	 *
111
+	 * @return void
112
+	 */
113
+	public function setQuotedPrintableValue($val) {
114
+
115
+		$val = quoted_printable_decode($val);
116
+
117
+		// Quoted printable only appears in vCard 2.1, and the only character
118
+		// that may be escaped there is ;. So we are simply splitting on just
119
+		// that.
120
+		//
121
+		// We also don't have to unescape \\, so all we need to look for is a ;
122
+		// that's not preceeded with a \.
123
+		$regex = '# (?<!\\\\) ; #x';
124
+		$matches = preg_split($regex, $val);
125
+		$this->setValue($matches);
126
+
127
+	}
128
+
129
+	/**
130
+	 * Returns a raw mime-dir representation of the value.
131
+	 *
132
+	 * @return string
133
+	 */
134
+	public function getRawMimeDirValue() {
135
+
136
+		$val = $this->getParts();
137
+
138
+		if (isset($this->minimumPropertyValues[$this->name])) {
139
+			$val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
140
+		}
141
+
142
+		foreach ($val as &$item) {
143
+
144
+			if (!is_array($item)) {
145
+				$item = [$item];
146
+			}
147
+
148
+			foreach ($item as &$subItem) {
149
+				$subItem = strtr(
150
+					$subItem,
151
+					[
152
+						'\\' => '\\\\',
153
+						';'  => '\;',
154
+						','  => '\,',
155
+						"\n" => '\n',
156
+						"\r" => "",
157
+					]
158
+				);
159
+			}
160
+			$item = implode(',', $item);
161
+
162
+		}
163
+
164
+		return implode($this->delimiter, $val);
165
+
166
+	}
167
+
168
+	/**
169
+	 * Returns the value, in the format it should be encoded for json.
170
+	 *
171
+	 * This method must always return an array.
172
+	 *
173
+	 * @return array
174
+	 */
175
+	public function getJsonValue() {
176
+
177
+		// Structured text values should always be returned as a single
178
+		// array-item. Multi-value text should be returned as multiple items in
179
+		// the top-array.
180
+		if (in_array($this->name, $this->structuredValues)) {
181
+			return [$this->getParts()];
182
+		}
183
+		return $this->getParts();
184
+
185
+	}
186
+
187
+	/**
188
+	 * Returns the type of value.
189
+	 *
190
+	 * This corresponds to the VALUE= parameter. Every property also has a
191
+	 * 'default' valueType.
192
+	 *
193
+	 * @return string
194
+	 */
195
+	public function getValueType() {
196
+
197
+		return 'TEXT';
198
+
199
+	}
200
+
201
+	/**
202
+	 * Turns the object back into a serialized blob.
203
+	 *
204
+	 * @return string
205
+	 */
206
+	public function serialize() {
207
+
208
+		// We need to kick in a special type of encoding, if it's a 2.1 vcard.
209
+		if ($this->root->getDocumentType() !== Document::VCARD21) {
210
+			return parent::serialize();
211
+		}
212
+
213
+		$val = $this->getParts();
214
+
215
+		if (isset($this->minimumPropertyValues[$this->name])) {
216
+			$val = array_pad($val, $this->minimumPropertyValues[$this->name], '');
217
+		}
218
+
219
+		// Imploding multiple parts into a single value, and splitting the
220
+		// values with ;.
221
+		if (count($val) > 1) {
222
+			foreach ($val as $k => $v) {
223
+				$val[$k] = str_replace(';', '\;', $v);
224
+			}
225
+			$val = implode(';', $val);
226
+		} else {
227
+			$val = $val[0];
228
+		}
229
+
230
+		$str = $this->name;
231
+		if ($this->group) $str = $this->group . '.' . $this->name;
232
+		foreach ($this->parameters as $param) {
233
+
234
+			if ($param->getValue() === 'QUOTED-PRINTABLE') {
235
+				continue;
236
+			}
237
+			$str .= ';' . $param->serialize();
238
+
239
+		}
240
+
241
+
242
+
243
+		// If the resulting value contains a \n, we must encode it as
244
+		// quoted-printable.
245
+		if (strpos($val, "\n") !== false) {
246
+
247
+			$str .= ';ENCODING=QUOTED-PRINTABLE:';
248
+			$lastLine = $str;
249
+			$out = null;
250
+
251
+			// The PHP built-in quoted-printable-encode does not correctly
252
+			// encode newlines for us. Specifically, the \r\n sequence must in
253
+			// vcards be encoded as =0D=OA and we must insert soft-newlines
254
+			// every 75 bytes.
255
+			for ($ii = 0;$ii < strlen($val);$ii++) {
256
+				$ord = ord($val[$ii]);
257
+				// These characters are encoded as themselves.
258
+				if ($ord >= 32 && $ord <= 126) {
259
+					$lastLine .= $val[$ii];
260
+				} else {
261
+					$lastLine .= '=' . strtoupper(bin2hex($val[$ii]));
262
+				}
263
+				if (strlen($lastLine) >= 75) {
264
+					// Soft line break
265
+					$out .= $lastLine . "=\r\n ";
266
+					$lastLine = null;
267
+				}
268
+
269
+			}
270
+			if (!is_null($lastLine)) $out .= $lastLine . "\r\n";
271
+			return $out;
272
+
273
+		} else {
274
+			$str .= ':' . $val;
275
+			$out = '';
276
+			while (strlen($str) > 0) {
277
+				if (strlen($str) > 75) {
278
+					$out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n";
279
+					$str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8');
280
+				} else {
281
+					$out .= $str . "\r\n";
282
+					$str = '';
283
+					break;
284
+				}
285
+			}
286
+
287
+			return $out;
288
+
289
+		}
290
+
291
+	}
292
+
293
+	/**
294
+	 * This method serializes only the value of a property. This is used to
295
+	 * create xCard or xCal documents.
296
+	 *
297
+	 * @param Xml\Writer $writer  XML writer.
298
+	 *
299
+	 * @return void
300
+	 */
301
+	protected function xmlSerializeValue(Xml\Writer $writer) {
302
+
303
+		$values = $this->getParts();
304
+
305
+		$map = function($items) use ($values, $writer) {
306
+			foreach ($items as $i => $item) {
307
+				$writer->writeElement(
308
+					$item,
309
+					!empty($values[$i]) ? $values[$i] : null
310
+				);
311
+			}
312
+		};
313
+
314
+		switch ($this->name) {
315
+
316
+			// Special-casing the REQUEST-STATUS property.
317
+			//
318
+			// See:
319
+			// http://tools.ietf.org/html/rfc6321#section-3.4.1.3
320
+			case 'REQUEST-STATUS':
321
+				$writer->writeElement('code', $values[0]);
322
+				$writer->writeElement('description', $values[1]);
323
+
324
+				if (isset($values[2])) {
325
+					$writer->writeElement('data', $values[2]);
326
+				}
327
+				break;
328
+
329
+			case 'N':
330
+				$map([
331
+					'surname',
332
+					'given',
333
+					'additional',
334
+					'prefix',
335
+					'suffix'
336
+				]);
337
+				break;
338
+
339
+			case 'GENDER':
340
+				$map([
341
+					'sex',
342
+					'text'
343
+				]);
344
+				break;
345
+
346
+			case 'ADR':
347
+				$map([
348
+					'pobox',
349
+					'ext',
350
+					'street',
351
+					'locality',
352
+					'region',
353
+					'code',
354
+					'country'
355
+				]);
356
+				break;
357
+
358
+			case 'CLIENTPIDMAP':
359
+				$map([
360
+					'sourceid',
361
+					'uri'
362
+				]);
363
+				break;
364
+
365
+			default:
366
+				parent::xmlSerializeValue($writer);
367
+		}
368
+
369
+	}
370
+
371
+	/**
372
+	 * Validates the node for correctness.
373
+	 *
374
+	 * The following options are supported:
375
+	 *   - Node::REPAIR - If something is broken, and automatic repair may
376
+	 *                    be attempted.
377
+	 *
378
+	 * An array is returned with warnings.
379
+	 *
380
+	 * Every item in the array has the following properties:
381
+	 *    * level - (number between 1 and 3 with severity information)
382
+	 *    * message - (human readable message)
383
+	 *    * node - (reference to the offending node)
384
+	 *
385
+	 * @param int $options
386
+	 *
387
+	 * @return array
388
+	 */
389
+	public function validate($options = 0) {
390
+
391
+		$warnings = parent::validate($options);
392
+
393
+		if (isset($this->minimumPropertyValues[$this->name])) {
394
+
395
+			$minimum = $this->minimumPropertyValues[$this->name];
396
+			$parts = $this->getParts();
397
+			if (count($parts) < $minimum) {
398
+				$warnings[] = [
399
+					'level'   => $options & self::REPAIR ? 1 : 3,
400
+					'message' => 'The ' . $this->name  . ' property must have at least ' . $minimum . ' values. It only has ' . count($parts),
401
+					'node'    => $this,
402
+				];
403
+				if ($options & self::REPAIR) {
404
+					$parts = array_pad($parts, $minimum, '');
405
+					$this->setParts($parts);
406
+				}
407
+			}
408
+
409
+		}
410
+		return $warnings;
411
+
412
+	}
413 413
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/Uri.php 2 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\Property;
4 4
 
5
-use Sabre\VObject\Property;
6 5
 use Sabre\VObject\Parameter;
6
+use Sabre\VObject\Property;
7 7
 
8 8
 /**
9 9
  * URI property.
Please login to merge, or discard this patch.
Indentation   +102 added lines, -102 removed lines patch added patch discarded remove patch
@@ -16,107 +16,107 @@
 block discarded – undo
16 16
  */
17 17
 class Uri extends Text {
18 18
 
19
-    /**
20
-     * In case this is a multi-value property. This string will be used as a
21
-     * delimiter.
22
-     *
23
-     * @var string|null
24
-     */
25
-    public $delimiter = null;
26
-
27
-    /**
28
-     * Returns the type of value.
29
-     *
30
-     * This corresponds to the VALUE= parameter. Every property also has a
31
-     * 'default' valueType.
32
-     *
33
-     * @return string
34
-     */
35
-    public function getValueType() {
36
-
37
-        return 'URI';
38
-
39
-    }
40
-
41
-    /**
42
-     * Returns an iterable list of children.
43
-     *
44
-     * @return array
45
-     */
46
-    public function parameters() {
47
-
48
-        $parameters = parent::parameters();
49
-        if (!isset($parameters['VALUE']) && in_array($this->name, ['URL', 'PHOTO'])) {
50
-            // If we are encoding a URI value, and this URI value has no
51
-            // VALUE=URI parameter, we add it anyway.
52
-            //
53
-            // This is not required by any spec, but both Apple iCal and Apple
54
-            // AddressBook (at least in version 10.8) will trip over this if
55
-            // this is not set, and so it improves compatibility.
56
-            //
57
-            // See Issue #227 and #235
58
-            $parameters['VALUE'] = new Parameter($this->root, 'VALUE', 'URI');
59
-        }
60
-        return $parameters;
61
-
62
-    }
63
-
64
-    /**
65
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
66
-     *
67
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
68
-     * not yet done, but parameters are not included.
69
-     *
70
-     * @param string $val
71
-     *
72
-     * @return void
73
-     */
74
-    public function setRawMimeDirValue($val) {
75
-
76
-        // Normally we don't need to do any type of unescaping for these
77
-        // properties, however.. we've noticed that Google Contacts
78
-        // specifically escapes the colon (:) with a blackslash. While I have
79
-        // no clue why they thought that was a good idea, I'm unescaping it
80
-        // anyway.
81
-        //
82
-        // Good thing backslashes are not allowed in urls. Makes it easy to
83
-        // assume that a backslash is always intended as an escape character.
84
-        if ($this->name === 'URL') {
85
-            $regex = '#  (?: (\\\\ (?: \\\\ | : ) ) ) #x';
86
-            $matches = preg_split($regex, $val, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
87
-            $newVal = '';
88
-            foreach ($matches as $match) {
89
-                switch ($match) {
90
-                    case '\:' :
91
-                        $newVal .= ':';
92
-                        break;
93
-                    default :
94
-                        $newVal .= $match;
95
-                        break;
96
-                }
97
-            }
98
-            $this->value = $newVal;
99
-        } else {
100
-            $this->value = strtr($val, ['\,' => ',']);
101
-        }
102
-
103
-    }
104
-
105
-    /**
106
-     * Returns a raw mime-dir representation of the value.
107
-     *
108
-     * @return string
109
-     */
110
-    public function getRawMimeDirValue() {
111
-
112
-        if (is_array($this->value)) {
113
-            $value = $this->value[0];
114
-        } else {
115
-            $value = $this->value;
116
-        }
117
-
118
-        return strtr($value, [',' => '\,']);
119
-
120
-    }
19
+	/**
20
+	 * In case this is a multi-value property. This string will be used as a
21
+	 * delimiter.
22
+	 *
23
+	 * @var string|null
24
+	 */
25
+	public $delimiter = null;
26
+
27
+	/**
28
+	 * Returns the type of value.
29
+	 *
30
+	 * This corresponds to the VALUE= parameter. Every property also has a
31
+	 * 'default' valueType.
32
+	 *
33
+	 * @return string
34
+	 */
35
+	public function getValueType() {
36
+
37
+		return 'URI';
38
+
39
+	}
40
+
41
+	/**
42
+	 * Returns an iterable list of children.
43
+	 *
44
+	 * @return array
45
+	 */
46
+	public function parameters() {
47
+
48
+		$parameters = parent::parameters();
49
+		if (!isset($parameters['VALUE']) && in_array($this->name, ['URL', 'PHOTO'])) {
50
+			// If we are encoding a URI value, and this URI value has no
51
+			// VALUE=URI parameter, we add it anyway.
52
+			//
53
+			// This is not required by any spec, but both Apple iCal and Apple
54
+			// AddressBook (at least in version 10.8) will trip over this if
55
+			// this is not set, and so it improves compatibility.
56
+			//
57
+			// See Issue #227 and #235
58
+			$parameters['VALUE'] = new Parameter($this->root, 'VALUE', 'URI');
59
+		}
60
+		return $parameters;
61
+
62
+	}
63
+
64
+	/**
65
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
66
+	 *
67
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
68
+	 * not yet done, but parameters are not included.
69
+	 *
70
+	 * @param string $val
71
+	 *
72
+	 * @return void
73
+	 */
74
+	public function setRawMimeDirValue($val) {
75
+
76
+		// Normally we don't need to do any type of unescaping for these
77
+		// properties, however.. we've noticed that Google Contacts
78
+		// specifically escapes the colon (:) with a blackslash. While I have
79
+		// no clue why they thought that was a good idea, I'm unescaping it
80
+		// anyway.
81
+		//
82
+		// Good thing backslashes are not allowed in urls. Makes it easy to
83
+		// assume that a backslash is always intended as an escape character.
84
+		if ($this->name === 'URL') {
85
+			$regex = '#  (?: (\\\\ (?: \\\\ | : ) ) ) #x';
86
+			$matches = preg_split($regex, $val, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
87
+			$newVal = '';
88
+			foreach ($matches as $match) {
89
+				switch ($match) {
90
+					case '\:' :
91
+						$newVal .= ':';
92
+						break;
93
+					default :
94
+						$newVal .= $match;
95
+						break;
96
+				}
97
+			}
98
+			$this->value = $newVal;
99
+		} else {
100
+			$this->value = strtr($val, ['\,' => ',']);
101
+		}
102
+
103
+	}
104
+
105
+	/**
106
+	 * Returns a raw mime-dir representation of the value.
107
+	 *
108
+	 * @return string
109
+	 */
110
+	public function getRawMimeDirValue() {
111
+
112
+		if (is_array($this->value)) {
113
+			$value = $this->value[0];
114
+		} else {
115
+			$value = $this->value;
116
+		}
117
+
118
+		return strtr($value, [',' => '\,']);
119
+
120
+	}
121 121
 
122 122
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/VCard/DateAndOrTime.php 4 patches
Unused Use Statements   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -2,9 +2,9 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\VObject\Property\VCard;
4 4
 
5
-use DateTimeInterface;
6
-use DateTimeImmutable;
7 5
 use DateTime;
6
+use DateTimeImmutable;
7
+use DateTimeInterface;
8 8
 use Sabre\VObject\DateTimeParser;
9 9
 use Sabre\VObject\InvalidDataException;
10 10
 use Sabre\VObject\Property;
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -278,7 +278,7 @@  discard block
 block discarded – undo
278 278
         // value-date = element date {
279 279
         //     xsd:string { pattern = "\d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d" }
280 280
         //   }
281
-        if (($d('year') ||  $d('month')  ||  $d('date'))
281
+        if (($d('year') || $d('month') || $d('date'))
282 282
             && (!$d('hour') && !$d('minute') && !$d('second') && !$d('timezone'))) {
283 283
 
284 284
             if ($d('year') && $d('month') && $d('date')) {
@@ -296,8 +296,8 @@  discard block
 block discarded – undo
296 296
         //     xsd:string { pattern = "(\d\d(\d\d(\d\d)?)?|-\d\d(\d\d?)|--\d\d)"
297 297
         //                          ~ "(Z|[+\-]\d\d(\d\d)?)?" }
298 298
         //   }
299
-        } elseif ((!$d('year') && !$d('month')  && !$d('date'))
300
-                  && ($d('hour') ||  $d('minute') ||  $d('second'))) {
299
+        } elseif ((!$d('year') && !$d('month') && !$d('date'))
300
+                  && ($d('hour') || $d('minute') || $d('second'))) {
301 301
 
302 302
             if ($d('hour')) {
303 303
                 $value .= $r('hour') . $r('minute') . $r('second');
Please login to merge, or discard this patch.
Indentation   +379 added lines, -379 removed lines patch added patch discarded remove patch
@@ -21,383 +21,383 @@
 block discarded – undo
21 21
  */
22 22
 class DateAndOrTime extends Property {
23 23
 
24
-    /**
25
-     * Field separator.
26
-     *
27
-     * @var null|string
28
-     */
29
-    public $delimiter = null;
30
-
31
-    /**
32
-     * Returns the type of value.
33
-     *
34
-     * This corresponds to the VALUE= parameter. Every property also has a
35
-     * 'default' valueType.
36
-     *
37
-     * @return string
38
-     */
39
-    public function getValueType() {
40
-
41
-        return 'DATE-AND-OR-TIME';
42
-
43
-    }
44
-
45
-    /**
46
-     * Sets a multi-valued property.
47
-     *
48
-     * You may also specify DateTime objects here.
49
-     *
50
-     * @param array $parts
51
-     *
52
-     * @return void
53
-     */
54
-    public function setParts(array $parts) {
55
-
56
-        if (count($parts) > 1) {
57
-            throw new \InvalidArgumentException('Only one value allowed');
58
-        }
59
-        if (isset($parts[0]) && $parts[0] instanceof \DateTime) {
60
-            $this->setDateTime($parts[0]);
61
-        } else {
62
-            parent::setParts($parts);
63
-        }
64
-
65
-    }
66
-
67
-    /**
68
-     * Updates the current value.
69
-     *
70
-     * This may be either a single, or multiple strings in an array.
71
-     *
72
-     * Instead of strings, you may also use DateTime here.
73
-     *
74
-     * @param string|array|\DateTime $value
75
-     *
76
-     * @return void
77
-     */
78
-    public function setValue($value) {
79
-
80
-        if ($value instanceof \DateTime) {
81
-            $this->setDateTime($value);
82
-        } else {
83
-            parent::setValue($value);
84
-        }
85
-
86
-    }
87
-
88
-    /**
89
-     * Sets the property as a DateTime object.
90
-     *
91
-     * @param DateTimeInterface $dt
92
-     *
93
-     * @return void
94
-     */
95
-    public function setDateTime(DateTimeInterface $dt) {
96
-
97
-        $tz = $dt->getTimeZone();
98
-        $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z']);
99
-
100
-        if ($isUtc) {
101
-            $value = $dt->format('Ymd\\THis\\Z');
102
-        } else {
103
-            // Calculating the offset.
104
-            $value = $dt->format('Ymd\\THisO');
105
-        }
106
-
107
-        $this->value = $value;
108
-
109
-    }
110
-
111
-    /**
112
-     * Returns a date-time value.
113
-     *
114
-     * Note that if this property contained more than 1 date-time, only the
115
-     * first will be returned. To get an array with multiple values, call
116
-     * getDateTimes.
117
-     *
118
-     * If no time was specified, we will always use midnight (in the default
119
-     * timezone) as the time.
120
-     *
121
-     * If parts of the date were omitted, such as the year, we will grab the
122
-     * current values for those. So at the time of writing, if the year was
123
-     * omitted, we would have filled in 2014.
124
-     *
125
-     * @return DateTimeImmutable
126
-     */
127
-    public function getDateTime() {
128
-
129
-        $now = new DateTime();
130
-
131
-        $tzFormat = $now->getTimezone()->getOffset($now) === 0 ? '\\Z' : 'O';
132
-        $nowParts = DateTimeParser::parseVCardDateTime($now->format('Ymd\\This' . $tzFormat));
133
-
134
-        $dateParts = DateTimeParser::parseVCardDateTime($this->getValue());
135
-
136
-        // This sets all the missing parts to the current date/time.
137
-        // So if the year was missing for a birthday, we're making it 'this
138
-        // year'.
139
-        foreach ($dateParts as $k => $v) {
140
-            if (is_null($v)) {
141
-                $dateParts[$k] = $nowParts[$k];
142
-            }
143
-        }
144
-        return new DateTimeImmutable("$dateParts[year]-$dateParts[month]-$dateParts[date] $dateParts[hour]:$dateParts[minute]:$dateParts[second] $dateParts[timezone]");
145
-
146
-    }
147
-
148
-    /**
149
-     * Returns the value, in the format it should be encoded for json.
150
-     *
151
-     * This method must always return an array.
152
-     *
153
-     * @return array
154
-     */
155
-    public function getJsonValue() {
156
-
157
-        $parts = DateTimeParser::parseVCardDateTime($this->getValue());
158
-
159
-        $dateStr = '';
160
-
161
-        // Year
162
-        if (!is_null($parts['year'])) {
163
-
164
-            $dateStr .= $parts['year'];
165
-
166
-            if (!is_null($parts['month'])) {
167
-                // If a year and a month is set, we need to insert a separator
168
-                // dash.
169
-                $dateStr .= '-';
170
-            }
171
-
172
-        } else {
173
-
174
-            if (!is_null($parts['month']) || !is_null($parts['date'])) {
175
-                // Inserting two dashes
176
-                $dateStr .= '--';
177
-            }
178
-
179
-        }
180
-
181
-        // Month
182
-        if (!is_null($parts['month'])) {
183
-
184
-            $dateStr .= $parts['month'];
185
-
186
-            if (isset($parts['date'])) {
187
-                // If month and date are set, we need the separator dash.
188
-                $dateStr .= '-';
189
-            }
190
-
191
-        } elseif (isset($parts['date'])) {
192
-            // If the month is empty, and a date is set, we need a 'empty
193
-            // dash'
194
-            $dateStr .= '-';
195
-        }
196
-
197
-        // Date
198
-        if (!is_null($parts['date'])) {
199
-            $dateStr .= $parts['date'];
200
-        }
201
-
202
-
203
-        // Early exit if we don't have a time string.
204
-        if (is_null($parts['hour']) && is_null($parts['minute']) && is_null($parts['second'])) {
205
-            return [$dateStr];
206
-        }
207
-
208
-        $dateStr .= 'T';
209
-
210
-        // Hour
211
-        if (!is_null($parts['hour'])) {
212
-
213
-            $dateStr .= $parts['hour'];
214
-
215
-            if (!is_null($parts['minute'])) {
216
-                $dateStr .= ':';
217
-            }
218
-
219
-        } else {
220
-            // We know either minute or second _must_ be set, so we insert a
221
-            // dash for an empty value.
222
-            $dateStr .= '-';
223
-        }
224
-
225
-        // Minute
226
-        if (!is_null($parts['minute'])) {
227
-
228
-            $dateStr .= $parts['minute'];
229
-
230
-            if (!is_null($parts['second'])) {
231
-                $dateStr .= ':';
232
-            }
233
-
234
-        } elseif (isset($parts['second'])) {
235
-            // Dash for empty minute
236
-            $dateStr .= '-';
237
-        }
238
-
239
-        // Second
240
-        if (!is_null($parts['second'])) {
241
-            $dateStr .= $parts['second'];
242
-        }
243
-
244
-        // Timezone
245
-        if (!is_null($parts['timezone'])) {
246
-            $dateStr .= $parts['timezone'];
247
-        }
248
-
249
-        return [$dateStr];
250
-
251
-    }
252
-
253
-    /**
254
-     * This method serializes only the value of a property. This is used to
255
-     * create xCard or xCal documents.
256
-     *
257
-     * @param Xml\Writer $writer  XML writer.
258
-     *
259
-     * @return void
260
-     */
261
-    protected function xmlSerializeValue(Xml\Writer $writer) {
262
-
263
-        $valueType = strtolower($this->getValueType());
264
-        $parts     = DateTimeParser::parseVCardDateAndOrTime($this->getValue());
265
-        $value     = '';
266
-
267
-        $d = function($part) use ($parts) {
268
-            return !is_null($parts[$part]);
269
-        };
270
-
271
-        $r = function($part) use ($parts) {
272
-            return $parts[$part];
273
-        };
274
-
275
-        // From the Relax NG Schema.
276
-        //
277
-        // # 4.3.1
278
-        // value-date = element date {
279
-        //     xsd:string { pattern = "\d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d" }
280
-        //   }
281
-        if (($d('year') ||  $d('month')  ||  $d('date'))
282
-            && (!$d('hour') && !$d('minute') && !$d('second') && !$d('timezone'))) {
283
-
284
-            if ($d('year') && $d('month') && $d('date')) {
285
-                $value .= $r('year') . $r('month') . $r('date');
286
-            } elseif ($d('year') && $d('month') && !$d('date')) {
287
-                $value .= $r('year') . '-' . $r('month');
288
-            } elseif (!$d('year') && $d('month')) {
289
-                $value .= '--' . $r('month') . $r('date');
290
-            } elseif (!$d('year') && !$d('month') && $d('date')) {
291
-                $value .= '---' . $r('date');
292
-            }
293
-
294
-        // # 4.3.2
295
-        // value-time = element time {
296
-        //     xsd:string { pattern = "(\d\d(\d\d(\d\d)?)?|-\d\d(\d\d?)|--\d\d)"
297
-        //                          ~ "(Z|[+\-]\d\d(\d\d)?)?" }
298
-        //   }
299
-        } elseif ((!$d('year') && !$d('month')  && !$d('date'))
300
-                  && ($d('hour') ||  $d('minute') ||  $d('second'))) {
301
-
302
-            if ($d('hour')) {
303
-                $value .= $r('hour') . $r('minute') . $r('second');
304
-            } elseif ($d('minute')) {
305
-                $value .= '-' . $r('minute') . $r('second');
306
-            } elseif ($d('second')) {
307
-                $value .= '--' . $r('second');
308
-            }
309
-
310
-            $value .= $r('timezone');
311
-
312
-        // # 4.3.3
313
-        // value-date-time = element date-time {
314
-        //     xsd:string { pattern = "(\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?"
315
-        //                          ~ "(Z|[+\-]\d\d(\d\d)?)?" }
316
-        //   }
317
-        } elseif ($d('date') && $d('hour')) {
318
-
319
-            if ($d('year') && $d('month') && $d('date')) {
320
-                $value .= $r('year') . $r('month') . $r('date');
321
-            } elseif (!$d('year') && $d('month') && $d('date')) {
322
-                $value .= '--' . $r('month') . $r('date');
323
-            } elseif (!$d('year') && !$d('month') && $d('date')) {
324
-                $value .= '---' . $r('date');
325
-            }
326
-
327
-            $value .= 'T' . $r('hour') . $r('minute') . $r('second') .
328
-                      $r('timezone');
329
-
330
-        }
331
-
332
-        $writer->writeElement($valueType, $value);
333
-
334
-    }
335
-
336
-    /**
337
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
338
-     *
339
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
340
-     * not yet done, but parameters are not included.
341
-     *
342
-     * @param string $val
343
-     *
344
-     * @return void
345
-     */
346
-    public function setRawMimeDirValue($val) {
347
-
348
-        $this->setValue($val);
349
-
350
-    }
351
-
352
-    /**
353
-     * Returns a raw mime-dir representation of the value.
354
-     *
355
-     * @return string
356
-     */
357
-    public function getRawMimeDirValue() {
358
-
359
-        return implode($this->delimiter, $this->getParts());
360
-
361
-    }
362
-
363
-    /**
364
-     * Validates the node for correctness.
365
-     *
366
-     * The following options are supported:
367
-     *   Node::REPAIR - May attempt to automatically repair the problem.
368
-     *
369
-     * This method returns an array with detected problems.
370
-     * Every element has the following properties:
371
-     *
372
-     *  * level - problem level.
373
-     *  * message - A human-readable string describing the issue.
374
-     *  * node - A reference to the problematic node.
375
-     *
376
-     * The level means:
377
-     *   1 - The issue was repaired (only happens if REPAIR was turned on)
378
-     *   2 - An inconsequential issue
379
-     *   3 - A severe issue.
380
-     *
381
-     * @param int $options
382
-     *
383
-     * @return array
384
-     */
385
-    public function validate($options = 0) {
386
-
387
-        $messages = parent::validate($options);
388
-        $value = $this->getValue();
389
-
390
-        try {
391
-            DateTimeParser::parseVCardDateTime($value);
392
-        } catch (InvalidDataException $e) {
393
-            $messages[] = [
394
-                'level'   => 3,
395
-                'message' => 'The supplied value (' . $value . ') is not a correct DATE-AND-OR-TIME property',
396
-                'node'    => $this,
397
-            ];
398
-        }
399
-
400
-        return $messages;
401
-
402
-    }
24
+	/**
25
+	 * Field separator.
26
+	 *
27
+	 * @var null|string
28
+	 */
29
+	public $delimiter = null;
30
+
31
+	/**
32
+	 * Returns the type of value.
33
+	 *
34
+	 * This corresponds to the VALUE= parameter. Every property also has a
35
+	 * 'default' valueType.
36
+	 *
37
+	 * @return string
38
+	 */
39
+	public function getValueType() {
40
+
41
+		return 'DATE-AND-OR-TIME';
42
+
43
+	}
44
+
45
+	/**
46
+	 * Sets a multi-valued property.
47
+	 *
48
+	 * You may also specify DateTime objects here.
49
+	 *
50
+	 * @param array $parts
51
+	 *
52
+	 * @return void
53
+	 */
54
+	public function setParts(array $parts) {
55
+
56
+		if (count($parts) > 1) {
57
+			throw new \InvalidArgumentException('Only one value allowed');
58
+		}
59
+		if (isset($parts[0]) && $parts[0] instanceof \DateTime) {
60
+			$this->setDateTime($parts[0]);
61
+		} else {
62
+			parent::setParts($parts);
63
+		}
64
+
65
+	}
66
+
67
+	/**
68
+	 * Updates the current value.
69
+	 *
70
+	 * This may be either a single, or multiple strings in an array.
71
+	 *
72
+	 * Instead of strings, you may also use DateTime here.
73
+	 *
74
+	 * @param string|array|\DateTime $value
75
+	 *
76
+	 * @return void
77
+	 */
78
+	public function setValue($value) {
79
+
80
+		if ($value instanceof \DateTime) {
81
+			$this->setDateTime($value);
82
+		} else {
83
+			parent::setValue($value);
84
+		}
85
+
86
+	}
87
+
88
+	/**
89
+	 * Sets the property as a DateTime object.
90
+	 *
91
+	 * @param DateTimeInterface $dt
92
+	 *
93
+	 * @return void
94
+	 */
95
+	public function setDateTime(DateTimeInterface $dt) {
96
+
97
+		$tz = $dt->getTimeZone();
98
+		$isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z']);
99
+
100
+		if ($isUtc) {
101
+			$value = $dt->format('Ymd\\THis\\Z');
102
+		} else {
103
+			// Calculating the offset.
104
+			$value = $dt->format('Ymd\\THisO');
105
+		}
106
+
107
+		$this->value = $value;
108
+
109
+	}
110
+
111
+	/**
112
+	 * Returns a date-time value.
113
+	 *
114
+	 * Note that if this property contained more than 1 date-time, only the
115
+	 * first will be returned. To get an array with multiple values, call
116
+	 * getDateTimes.
117
+	 *
118
+	 * If no time was specified, we will always use midnight (in the default
119
+	 * timezone) as the time.
120
+	 *
121
+	 * If parts of the date were omitted, such as the year, we will grab the
122
+	 * current values for those. So at the time of writing, if the year was
123
+	 * omitted, we would have filled in 2014.
124
+	 *
125
+	 * @return DateTimeImmutable
126
+	 */
127
+	public function getDateTime() {
128
+
129
+		$now = new DateTime();
130
+
131
+		$tzFormat = $now->getTimezone()->getOffset($now) === 0 ? '\\Z' : 'O';
132
+		$nowParts = DateTimeParser::parseVCardDateTime($now->format('Ymd\\This' . $tzFormat));
133
+
134
+		$dateParts = DateTimeParser::parseVCardDateTime($this->getValue());
135
+
136
+		// This sets all the missing parts to the current date/time.
137
+		// So if the year was missing for a birthday, we're making it 'this
138
+		// year'.
139
+		foreach ($dateParts as $k => $v) {
140
+			if (is_null($v)) {
141
+				$dateParts[$k] = $nowParts[$k];
142
+			}
143
+		}
144
+		return new DateTimeImmutable("$dateParts[year]-$dateParts[month]-$dateParts[date] $dateParts[hour]:$dateParts[minute]:$dateParts[second] $dateParts[timezone]");
145
+
146
+	}
147
+
148
+	/**
149
+	 * Returns the value, in the format it should be encoded for json.
150
+	 *
151
+	 * This method must always return an array.
152
+	 *
153
+	 * @return array
154
+	 */
155
+	public function getJsonValue() {
156
+
157
+		$parts = DateTimeParser::parseVCardDateTime($this->getValue());
158
+
159
+		$dateStr = '';
160
+
161
+		// Year
162
+		if (!is_null($parts['year'])) {
163
+
164
+			$dateStr .= $parts['year'];
165
+
166
+			if (!is_null($parts['month'])) {
167
+				// If a year and a month is set, we need to insert a separator
168
+				// dash.
169
+				$dateStr .= '-';
170
+			}
171
+
172
+		} else {
173
+
174
+			if (!is_null($parts['month']) || !is_null($parts['date'])) {
175
+				// Inserting two dashes
176
+				$dateStr .= '--';
177
+			}
178
+
179
+		}
180
+
181
+		// Month
182
+		if (!is_null($parts['month'])) {
183
+
184
+			$dateStr .= $parts['month'];
185
+
186
+			if (isset($parts['date'])) {
187
+				// If month and date are set, we need the separator dash.
188
+				$dateStr .= '-';
189
+			}
190
+
191
+		} elseif (isset($parts['date'])) {
192
+			// If the month is empty, and a date is set, we need a 'empty
193
+			// dash'
194
+			$dateStr .= '-';
195
+		}
196
+
197
+		// Date
198
+		if (!is_null($parts['date'])) {
199
+			$dateStr .= $parts['date'];
200
+		}
201
+
202
+
203
+		// Early exit if we don't have a time string.
204
+		if (is_null($parts['hour']) && is_null($parts['minute']) && is_null($parts['second'])) {
205
+			return [$dateStr];
206
+		}
207
+
208
+		$dateStr .= 'T';
209
+
210
+		// Hour
211
+		if (!is_null($parts['hour'])) {
212
+
213
+			$dateStr .= $parts['hour'];
214
+
215
+			if (!is_null($parts['minute'])) {
216
+				$dateStr .= ':';
217
+			}
218
+
219
+		} else {
220
+			// We know either minute or second _must_ be set, so we insert a
221
+			// dash for an empty value.
222
+			$dateStr .= '-';
223
+		}
224
+
225
+		// Minute
226
+		if (!is_null($parts['minute'])) {
227
+
228
+			$dateStr .= $parts['minute'];
229
+
230
+			if (!is_null($parts['second'])) {
231
+				$dateStr .= ':';
232
+			}
233
+
234
+		} elseif (isset($parts['second'])) {
235
+			// Dash for empty minute
236
+			$dateStr .= '-';
237
+		}
238
+
239
+		// Second
240
+		if (!is_null($parts['second'])) {
241
+			$dateStr .= $parts['second'];
242
+		}
243
+
244
+		// Timezone
245
+		if (!is_null($parts['timezone'])) {
246
+			$dateStr .= $parts['timezone'];
247
+		}
248
+
249
+		return [$dateStr];
250
+
251
+	}
252
+
253
+	/**
254
+	 * This method serializes only the value of a property. This is used to
255
+	 * create xCard or xCal documents.
256
+	 *
257
+	 * @param Xml\Writer $writer  XML writer.
258
+	 *
259
+	 * @return void
260
+	 */
261
+	protected function xmlSerializeValue(Xml\Writer $writer) {
262
+
263
+		$valueType = strtolower($this->getValueType());
264
+		$parts     = DateTimeParser::parseVCardDateAndOrTime($this->getValue());
265
+		$value     = '';
266
+
267
+		$d = function($part) use ($parts) {
268
+			return !is_null($parts[$part]);
269
+		};
270
+
271
+		$r = function($part) use ($parts) {
272
+			return $parts[$part];
273
+		};
274
+
275
+		// From the Relax NG Schema.
276
+		//
277
+		// # 4.3.1
278
+		// value-date = element date {
279
+		//     xsd:string { pattern = "\d{8}|\d{4}-\d\d|--\d\d(\d\d)?|---\d\d" }
280
+		//   }
281
+		if (($d('year') ||  $d('month')  ||  $d('date'))
282
+			&& (!$d('hour') && !$d('minute') && !$d('second') && !$d('timezone'))) {
283
+
284
+			if ($d('year') && $d('month') && $d('date')) {
285
+				$value .= $r('year') . $r('month') . $r('date');
286
+			} elseif ($d('year') && $d('month') && !$d('date')) {
287
+				$value .= $r('year') . '-' . $r('month');
288
+			} elseif (!$d('year') && $d('month')) {
289
+				$value .= '--' . $r('month') . $r('date');
290
+			} elseif (!$d('year') && !$d('month') && $d('date')) {
291
+				$value .= '---' . $r('date');
292
+			}
293
+
294
+		// # 4.3.2
295
+		// value-time = element time {
296
+		//     xsd:string { pattern = "(\d\d(\d\d(\d\d)?)?|-\d\d(\d\d?)|--\d\d)"
297
+		//                          ~ "(Z|[+\-]\d\d(\d\d)?)?" }
298
+		//   }
299
+		} elseif ((!$d('year') && !$d('month')  && !$d('date'))
300
+				  && ($d('hour') ||  $d('minute') ||  $d('second'))) {
301
+
302
+			if ($d('hour')) {
303
+				$value .= $r('hour') . $r('minute') . $r('second');
304
+			} elseif ($d('minute')) {
305
+				$value .= '-' . $r('minute') . $r('second');
306
+			} elseif ($d('second')) {
307
+				$value .= '--' . $r('second');
308
+			}
309
+
310
+			$value .= $r('timezone');
311
+
312
+		// # 4.3.3
313
+		// value-date-time = element date-time {
314
+		//     xsd:string { pattern = "(\d{8}|--\d{4}|---\d\d)T\d\d(\d\d(\d\d)?)?"
315
+		//                          ~ "(Z|[+\-]\d\d(\d\d)?)?" }
316
+		//   }
317
+		} elseif ($d('date') && $d('hour')) {
318
+
319
+			if ($d('year') && $d('month') && $d('date')) {
320
+				$value .= $r('year') . $r('month') . $r('date');
321
+			} elseif (!$d('year') && $d('month') && $d('date')) {
322
+				$value .= '--' . $r('month') . $r('date');
323
+			} elseif (!$d('year') && !$d('month') && $d('date')) {
324
+				$value .= '---' . $r('date');
325
+			}
326
+
327
+			$value .= 'T' . $r('hour') . $r('minute') . $r('second') .
328
+					  $r('timezone');
329
+
330
+		}
331
+
332
+		$writer->writeElement($valueType, $value);
333
+
334
+	}
335
+
336
+	/**
337
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
338
+	 *
339
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
340
+	 * not yet done, but parameters are not included.
341
+	 *
342
+	 * @param string $val
343
+	 *
344
+	 * @return void
345
+	 */
346
+	public function setRawMimeDirValue($val) {
347
+
348
+		$this->setValue($val);
349
+
350
+	}
351
+
352
+	/**
353
+	 * Returns a raw mime-dir representation of the value.
354
+	 *
355
+	 * @return string
356
+	 */
357
+	public function getRawMimeDirValue() {
358
+
359
+		return implode($this->delimiter, $this->getParts());
360
+
361
+	}
362
+
363
+	/**
364
+	 * Validates the node for correctness.
365
+	 *
366
+	 * The following options are supported:
367
+	 *   Node::REPAIR - May attempt to automatically repair the problem.
368
+	 *
369
+	 * This method returns an array with detected problems.
370
+	 * Every element has the following properties:
371
+	 *
372
+	 *  * level - problem level.
373
+	 *  * message - A human-readable string describing the issue.
374
+	 *  * node - A reference to the problematic node.
375
+	 *
376
+	 * The level means:
377
+	 *   1 - The issue was repaired (only happens if REPAIR was turned on)
378
+	 *   2 - An inconsequential issue
379
+	 *   3 - A severe issue.
380
+	 *
381
+	 * @param int $options
382
+	 *
383
+	 * @return array
384
+	 */
385
+	public function validate($options = 0) {
386
+
387
+		$messages = parent::validate($options);
388
+		$value = $this->getValue();
389
+
390
+		try {
391
+			DateTimeParser::parseVCardDateTime($value);
392
+		} catch (InvalidDataException $e) {
393
+			$messages[] = [
394
+				'level'   => 3,
395
+				'message' => 'The supplied value (' . $value . ') is not a correct DATE-AND-OR-TIME property',
396
+				'node'    => $this,
397
+			];
398
+		}
399
+
400
+		return $messages;
401
+
402
+	}
403 403
 }
Please login to merge, or discard this patch.
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
      *
72 72
      * Instead of strings, you may also use DateTime here.
73 73
      *
74
-     * @param string|array|\DateTime $value
74
+     * @param string $value
75 75
      *
76 76
      * @return void
77 77
      */
@@ -150,7 +150,7 @@  discard block
 block discarded – undo
150 150
      *
151 151
      * This method must always return an array.
152 152
      *
153
-     * @return array
153
+     * @return string[]
154 154
      */
155 155
     public function getJsonValue() {
156 156
 
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Recur/EventIterator.php 3 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\Recur;
4 4
 
5
-use DateTimeZone;
6 5
 use DateTimeImmutable;
7 6
 use DateTimeInterface;
7
+use DateTimeZone;
8 8
 use InvalidArgumentException;
9 9
 use Sabre\VObject\Component;
10 10
 use Sabre\VObject\Component\VEvent;
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -105,7 +105,7 @@
 block discarded – undo
105 105
             $events = [$input];
106 106
         } else {
107 107
             // Calendar + UID mode.
108
-            $uid = (string)$uid;
108
+            $uid = (string) $uid;
109 109
             if (!$uid) {
110 110
                 throw new InvalidArgumentException('The UID argument is required when a VCALENDAR is passed to this constructor');
111 111
             }
Please login to merge, or discard this patch.
Indentation   +445 added lines, -445 removed lines patch added patch discarded remove patch
@@ -58,450 +58,450 @@
 block discarded – undo
58 58
  */
59 59
 class EventIterator implements \Iterator {
60 60
 
61
-    /**
62
-     * Reference timeZone for floating dates and times.
63
-     *
64
-     * @var DateTimeZone
65
-     */
66
-    protected $timeZone;
67
-
68
-    /**
69
-     * True if we're iterating an all-day event.
70
-     *
71
-     * @var bool
72
-     */
73
-    protected $allDay = false;
74
-
75
-    /**
76
-     * Creates the iterator.
77
-     *
78
-     * There's three ways to set up the iterator.
79
-     *
80
-     * 1. You can pass a VCALENDAR component and a UID.
81
-     * 2. You can pass an array of VEVENTs (all UIDS should match).
82
-     * 3. You can pass a single VEVENT component.
83
-     *
84
-     * Only the second method is recomended. The other 1 and 3 will be removed
85
-     * at some point in the future.
86
-     *
87
-     * The $uid parameter is only required for the first method.
88
-     *
89
-     * @param Component|array $input
90
-     * @param string|null $uid
91
-     * @param DateTimeZone $timeZone Reference timezone for floating dates and
92
-     *                               times.
93
-     */
94
-    public function __construct($input, $uid = null, DateTimeZone $timeZone = null) {
95
-
96
-        if (is_null($timeZone)) {
97
-            $timeZone = new DateTimeZone('UTC');
98
-        }
99
-        $this->timeZone = $timeZone;
100
-
101
-        if (is_array($input)) {
102
-            $events = $input;
103
-        } elseif ($input instanceof VEvent) {
104
-            // Single instance mode.
105
-            $events = [$input];
106
-        } else {
107
-            // Calendar + UID mode.
108
-            $uid = (string)$uid;
109
-            if (!$uid) {
110
-                throw new InvalidArgumentException('The UID argument is required when a VCALENDAR is passed to this constructor');
111
-            }
112
-            if (!isset($input->VEVENT)) {
113
-                throw new InvalidArgumentException('No events found in this calendar');
114
-            }
115
-            $events = $input->getByUID($uid);
116
-
117
-        }
118
-
119
-        foreach ($events as $vevent) {
120
-
121
-            if (!isset($vevent->{'RECURRENCE-ID'})) {
122
-
123
-                $this->masterEvent = $vevent;
124
-
125
-            } else {
126
-
127
-                $this->exceptions[
128
-                    $vevent->{'RECURRENCE-ID'}->getDateTime($this->timeZone)->getTimeStamp()
129
-                ] = true;
130
-                $this->overriddenEvents[] = $vevent;
131
-
132
-            }
133
-
134
-        }
135
-
136
-        if (!$this->masterEvent) {
137
-            // No base event was found. CalDAV does allow cases where only
138
-            // overridden instances are stored.
139
-            //
140
-            // In this particular case, we're just going to grab the first
141
-            // event and use that instead. This may not always give the
142
-            // desired result.
143
-            if (!count($this->overriddenEvents)) {
144
-                throw new InvalidArgumentException('This VCALENDAR did not have an event with UID: ' . $uid);
145
-            }
146
-            $this->masterEvent = array_shift($this->overriddenEvents);
147
-        }
148
-
149
-        $this->startDate = $this->masterEvent->DTSTART->getDateTime($this->timeZone);
150
-        $this->allDay = !$this->masterEvent->DTSTART->hasTime();
151
-
152
-        if (isset($this->masterEvent->EXDATE)) {
153
-
154
-            foreach ($this->masterEvent->EXDATE as $exDate) {
155
-
156
-                foreach ($exDate->getDateTimes($this->timeZone) as $dt) {
157
-                    $this->exceptions[$dt->getTimeStamp()] = true;
158
-                }
159
-
160
-            }
161
-
162
-        }
163
-
164
-        if (isset($this->masterEvent->DTEND)) {
165
-            $this->eventDuration =
166
-                $this->masterEvent->DTEND->getDateTime($this->timeZone)->getTimeStamp() -
167
-                $this->startDate->getTimeStamp();
168
-        } elseif (isset($this->masterEvent->DURATION)) {
169
-            $duration = $this->masterEvent->DURATION->getDateInterval();
170
-            $end = clone $this->startDate;
171
-            $end = $end->add($duration);
172
-            $this->eventDuration = $end->getTimeStamp() - $this->startDate->getTimeStamp();
173
-        } elseif ($this->allDay) {
174
-            $this->eventDuration = 3600 * 24;
175
-        } else {
176
-            $this->eventDuration = 0;
177
-        }
178
-
179
-        if (isset($this->masterEvent->RDATE)) {
180
-            $this->recurIterator = new RDateIterator(
181
-                $this->masterEvent->RDATE->getParts(),
182
-                $this->startDate
183
-            );
184
-        } elseif (isset($this->masterEvent->RRULE)) {
185
-            $this->recurIterator = new RRuleIterator(
186
-                $this->masterEvent->RRULE->getParts(),
187
-                $this->startDate
188
-            );
189
-        } else {
190
-            $this->recurIterator = new RRuleIterator(
191
-                [
192
-                    'FREQ'  => 'DAILY',
193
-                    'COUNT' => 1,
194
-                ],
195
-                $this->startDate
196
-            );
197
-        }
198
-
199
-        $this->rewind();
200
-        if (!$this->valid()) {
201
-            throw new NoInstancesException('This recurrence rule does not generate any valid instances');
202
-        }
203
-
204
-    }
205
-
206
-    /**
207
-     * Returns the date for the current position of the iterator.
208
-     *
209
-     * @return DateTimeImmutable
210
-     */
211
-    public function current() {
212
-
213
-        if ($this->currentDate) {
214
-            return clone $this->currentDate;
215
-        }
216
-
217
-    }
218
-
219
-    /**
220
-     * This method returns the start date for the current iteration of the
221
-     * event.
222
-     *
223
-     * @return DateTimeImmutable
224
-     */
225
-    public function getDtStart() {
226
-
227
-        if ($this->currentDate) {
228
-            return clone $this->currentDate;
229
-        }
230
-
231
-    }
232
-
233
-    /**
234
-     * This method returns the end date for the current iteration of the
235
-     * event.
236
-     *
237
-     * @return DateTimeImmutable
238
-     */
239
-    public function getDtEnd() {
240
-
241
-        if (!$this->valid()) {
242
-            return;
243
-        }
244
-        $end = clone $this->currentDate;
245
-        return $end->modify('+' . $this->eventDuration . ' seconds');
246
-
247
-    }
248
-
249
-    /**
250
-     * Returns a VEVENT for the current iterations of the event.
251
-     *
252
-     * This VEVENT will have a recurrence id, and it's DTSTART and DTEND
253
-     * altered.
254
-     *
255
-     * @return VEvent
256
-     */
257
-    public function getEventObject() {
258
-
259
-        if ($this->currentOverriddenEvent) {
260
-            return $this->currentOverriddenEvent;
261
-        }
262
-
263
-        $event = clone $this->masterEvent;
264
-
265
-        // Ignoring the following block, because PHPUnit's code coverage
266
-        // ignores most of these lines, and this messes with our stats.
267
-        //
268
-        // @codeCoverageIgnoreStart
269
-        unset(
270
-            $event->RRULE,
271
-            $event->EXDATE,
272
-            $event->RDATE,
273
-            $event->EXRULE,
274
-            $event->{'RECURRENCE-ID'}
275
-        );
276
-        // @codeCoverageIgnoreEnd
277
-
278
-        $event->DTSTART->setDateTime($this->getDtStart(), $event->DTSTART->isFloating());
279
-        if (isset($event->DTEND)) {
280
-            $event->DTEND->setDateTime($this->getDtEnd(), $event->DTEND->isFloating());
281
-        }
282
-        $recurid = clone $event->DTSTART;
283
-        $recurid->name = 'RECURRENCE-ID';
284
-        $event->add($recurid);
285
-        return $event;
286
-
287
-    }
288
-
289
-    /**
290
-     * Returns the current position of the iterator.
291
-     *
292
-     * This is for us simply a 0-based index.
293
-     *
294
-     * @return int
295
-     */
296
-    public function key() {
297
-
298
-        // The counter is always 1 ahead.
299
-        return $this->counter - 1;
300
-
301
-    }
302
-
303
-    /**
304
-     * This is called after next, to see if the iterator is still at a valid
305
-     * position, or if it's at the end.
306
-     *
307
-     * @return bool
308
-     */
309
-    public function valid() {
310
-
311
-        if ($this->counter > Settings::$maxRecurrences && Settings::$maxRecurrences !== -1) {
312
-            throw new MaxInstancesExceededException('Recurring events are only allowed to generate ' . Settings::$maxRecurrences);
313
-        }
314
-        return !!$this->currentDate;
315
-
316
-    }
317
-
318
-    /**
319
-     * Sets the iterator back to the starting point.
320
-     */
321
-    public function rewind() {
322
-
323
-        $this->recurIterator->rewind();
324
-        // re-creating overridden event index.
325
-        $index = [];
326
-        foreach ($this->overriddenEvents as $key => $event) {
327
-            $stamp = $event->DTSTART->getDateTime($this->timeZone)->getTimeStamp();
328
-            $index[$stamp] = $key;
329
-        }
330
-        krsort($index);
331
-        $this->counter = 0;
332
-        $this->overriddenEventsIndex = $index;
333
-        $this->currentOverriddenEvent = null;
334
-
335
-        $this->nextDate = null;
336
-        $this->currentDate = clone $this->startDate;
337
-
338
-        $this->next();
339
-
340
-    }
341
-
342
-    /**
343
-     * Advances the iterator with one step.
344
-     *
345
-     * @return void
346
-     */
347
-    public function next() {
348
-
349
-        $this->currentOverriddenEvent = null;
350
-        $this->counter++;
351
-        if ($this->nextDate) {
352
-            // We had a stored value.
353
-            $nextDate = $this->nextDate;
354
-            $this->nextDate = null;
355
-        } else {
356
-            // We need to ask rruleparser for the next date.
357
-            // We need to do this until we find a date that's not in the
358
-            // exception list.
359
-            do {
360
-                if (!$this->recurIterator->valid()) {
361
-                    $nextDate = null;
362
-                    break;
363
-                }
364
-                $nextDate = $this->recurIterator->current();
365
-                $this->recurIterator->next();
366
-            } while (isset($this->exceptions[$nextDate->getTimeStamp()]));
367
-
368
-        }
369
-
370
-
371
-        // $nextDate now contains what rrule thinks is the next one, but an
372
-        // overridden event may cut ahead.
373
-        if ($this->overriddenEventsIndex) {
374
-
375
-            $offset = end($this->overriddenEventsIndex);
376
-            $timestamp = key($this->overriddenEventsIndex);
377
-            if (!$nextDate || $timestamp < $nextDate->getTimeStamp()) {
378
-                // Overridden event comes first.
379
-                $this->currentOverriddenEvent = $this->overriddenEvents[$offset];
380
-
381
-                // Putting the rrule next date aside.
382
-                $this->nextDate = $nextDate;
383
-                $this->currentDate = $this->currentOverriddenEvent->DTSTART->getDateTime($this->timeZone);
384
-
385
-                // Ensuring that this item will only be used once.
386
-                array_pop($this->overriddenEventsIndex);
387
-
388
-                // Exit point!
389
-                return;
390
-
391
-            }
392
-
393
-        }
394
-
395
-        $this->currentDate = $nextDate;
396
-
397
-    }
398
-
399
-    /**
400
-     * Quickly jump to a date in the future.
401
-     *
402
-     * @param DateTimeInterface $dateTime
403
-     */
404
-    public function fastForward(DateTimeInterface $dateTime) {
405
-
406
-        while ($this->valid() && $this->getDtEnd() < $dateTime) {
407
-            $this->next();
408
-        }
409
-
410
-    }
411
-
412
-    /**
413
-     * Returns true if this recurring event never ends.
414
-     *
415
-     * @return bool
416
-     */
417
-    public function isInfinite() {
418
-
419
-        return $this->recurIterator->isInfinite();
420
-
421
-    }
422
-
423
-    /**
424
-     * RRULE parser.
425
-     *
426
-     * @var RRuleIterator
427
-     */
428
-    protected $recurIterator;
429
-
430
-    /**
431
-     * The duration, in seconds, of the master event.
432
-     *
433
-     * We use this to calculate the DTEND for subsequent events.
434
-     */
435
-    protected $eventDuration;
436
-
437
-    /**
438
-     * A reference to the main (master) event.
439
-     *
440
-     * @var VEVENT
441
-     */
442
-    protected $masterEvent;
443
-
444
-    /**
445
-     * List of overridden events.
446
-     *
447
-     * @var array
448
-     */
449
-    protected $overriddenEvents = [];
450
-
451
-    /**
452
-     * Overridden event index.
453
-     *
454
-     * Key is timestamp, value is the index of the item in the $overriddenEvent
455
-     * property.
456
-     *
457
-     * @var array
458
-     */
459
-    protected $overriddenEventsIndex;
460
-
461
-    /**
462
-     * A list of recurrence-id's that are either part of EXDATE, or are
463
-     * overridden.
464
-     *
465
-     * @var array
466
-     */
467
-    protected $exceptions = [];
468
-
469
-    /**
470
-     * Internal event counter.
471
-     *
472
-     * @var int
473
-     */
474
-    protected $counter;
475
-
476
-    /**
477
-     * The very start of the iteration process.
478
-     *
479
-     * @var DateTimeImmutable
480
-     */
481
-    protected $startDate;
482
-
483
-    /**
484
-     * Where we are currently in the iteration process.
485
-     *
486
-     * @var DateTimeImmutable
487
-     */
488
-    protected $currentDate;
489
-
490
-    /**
491
-     * The next date from the rrule parser.
492
-     *
493
-     * Sometimes we need to temporary store the next date, because an
494
-     * overridden event came before.
495
-     *
496
-     * @var DateTimeImmutable
497
-     */
498
-    protected $nextDate;
499
-
500
-    /**
501
-     * The event that overwrites the current iteration
502
-     *
503
-     * @var VEVENT
504
-     */
505
-    protected $currentOverriddenEvent;
61
+	/**
62
+	 * Reference timeZone for floating dates and times.
63
+	 *
64
+	 * @var DateTimeZone
65
+	 */
66
+	protected $timeZone;
67
+
68
+	/**
69
+	 * True if we're iterating an all-day event.
70
+	 *
71
+	 * @var bool
72
+	 */
73
+	protected $allDay = false;
74
+
75
+	/**
76
+	 * Creates the iterator.
77
+	 *
78
+	 * There's three ways to set up the iterator.
79
+	 *
80
+	 * 1. You can pass a VCALENDAR component and a UID.
81
+	 * 2. You can pass an array of VEVENTs (all UIDS should match).
82
+	 * 3. You can pass a single VEVENT component.
83
+	 *
84
+	 * Only the second method is recomended. The other 1 and 3 will be removed
85
+	 * at some point in the future.
86
+	 *
87
+	 * The $uid parameter is only required for the first method.
88
+	 *
89
+	 * @param Component|array $input
90
+	 * @param string|null $uid
91
+	 * @param DateTimeZone $timeZone Reference timezone for floating dates and
92
+	 *                               times.
93
+	 */
94
+	public function __construct($input, $uid = null, DateTimeZone $timeZone = null) {
95
+
96
+		if (is_null($timeZone)) {
97
+			$timeZone = new DateTimeZone('UTC');
98
+		}
99
+		$this->timeZone = $timeZone;
100
+
101
+		if (is_array($input)) {
102
+			$events = $input;
103
+		} elseif ($input instanceof VEvent) {
104
+			// Single instance mode.
105
+			$events = [$input];
106
+		} else {
107
+			// Calendar + UID mode.
108
+			$uid = (string)$uid;
109
+			if (!$uid) {
110
+				throw new InvalidArgumentException('The UID argument is required when a VCALENDAR is passed to this constructor');
111
+			}
112
+			if (!isset($input->VEVENT)) {
113
+				throw new InvalidArgumentException('No events found in this calendar');
114
+			}
115
+			$events = $input->getByUID($uid);
116
+
117
+		}
118
+
119
+		foreach ($events as $vevent) {
120
+
121
+			if (!isset($vevent->{'RECURRENCE-ID'})) {
122
+
123
+				$this->masterEvent = $vevent;
124
+
125
+			} else {
126
+
127
+				$this->exceptions[
128
+					$vevent->{'RECURRENCE-ID'}->getDateTime($this->timeZone)->getTimeStamp()
129
+				] = true;
130
+				$this->overriddenEvents[] = $vevent;
131
+
132
+			}
133
+
134
+		}
135
+
136
+		if (!$this->masterEvent) {
137
+			// No base event was found. CalDAV does allow cases where only
138
+			// overridden instances are stored.
139
+			//
140
+			// In this particular case, we're just going to grab the first
141
+			// event and use that instead. This may not always give the
142
+			// desired result.
143
+			if (!count($this->overriddenEvents)) {
144
+				throw new InvalidArgumentException('This VCALENDAR did not have an event with UID: ' . $uid);
145
+			}
146
+			$this->masterEvent = array_shift($this->overriddenEvents);
147
+		}
148
+
149
+		$this->startDate = $this->masterEvent->DTSTART->getDateTime($this->timeZone);
150
+		$this->allDay = !$this->masterEvent->DTSTART->hasTime();
151
+
152
+		if (isset($this->masterEvent->EXDATE)) {
153
+
154
+			foreach ($this->masterEvent->EXDATE as $exDate) {
155
+
156
+				foreach ($exDate->getDateTimes($this->timeZone) as $dt) {
157
+					$this->exceptions[$dt->getTimeStamp()] = true;
158
+				}
159
+
160
+			}
161
+
162
+		}
163
+
164
+		if (isset($this->masterEvent->DTEND)) {
165
+			$this->eventDuration =
166
+				$this->masterEvent->DTEND->getDateTime($this->timeZone)->getTimeStamp() -
167
+				$this->startDate->getTimeStamp();
168
+		} elseif (isset($this->masterEvent->DURATION)) {
169
+			$duration = $this->masterEvent->DURATION->getDateInterval();
170
+			$end = clone $this->startDate;
171
+			$end = $end->add($duration);
172
+			$this->eventDuration = $end->getTimeStamp() - $this->startDate->getTimeStamp();
173
+		} elseif ($this->allDay) {
174
+			$this->eventDuration = 3600 * 24;
175
+		} else {
176
+			$this->eventDuration = 0;
177
+		}
178
+
179
+		if (isset($this->masterEvent->RDATE)) {
180
+			$this->recurIterator = new RDateIterator(
181
+				$this->masterEvent->RDATE->getParts(),
182
+				$this->startDate
183
+			);
184
+		} elseif (isset($this->masterEvent->RRULE)) {
185
+			$this->recurIterator = new RRuleIterator(
186
+				$this->masterEvent->RRULE->getParts(),
187
+				$this->startDate
188
+			);
189
+		} else {
190
+			$this->recurIterator = new RRuleIterator(
191
+				[
192
+					'FREQ'  => 'DAILY',
193
+					'COUNT' => 1,
194
+				],
195
+				$this->startDate
196
+			);
197
+		}
198
+
199
+		$this->rewind();
200
+		if (!$this->valid()) {
201
+			throw new NoInstancesException('This recurrence rule does not generate any valid instances');
202
+		}
203
+
204
+	}
205
+
206
+	/**
207
+	 * Returns the date for the current position of the iterator.
208
+	 *
209
+	 * @return DateTimeImmutable
210
+	 */
211
+	public function current() {
212
+
213
+		if ($this->currentDate) {
214
+			return clone $this->currentDate;
215
+		}
216
+
217
+	}
218
+
219
+	/**
220
+	 * This method returns the start date for the current iteration of the
221
+	 * event.
222
+	 *
223
+	 * @return DateTimeImmutable
224
+	 */
225
+	public function getDtStart() {
226
+
227
+		if ($this->currentDate) {
228
+			return clone $this->currentDate;
229
+		}
230
+
231
+	}
232
+
233
+	/**
234
+	 * This method returns the end date for the current iteration of the
235
+	 * event.
236
+	 *
237
+	 * @return DateTimeImmutable
238
+	 */
239
+	public function getDtEnd() {
240
+
241
+		if (!$this->valid()) {
242
+			return;
243
+		}
244
+		$end = clone $this->currentDate;
245
+		return $end->modify('+' . $this->eventDuration . ' seconds');
246
+
247
+	}
248
+
249
+	/**
250
+	 * Returns a VEVENT for the current iterations of the event.
251
+	 *
252
+	 * This VEVENT will have a recurrence id, and it's DTSTART and DTEND
253
+	 * altered.
254
+	 *
255
+	 * @return VEvent
256
+	 */
257
+	public function getEventObject() {
258
+
259
+		if ($this->currentOverriddenEvent) {
260
+			return $this->currentOverriddenEvent;
261
+		}
262
+
263
+		$event = clone $this->masterEvent;
264
+
265
+		// Ignoring the following block, because PHPUnit's code coverage
266
+		// ignores most of these lines, and this messes with our stats.
267
+		//
268
+		// @codeCoverageIgnoreStart
269
+		unset(
270
+			$event->RRULE,
271
+			$event->EXDATE,
272
+			$event->RDATE,
273
+			$event->EXRULE,
274
+			$event->{'RECURRENCE-ID'}
275
+		);
276
+		// @codeCoverageIgnoreEnd
277
+
278
+		$event->DTSTART->setDateTime($this->getDtStart(), $event->DTSTART->isFloating());
279
+		if (isset($event->DTEND)) {
280
+			$event->DTEND->setDateTime($this->getDtEnd(), $event->DTEND->isFloating());
281
+		}
282
+		$recurid = clone $event->DTSTART;
283
+		$recurid->name = 'RECURRENCE-ID';
284
+		$event->add($recurid);
285
+		return $event;
286
+
287
+	}
288
+
289
+	/**
290
+	 * Returns the current position of the iterator.
291
+	 *
292
+	 * This is for us simply a 0-based index.
293
+	 *
294
+	 * @return int
295
+	 */
296
+	public function key() {
297
+
298
+		// The counter is always 1 ahead.
299
+		return $this->counter - 1;
300
+
301
+	}
302
+
303
+	/**
304
+	 * This is called after next, to see if the iterator is still at a valid
305
+	 * position, or if it's at the end.
306
+	 *
307
+	 * @return bool
308
+	 */
309
+	public function valid() {
310
+
311
+		if ($this->counter > Settings::$maxRecurrences && Settings::$maxRecurrences !== -1) {
312
+			throw new MaxInstancesExceededException('Recurring events are only allowed to generate ' . Settings::$maxRecurrences);
313
+		}
314
+		return !!$this->currentDate;
315
+
316
+	}
317
+
318
+	/**
319
+	 * Sets the iterator back to the starting point.
320
+	 */
321
+	public function rewind() {
322
+
323
+		$this->recurIterator->rewind();
324
+		// re-creating overridden event index.
325
+		$index = [];
326
+		foreach ($this->overriddenEvents as $key => $event) {
327
+			$stamp = $event->DTSTART->getDateTime($this->timeZone)->getTimeStamp();
328
+			$index[$stamp] = $key;
329
+		}
330
+		krsort($index);
331
+		$this->counter = 0;
332
+		$this->overriddenEventsIndex = $index;
333
+		$this->currentOverriddenEvent = null;
334
+
335
+		$this->nextDate = null;
336
+		$this->currentDate = clone $this->startDate;
337
+
338
+		$this->next();
339
+
340
+	}
341
+
342
+	/**
343
+	 * Advances the iterator with one step.
344
+	 *
345
+	 * @return void
346
+	 */
347
+	public function next() {
348
+
349
+		$this->currentOverriddenEvent = null;
350
+		$this->counter++;
351
+		if ($this->nextDate) {
352
+			// We had a stored value.
353
+			$nextDate = $this->nextDate;
354
+			$this->nextDate = null;
355
+		} else {
356
+			// We need to ask rruleparser for the next date.
357
+			// We need to do this until we find a date that's not in the
358
+			// exception list.
359
+			do {
360
+				if (!$this->recurIterator->valid()) {
361
+					$nextDate = null;
362
+					break;
363
+				}
364
+				$nextDate = $this->recurIterator->current();
365
+				$this->recurIterator->next();
366
+			} while (isset($this->exceptions[$nextDate->getTimeStamp()]));
367
+
368
+		}
369
+
370
+
371
+		// $nextDate now contains what rrule thinks is the next one, but an
372
+		// overridden event may cut ahead.
373
+		if ($this->overriddenEventsIndex) {
374
+
375
+			$offset = end($this->overriddenEventsIndex);
376
+			$timestamp = key($this->overriddenEventsIndex);
377
+			if (!$nextDate || $timestamp < $nextDate->getTimeStamp()) {
378
+				// Overridden event comes first.
379
+				$this->currentOverriddenEvent = $this->overriddenEvents[$offset];
380
+
381
+				// Putting the rrule next date aside.
382
+				$this->nextDate = $nextDate;
383
+				$this->currentDate = $this->currentOverriddenEvent->DTSTART->getDateTime($this->timeZone);
384
+
385
+				// Ensuring that this item will only be used once.
386
+				array_pop($this->overriddenEventsIndex);
387
+
388
+				// Exit point!
389
+				return;
390
+
391
+			}
392
+
393
+		}
394
+
395
+		$this->currentDate = $nextDate;
396
+
397
+	}
398
+
399
+	/**
400
+	 * Quickly jump to a date in the future.
401
+	 *
402
+	 * @param DateTimeInterface $dateTime
403
+	 */
404
+	public function fastForward(DateTimeInterface $dateTime) {
405
+
406
+		while ($this->valid() && $this->getDtEnd() < $dateTime) {
407
+			$this->next();
408
+		}
409
+
410
+	}
411
+
412
+	/**
413
+	 * Returns true if this recurring event never ends.
414
+	 *
415
+	 * @return bool
416
+	 */
417
+	public function isInfinite() {
418
+
419
+		return $this->recurIterator->isInfinite();
420
+
421
+	}
422
+
423
+	/**
424
+	 * RRULE parser.
425
+	 *
426
+	 * @var RRuleIterator
427
+	 */
428
+	protected $recurIterator;
429
+
430
+	/**
431
+	 * The duration, in seconds, of the master event.
432
+	 *
433
+	 * We use this to calculate the DTEND for subsequent events.
434
+	 */
435
+	protected $eventDuration;
436
+
437
+	/**
438
+	 * A reference to the main (master) event.
439
+	 *
440
+	 * @var VEVENT
441
+	 */
442
+	protected $masterEvent;
443
+
444
+	/**
445
+	 * List of overridden events.
446
+	 *
447
+	 * @var array
448
+	 */
449
+	protected $overriddenEvents = [];
450
+
451
+	/**
452
+	 * Overridden event index.
453
+	 *
454
+	 * Key is timestamp, value is the index of the item in the $overriddenEvent
455
+	 * property.
456
+	 *
457
+	 * @var array
458
+	 */
459
+	protected $overriddenEventsIndex;
460
+
461
+	/**
462
+	 * A list of recurrence-id's that are either part of EXDATE, or are
463
+	 * overridden.
464
+	 *
465
+	 * @var array
466
+	 */
467
+	protected $exceptions = [];
468
+
469
+	/**
470
+	 * Internal event counter.
471
+	 *
472
+	 * @var int
473
+	 */
474
+	protected $counter;
475
+
476
+	/**
477
+	 * The very start of the iteration process.
478
+	 *
479
+	 * @var DateTimeImmutable
480
+	 */
481
+	protected $startDate;
482
+
483
+	/**
484
+	 * Where we are currently in the iteration process.
485
+	 *
486
+	 * @var DateTimeImmutable
487
+	 */
488
+	protected $currentDate;
489
+
490
+	/**
491
+	 * The next date from the rrule parser.
492
+	 *
493
+	 * Sometimes we need to temporary store the next date, because an
494
+	 * overridden event came before.
495
+	 *
496
+	 * @var DateTimeImmutable
497
+	 */
498
+	protected $nextDate;
499
+
500
+	/**
501
+	 * The event that overwrites the current iteration
502
+	 *
503
+	 * @var VEVENT
504
+	 */
505
+	protected $currentOverriddenEvent;
506 506
 
507 507
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Recur/RDateIterator.php 3 patches
Doc Comments   -1 removed lines patch added patch discarded remove patch
@@ -158,7 +158,6 @@
 block discarded – undo
158 158
      * This method receives a string from an RRULE property, and populates this
159 159
      * class with all the values.
160 160
      *
161
-     * @param string|array $rrule
162 161
      *
163 162
      * @return void
164 163
      */
Please login to merge, or discard this patch.
Indentation   +155 added lines, -155 removed lines patch added patch discarded remove patch
@@ -21,162 +21,162 @@
 block discarded – undo
21 21
  */
22 22
 class RDateIterator implements Iterator {
23 23
 
24
-    /**
25
-     * Creates the Iterator.
26
-     *
27
-     * @param string|array $rrule
28
-     * @param DateTimeInterface $start
29
-     */
30
-    public function __construct($rrule, DateTimeInterface $start) {
24
+	/**
25
+	 * Creates the Iterator.
26
+	 *
27
+	 * @param string|array $rrule
28
+	 * @param DateTimeInterface $start
29
+	 */
30
+	public function __construct($rrule, DateTimeInterface $start) {
31 31
 
32
-        $this->startDate = $start;
33
-        $this->parseRDate($rrule);
34
-        $this->currentDate = clone $this->startDate;
35
-
36
-    }
32
+		$this->startDate = $start;
33
+		$this->parseRDate($rrule);
34
+		$this->currentDate = clone $this->startDate;
35
+
36
+	}
37 37
 
38
-    /* Implementation of the Iterator interface {{{ */
39
-
40
-    public function current() {
41
-
42
-        if (!$this->valid()) return;
43
-        return clone $this->currentDate;
44
-
45
-    }
46
-
47
-    /**
48
-     * Returns the current item number.
49
-     *
50
-     * @return int
51
-     */
52
-    public function key() {
53
-
54
-        return $this->counter;
55
-
56
-    }
57
-
58
-    /**
59
-     * Returns whether the current item is a valid item for the recurrence
60
-     * iterator.
61
-     *
62
-     * @return bool
63
-     */
64
-    public function valid() {
65
-
66
-        return ($this->counter <= count($this->dates));
67
-
68
-    }
69
-
70
-    /**
71
-     * Resets the iterator.
72
-     *
73
-     * @return void
74
-     */
75
-    public function rewind() {
76
-
77
-        $this->currentDate = clone $this->startDate;
78
-        $this->counter = 0;
79
-
80
-    }
81
-
82
-    /**
83
-     * Goes on to the next iteration.
84
-     *
85
-     * @return void
86
-     */
87
-    public function next() {
88
-
89
-        $this->counter++;
90
-        if (!$this->valid()) return;
91
-
92
-        $this->currentDate =
93
-            DateTimeParser::parse(
94
-                $this->dates[$this->counter - 1],
95
-                $this->startDate->getTimezone()
96
-            );
97
-
98
-    }
99
-
100
-    /* End of Iterator implementation }}} */
101
-
102
-    /**
103
-     * Returns true if this recurring event never ends.
104
-     *
105
-     * @return bool
106
-     */
107
-    public function isInfinite() {
108
-
109
-        return false;
110
-
111
-    }
112
-
113
-    /**
114
-     * This method allows you to quickly go to the next occurrence after the
115
-     * specified date.
116
-     *
117
-     * @param DateTimeInterface $dt
118
-     *
119
-     * @return void
120
-     */
121
-    public function fastForward(DateTimeInterface $dt) {
122
-
123
-        while ($this->valid() && $this->currentDate < $dt) {
124
-            $this->next();
125
-        }
126
-
127
-    }
128
-
129
-    /**
130
-     * The reference start date/time for the rrule.
131
-     *
132
-     * All calculations are based on this initial date.
133
-     *
134
-     * @var DateTimeInterface
135
-     */
136
-    protected $startDate;
137
-
138
-    /**
139
-     * The date of the current iteration. You can get this by calling
140
-     * ->current().
141
-     *
142
-     * @var DateTimeInterface
143
-     */
144
-    protected $currentDate;
145
-
146
-    /**
147
-     * The current item in the list.
148
-     *
149
-     * You can get this number with the key() method.
150
-     *
151
-     * @var int
152
-     */
153
-    protected $counter = 0;
154
-
155
-    /* }}} */
156
-
157
-    /**
158
-     * This method receives a string from an RRULE property, and populates this
159
-     * class with all the values.
160
-     *
161
-     * @param string|array $rrule
162
-     *
163
-     * @return void
164
-     */
165
-    protected function parseRDate($rdate) {
166
-
167
-        if (is_string($rdate)) {
168
-            $rdate = explode(',', $rdate);
169
-        }
170
-
171
-        $this->dates = $rdate;
172
-
173
-    }
174
-
175
-    /**
176
-     * Array with the RRULE dates
177
-     *
178
-     * @var array
179
-     */
180
-    protected $dates = [];
38
+	/* Implementation of the Iterator interface {{{ */
39
+
40
+	public function current() {
41
+
42
+		if (!$this->valid()) return;
43
+		return clone $this->currentDate;
44
+
45
+	}
46
+
47
+	/**
48
+	 * Returns the current item number.
49
+	 *
50
+	 * @return int
51
+	 */
52
+	public function key() {
53
+
54
+		return $this->counter;
55
+
56
+	}
57
+
58
+	/**
59
+	 * Returns whether the current item is a valid item for the recurrence
60
+	 * iterator.
61
+	 *
62
+	 * @return bool
63
+	 */
64
+	public function valid() {
65
+
66
+		return ($this->counter <= count($this->dates));
67
+
68
+	}
69
+
70
+	/**
71
+	 * Resets the iterator.
72
+	 *
73
+	 * @return void
74
+	 */
75
+	public function rewind() {
76
+
77
+		$this->currentDate = clone $this->startDate;
78
+		$this->counter = 0;
79
+
80
+	}
81
+
82
+	/**
83
+	 * Goes on to the next iteration.
84
+	 *
85
+	 * @return void
86
+	 */
87
+	public function next() {
88
+
89
+		$this->counter++;
90
+		if (!$this->valid()) return;
91
+
92
+		$this->currentDate =
93
+			DateTimeParser::parse(
94
+				$this->dates[$this->counter - 1],
95
+				$this->startDate->getTimezone()
96
+			);
97
+
98
+	}
99
+
100
+	/* End of Iterator implementation }}} */
101
+
102
+	/**
103
+	 * Returns true if this recurring event never ends.
104
+	 *
105
+	 * @return bool
106
+	 */
107
+	public function isInfinite() {
108
+
109
+		return false;
110
+
111
+	}
112
+
113
+	/**
114
+	 * This method allows you to quickly go to the next occurrence after the
115
+	 * specified date.
116
+	 *
117
+	 * @param DateTimeInterface $dt
118
+	 *
119
+	 * @return void
120
+	 */
121
+	public function fastForward(DateTimeInterface $dt) {
122
+
123
+		while ($this->valid() && $this->currentDate < $dt) {
124
+			$this->next();
125
+		}
126
+
127
+	}
128
+
129
+	/**
130
+	 * The reference start date/time for the rrule.
131
+	 *
132
+	 * All calculations are based on this initial date.
133
+	 *
134
+	 * @var DateTimeInterface
135
+	 */
136
+	protected $startDate;
137
+
138
+	/**
139
+	 * The date of the current iteration. You can get this by calling
140
+	 * ->current().
141
+	 *
142
+	 * @var DateTimeInterface
143
+	 */
144
+	protected $currentDate;
145
+
146
+	/**
147
+	 * The current item in the list.
148
+	 *
149
+	 * You can get this number with the key() method.
150
+	 *
151
+	 * @var int
152
+	 */
153
+	protected $counter = 0;
154
+
155
+	/* }}} */
156
+
157
+	/**
158
+	 * This method receives a string from an RRULE property, and populates this
159
+	 * class with all the values.
160
+	 *
161
+	 * @param string|array $rrule
162
+	 *
163
+	 * @return void
164
+	 */
165
+	protected function parseRDate($rdate) {
166
+
167
+		if (is_string($rdate)) {
168
+			$rdate = explode(',', $rdate);
169
+		}
170
+
171
+		$this->dates = $rdate;
172
+
173
+	}
174
+
175
+	/**
176
+	 * Array with the RRULE dates
177
+	 *
178
+	 * @var array
179
+	 */
180
+	protected $dates = [];
181 181
 
182 182
 }
Please login to merge, or discard this patch.
Braces   +6 added lines, -2 removed lines patch added patch discarded remove patch
@@ -39,7 +39,9 @@  discard block
 block discarded – undo
39 39
 
40 40
     public function current() {
41 41
 
42
-        if (!$this->valid()) return;
42
+        if (!$this->valid()) {
43
+        	return;
44
+        }
43 45
         return clone $this->currentDate;
44 46
 
45 47
     }
@@ -87,7 +89,9 @@  discard block
 block discarded – undo
87 89
     public function next() {
88 90
 
89 91
         $this->counter++;
90
-        if (!$this->valid()) return;
92
+        if (!$this->valid()) {
93
+        	return;
94
+        }
91 95
 
92 96
         $this->currentDate =
93 97
             DateTimeParser::parse(
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Recur/RRuleIterator.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\Recur;
4 4
 
5
-use DateTimeInterface;
6 5
 use DateTimeImmutable;
6
+use DateTimeInterface;
7 7
 use Iterator;
8 8
 use Sabre\VObject\DateTimeParser;
9 9
 use Sabre\VObject\InvalidDataException;
Please login to merge, or discard this patch.
Spacing   +27 added lines, -27 removed lines patch added patch discarded remove patch
@@ -374,8 +374,8 @@  discard block
 block discarded – undo
374 374
             $currentHour = $this->currentDate->format('G');
375 375
 
376 376
         } while (
377
-            ($this->byDay   && !in_array($currentDay, $recurrenceDays)) ||
378
-            ($this->byHour  && !in_array($currentHour, $recurrenceHours)) ||
377
+            ($this->byDay && !in_array($currentDay, $recurrenceDays)) ||
378
+            ($this->byHour && !in_array($currentHour, $recurrenceHours)) ||
379 379
             ($this->byMonth && !in_array($currentMonth, $recurrenceMonths))
380 380
         );
381 381
 
@@ -413,10 +413,10 @@  discard block
 block discarded – undo
413 413
             }
414 414
 
415 415
             // Current day of the week
416
-            $currentDay = (int)$this->currentDate->format('w');
416
+            $currentDay = (int) $this->currentDate->format('w');
417 417
 
418 418
             // Current hour of the day
419
-            $currentHour = (int)$this->currentDate->format('G');
419
+            $currentHour = (int) $this->currentDate->format('G');
420 420
 
421 421
             // We need to roll over to the next week
422 422
             if ($currentDay === $firstDay && (!$this->byHour || $currentHour == '0')) {
@@ -491,9 +491,9 @@  discard block
 block discarded – undo
491 491
         }
492 492
 
493 493
         $this->currentDate = $this->currentDate->setDate(
494
-            (int)$this->currentDate->format('Y'),
495
-            (int)$this->currentDate->format('n'),
496
-            (int)$occurrence
494
+            (int) $this->currentDate->format('Y'),
495
+            (int) $this->currentDate->format('n'),
496
+            (int) $occurrence
497 497
         );
498 498
 
499 499
     }
@@ -582,18 +582,18 @@  discard block
 block discarded – undo
582 582
                 } while (!in_array($currentMonth, $this->byMonth));
583 583
 
584 584
                 $this->currentDate = $this->currentDate->setDate(
585
-                    (int)$currentYear,
586
-                    (int)$currentMonth,
587
-                    (int)$currentDayOfMonth
585
+                    (int) $currentYear,
586
+                    (int) $currentMonth,
587
+                    (int) $currentDayOfMonth
588 588
                 );
589 589
 
590 590
             }
591 591
 
592 592
             // If we made it here, it means we got a valid occurrence
593 593
             $this->currentDate = $this->currentDate->setDate(
594
-                (int)$currentYear,
595
-                (int)$currentMonth,
596
-                (int)$occurrence
594
+                (int) $currentYear,
595
+                (int) $currentMonth,
596
+                (int) $occurrence
597 597
             );
598 598
             return;
599 599
 
@@ -610,9 +610,9 @@  discard block
 block discarded – undo
610 610
                 }
611 611
             } while (!in_array($currentMonth, $this->byMonth));
612 612
             $this->currentDate = $this->currentDate->setDate(
613
-                (int)$currentYear,
614
-                (int)$currentMonth,
615
-                (int)$currentDayOfMonth
613
+                (int) $currentYear,
614
+                (int) $currentMonth,
615
+                (int) $currentDayOfMonth
616 616
             );
617 617
 
618 618
             return;
@@ -673,7 +673,7 @@  discard block
 block discarded – undo
673 673
                     // No break
674 674
 
675 675
                 case 'COUNT' :
676
-                    $val = (int)$value;
676
+                    $val = (int) $value;
677 677
                     if ($val < 1) {
678 678
                         throw new InvalidDataException(strtoupper($key) . ' in RRULE must be a positive integer!');
679 679
                     }
@@ -682,19 +682,19 @@  discard block
 block discarded – undo
682 682
                     break;
683 683
 
684 684
                 case 'BYSECOND' :
685
-                    $this->bySecond = (array)$value;
685
+                    $this->bySecond = (array) $value;
686 686
                     break;
687 687
 
688 688
                 case 'BYMINUTE' :
689
-                    $this->byMinute = (array)$value;
689
+                    $this->byMinute = (array) $value;
690 690
                     break;
691 691
 
692 692
                 case 'BYHOUR' :
693
-                    $this->byHour = (array)$value;
693
+                    $this->byHour = (array) $value;
694 694
                     break;
695 695
 
696 696
                 case 'BYDAY' :
697
-                    $value = (array)$value;
697
+                    $value = (array) $value;
698 698
                     foreach ($value as $part) {
699 699
                         if (!preg_match('#^  (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', $part)) {
700 700
                             throw new InvalidDataException('Invalid part in BYDAY clause: ' . $part);
@@ -704,23 +704,23 @@  discard block
 block discarded – undo
704 704
                     break;
705 705
 
706 706
                 case 'BYMONTHDAY' :
707
-                    $this->byMonthDay = (array)$value;
707
+                    $this->byMonthDay = (array) $value;
708 708
                     break;
709 709
 
710 710
                 case 'BYYEARDAY' :
711
-                    $this->byYearDay = (array)$value;
711
+                    $this->byYearDay = (array) $value;
712 712
                     break;
713 713
 
714 714
                 case 'BYWEEKNO' :
715
-                    $this->byWeekNo = (array)$value;
715
+                    $this->byWeekNo = (array) $value;
716 716
                     break;
717 717
 
718 718
                 case 'BYMONTH' :
719
-                    $this->byMonth = (array)$value;
719
+                    $this->byMonth = (array) $value;
720 720
                     break;
721 721
 
722 722
                 case 'BYSETPOS' :
723
-                    $this->bySetPos = (array)$value;
723
+                    $this->bySetPos = (array) $value;
724 724
                     break;
725 725
 
726 726
                 case 'WKST' :
@@ -792,7 +792,7 @@  discard block
 block discarded – undo
792 792
             // possible that the user only really wanted the 1st, 2nd or last
793 793
             // wednesday.
794 794
             if (strlen($day) > 2) {
795
-                $offset = (int)substr($day, 0, -2);
795
+                $offset = (int) substr($day, 0, -2);
796 796
 
797 797
                 if ($offset > 0) {
798 798
                     // It is possible that the day does not exist, such as a
Please login to merge, or discard this patch.
Indentation   +885 added lines, -885 removed lines patch added patch discarded remove patch
@@ -24,897 +24,897 @@
 block discarded – undo
24 24
  */
25 25
 class RRuleIterator implements Iterator {
26 26
 
27
-    /**
28
-     * Creates the Iterator.
29
-     *
30
-     * @param string|array $rrule
31
-     * @param DateTimeInterface $start
32
-     */
33
-    public function __construct($rrule, DateTimeInterface $start) {
27
+	/**
28
+	 * Creates the Iterator.
29
+	 *
30
+	 * @param string|array $rrule
31
+	 * @param DateTimeInterface $start
32
+	 */
33
+	public function __construct($rrule, DateTimeInterface $start) {
34 34
 
35
-        $this->startDate = $start;
36
-        $this->parseRRule($rrule);
37
-        $this->currentDate = clone $this->startDate;
35
+		$this->startDate = $start;
36
+		$this->parseRRule($rrule);
37
+		$this->currentDate = clone $this->startDate;
38 38
 
39
-    }
39
+	}
40 40
 
41
-    /* Implementation of the Iterator interface {{{ */
41
+	/* Implementation of the Iterator interface {{{ */
42 42
 
43
-    public function current() {
43
+	public function current() {
44 44
 
45
-        if (!$this->valid()) return;
46
-        return clone $this->currentDate;
45
+		if (!$this->valid()) return;
46
+		return clone $this->currentDate;
47 47
 
48
-    }
48
+	}
49 49
 
50
-    /**
51
-     * Returns the current item number.
52
-     *
53
-     * @return int
54
-     */
55
-    public function key() {
50
+	/**
51
+	 * Returns the current item number.
52
+	 *
53
+	 * @return int
54
+	 */
55
+	public function key() {
56 56
 
57
-        return $this->counter;
58
-
59
-    }
60
-
61
-    /**
62
-     * Returns whether the current item is a valid item for the recurrence
63
-     * iterator. This will return false if we've gone beyond the UNTIL or COUNT
64
-     * statements.
65
-     *
66
-     * @return bool
67
-     */
68
-    public function valid() {
69
-
70
-        if (!is_null($this->count)) {
71
-            return $this->counter < $this->count;
72
-        }
73
-        return is_null($this->until) || $this->currentDate <= $this->until;
74
-
75
-    }
76
-
77
-    /**
78
-     * Resets the iterator.
79
-     *
80
-     * @return void
81
-     */
82
-    public function rewind() {
83
-
84
-        $this->currentDate = clone $this->startDate;
85
-        $this->counter = 0;
86
-
87
-    }
88
-
89
-    /**
90
-     * Goes on to the next iteration.
91
-     *
92
-     * @return void
93
-     */
94
-    public function next() {
95
-
96
-        // Otherwise, we find the next event in the normal RRULE
97
-        // sequence.
98
-        switch ($this->frequency) {
99
-
100
-            case 'hourly' :
101
-                $this->nextHourly();
102
-                break;
103
-
104
-            case 'daily' :
105
-                $this->nextDaily();
106
-                break;
107
-
108
-            case 'weekly' :
109
-                $this->nextWeekly();
110
-                break;
111
-
112
-            case 'monthly' :
113
-                $this->nextMonthly();
114
-                break;
115
-
116
-            case 'yearly' :
117
-                $this->nextYearly();
118
-                break;
119
-
120
-        }
121
-        $this->counter++;
122
-
123
-    }
124
-
125
-    /* End of Iterator implementation }}} */
126
-
127
-    /**
128
-     * Returns true if this recurring event never ends.
129
-     *
130
-     * @return bool
131
-     */
132
-    public function isInfinite() {
133
-
134
-        return !$this->count && !$this->until;
135
-
136
-    }
137
-
138
-    /**
139
-     * This method allows you to quickly go to the next occurrence after the
140
-     * specified date.
141
-     *
142
-     * @param DateTimeInterface $dt
143
-     *
144
-     * @return void
145
-     */
146
-    public function fastForward(DateTimeInterface $dt) {
147
-
148
-        while ($this->valid() && $this->currentDate < $dt) {
149
-            $this->next();
150
-        }
151
-
152
-    }
153
-
154
-    /**
155
-     * The reference start date/time for the rrule.
156
-     *
157
-     * All calculations are based on this initial date.
158
-     *
159
-     * @var DateTimeInterface
160
-     */
161
-    protected $startDate;
162
-
163
-    /**
164
-     * The date of the current iteration. You can get this by calling
165
-     * ->current().
166
-     *
167
-     * @var DateTimeInterface
168
-     */
169
-    protected $currentDate;
170
-
171
-    /**
172
-     * Frequency is one of: secondly, minutely, hourly, daily, weekly, monthly,
173
-     * yearly.
174
-     *
175
-     * @var string
176
-     */
177
-    protected $frequency;
178
-
179
-    /**
180
-     * The number of recurrences, or 'null' if infinitely recurring.
181
-     *
182
-     * @var int
183
-     */
184
-    protected $count;
185
-
186
-    /**
187
-     * The interval.
188
-     *
189
-     * If for example frequency is set to daily, interval = 2 would mean every
190
-     * 2 days.
191
-     *
192
-     * @var int
193
-     */
194
-    protected $interval = 1;
195
-
196
-    /**
197
-     * The last instance of this recurrence, inclusively.
198
-     *
199
-     * @var DateTimeInterface|null
200
-     */
201
-    protected $until;
202
-
203
-    /**
204
-     * Which seconds to recur.
205
-     *
206
-     * This is an array of integers (between 0 and 60)
207
-     *
208
-     * @var array
209
-     */
210
-    protected $bySecond;
211
-
212
-    /**
213
-     * Which minutes to recur.
214
-     *
215
-     * This is an array of integers (between 0 and 59)
216
-     *
217
-     * @var array
218
-     */
219
-    protected $byMinute;
220
-
221
-    /**
222
-     * Which hours to recur.
223
-     *
224
-     * This is an array of integers (between 0 and 23)
225
-     *
226
-     * @var array
227
-     */
228
-    protected $byHour;
229
-
230
-    /**
231
-     * The current item in the list.
232
-     *
233
-     * You can get this number with the key() method.
234
-     *
235
-     * @var int
236
-     */
237
-    protected $counter = 0;
238
-
239
-    /**
240
-     * Which weekdays to recur.
241
-     *
242
-     * This is an array of weekdays
243
-     *
244
-     * This may also be preceeded by a positive or negative integer. If present,
245
-     * this indicates the nth occurrence of a specific day within the monthly or
246
-     * yearly rrule. For instance, -2TU indicates the second-last tuesday of
247
-     * the month, or year.
248
-     *
249
-     * @var array
250
-     */
251
-    protected $byDay;
252
-
253
-    /**
254
-     * Which days of the month to recur.
255
-     *
256
-     * This is an array of days of the months (1-31). The value can also be
257
-     * negative. -5 for instance means the 5th last day of the month.
258
-     *
259
-     * @var array
260
-     */
261
-    protected $byMonthDay;
262
-
263
-    /**
264
-     * Which days of the year to recur.
265
-     *
266
-     * This is an array with days of the year (1 to 366). The values can also
267
-     * be negative. For instance, -1 will always represent the last day of the
268
-     * year. (December 31st).
269
-     *
270
-     * @var array
271
-     */
272
-    protected $byYearDay;
273
-
274
-    /**
275
-     * Which week numbers to recur.
276
-     *
277
-     * This is an array of integers from 1 to 53. The values can also be
278
-     * negative. -1 will always refer to the last week of the year.
279
-     *
280
-     * @var array
281
-     */
282
-    protected $byWeekNo;
283
-
284
-    /**
285
-     * Which months to recur.
286
-     *
287
-     * This is an array of integers from 1 to 12.
288
-     *
289
-     * @var array
290
-     */
291
-    protected $byMonth;
292
-
293
-    /**
294
-     * Which items in an existing st to recur.
295
-     *
296
-     * These numbers work together with an existing by* rule. It specifies
297
-     * exactly which items of the existing by-rule to filter.
298
-     *
299
-     * Valid values are 1 to 366 and -1 to -366. As an example, this can be
300
-     * used to recur the last workday of the month.
301
-     *
302
-     * This would be done by setting frequency to 'monthly', byDay to
303
-     * 'MO,TU,WE,TH,FR' and bySetPos to -1.
304
-     *
305
-     * @var array
306
-     */
307
-    protected $bySetPos;
308
-
309
-    /**
310
-     * When the week starts.
311
-     *
312
-     * @var string
313
-     */
314
-    protected $weekStart = 'MO';
315
-
316
-    /* Functions that advance the iterator {{{ */
317
-
318
-    /**
319
-     * Does the processing for advancing the iterator for hourly frequency.
320
-     *
321
-     * @return void
322
-     */
323
-    protected function nextHourly() {
324
-
325
-        $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' hours');
326
-
327
-    }
328
-
329
-    /**
330
-     * Does the processing for advancing the iterator for daily frequency.
331
-     *
332
-     * @return void
333
-     */
334
-    protected function nextDaily() {
335
-
336
-        if (!$this->byHour && !$this->byDay) {
337
-            $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' days');
338
-            return;
339
-        }
340
-
341
-        if (!empty($this->byHour)) {
342
-            $recurrenceHours = $this->getHours();
343
-        }
344
-
345
-        if (!empty($this->byDay)) {
346
-            $recurrenceDays = $this->getDays();
347
-        }
348
-
349
-        if (!empty($this->byMonth)) {
350
-            $recurrenceMonths = $this->getMonths();
351
-        }
352
-
353
-        do {
354
-            if ($this->byHour) {
355
-                if ($this->currentDate->format('G') == '23') {
356
-                    // to obey the interval rule
357
-                    $this->currentDate = $this->currentDate->modify('+' . $this->interval - 1 . ' days');
358
-                }
359
-
360
-                $this->currentDate = $this->currentDate->modify('+1 hours');
361
-
362
-            } else {
363
-                $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' days');
364
-
365
-            }
366
-
367
-            // Current month of the year
368
-            $currentMonth = $this->currentDate->format('n');
369
-
370
-            // Current day of the week
371
-            $currentDay = $this->currentDate->format('w');
372
-
373
-            // Current hour of the day
374
-            $currentHour = $this->currentDate->format('G');
375
-
376
-        } while (
377
-            ($this->byDay   && !in_array($currentDay, $recurrenceDays)) ||
378
-            ($this->byHour  && !in_array($currentHour, $recurrenceHours)) ||
379
-            ($this->byMonth && !in_array($currentMonth, $recurrenceMonths))
380
-        );
381
-
382
-    }
383
-
384
-    /**
385
-     * Does the processing for advancing the iterator for weekly frequency.
386
-     *
387
-     * @return void
388
-     */
389
-    protected function nextWeekly() {
390
-
391
-        if (!$this->byHour && !$this->byDay) {
392
-            $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' weeks');
393
-            return;
394
-        }
395
-
396
-        if ($this->byHour) {
397
-            $recurrenceHours = $this->getHours();
398
-        }
399
-
400
-        if ($this->byDay) {
401
-            $recurrenceDays = $this->getDays();
402
-        }
403
-
404
-        // First day of the week:
405
-        $firstDay = $this->dayMap[$this->weekStart];
406
-
407
-        do {
408
-
409
-            if ($this->byHour) {
410
-                $this->currentDate = $this->currentDate->modify('+1 hours');
411
-            } else {
412
-                $this->currentDate = $this->currentDate->modify('+1 days');
413
-            }
414
-
415
-            // Current day of the week
416
-            $currentDay = (int)$this->currentDate->format('w');
417
-
418
-            // Current hour of the day
419
-            $currentHour = (int)$this->currentDate->format('G');
420
-
421
-            // We need to roll over to the next week
422
-            if ($currentDay === $firstDay && (!$this->byHour || $currentHour == '0')) {
423
-                $this->currentDate = $this->currentDate->modify('+' . $this->interval - 1 . ' weeks');
424
-
425
-                // We need to go to the first day of this week, but only if we
426
-                // are not already on this first day of this week.
427
-                if ($this->currentDate->format('w') != $firstDay) {
428
-                    $this->currentDate = $this->currentDate->modify('last ' . $this->dayNames[$this->dayMap[$this->weekStart]]);
429
-                }
430
-            }
431
-
432
-            // We have a match
433
-        } while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours)));
434
-    }
435
-
436
-    /**
437
-     * Does the processing for advancing the iterator for monthly frequency.
438
-     *
439
-     * @return void
440
-     */
441
-    protected function nextMonthly() {
442
-
443
-        $currentDayOfMonth = $this->currentDate->format('j');
444
-        if (!$this->byMonthDay && !$this->byDay) {
445
-
446
-            // If the current day is higher than the 28th, rollover can
447
-            // occur to the next month. We Must skip these invalid
448
-            // entries.
449
-            if ($currentDayOfMonth < 29) {
450
-                $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' months');
451
-            } else {
452
-                $increase = 0;
453
-                do {
454
-                    $increase++;
455
-                    $tempDate = clone $this->currentDate;
456
-                    $tempDate = $tempDate->modify('+ ' . ($this->interval * $increase) . ' months');
457
-                } while ($tempDate->format('j') != $currentDayOfMonth);
458
-                $this->currentDate = $tempDate;
459
-            }
460
-            return;
461
-        }
462
-
463
-        while (true) {
464
-
465
-            $occurrences = $this->getMonthlyOccurrences();
466
-
467
-            foreach ($occurrences as $occurrence) {
468
-
469
-                // The first occurrence thats higher than the current
470
-                // day of the month wins.
471
-                if ($occurrence > $currentDayOfMonth) {
472
-                    break 2;
473
-                }
474
-
475
-            }
476
-
477
-            // If we made it all the way here, it means there were no
478
-            // valid occurrences, and we need to advance to the next
479
-            // month.
480
-            //
481
-            // This line does not currently work in hhvm. Temporary workaround
482
-            // follows:
483
-            $this->currentDate = new DateTimeImmutable($this->currentDate->format('Y-m-1 H:i:s'), $this->currentDate->getTimezone());
484
-            // end of workaround
485
-            $this->currentDate = $this->currentDate->modify('+ ' . $this->interval . ' months');
486
-
487
-            // This goes to 0 because we need to start counting at the
488
-            // beginning.
489
-            $currentDayOfMonth = 0;
490
-
491
-        }
492
-
493
-        $this->currentDate = $this->currentDate->setDate(
494
-            (int)$this->currentDate->format('Y'),
495
-            (int)$this->currentDate->format('n'),
496
-            (int)$occurrence
497
-        );
498
-
499
-    }
500
-
501
-    /**
502
-     * Does the processing for advancing the iterator for yearly frequency.
503
-     *
504
-     * @return void
505
-     */
506
-    protected function nextYearly() {
507
-
508
-        $currentMonth = $this->currentDate->format('n');
509
-        $currentYear = $this->currentDate->format('Y');
510
-        $currentDayOfMonth = $this->currentDate->format('j');
511
-
512
-        // No sub-rules, so we just advance by year
513
-        if (empty($this->byMonth)) {
514
-
515
-            // Unless it was a leap day!
516
-            if ($currentMonth == 2 && $currentDayOfMonth == 29) {
517
-
518
-                $counter = 0;
519
-                do {
520
-                    $counter++;
521
-                    // Here we increase the year count by the interval, until
522
-                    // we hit a date that's also in a leap year.
523
-                    //
524
-                    // We could just find the next interval that's dividable by
525
-                    // 4, but that would ignore the rule that there's no leap
526
-                    // year every year that's dividable by a 100, but not by
527
-                    // 400. (1800, 1900, 2100). So we just rely on the datetime
528
-                    // functions instead.
529
-                    $nextDate = clone $this->currentDate;
530
-                    $nextDate = $nextDate->modify('+ ' . ($this->interval * $counter) . ' years');
531
-                } while ($nextDate->format('n') != 2);
532
-
533
-                $this->currentDate = $nextDate;
534
-
535
-                return;
536
-
537
-            }
538
-
539
-            // The easiest form
540
-            $this->currentDate = $this->currentDate->modify('+' . $this->interval . ' years');
541
-            return;
542
-
543
-        }
544
-
545
-        $currentMonth = $this->currentDate->format('n');
546
-        $currentYear = $this->currentDate->format('Y');
547
-        $currentDayOfMonth = $this->currentDate->format('j');
548
-
549
-        $advancedToNewMonth = false;
550
-
551
-        // If we got a byDay or getMonthDay filter, we must first expand
552
-        // further.
553
-        if ($this->byDay || $this->byMonthDay) {
554
-
555
-            while (true) {
556
-
557
-                $occurrences = $this->getMonthlyOccurrences();
558
-
559
-                foreach ($occurrences as $occurrence) {
560
-
561
-                    // The first occurrence that's higher than the current
562
-                    // day of the month wins.
563
-                    // If we advanced to the next month or year, the first
564
-                    // occurrence is always correct.
565
-                    if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) {
566
-                        break 2;
567
-                    }
568
-
569
-                }
570
-
571
-                // If we made it here, it means we need to advance to
572
-                // the next month or year.
573
-                $currentDayOfMonth = 1;
574
-                $advancedToNewMonth = true;
575
-                do {
576
-
577
-                    $currentMonth++;
578
-                    if ($currentMonth > 12) {
579
-                        $currentYear += $this->interval;
580
-                        $currentMonth = 1;
581
-                    }
582
-                } while (!in_array($currentMonth, $this->byMonth));
583
-
584
-                $this->currentDate = $this->currentDate->setDate(
585
-                    (int)$currentYear,
586
-                    (int)$currentMonth,
587
-                    (int)$currentDayOfMonth
588
-                );
589
-
590
-            }
591
-
592
-            // If we made it here, it means we got a valid occurrence
593
-            $this->currentDate = $this->currentDate->setDate(
594
-                (int)$currentYear,
595
-                (int)$currentMonth,
596
-                (int)$occurrence
597
-            );
598
-            return;
599
-
600
-        } else {
601
-
602
-            // These are the 'byMonth' rules, if there are no byDay or
603
-            // byMonthDay sub-rules.
604
-            do {
605
-
606
-                $currentMonth++;
607
-                if ($currentMonth > 12) {
608
-                    $currentYear += $this->interval;
609
-                    $currentMonth = 1;
610
-                }
611
-            } while (!in_array($currentMonth, $this->byMonth));
612
-            $this->currentDate = $this->currentDate->setDate(
613
-                (int)$currentYear,
614
-                (int)$currentMonth,
615
-                (int)$currentDayOfMonth
616
-            );
617
-
618
-            return;
619
-
620
-        }
621
-
622
-    }
623
-
624
-    /* }}} */
625
-
626
-    /**
627
-     * This method receives a string from an RRULE property, and populates this
628
-     * class with all the values.
629
-     *
630
-     * @param string|array $rrule
631
-     *
632
-     * @return void
633
-     */
634
-    protected function parseRRule($rrule) {
635
-
636
-        if (is_string($rrule)) {
637
-            $rrule = Property\ICalendar\Recur::stringToArray($rrule);
638
-        }
639
-
640
-        foreach ($rrule as $key => $value) {
641
-
642
-            $key = strtoupper($key);
643
-            switch ($key) {
644
-
645
-                case 'FREQ' :
646
-                    $value = strtolower($value);
647
-                    if (!in_array(
648
-                        $value,
649
-                        ['secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly']
650
-                    )) {
651
-                        throw new InvalidDataException('Unknown value for FREQ=' . strtoupper($value));
652
-                    }
653
-                    $this->frequency = $value;
654
-                    break;
655
-
656
-                case 'UNTIL' :
657
-                    $this->until = DateTimeParser::parse($value, $this->startDate->getTimezone());
658
-
659
-                    // In some cases events are generated with an UNTIL=
660
-                    // parameter before the actual start of the event.
661
-                    //
662
-                    // Not sure why this is happening. We assume that the
663
-                    // intention was that the event only recurs once.
664
-                    //
665
-                    // So we are modifying the parameter so our code doesn't
666
-                    // break.
667
-                    if ($this->until < $this->startDate) {
668
-                        $this->until = $this->startDate;
669
-                    }
670
-                    break;
671
-
672
-                case 'INTERVAL' :
673
-                    // No break
674
-
675
-                case 'COUNT' :
676
-                    $val = (int)$value;
677
-                    if ($val < 1) {
678
-                        throw new InvalidDataException(strtoupper($key) . ' in RRULE must be a positive integer!');
679
-                    }
680
-                    $key = strtolower($key);
681
-                    $this->$key = $val;
682
-                    break;
683
-
684
-                case 'BYSECOND' :
685
-                    $this->bySecond = (array)$value;
686
-                    break;
687
-
688
-                case 'BYMINUTE' :
689
-                    $this->byMinute = (array)$value;
690
-                    break;
691
-
692
-                case 'BYHOUR' :
693
-                    $this->byHour = (array)$value;
694
-                    break;
695
-
696
-                case 'BYDAY' :
697
-                    $value = (array)$value;
698
-                    foreach ($value as $part) {
699
-                        if (!preg_match('#^  (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', $part)) {
700
-                            throw new InvalidDataException('Invalid part in BYDAY clause: ' . $part);
701
-                        }
702
-                    }
703
-                    $this->byDay = $value;
704
-                    break;
705
-
706
-                case 'BYMONTHDAY' :
707
-                    $this->byMonthDay = (array)$value;
708
-                    break;
709
-
710
-                case 'BYYEARDAY' :
711
-                    $this->byYearDay = (array)$value;
712
-                    break;
713
-
714
-                case 'BYWEEKNO' :
715
-                    $this->byWeekNo = (array)$value;
716
-                    break;
717
-
718
-                case 'BYMONTH' :
719
-                    $this->byMonth = (array)$value;
720
-                    break;
721
-
722
-                case 'BYSETPOS' :
723
-                    $this->bySetPos = (array)$value;
724
-                    break;
725
-
726
-                case 'WKST' :
727
-                    $this->weekStart = strtoupper($value);
728
-                    break;
729
-
730
-                default:
731
-                    throw new InvalidDataException('Not supported: ' . strtoupper($key));
732
-
733
-            }
734
-
735
-        }
736
-
737
-    }
738
-
739
-    /**
740
-     * Mappings between the day number and english day name.
741
-     *
742
-     * @var array
743
-     */
744
-    protected $dayNames = [
745
-        0 => 'Sunday',
746
-        1 => 'Monday',
747
-        2 => 'Tuesday',
748
-        3 => 'Wednesday',
749
-        4 => 'Thursday',
750
-        5 => 'Friday',
751
-        6 => 'Saturday',
752
-    ];
753
-
754
-    /**
755
-     * Returns all the occurrences for a monthly frequency with a 'byDay' or
756
-     * 'byMonthDay' expansion for the current month.
757
-     *
758
-     * The returned list is an array of integers with the day of month (1-31).
759
-     *
760
-     * @return array
761
-     */
762
-    protected function getMonthlyOccurrences() {
763
-
764
-        $startDate = clone $this->currentDate;
765
-
766
-        $byDayResults = [];
767
-
768
-        // Our strategy is to simply go through the byDays, advance the date to
769
-        // that point and add it to the results.
770
-        if ($this->byDay) foreach ($this->byDay as $day) {
771
-
772
-            $dayName = $this->dayNames[$this->dayMap[substr($day, -2)]];
773
-
774
-
775
-            // Dayname will be something like 'wednesday'. Now we need to find
776
-            // all wednesdays in this month.
777
-            $dayHits = [];
778
-
779
-            // workaround for missing 'first day of the month' support in hhvm
780
-            $checkDate = new \DateTime($startDate->format('Y-m-1'));
781
-            // workaround modify always advancing the date even if the current day is a $dayName in hhvm
782
-            if ($checkDate->format('l') !== $dayName) {
783
-                $checkDate = $checkDate->modify($dayName);
784
-            }
785
-
786
-            do {
787
-                $dayHits[] = $checkDate->format('j');
788
-                $checkDate = $checkDate->modify('next ' . $dayName);
789
-            } while ($checkDate->format('n') === $startDate->format('n'));
790
-
791
-            // So now we have 'all wednesdays' for month. It is however
792
-            // possible that the user only really wanted the 1st, 2nd or last
793
-            // wednesday.
794
-            if (strlen($day) > 2) {
795
-                $offset = (int)substr($day, 0, -2);
796
-
797
-                if ($offset > 0) {
798
-                    // It is possible that the day does not exist, such as a
799
-                    // 5th or 6th wednesday of the month.
800
-                    if (isset($dayHits[$offset - 1])) {
801
-                        $byDayResults[] = $dayHits[$offset - 1];
802
-                    }
803
-                } else {
804
-
805
-                    // if it was negative we count from the end of the array
806
-                    // might not exist, fx. -5th tuesday
807
-                    if (isset($dayHits[count($dayHits) + $offset])) {
808
-                        $byDayResults[] = $dayHits[count($dayHits) + $offset];
809
-                    }
810
-                }
811
-            } else {
812
-                // There was no counter (first, second, last wednesdays), so we
813
-                // just need to add the all to the list).
814
-                $byDayResults = array_merge($byDayResults, $dayHits);
815
-
816
-            }
817
-
818
-        }
819
-
820
-        $byMonthDayResults = [];
821
-        if ($this->byMonthDay) foreach ($this->byMonthDay as $monthDay) {
822
-
823
-            // Removing values that are out of range for this month
824
-            if ($monthDay > $startDate->format('t') ||
825
-                $monthDay < 0 - $startDate->format('t')) {
826
-                    continue;
827
-            }
828
-            if ($monthDay > 0) {
829
-                $byMonthDayResults[] = $monthDay;
830
-            } else {
831
-                // Negative values
832
-                $byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay;
833
-            }
834
-        }
835
-
836
-        // If there was just byDay or just byMonthDay, they just specify our
837
-        // (almost) final list. If both were provided, then byDay limits the
838
-        // list.
839
-        if ($this->byMonthDay && $this->byDay) {
840
-            $result = array_intersect($byMonthDayResults, $byDayResults);
841
-        } elseif ($this->byMonthDay) {
842
-            $result = $byMonthDayResults;
843
-        } else {
844
-            $result = $byDayResults;
845
-        }
846
-        $result = array_unique($result);
847
-        sort($result, SORT_NUMERIC);
848
-
849
-        // The last thing that needs checking is the BYSETPOS. If it's set, it
850
-        // means only certain items in the set survive the filter.
851
-        if (!$this->bySetPos) {
852
-            return $result;
853
-        }
854
-
855
-        $filteredResult = [];
856
-        foreach ($this->bySetPos as $setPos) {
857
-
858
-            if ($setPos < 0) {
859
-                $setPos = count($result) + ($setPos + 1);
860
-            }
861
-            if (isset($result[$setPos - 1])) {
862
-                $filteredResult[] = $result[$setPos - 1];
863
-            }
864
-        }
865
-
866
-        sort($filteredResult, SORT_NUMERIC);
867
-        return $filteredResult;
868
-
869
-    }
870
-
871
-    /**
872
-     * Simple mapping from iCalendar day names to day numbers.
873
-     *
874
-     * @var array
875
-     */
876
-    protected $dayMap = [
877
-        'SU' => 0,
878
-        'MO' => 1,
879
-        'TU' => 2,
880
-        'WE' => 3,
881
-        'TH' => 4,
882
-        'FR' => 5,
883
-        'SA' => 6,
884
-    ];
885
-
886
-    protected function getHours() {
887
-
888
-        $recurrenceHours = [];
889
-        foreach ($this->byHour as $byHour) {
890
-            $recurrenceHours[] = $byHour;
891
-        }
892
-
893
-        return $recurrenceHours;
894
-    }
895
-
896
-    protected function getDays() {
897
-
898
-        $recurrenceDays = [];
899
-        foreach ($this->byDay as $byDay) {
900
-
901
-            // The day may be preceeded with a positive (+n) or
902
-            // negative (-n) integer. However, this does not make
903
-            // sense in 'weekly' so we ignore it here.
904
-            $recurrenceDays[] = $this->dayMap[substr($byDay, -2)];
905
-
906
-        }
907
-
908
-        return $recurrenceDays;
909
-    }
910
-
911
-    protected function getMonths() {
912
-
913
-        $recurrenceMonths = [];
914
-        foreach ($this->byMonth as $byMonth) {
915
-            $recurrenceMonths[] = $byMonth;
916
-        }
917
-
918
-        return $recurrenceMonths;
919
-    }
57
+		return $this->counter;
58
+
59
+	}
60
+
61
+	/**
62
+	 * Returns whether the current item is a valid item for the recurrence
63
+	 * iterator. This will return false if we've gone beyond the UNTIL or COUNT
64
+	 * statements.
65
+	 *
66
+	 * @return bool
67
+	 */
68
+	public function valid() {
69
+
70
+		if (!is_null($this->count)) {
71
+			return $this->counter < $this->count;
72
+		}
73
+		return is_null($this->until) || $this->currentDate <= $this->until;
74
+
75
+	}
76
+
77
+	/**
78
+	 * Resets the iterator.
79
+	 *
80
+	 * @return void
81
+	 */
82
+	public function rewind() {
83
+
84
+		$this->currentDate = clone $this->startDate;
85
+		$this->counter = 0;
86
+
87
+	}
88
+
89
+	/**
90
+	 * Goes on to the next iteration.
91
+	 *
92
+	 * @return void
93
+	 */
94
+	public function next() {
95
+
96
+		// Otherwise, we find the next event in the normal RRULE
97
+		// sequence.
98
+		switch ($this->frequency) {
99
+
100
+			case 'hourly' :
101
+				$this->nextHourly();
102
+				break;
103
+
104
+			case 'daily' :
105
+				$this->nextDaily();
106
+				break;
107
+
108
+			case 'weekly' :
109
+				$this->nextWeekly();
110
+				break;
111
+
112
+			case 'monthly' :
113
+				$this->nextMonthly();
114
+				break;
115
+
116
+			case 'yearly' :
117
+				$this->nextYearly();
118
+				break;
119
+
120
+		}
121
+		$this->counter++;
122
+
123
+	}
124
+
125
+	/* End of Iterator implementation }}} */
126
+
127
+	/**
128
+	 * Returns true if this recurring event never ends.
129
+	 *
130
+	 * @return bool
131
+	 */
132
+	public function isInfinite() {
133
+
134
+		return !$this->count && !$this->until;
135
+
136
+	}
137
+
138
+	/**
139
+	 * This method allows you to quickly go to the next occurrence after the
140
+	 * specified date.
141
+	 *
142
+	 * @param DateTimeInterface $dt
143
+	 *
144
+	 * @return void
145
+	 */
146
+	public function fastForward(DateTimeInterface $dt) {
147
+
148
+		while ($this->valid() && $this->currentDate < $dt) {
149
+			$this->next();
150
+		}
151
+
152
+	}
153
+
154
+	/**
155
+	 * The reference start date/time for the rrule.
156
+	 *
157
+	 * All calculations are based on this initial date.
158
+	 *
159
+	 * @var DateTimeInterface
160
+	 */
161
+	protected $startDate;
162
+
163
+	/**
164
+	 * The date of the current iteration. You can get this by calling
165
+	 * ->current().
166
+	 *
167
+	 * @var DateTimeInterface
168
+	 */
169
+	protected $currentDate;
170
+
171
+	/**
172
+	 * Frequency is one of: secondly, minutely, hourly, daily, weekly, monthly,
173
+	 * yearly.
174
+	 *
175
+	 * @var string
176
+	 */
177
+	protected $frequency;
178
+
179
+	/**
180
+	 * The number of recurrences, or 'null' if infinitely recurring.
181
+	 *
182
+	 * @var int
183
+	 */
184
+	protected $count;
185
+
186
+	/**
187
+	 * The interval.
188
+	 *
189
+	 * If for example frequency is set to daily, interval = 2 would mean every
190
+	 * 2 days.
191
+	 *
192
+	 * @var int
193
+	 */
194
+	protected $interval = 1;
195
+
196
+	/**
197
+	 * The last instance of this recurrence, inclusively.
198
+	 *
199
+	 * @var DateTimeInterface|null
200
+	 */
201
+	protected $until;
202
+
203
+	/**
204
+	 * Which seconds to recur.
205
+	 *
206
+	 * This is an array of integers (between 0 and 60)
207
+	 *
208
+	 * @var array
209
+	 */
210
+	protected $bySecond;
211
+
212
+	/**
213
+	 * Which minutes to recur.
214
+	 *
215
+	 * This is an array of integers (between 0 and 59)
216
+	 *
217
+	 * @var array
218
+	 */
219
+	protected $byMinute;
220
+
221
+	/**
222
+	 * Which hours to recur.
223
+	 *
224
+	 * This is an array of integers (between 0 and 23)
225
+	 *
226
+	 * @var array
227
+	 */
228
+	protected $byHour;
229
+
230
+	/**
231
+	 * The current item in the list.
232
+	 *
233
+	 * You can get this number with the key() method.
234
+	 *
235
+	 * @var int
236
+	 */
237
+	protected $counter = 0;
238
+
239
+	/**
240
+	 * Which weekdays to recur.
241
+	 *
242
+	 * This is an array of weekdays
243
+	 *
244
+	 * This may also be preceeded by a positive or negative integer. If present,
245
+	 * this indicates the nth occurrence of a specific day within the monthly or
246
+	 * yearly rrule. For instance, -2TU indicates the second-last tuesday of
247
+	 * the month, or year.
248
+	 *
249
+	 * @var array
250
+	 */
251
+	protected $byDay;
252
+
253
+	/**
254
+	 * Which days of the month to recur.
255
+	 *
256
+	 * This is an array of days of the months (1-31). The value can also be
257
+	 * negative. -5 for instance means the 5th last day of the month.
258
+	 *
259
+	 * @var array
260
+	 */
261
+	protected $byMonthDay;
262
+
263
+	/**
264
+	 * Which days of the year to recur.
265
+	 *
266
+	 * This is an array with days of the year (1 to 366). The values can also
267
+	 * be negative. For instance, -1 will always represent the last day of the
268
+	 * year. (December 31st).
269
+	 *
270
+	 * @var array
271
+	 */
272
+	protected $byYearDay;
273
+
274
+	/**
275
+	 * Which week numbers to recur.
276
+	 *
277
+	 * This is an array of integers from 1 to 53. The values can also be
278
+	 * negative. -1 will always refer to the last week of the year.
279
+	 *
280
+	 * @var array
281
+	 */
282
+	protected $byWeekNo;
283
+
284
+	/**
285
+	 * Which months to recur.
286
+	 *
287
+	 * This is an array of integers from 1 to 12.
288
+	 *
289
+	 * @var array
290
+	 */
291
+	protected $byMonth;
292
+
293
+	/**
294
+	 * Which items in an existing st to recur.
295
+	 *
296
+	 * These numbers work together with an existing by* rule. It specifies
297
+	 * exactly which items of the existing by-rule to filter.
298
+	 *
299
+	 * Valid values are 1 to 366 and -1 to -366. As an example, this can be
300
+	 * used to recur the last workday of the month.
301
+	 *
302
+	 * This would be done by setting frequency to 'monthly', byDay to
303
+	 * 'MO,TU,WE,TH,FR' and bySetPos to -1.
304
+	 *
305
+	 * @var array
306
+	 */
307
+	protected $bySetPos;
308
+
309
+	/**
310
+	 * When the week starts.
311
+	 *
312
+	 * @var string
313
+	 */
314
+	protected $weekStart = 'MO';
315
+
316
+	/* Functions that advance the iterator {{{ */
317
+
318
+	/**
319
+	 * Does the processing for advancing the iterator for hourly frequency.
320
+	 *
321
+	 * @return void
322
+	 */
323
+	protected function nextHourly() {
324
+
325
+		$this->currentDate = $this->currentDate->modify('+' . $this->interval . ' hours');
326
+
327
+	}
328
+
329
+	/**
330
+	 * Does the processing for advancing the iterator for daily frequency.
331
+	 *
332
+	 * @return void
333
+	 */
334
+	protected function nextDaily() {
335
+
336
+		if (!$this->byHour && !$this->byDay) {
337
+			$this->currentDate = $this->currentDate->modify('+' . $this->interval . ' days');
338
+			return;
339
+		}
340
+
341
+		if (!empty($this->byHour)) {
342
+			$recurrenceHours = $this->getHours();
343
+		}
344
+
345
+		if (!empty($this->byDay)) {
346
+			$recurrenceDays = $this->getDays();
347
+		}
348
+
349
+		if (!empty($this->byMonth)) {
350
+			$recurrenceMonths = $this->getMonths();
351
+		}
352
+
353
+		do {
354
+			if ($this->byHour) {
355
+				if ($this->currentDate->format('G') == '23') {
356
+					// to obey the interval rule
357
+					$this->currentDate = $this->currentDate->modify('+' . $this->interval - 1 . ' days');
358
+				}
359
+
360
+				$this->currentDate = $this->currentDate->modify('+1 hours');
361
+
362
+			} else {
363
+				$this->currentDate = $this->currentDate->modify('+' . $this->interval . ' days');
364
+
365
+			}
366
+
367
+			// Current month of the year
368
+			$currentMonth = $this->currentDate->format('n');
369
+
370
+			// Current day of the week
371
+			$currentDay = $this->currentDate->format('w');
372
+
373
+			// Current hour of the day
374
+			$currentHour = $this->currentDate->format('G');
375
+
376
+		} while (
377
+			($this->byDay   && !in_array($currentDay, $recurrenceDays)) ||
378
+			($this->byHour  && !in_array($currentHour, $recurrenceHours)) ||
379
+			($this->byMonth && !in_array($currentMonth, $recurrenceMonths))
380
+		);
381
+
382
+	}
383
+
384
+	/**
385
+	 * Does the processing for advancing the iterator for weekly frequency.
386
+	 *
387
+	 * @return void
388
+	 */
389
+	protected function nextWeekly() {
390
+
391
+		if (!$this->byHour && !$this->byDay) {
392
+			$this->currentDate = $this->currentDate->modify('+' . $this->interval . ' weeks');
393
+			return;
394
+		}
395
+
396
+		if ($this->byHour) {
397
+			$recurrenceHours = $this->getHours();
398
+		}
399
+
400
+		if ($this->byDay) {
401
+			$recurrenceDays = $this->getDays();
402
+		}
403
+
404
+		// First day of the week:
405
+		$firstDay = $this->dayMap[$this->weekStart];
406
+
407
+		do {
408
+
409
+			if ($this->byHour) {
410
+				$this->currentDate = $this->currentDate->modify('+1 hours');
411
+			} else {
412
+				$this->currentDate = $this->currentDate->modify('+1 days');
413
+			}
414
+
415
+			// Current day of the week
416
+			$currentDay = (int)$this->currentDate->format('w');
417
+
418
+			// Current hour of the day
419
+			$currentHour = (int)$this->currentDate->format('G');
420
+
421
+			// We need to roll over to the next week
422
+			if ($currentDay === $firstDay && (!$this->byHour || $currentHour == '0')) {
423
+				$this->currentDate = $this->currentDate->modify('+' . $this->interval - 1 . ' weeks');
424
+
425
+				// We need to go to the first day of this week, but only if we
426
+				// are not already on this first day of this week.
427
+				if ($this->currentDate->format('w') != $firstDay) {
428
+					$this->currentDate = $this->currentDate->modify('last ' . $this->dayNames[$this->dayMap[$this->weekStart]]);
429
+				}
430
+			}
431
+
432
+			// We have a match
433
+		} while (($this->byDay && !in_array($currentDay, $recurrenceDays)) || ($this->byHour && !in_array($currentHour, $recurrenceHours)));
434
+	}
435
+
436
+	/**
437
+	 * Does the processing for advancing the iterator for monthly frequency.
438
+	 *
439
+	 * @return void
440
+	 */
441
+	protected function nextMonthly() {
442
+
443
+		$currentDayOfMonth = $this->currentDate->format('j');
444
+		if (!$this->byMonthDay && !$this->byDay) {
445
+
446
+			// If the current day is higher than the 28th, rollover can
447
+			// occur to the next month. We Must skip these invalid
448
+			// entries.
449
+			if ($currentDayOfMonth < 29) {
450
+				$this->currentDate = $this->currentDate->modify('+' . $this->interval . ' months');
451
+			} else {
452
+				$increase = 0;
453
+				do {
454
+					$increase++;
455
+					$tempDate = clone $this->currentDate;
456
+					$tempDate = $tempDate->modify('+ ' . ($this->interval * $increase) . ' months');
457
+				} while ($tempDate->format('j') != $currentDayOfMonth);
458
+				$this->currentDate = $tempDate;
459
+			}
460
+			return;
461
+		}
462
+
463
+		while (true) {
464
+
465
+			$occurrences = $this->getMonthlyOccurrences();
466
+
467
+			foreach ($occurrences as $occurrence) {
468
+
469
+				// The first occurrence thats higher than the current
470
+				// day of the month wins.
471
+				if ($occurrence > $currentDayOfMonth) {
472
+					break 2;
473
+				}
474
+
475
+			}
476
+
477
+			// If we made it all the way here, it means there were no
478
+			// valid occurrences, and we need to advance to the next
479
+			// month.
480
+			//
481
+			// This line does not currently work in hhvm. Temporary workaround
482
+			// follows:
483
+			$this->currentDate = new DateTimeImmutable($this->currentDate->format('Y-m-1 H:i:s'), $this->currentDate->getTimezone());
484
+			// end of workaround
485
+			$this->currentDate = $this->currentDate->modify('+ ' . $this->interval . ' months');
486
+
487
+			// This goes to 0 because we need to start counting at the
488
+			// beginning.
489
+			$currentDayOfMonth = 0;
490
+
491
+		}
492
+
493
+		$this->currentDate = $this->currentDate->setDate(
494
+			(int)$this->currentDate->format('Y'),
495
+			(int)$this->currentDate->format('n'),
496
+			(int)$occurrence
497
+		);
498
+
499
+	}
500
+
501
+	/**
502
+	 * Does the processing for advancing the iterator for yearly frequency.
503
+	 *
504
+	 * @return void
505
+	 */
506
+	protected function nextYearly() {
507
+
508
+		$currentMonth = $this->currentDate->format('n');
509
+		$currentYear = $this->currentDate->format('Y');
510
+		$currentDayOfMonth = $this->currentDate->format('j');
511
+
512
+		// No sub-rules, so we just advance by year
513
+		if (empty($this->byMonth)) {
514
+
515
+			// Unless it was a leap day!
516
+			if ($currentMonth == 2 && $currentDayOfMonth == 29) {
517
+
518
+				$counter = 0;
519
+				do {
520
+					$counter++;
521
+					// Here we increase the year count by the interval, until
522
+					// we hit a date that's also in a leap year.
523
+					//
524
+					// We could just find the next interval that's dividable by
525
+					// 4, but that would ignore the rule that there's no leap
526
+					// year every year that's dividable by a 100, but not by
527
+					// 400. (1800, 1900, 2100). So we just rely on the datetime
528
+					// functions instead.
529
+					$nextDate = clone $this->currentDate;
530
+					$nextDate = $nextDate->modify('+ ' . ($this->interval * $counter) . ' years');
531
+				} while ($nextDate->format('n') != 2);
532
+
533
+				$this->currentDate = $nextDate;
534
+
535
+				return;
536
+
537
+			}
538
+
539
+			// The easiest form
540
+			$this->currentDate = $this->currentDate->modify('+' . $this->interval . ' years');
541
+			return;
542
+
543
+		}
544
+
545
+		$currentMonth = $this->currentDate->format('n');
546
+		$currentYear = $this->currentDate->format('Y');
547
+		$currentDayOfMonth = $this->currentDate->format('j');
548
+
549
+		$advancedToNewMonth = false;
550
+
551
+		// If we got a byDay or getMonthDay filter, we must first expand
552
+		// further.
553
+		if ($this->byDay || $this->byMonthDay) {
554
+
555
+			while (true) {
556
+
557
+				$occurrences = $this->getMonthlyOccurrences();
558
+
559
+				foreach ($occurrences as $occurrence) {
560
+
561
+					// The first occurrence that's higher than the current
562
+					// day of the month wins.
563
+					// If we advanced to the next month or year, the first
564
+					// occurrence is always correct.
565
+					if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) {
566
+						break 2;
567
+					}
568
+
569
+				}
570
+
571
+				// If we made it here, it means we need to advance to
572
+				// the next month or year.
573
+				$currentDayOfMonth = 1;
574
+				$advancedToNewMonth = true;
575
+				do {
576
+
577
+					$currentMonth++;
578
+					if ($currentMonth > 12) {
579
+						$currentYear += $this->interval;
580
+						$currentMonth = 1;
581
+					}
582
+				} while (!in_array($currentMonth, $this->byMonth));
583
+
584
+				$this->currentDate = $this->currentDate->setDate(
585
+					(int)$currentYear,
586
+					(int)$currentMonth,
587
+					(int)$currentDayOfMonth
588
+				);
589
+
590
+			}
591
+
592
+			// If we made it here, it means we got a valid occurrence
593
+			$this->currentDate = $this->currentDate->setDate(
594
+				(int)$currentYear,
595
+				(int)$currentMonth,
596
+				(int)$occurrence
597
+			);
598
+			return;
599
+
600
+		} else {
601
+
602
+			// These are the 'byMonth' rules, if there are no byDay or
603
+			// byMonthDay sub-rules.
604
+			do {
605
+
606
+				$currentMonth++;
607
+				if ($currentMonth > 12) {
608
+					$currentYear += $this->interval;
609
+					$currentMonth = 1;
610
+				}
611
+			} while (!in_array($currentMonth, $this->byMonth));
612
+			$this->currentDate = $this->currentDate->setDate(
613
+				(int)$currentYear,
614
+				(int)$currentMonth,
615
+				(int)$currentDayOfMonth
616
+			);
617
+
618
+			return;
619
+
620
+		}
621
+
622
+	}
623
+
624
+	/* }}} */
625
+
626
+	/**
627
+	 * This method receives a string from an RRULE property, and populates this
628
+	 * class with all the values.
629
+	 *
630
+	 * @param string|array $rrule
631
+	 *
632
+	 * @return void
633
+	 */
634
+	protected function parseRRule($rrule) {
635
+
636
+		if (is_string($rrule)) {
637
+			$rrule = Property\ICalendar\Recur::stringToArray($rrule);
638
+		}
639
+
640
+		foreach ($rrule as $key => $value) {
641
+
642
+			$key = strtoupper($key);
643
+			switch ($key) {
644
+
645
+				case 'FREQ' :
646
+					$value = strtolower($value);
647
+					if (!in_array(
648
+						$value,
649
+						['secondly', 'minutely', 'hourly', 'daily', 'weekly', 'monthly', 'yearly']
650
+					)) {
651
+						throw new InvalidDataException('Unknown value for FREQ=' . strtoupper($value));
652
+					}
653
+					$this->frequency = $value;
654
+					break;
655
+
656
+				case 'UNTIL' :
657
+					$this->until = DateTimeParser::parse($value, $this->startDate->getTimezone());
658
+
659
+					// In some cases events are generated with an UNTIL=
660
+					// parameter before the actual start of the event.
661
+					//
662
+					// Not sure why this is happening. We assume that the
663
+					// intention was that the event only recurs once.
664
+					//
665
+					// So we are modifying the parameter so our code doesn't
666
+					// break.
667
+					if ($this->until < $this->startDate) {
668
+						$this->until = $this->startDate;
669
+					}
670
+					break;
671
+
672
+				case 'INTERVAL' :
673
+					// No break
674
+
675
+				case 'COUNT' :
676
+					$val = (int)$value;
677
+					if ($val < 1) {
678
+						throw new InvalidDataException(strtoupper($key) . ' in RRULE must be a positive integer!');
679
+					}
680
+					$key = strtolower($key);
681
+					$this->$key = $val;
682
+					break;
683
+
684
+				case 'BYSECOND' :
685
+					$this->bySecond = (array)$value;
686
+					break;
687
+
688
+				case 'BYMINUTE' :
689
+					$this->byMinute = (array)$value;
690
+					break;
691
+
692
+				case 'BYHOUR' :
693
+					$this->byHour = (array)$value;
694
+					break;
695
+
696
+				case 'BYDAY' :
697
+					$value = (array)$value;
698
+					foreach ($value as $part) {
699
+						if (!preg_match('#^  (-|\+)? ([1-5])? (MO|TU|WE|TH|FR|SA|SU) $# xi', $part)) {
700
+							throw new InvalidDataException('Invalid part in BYDAY clause: ' . $part);
701
+						}
702
+					}
703
+					$this->byDay = $value;
704
+					break;
705
+
706
+				case 'BYMONTHDAY' :
707
+					$this->byMonthDay = (array)$value;
708
+					break;
709
+
710
+				case 'BYYEARDAY' :
711
+					$this->byYearDay = (array)$value;
712
+					break;
713
+
714
+				case 'BYWEEKNO' :
715
+					$this->byWeekNo = (array)$value;
716
+					break;
717
+
718
+				case 'BYMONTH' :
719
+					$this->byMonth = (array)$value;
720
+					break;
721
+
722
+				case 'BYSETPOS' :
723
+					$this->bySetPos = (array)$value;
724
+					break;
725
+
726
+				case 'WKST' :
727
+					$this->weekStart = strtoupper($value);
728
+					break;
729
+
730
+				default:
731
+					throw new InvalidDataException('Not supported: ' . strtoupper($key));
732
+
733
+			}
734
+
735
+		}
736
+
737
+	}
738
+
739
+	/**
740
+	 * Mappings between the day number and english day name.
741
+	 *
742
+	 * @var array
743
+	 */
744
+	protected $dayNames = [
745
+		0 => 'Sunday',
746
+		1 => 'Monday',
747
+		2 => 'Tuesday',
748
+		3 => 'Wednesday',
749
+		4 => 'Thursday',
750
+		5 => 'Friday',
751
+		6 => 'Saturday',
752
+	];
753
+
754
+	/**
755
+	 * Returns all the occurrences for a monthly frequency with a 'byDay' or
756
+	 * 'byMonthDay' expansion for the current month.
757
+	 *
758
+	 * The returned list is an array of integers with the day of month (1-31).
759
+	 *
760
+	 * @return array
761
+	 */
762
+	protected function getMonthlyOccurrences() {
763
+
764
+		$startDate = clone $this->currentDate;
765
+
766
+		$byDayResults = [];
767
+
768
+		// Our strategy is to simply go through the byDays, advance the date to
769
+		// that point and add it to the results.
770
+		if ($this->byDay) foreach ($this->byDay as $day) {
771
+
772
+			$dayName = $this->dayNames[$this->dayMap[substr($day, -2)]];
773
+
774
+
775
+			// Dayname will be something like 'wednesday'. Now we need to find
776
+			// all wednesdays in this month.
777
+			$dayHits = [];
778
+
779
+			// workaround for missing 'first day of the month' support in hhvm
780
+			$checkDate = new \DateTime($startDate->format('Y-m-1'));
781
+			// workaround modify always advancing the date even if the current day is a $dayName in hhvm
782
+			if ($checkDate->format('l') !== $dayName) {
783
+				$checkDate = $checkDate->modify($dayName);
784
+			}
785
+
786
+			do {
787
+				$dayHits[] = $checkDate->format('j');
788
+				$checkDate = $checkDate->modify('next ' . $dayName);
789
+			} while ($checkDate->format('n') === $startDate->format('n'));
790
+
791
+			// So now we have 'all wednesdays' for month. It is however
792
+			// possible that the user only really wanted the 1st, 2nd or last
793
+			// wednesday.
794
+			if (strlen($day) > 2) {
795
+				$offset = (int)substr($day, 0, -2);
796
+
797
+				if ($offset > 0) {
798
+					// It is possible that the day does not exist, such as a
799
+					// 5th or 6th wednesday of the month.
800
+					if (isset($dayHits[$offset - 1])) {
801
+						$byDayResults[] = $dayHits[$offset - 1];
802
+					}
803
+				} else {
804
+
805
+					// if it was negative we count from the end of the array
806
+					// might not exist, fx. -5th tuesday
807
+					if (isset($dayHits[count($dayHits) + $offset])) {
808
+						$byDayResults[] = $dayHits[count($dayHits) + $offset];
809
+					}
810
+				}
811
+			} else {
812
+				// There was no counter (first, second, last wednesdays), so we
813
+				// just need to add the all to the list).
814
+				$byDayResults = array_merge($byDayResults, $dayHits);
815
+
816
+			}
817
+
818
+		}
819
+
820
+		$byMonthDayResults = [];
821
+		if ($this->byMonthDay) foreach ($this->byMonthDay as $monthDay) {
822
+
823
+			// Removing values that are out of range for this month
824
+			if ($monthDay > $startDate->format('t') ||
825
+				$monthDay < 0 - $startDate->format('t')) {
826
+					continue;
827
+			}
828
+			if ($monthDay > 0) {
829
+				$byMonthDayResults[] = $monthDay;
830
+			} else {
831
+				// Negative values
832
+				$byMonthDayResults[] = $startDate->format('t') + 1 + $monthDay;
833
+			}
834
+		}
835
+
836
+		// If there was just byDay or just byMonthDay, they just specify our
837
+		// (almost) final list. If both were provided, then byDay limits the
838
+		// list.
839
+		if ($this->byMonthDay && $this->byDay) {
840
+			$result = array_intersect($byMonthDayResults, $byDayResults);
841
+		} elseif ($this->byMonthDay) {
842
+			$result = $byMonthDayResults;
843
+		} else {
844
+			$result = $byDayResults;
845
+		}
846
+		$result = array_unique($result);
847
+		sort($result, SORT_NUMERIC);
848
+
849
+		// The last thing that needs checking is the BYSETPOS. If it's set, it
850
+		// means only certain items in the set survive the filter.
851
+		if (!$this->bySetPos) {
852
+			return $result;
853
+		}
854
+
855
+		$filteredResult = [];
856
+		foreach ($this->bySetPos as $setPos) {
857
+
858
+			if ($setPos < 0) {
859
+				$setPos = count($result) + ($setPos + 1);
860
+			}
861
+			if (isset($result[$setPos - 1])) {
862
+				$filteredResult[] = $result[$setPos - 1];
863
+			}
864
+		}
865
+
866
+		sort($filteredResult, SORT_NUMERIC);
867
+		return $filteredResult;
868
+
869
+	}
870
+
871
+	/**
872
+	 * Simple mapping from iCalendar day names to day numbers.
873
+	 *
874
+	 * @var array
875
+	 */
876
+	protected $dayMap = [
877
+		'SU' => 0,
878
+		'MO' => 1,
879
+		'TU' => 2,
880
+		'WE' => 3,
881
+		'TH' => 4,
882
+		'FR' => 5,
883
+		'SA' => 6,
884
+	];
885
+
886
+	protected function getHours() {
887
+
888
+		$recurrenceHours = [];
889
+		foreach ($this->byHour as $byHour) {
890
+			$recurrenceHours[] = $byHour;
891
+		}
892
+
893
+		return $recurrenceHours;
894
+	}
895
+
896
+	protected function getDays() {
897
+
898
+		$recurrenceDays = [];
899
+		foreach ($this->byDay as $byDay) {
900
+
901
+			// The day may be preceeded with a positive (+n) or
902
+			// negative (-n) integer. However, this does not make
903
+			// sense in 'weekly' so we ignore it here.
904
+			$recurrenceDays[] = $this->dayMap[substr($byDay, -2)];
905
+
906
+		}
907
+
908
+		return $recurrenceDays;
909
+	}
910
+
911
+	protected function getMonths() {
912
+
913
+		$recurrenceMonths = [];
914
+		foreach ($this->byMonth as $byMonth) {
915
+			$recurrenceMonths[] = $byMonth;
916
+		}
917
+
918
+		return $recurrenceMonths;
919
+	}
920 920
 }
Please login to merge, or discard this patch.
Braces   +9 added lines, -3 removed lines patch added patch discarded remove patch
@@ -42,7 +42,9 @@  discard block
 block discarded – undo
42 42
 
43 43
     public function current() {
44 44
 
45
-        if (!$this->valid()) return;
45
+        if (!$this->valid()) {
46
+        	return;
47
+        }
46 48
         return clone $this->currentDate;
47 49
 
48 50
     }
@@ -767,9 +769,11 @@  discard block
 block discarded – undo
767 769
 
768 770
         // Our strategy is to simply go through the byDays, advance the date to
769 771
         // that point and add it to the results.
770
-        if ($this->byDay) foreach ($this->byDay as $day) {
772
+        if ($this->byDay) {
773
+        	foreach ($this->byDay as $day) {
771 774
 
772 775
             $dayName = $this->dayNames[$this->dayMap[substr($day, -2)]];
776
+        }
773 777
 
774 778
 
775 779
             // Dayname will be something like 'wednesday'. Now we need to find
@@ -818,12 +822,14 @@  discard block
 block discarded – undo
818 822
         }
819 823
 
820 824
         $byMonthDayResults = [];
821
-        if ($this->byMonthDay) foreach ($this->byMonthDay as $monthDay) {
825
+        if ($this->byMonthDay) {
826
+        	foreach ($this->byMonthDay as $monthDay) {
822 827
 
823 828
             // Removing values that are out of range for this month
824 829
             if ($monthDay > $startDate->format('t') ||
825 830
                 $monthDay < 0 - $startDate->format('t')) {
826 831
                     continue;
832
+        }
827 833
             }
828 834
             if ($monthDay > 0) {
829 835
                 $byMonthDayResults[] = $monthDay;
Please login to merge, or discard this patch.