Completed
Push — developer ( e1a3df...61e59d )
by Błażej
172:03 queued 124:42
created
libraries/SabreDAV/VObject/Property/ICalendar/Period.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   +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/Time.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -44,7 +44,7 @@
 block discarded – undo
44 44
      *
45 45
      * This method must always return an array.
46 46
      *
47
-     * @return array
47
+     * @return string[]
48 48
      */
49 49
     function getJsonValue() {
50 50
 
Please login to merge, or discard this patch.
Indentation   +125 added lines, -125 removed lines patch added patch discarded remove patch
@@ -15,130 +15,130 @@
 block discarded – undo
15 15
  */
16 16
 class Time extends Text {
17 17
 
18
-    /**
19
-     * In case this is a multi-value property. This string will be used as a
20
-     * delimiter.
21
-     *
22
-     * @var string|null
23
-     */
24
-    public $delimiter = null;
25
-
26
-    /**
27
-     * Returns the type of value.
28
-     *
29
-     * This corresponds to the VALUE= parameter. Every property also has a
30
-     * 'default' valueType.
31
-     *
32
-     * @return string
33
-     */
34
-    public function getValueType() {
35
-
36
-        return 'TIME';
37
-
38
-    }
39
-
40
-    /**
41
-     * Sets the JSON value, as it would appear in a jCard or jCal object.
42
-     *
43
-     * The value must always be an array.
44
-     *
45
-     * @param array $value
46
-     *
47
-     * @return void
48
-     */
49
-    public function setJsonValue(array $value) {
50
-
51
-        // Removing colons from value.
52
-        $value = str_replace(
53
-            ':',
54
-            '',
55
-            $value
56
-        );
57
-
58
-        if (count($value) === 1) {
59
-            $this->setValue(reset($value));
60
-        } else {
61
-            $this->setValue($value);
62
-        }
63
-
64
-    }
65
-
66
-    /**
67
-     * Returns the value, in the format it should be encoded for json.
68
-     *
69
-     * This method must always return an array.
70
-     *
71
-     * @return array
72
-     */
73
-    public function getJsonValue() {
74
-
75
-        $parts = DateTimeParser::parseVCardTime($this->getValue());
76
-        $timeStr = '';
77
-
78
-        // Hour
79
-        if (!is_null($parts['hour'])) {
80
-            $timeStr .= $parts['hour'];
81
-
82
-            if (!is_null($parts['minute'])) {
83
-                $timeStr .= ':';
84
-            }
85
-        } else {
86
-            // We know either minute or second _must_ be set, so we insert a
87
-            // dash for an empty value.
88
-            $timeStr .= '-';
89
-        }
90
-
91
-        // Minute
92
-        if (!is_null($parts['minute'])) {
93
-            $timeStr .= $parts['minute'];
94
-
95
-            if (!is_null($parts['second'])) {
96
-                $timeStr .= ':';
97
-            }
98
-        } else {
99
-            if (isset($parts['second'])) {
100
-                // Dash for empty minute
101
-                $timeStr .= '-';
102
-            }
103
-        }
104
-
105
-        // Second
106
-        if (!is_null($parts['second'])) {
107
-            $timeStr .= $parts['second'];
108
-        }
109
-
110
-        // Timezone
111
-        if (!is_null($parts['timezone'])) {
112
-            if ($parts['timezone'] === 'Z') {
113
-                $timeStr .= 'Z';
114
-            } else {
115
-                $timeStr .=
116
-                    preg_replace('/([0-9]{2})([0-9]{2})$/', '$1:$2', $parts['timezone']);
117
-            }
118
-        }
119
-
120
-        return [$timeStr];
121
-
122
-    }
123
-
124
-    /**
125
-     * Hydrate data from a XML subtree, as it would appear in a xCard or xCal
126
-     * object.
127
-     *
128
-     * @param array $value
129
-     *
130
-     * @return void
131
-     */
132
-    public function setXmlValue(array $value) {
133
-
134
-        $value = array_map(
135
-            function($value) {
136
-                return str_replace(':', '', $value);
137
-            },
138
-            $value
139
-        );
140
-        parent::setXmlValue($value);
141
-
142
-    }
18
+	/**
19
+	 * In case this is a multi-value property. This string will be used as a
20
+	 * delimiter.
21
+	 *
22
+	 * @var string|null
23
+	 */
24
+	public $delimiter = null;
25
+
26
+	/**
27
+	 * Returns the type of value.
28
+	 *
29
+	 * This corresponds to the VALUE= parameter. Every property also has a
30
+	 * 'default' valueType.
31
+	 *
32
+	 * @return string
33
+	 */
34
+	public function getValueType() {
35
+
36
+		return 'TIME';
37
+
38
+	}
39
+
40
+	/**
41
+	 * Sets the JSON value, as it would appear in a jCard or jCal object.
42
+	 *
43
+	 * The value must always be an array.
44
+	 *
45
+	 * @param array $value
46
+	 *
47
+	 * @return void
48
+	 */
49
+	public function setJsonValue(array $value) {
50
+
51
+		// Removing colons from value.
52
+		$value = str_replace(
53
+			':',
54
+			'',
55
+			$value
56
+		);
57
+
58
+		if (count($value) === 1) {
59
+			$this->setValue(reset($value));
60
+		} else {
61
+			$this->setValue($value);
62
+		}
63
+
64
+	}
65
+
66
+	/**
67
+	 * Returns the value, in the format it should be encoded for json.
68
+	 *
69
+	 * This method must always return an array.
70
+	 *
71
+	 * @return array
72
+	 */
73
+	public function getJsonValue() {
74
+
75
+		$parts = DateTimeParser::parseVCardTime($this->getValue());
76
+		$timeStr = '';
77
+
78
+		// Hour
79
+		if (!is_null($parts['hour'])) {
80
+			$timeStr .= $parts['hour'];
81
+
82
+			if (!is_null($parts['minute'])) {
83
+				$timeStr .= ':';
84
+			}
85
+		} else {
86
+			// We know either minute or second _must_ be set, so we insert a
87
+			// dash for an empty value.
88
+			$timeStr .= '-';
89
+		}
90
+
91
+		// Minute
92
+		if (!is_null($parts['minute'])) {
93
+			$timeStr .= $parts['minute'];
94
+
95
+			if (!is_null($parts['second'])) {
96
+				$timeStr .= ':';
97
+			}
98
+		} else {
99
+			if (isset($parts['second'])) {
100
+				// Dash for empty minute
101
+				$timeStr .= '-';
102
+			}
103
+		}
104
+
105
+		// Second
106
+		if (!is_null($parts['second'])) {
107
+			$timeStr .= $parts['second'];
108
+		}
109
+
110
+		// Timezone
111
+		if (!is_null($parts['timezone'])) {
112
+			if ($parts['timezone'] === 'Z') {
113
+				$timeStr .= 'Z';
114
+			} else {
115
+				$timeStr .=
116
+					preg_replace('/([0-9]{2})([0-9]{2})$/', '$1:$2', $parts['timezone']);
117
+			}
118
+		}
119
+
120
+		return [$timeStr];
121
+
122
+	}
123
+
124
+	/**
125
+	 * Hydrate data from a XML subtree, as it would appear in a xCard or xCal
126
+	 * object.
127
+	 *
128
+	 * @param array $value
129
+	 *
130
+	 * @return void
131
+	 */
132
+	public function setXmlValue(array $value) {
133
+
134
+		$value = array_map(
135
+			function($value) {
136
+				return str_replace(':', '', $value);
137
+			},
138
+			$value
139
+		);
140
+		parent::setXmlValue($value);
141
+
142
+	}
143 143
 
144 144
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/ICalendar/DateTime.php 1 patch
Indentation   +375 added lines, -375 removed lines patch added patch discarded remove patch
@@ -26,379 +26,379 @@
 block discarded – undo
26 26
  */
27 27
 class DateTime extends Property {
28 28
 
29
-    /**
30
-     * In case this is a multi-value property. This string will be used as a
31
-     * delimiter.
32
-     *
33
-     * @var string|null
34
-     */
35
-    public $delimiter = ',';
36
-
37
-    /**
38
-     * Sets a multi-valued property.
39
-     *
40
-     * You may also specify DateTime objects here.
41
-     *
42
-     * @param array $parts
43
-     *
44
-     * @return void
45
-     */
46
-    public function setParts(array $parts) {
47
-
48
-        if (isset($parts[0]) && $parts[0] instanceof DateTimeInterface) {
49
-            $this->setDateTimes($parts);
50
-        } else {
51
-            parent::setParts($parts);
52
-        }
53
-
54
-    }
55
-
56
-    /**
57
-     * Updates the current value.
58
-     *
59
-     * This may be either a single, or multiple strings in an array.
60
-     *
61
-     * Instead of strings, you may also use DateTime here.
62
-     *
63
-     * @param string|array|DateTimeInterface $value
64
-     *
65
-     * @return void
66
-     */
67
-    public function setValue($value) {
68
-
69
-        if (is_array($value) && isset($value[0]) && $value[0] instanceof DateTimeInterface) {
70
-            $this->setDateTimes($value);
71
-        } elseif ($value instanceof DateTimeInterface) {
72
-            $this->setDateTimes([$value]);
73
-        } else {
74
-            parent::setValue($value);
75
-        }
76
-
77
-    }
78
-
79
-    /**
80
-     * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
81
-     *
82
-     * This has been 'unfolded', so only 1 line will be passed. Unescaping is
83
-     * not yet done, but parameters are not included.
84
-     *
85
-     * @param string $val
86
-     *
87
-     * @return void
88
-     */
89
-    public function setRawMimeDirValue($val) {
90
-
91
-        $this->setValue(explode($this->delimiter, $val));
92
-
93
-    }
94
-
95
-    /**
96
-     * Returns a raw mime-dir representation of the value.
97
-     *
98
-     * @return string
99
-     */
100
-    public function getRawMimeDirValue() {
101
-
102
-        return implode($this->delimiter, $this->getParts());
103
-
104
-    }
105
-
106
-    /**
107
-     * Returns true if this is a DATE-TIME value, false if it's a DATE.
108
-     *
109
-     * @return bool
110
-     */
111
-    public function hasTime() {
112
-
113
-        return strtoupper((string)$this['VALUE']) !== 'DATE';
114
-
115
-    }
116
-
117
-    /**
118
-     * Returns true if this is a floating DATE or DATE-TIME.
119
-     *
120
-     * Note that DATE is always floating.
121
-     */
122
-    public function isFloating() {
123
-
124
-        return
125
-            !$this->hasTime() ||
126
-            (
127
-                !isset($this['TZID']) &&
128
-                strpos($this->getValue(), 'Z') === false
129
-            );
130
-
131
-    }
132
-
133
-    /**
134
-     * Returns a date-time value.
135
-     *
136
-     * Note that if this property contained more than 1 date-time, only the
137
-     * first will be returned. To get an array with multiple values, call
138
-     * getDateTimes.
139
-     *
140
-     * If no timezone information is known, because it's either an all-day
141
-     * property or floating time, we will use the DateTimeZone argument to
142
-     * figure out the exact date.
143
-     *
144
-     * @param DateTimeZone $timeZone
145
-     *
146
-     * @return DateTimeImmutable
147
-     */
148
-    public function getDateTime(DateTimeZone $timeZone = null) {
149
-
150
-        $dt = $this->getDateTimes($timeZone);
151
-        if (!$dt) return;
152
-
153
-        return $dt[0];
154
-
155
-    }
156
-
157
-    /**
158
-     * Returns multiple date-time values.
159
-     *
160
-     * If no timezone information is known, because it's either an all-day
161
-     * property or floating time, we will use the DateTimeZone argument to
162
-     * figure out the exact date.
163
-     *
164
-     * @param DateTimeZone $timeZone
165
-     *
166
-     * @return DateTimeImmutable[]
167
-     * @return \DateTime[]
168
-     */
169
-    public function getDateTimes(DateTimeZone $timeZone = null) {
170
-
171
-        // Does the property have a TZID?
172
-        $tzid = $this['TZID'];
173
-
174
-        if ($tzid) {
175
-            $timeZone = TimeZoneUtil::getTimeZone((string)$tzid, $this->root);
176
-        }
177
-
178
-        $dts = [];
179
-        foreach ($this->getParts() as $part) {
180
-            $dts[] = DateTimeParser::parse($part, $timeZone);
181
-        }
182
-        return $dts;
183
-
184
-    }
185
-
186
-    /**
187
-     * Sets the property as a DateTime object.
188
-     *
189
-     * @param DateTimeInterface $dt
190
-     * @param bool isFloating If set to true, timezones will be ignored.
191
-     *
192
-     * @return void
193
-     */
194
-    public function setDateTime(DateTimeInterface $dt, $isFloating = false) {
195
-
196
-        $this->setDateTimes([$dt], $isFloating);
197
-
198
-    }
199
-
200
-    /**
201
-     * Sets the property as multiple date-time objects.
202
-     *
203
-     * The first value will be used as a reference for the timezones, and all
204
-     * the otehr values will be adjusted for that timezone
205
-     *
206
-     * @param DateTimeInterface[] $dt
207
-     * @param bool isFloating If set to true, timezones will be ignored.
208
-     *
209
-     * @return void
210
-     */
211
-    public function setDateTimes(array $dt, $isFloating = false) {
212
-
213
-        $values = [];
214
-
215
-        if ($this->hasTime()) {
216
-
217
-            $tz = null;
218
-            $isUtc = false;
219
-
220
-            foreach ($dt as $d) {
221
-
222
-                if ($isFloating) {
223
-                    $values[] = $d->format('Ymd\\THis');
224
-                    continue;
225
-                }
226
-                if (is_null($tz)) {
227
-                    $tz = $d->getTimeZone();
228
-                    $isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00']);
229
-                    if (!$isUtc) {
230
-                        $this->offsetSet('TZID', $tz->getName());
231
-                    }
232
-                } else {
233
-                    $d = $d->setTimeZone($tz);
234
-                }
235
-
236
-                if ($isUtc) {
237
-                    $values[] = $d->format('Ymd\\THis\\Z');
238
-                } else {
239
-                    $values[] = $d->format('Ymd\\THis');
240
-                }
241
-
242
-            }
243
-            if ($isUtc || $isFloating) {
244
-                $this->offsetUnset('TZID');
245
-            }
246
-
247
-        } else {
248
-
249
-            foreach ($dt as $d) {
250
-
251
-                $values[] = $d->format('Ymd');
252
-
253
-            }
254
-            $this->offsetUnset('TZID');
255
-
256
-        }
257
-
258
-        $this->value = $values;
259
-
260
-    }
261
-
262
-    /**
263
-     * Returns the type of value.
264
-     *
265
-     * This corresponds to the VALUE= parameter. Every property also has a
266
-     * 'default' valueType.
267
-     *
268
-     * @return string
269
-     */
270
-    public function getValueType() {
271
-
272
-        return $this->hasTime() ? 'DATE-TIME' : 'DATE';
273
-
274
-    }
275
-
276
-    /**
277
-     * Returns the value, in the format it should be encoded for JSON.
278
-     *
279
-     * This method must always return an array.
280
-     *
281
-     * @return array
282
-     */
283
-    public function getJsonValue() {
284
-
285
-        $dts = $this->getDateTimes();
286
-        $hasTime = $this->hasTime();
287
-        $isFloating = $this->isFloating();
288
-
289
-        $tz = $dts[0]->getTimeZone();
290
-        $isUtc = $isFloating ? false : in_array($tz->getName(), ['UTC', 'GMT', 'Z']);
291
-
292
-        return array_map(
293
-            function(DateTimeInterface $dt) use ($hasTime, $isUtc) {
294
-
295
-                if ($hasTime) {
296
-                    return $dt->format('Y-m-d\\TH:i:s') . ($isUtc ? 'Z' : '');
297
-                } else {
298
-                    return $dt->format('Y-m-d');
299
-                }
300
-
301
-            },
302
-            $dts
303
-        );
304
-
305
-    }
306
-
307
-    /**
308
-     * Sets the json value, as it would appear in a jCard or jCal object.
309
-     *
310
-     * The value must always be an array.
311
-     *
312
-     * @param array $value
313
-     *
314
-     * @return void
315
-     */
316
-    public function setJsonValue(array $value) {
317
-
318
-        // dates and times in jCal have one difference to dates and times in
319
-        // iCalendar. In jCal date-parts are separated by dashes, and
320
-        // time-parts are separated by colons. It makes sense to just remove
321
-        // those.
322
-        $this->setValue(
323
-            array_map(
324
-                public function($item) {
325
-
326
-                    return strtr($item, [':' => '', '-' => '']);
327
-
328
-                },
329
-                $value
330
-            )
331
-        );
332
-
333
-    }
334
-
335
-    /**
336
-     * We need to intercept offsetSet, because it may be used to alter the
337
-     * VALUE from DATE-TIME to DATE or vice-versa.
338
-     *
339
-     * @param string $name
340
-     * @param mixed $value
341
-     *
342
-     * @return void
343
-     */
344
-    public function offsetSet($name, $value) {
345
-
346
-        parent::offsetSet($name, $value);
347
-        if (strtoupper($name) !== 'VALUE') {
348
-            return;
349
-        }
350
-
351
-        // This will ensure that dates are correctly encoded.
352
-        $this->setDateTimes($this->getDateTimes());
353
-
354
-    }
355
-
356
-    /**
357
-     * Validates the node for correctness.
358
-     *
359
-     * The following options are supported:
360
-     *   Node::REPAIR - May attempt to automatically repair the problem.
361
-     *
362
-     * This method returns an array with detected problems.
363
-     * Every element has the following properties:
364
-     *
365
-     *  * level - problem level.
366
-     *  * message - A human-readable string describing the issue.
367
-     *  * node - A reference to the problematic node.
368
-     *
369
-     * The level means:
370
-     *   1 - The issue was repaired (only happens if REPAIR was turned on)
371
-     *   2 - An inconsequential issue
372
-     *   3 - A severe issue.
373
-     *
374
-     * @param int $options
375
-     *
376
-     * @return array
377
-     */
378
-    public function validate($options = 0) {
379
-
380
-        $messages = parent::validate($options);
381
-        $valueType = $this->getValueType();
382
-        $values = $this->getParts();
383
-        try {
384
-            foreach ($values as $value) {
385
-                switch ($valueType) {
386
-                    case 'DATE' :
387
-                        DateTimeParser::parseDate($value);
388
-                        break;
389
-                    case 'DATE-TIME' :
390
-                        DateTimeParser::parseDateTime($value);
391
-                        break;
392
-                }
393
-            }
394
-        } catch (InvalidDataException $e) {
395
-            $messages[] = [
396
-                'level'   => 3,
397
-                'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType,
398
-                'node'    => $this,
399
-            ];
400
-        }
401
-        return $messages;
402
-
403
-    }
29
+	/**
30
+	 * In case this is a multi-value property. This string will be used as a
31
+	 * delimiter.
32
+	 *
33
+	 * @var string|null
34
+	 */
35
+	public $delimiter = ',';
36
+
37
+	/**
38
+	 * Sets a multi-valued property.
39
+	 *
40
+	 * You may also specify DateTime objects here.
41
+	 *
42
+	 * @param array $parts
43
+	 *
44
+	 * @return void
45
+	 */
46
+	public function setParts(array $parts) {
47
+
48
+		if (isset($parts[0]) && $parts[0] instanceof DateTimeInterface) {
49
+			$this->setDateTimes($parts);
50
+		} else {
51
+			parent::setParts($parts);
52
+		}
53
+
54
+	}
55
+
56
+	/**
57
+	 * Updates the current value.
58
+	 *
59
+	 * This may be either a single, or multiple strings in an array.
60
+	 *
61
+	 * Instead of strings, you may also use DateTime here.
62
+	 *
63
+	 * @param string|array|DateTimeInterface $value
64
+	 *
65
+	 * @return void
66
+	 */
67
+	public function setValue($value) {
68
+
69
+		if (is_array($value) && isset($value[0]) && $value[0] instanceof DateTimeInterface) {
70
+			$this->setDateTimes($value);
71
+		} elseif ($value instanceof DateTimeInterface) {
72
+			$this->setDateTimes([$value]);
73
+		} else {
74
+			parent::setValue($value);
75
+		}
76
+
77
+	}
78
+
79
+	/**
80
+	 * Sets a raw value coming from a mimedir (iCalendar/vCard) file.
81
+	 *
82
+	 * This has been 'unfolded', so only 1 line will be passed. Unescaping is
83
+	 * not yet done, but parameters are not included.
84
+	 *
85
+	 * @param string $val
86
+	 *
87
+	 * @return void
88
+	 */
89
+	public function setRawMimeDirValue($val) {
90
+
91
+		$this->setValue(explode($this->delimiter, $val));
92
+
93
+	}
94
+
95
+	/**
96
+	 * Returns a raw mime-dir representation of the value.
97
+	 *
98
+	 * @return string
99
+	 */
100
+	public function getRawMimeDirValue() {
101
+
102
+		return implode($this->delimiter, $this->getParts());
103
+
104
+	}
105
+
106
+	/**
107
+	 * Returns true if this is a DATE-TIME value, false if it's a DATE.
108
+	 *
109
+	 * @return bool
110
+	 */
111
+	public function hasTime() {
112
+
113
+		return strtoupper((string)$this['VALUE']) !== 'DATE';
114
+
115
+	}
116
+
117
+	/**
118
+	 * Returns true if this is a floating DATE or DATE-TIME.
119
+	 *
120
+	 * Note that DATE is always floating.
121
+	 */
122
+	public function isFloating() {
123
+
124
+		return
125
+			!$this->hasTime() ||
126
+			(
127
+				!isset($this['TZID']) &&
128
+				strpos($this->getValue(), 'Z') === false
129
+			);
130
+
131
+	}
132
+
133
+	/**
134
+	 * Returns a date-time value.
135
+	 *
136
+	 * Note that if this property contained more than 1 date-time, only the
137
+	 * first will be returned. To get an array with multiple values, call
138
+	 * getDateTimes.
139
+	 *
140
+	 * If no timezone information is known, because it's either an all-day
141
+	 * property or floating time, we will use the DateTimeZone argument to
142
+	 * figure out the exact date.
143
+	 *
144
+	 * @param DateTimeZone $timeZone
145
+	 *
146
+	 * @return DateTimeImmutable
147
+	 */
148
+	public function getDateTime(DateTimeZone $timeZone = null) {
149
+
150
+		$dt = $this->getDateTimes($timeZone);
151
+		if (!$dt) return;
152
+
153
+		return $dt[0];
154
+
155
+	}
156
+
157
+	/**
158
+	 * Returns multiple date-time values.
159
+	 *
160
+	 * If no timezone information is known, because it's either an all-day
161
+	 * property or floating time, we will use the DateTimeZone argument to
162
+	 * figure out the exact date.
163
+	 *
164
+	 * @param DateTimeZone $timeZone
165
+	 *
166
+	 * @return DateTimeImmutable[]
167
+	 * @return \DateTime[]
168
+	 */
169
+	public function getDateTimes(DateTimeZone $timeZone = null) {
170
+
171
+		// Does the property have a TZID?
172
+		$tzid = $this['TZID'];
173
+
174
+		if ($tzid) {
175
+			$timeZone = TimeZoneUtil::getTimeZone((string)$tzid, $this->root);
176
+		}
177
+
178
+		$dts = [];
179
+		foreach ($this->getParts() as $part) {
180
+			$dts[] = DateTimeParser::parse($part, $timeZone);
181
+		}
182
+		return $dts;
183
+
184
+	}
185
+
186
+	/**
187
+	 * Sets the property as a DateTime object.
188
+	 *
189
+	 * @param DateTimeInterface $dt
190
+	 * @param bool isFloating If set to true, timezones will be ignored.
191
+	 *
192
+	 * @return void
193
+	 */
194
+	public function setDateTime(DateTimeInterface $dt, $isFloating = false) {
195
+
196
+		$this->setDateTimes([$dt], $isFloating);
197
+
198
+	}
199
+
200
+	/**
201
+	 * Sets the property as multiple date-time objects.
202
+	 *
203
+	 * The first value will be used as a reference for the timezones, and all
204
+	 * the otehr values will be adjusted for that timezone
205
+	 *
206
+	 * @param DateTimeInterface[] $dt
207
+	 * @param bool isFloating If set to true, timezones will be ignored.
208
+	 *
209
+	 * @return void
210
+	 */
211
+	public function setDateTimes(array $dt, $isFloating = false) {
212
+
213
+		$values = [];
214
+
215
+		if ($this->hasTime()) {
216
+
217
+			$tz = null;
218
+			$isUtc = false;
219
+
220
+			foreach ($dt as $d) {
221
+
222
+				if ($isFloating) {
223
+					$values[] = $d->format('Ymd\\THis');
224
+					continue;
225
+				}
226
+				if (is_null($tz)) {
227
+					$tz = $d->getTimeZone();
228
+					$isUtc = in_array($tz->getName(), ['UTC', 'GMT', 'Z', '+00:00']);
229
+					if (!$isUtc) {
230
+						$this->offsetSet('TZID', $tz->getName());
231
+					}
232
+				} else {
233
+					$d = $d->setTimeZone($tz);
234
+				}
235
+
236
+				if ($isUtc) {
237
+					$values[] = $d->format('Ymd\\THis\\Z');
238
+				} else {
239
+					$values[] = $d->format('Ymd\\THis');
240
+				}
241
+
242
+			}
243
+			if ($isUtc || $isFloating) {
244
+				$this->offsetUnset('TZID');
245
+			}
246
+
247
+		} else {
248
+
249
+			foreach ($dt as $d) {
250
+
251
+				$values[] = $d->format('Ymd');
252
+
253
+			}
254
+			$this->offsetUnset('TZID');
255
+
256
+		}
257
+
258
+		$this->value = $values;
259
+
260
+	}
261
+
262
+	/**
263
+	 * Returns the type of value.
264
+	 *
265
+	 * This corresponds to the VALUE= parameter. Every property also has a
266
+	 * 'default' valueType.
267
+	 *
268
+	 * @return string
269
+	 */
270
+	public function getValueType() {
271
+
272
+		return $this->hasTime() ? 'DATE-TIME' : 'DATE';
273
+
274
+	}
275
+
276
+	/**
277
+	 * Returns the value, in the format it should be encoded for JSON.
278
+	 *
279
+	 * This method must always return an array.
280
+	 *
281
+	 * @return array
282
+	 */
283
+	public function getJsonValue() {
284
+
285
+		$dts = $this->getDateTimes();
286
+		$hasTime = $this->hasTime();
287
+		$isFloating = $this->isFloating();
288
+
289
+		$tz = $dts[0]->getTimeZone();
290
+		$isUtc = $isFloating ? false : in_array($tz->getName(), ['UTC', 'GMT', 'Z']);
291
+
292
+		return array_map(
293
+			function(DateTimeInterface $dt) use ($hasTime, $isUtc) {
294
+
295
+				if ($hasTime) {
296
+					return $dt->format('Y-m-d\\TH:i:s') . ($isUtc ? 'Z' : '');
297
+				} else {
298
+					return $dt->format('Y-m-d');
299
+				}
300
+
301
+			},
302
+			$dts
303
+		);
304
+
305
+	}
306
+
307
+	/**
308
+	 * Sets the json value, as it would appear in a jCard or jCal object.
309
+	 *
310
+	 * The value must always be an array.
311
+	 *
312
+	 * @param array $value
313
+	 *
314
+	 * @return void
315
+	 */
316
+	public function setJsonValue(array $value) {
317
+
318
+		// dates and times in jCal have one difference to dates and times in
319
+		// iCalendar. In jCal date-parts are separated by dashes, and
320
+		// time-parts are separated by colons. It makes sense to just remove
321
+		// those.
322
+		$this->setValue(
323
+			array_map(
324
+				public function($item) {
325
+
326
+					return strtr($item, [':' => '', '-' => '']);
327
+
328
+				},
329
+				$value
330
+			)
331
+		);
332
+
333
+	}
334
+
335
+	/**
336
+	 * We need to intercept offsetSet, because it may be used to alter the
337
+	 * VALUE from DATE-TIME to DATE or vice-versa.
338
+	 *
339
+	 * @param string $name
340
+	 * @param mixed $value
341
+	 *
342
+	 * @return void
343
+	 */
344
+	public function offsetSet($name, $value) {
345
+
346
+		parent::offsetSet($name, $value);
347
+		if (strtoupper($name) !== 'VALUE') {
348
+			return;
349
+		}
350
+
351
+		// This will ensure that dates are correctly encoded.
352
+		$this->setDateTimes($this->getDateTimes());
353
+
354
+	}
355
+
356
+	/**
357
+	 * Validates the node for correctness.
358
+	 *
359
+	 * The following options are supported:
360
+	 *   Node::REPAIR - May attempt to automatically repair the problem.
361
+	 *
362
+	 * This method returns an array with detected problems.
363
+	 * Every element has the following properties:
364
+	 *
365
+	 *  * level - problem level.
366
+	 *  * message - A human-readable string describing the issue.
367
+	 *  * node - A reference to the problematic node.
368
+	 *
369
+	 * The level means:
370
+	 *   1 - The issue was repaired (only happens if REPAIR was turned on)
371
+	 *   2 - An inconsequential issue
372
+	 *   3 - A severe issue.
373
+	 *
374
+	 * @param int $options
375
+	 *
376
+	 * @return array
377
+	 */
378
+	public function validate($options = 0) {
379
+
380
+		$messages = parent::validate($options);
381
+		$valueType = $this->getValueType();
382
+		$values = $this->getParts();
383
+		try {
384
+			foreach ($values as $value) {
385
+				switch ($valueType) {
386
+					case 'DATE' :
387
+						DateTimeParser::parseDate($value);
388
+						break;
389
+					case 'DATE-TIME' :
390
+						DateTimeParser::parseDateTime($value);
391
+						break;
392
+				}
393
+			}
394
+		} catch (InvalidDataException $e) {
395
+			$messages[] = [
396
+				'level'   => 3,
397
+				'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType,
398
+				'node'    => $this,
399
+			];
400
+		}
401
+		return $messages;
402
+
403
+	}
404 404
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Property/UtcOffset.php 1 patch
Indentation   +52 added lines, -52 removed lines patch added patch discarded remove patch
@@ -13,65 +13,65 @@
 block discarded – undo
13 13
  */
14 14
 class UtcOffset extends Text {
15 15
 
16
-    /**
17
-     * In case this is a multi-value property. This string will be used as a
18
-     * delimiter.
19
-     *
20
-     * @var string|null
21
-     */
22
-    public $delimiter = null;
16
+	/**
17
+	 * In case this is a multi-value property. This string will be used as a
18
+	 * delimiter.
19
+	 *
20
+	 * @var string|null
21
+	 */
22
+	public $delimiter = null;
23 23
 
24
-    /**
25
-     * Returns the type of value.
26
-     *
27
-     * This corresponds to the VALUE= parameter. Every property also has a
28
-     * 'default' valueType.
29
-     *
30
-     * @return string
31
-     */
32
-    public function getValueType() {
24
+	/**
25
+	 * Returns the type of value.
26
+	 *
27
+	 * This corresponds to the VALUE= parameter. Every property also has a
28
+	 * 'default' valueType.
29
+	 *
30
+	 * @return string
31
+	 */
32
+	public function getValueType() {
33 33
 
34
-        return 'UTC-OFFSET';
34
+		return 'UTC-OFFSET';
35 35
 
36
-    }
36
+	}
37 37
 
38
-    /**
39
-     * Sets the JSON value, as it would appear in a jCard or jCal object.
40
-     *
41
-     * The value must always be an array.
42
-     *
43
-     * @param array $value
44
-     *
45
-     * @return void
46
-     */
47
-    public function setJsonValue(array $value) {
38
+	/**
39
+	 * Sets the JSON value, as it would appear in a jCard or jCal object.
40
+	 *
41
+	 * The value must always be an array.
42
+	 *
43
+	 * @param array $value
44
+	 *
45
+	 * @return void
46
+	 */
47
+	public function setJsonValue(array $value) {
48 48
 
49
-        $value = array_map(
50
-            function($value) {
51
-                return str_replace(':', '', $value);
52
-            },
53
-            $value
54
-        );
55
-        parent::setJsonValue($value);
49
+		$value = array_map(
50
+			function($value) {
51
+				return str_replace(':', '', $value);
52
+			},
53
+			$value
54
+		);
55
+		parent::setJsonValue($value);
56 56
 
57
-    }
57
+	}
58 58
 
59
-    /**
60
-     * Returns the value, in the format it should be encoded for JSON.
61
-     *
62
-     * This method must always return an array.
63
-     *
64
-     * @return array
65
-     */
66
-    public function getJsonValue() {
59
+	/**
60
+	 * Returns the value, in the format it should be encoded for JSON.
61
+	 *
62
+	 * This method must always return an array.
63
+	 *
64
+	 * @return array
65
+	 */
66
+	public function getJsonValue() {
67 67
 
68
-        return array_map(
69
-            public function($value) {
70
-                return substr($value, 0, -2) . ':' .
71
-                       substr($value, -2);
72
-            },
73
-            parent::getJsonValue()
74
-        );
68
+		return array_map(
69
+			public function($value) {
70
+				return substr($value, 0, -2) . ':' .
71
+					   substr($value, -2);
72
+			},
73
+			parent::getJsonValue()
74
+		);
75 75
 
76
-    }
76
+	}
77 77
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Parser/Json.php 1 patch
Indentation   +175 added lines, -175 removed lines patch added patch discarded remove patch
@@ -18,180 +18,180 @@
 block discarded – undo
18 18
  */
19 19
 class Json extends Parser {
20 20
 
21
-    /**
22
-     * The input data.
23
-     *
24
-     * @var array
25
-     */
26
-    protected $input;
27
-
28
-    /**
29
-     * Root component.
30
-     *
31
-     * @var Document
32
-     */
33
-    protected $root;
34
-
35
-    /**
36
-     * This method starts the parsing process.
37
-     *
38
-     * If the input was not supplied during construction, it's possible to pass
39
-     * it here instead.
40
-     *
41
-     * If either input or options are not supplied, the defaults will be used.
42
-     *
43
-     * @param resource|string|array|null $input
44
-     * @param int $options
45
-     *
46
-     * @return Sabre\VObject\Document
47
-     */
48
-    public function parse($input = null, $options = 0) {
49
-
50
-        if (!is_null($input)) {
51
-            $this->setInput($input);
52
-        }
53
-        if (is_null($this->input)) {
54
-            throw new EofException('End of input stream, or no input supplied');
55
-        }
56
-
57
-        if (0 !== $options) {
58
-            $this->options = $options;
59
-        }
60
-
61
-        switch ($this->input[0]) {
62
-            case 'vcalendar' :
63
-                $this->root = new VCalendar([], false);
64
-                break;
65
-            case 'vcard' :
66
-                $this->root = new VCard([], false);
67
-                break;
68
-            default :
69
-                throw new ParseException('The root component must either be a vcalendar, or a vcard');
70
-
71
-        }
72
-        foreach ($this->input[1] as $prop) {
73
-            $this->root->add($this->parseProperty($prop));
74
-        }
75
-        if (isset($this->input[2])) foreach ($this->input[2] as $comp) {
76
-            $this->root->add($this->parseComponent($comp));
77
-        }
78
-
79
-        // Resetting the input so we can throw an feof exception the next time.
80
-        $this->input = null;
81
-
82
-        return $this->root;
83
-
84
-    }
85
-
86
-    /**
87
-     * Parses a component.
88
-     *
89
-     * @param array $jComp
90
-     *
91
-     * @return \Sabre\VObject\Component
92
-     */
93
-    public function parseComponent(array $jComp) {
94
-
95
-        // We can remove $self from PHP 5.4 onward.
96
-        $self = $this;
97
-
98
-        $properties = array_map(
99
-            function($jProp) use ($self) {
100
-                return $self->parseProperty($jProp);
101
-            },
102
-            $jComp[1]
103
-        );
104
-
105
-        if (isset($jComp[2])) {
106
-
107
-            $components = array_map(
108
-                public function($jComp) use ($self) {
109
-                    return $self->parseComponent($jComp);
110
-                },
111
-                $jComp[2]
112
-            );
113
-
114
-        } else $components = [];
115
-
116
-        return $this->root->createComponent(
117
-            $jComp[0],
118
-            array_merge($properties, $components),
119
-            $defaults = false
120
-        );
121
-
122
-    }
123
-
124
-    /**
125
-     * Parses properties.
126
-     *
127
-     * @param array $jProp
128
-     *
129
-     * @return \Sabre\VObject\Property
130
-     */
131
-    public function parseProperty(array $jProp) {
132
-
133
-        list(
134
-            $propertyName,
135
-            $parameters,
136
-            $valueType
137
-        ) = $jProp;
138
-
139
-        $propertyName = strtoupper($propertyName);
140
-
141
-        // This is the default class we would be using if we didn't know the
142
-        // value type. We're using this value later in this function.
143
-        $defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName);
144
-
145
-        $parameters = (array)$parameters;
146
-
147
-        $value = array_slice($jProp, 3);
148
-
149
-        $valueType = strtoupper($valueType);
150
-
151
-        if (isset($parameters['group'])) {
152
-            $propertyName = $parameters['group'] . '.' . $propertyName;
153
-            unset($parameters['group']);
154
-        }
155
-
156
-        $prop = $this->root->createProperty($propertyName, null, $parameters, $valueType);
157
-        $prop->setJsonValue($value);
158
-
159
-        // We have to do something awkward here. FlatText as well as Text
160
-        // represents TEXT values. We have to normalize these here. In the
161
-        // future we can get rid of FlatText once we're allowed to break BC
162
-        // again.
163
-        if ($defaultPropertyClass === 'Sabre\VObject\Property\FlatText') {
164
-            $defaultPropertyClass = 'Sabre\VObject\Property\Text';
165
-        }
166
-
167
-        // If the value type we received (e.g.: TEXT) was not the default value
168
-        // type for the given property (e.g.: BDAY), we need to add a VALUE=
169
-        // parameter.
170
-        if ($defaultPropertyClass !== get_class($prop)) {
171
-            $prop["VALUE"] = $valueType;
172
-        }
173
-
174
-        return $prop;
175
-
176
-    }
177
-
178
-    /**
179
-     * Sets the input data.
180
-     *
181
-     * @param resource|string|array $input
182
-     *
183
-     * @return void
184
-     */
185
-    public function setInput($input) {
186
-
187
-        if (is_resource($input)) {
188
-            $input = stream_get_contents($input);
189
-        }
190
-        if (is_string($input)) {
191
-            $input = json_decode($input);
192
-        }
193
-        $this->input = $input;
194
-
195
-    }
21
+	/**
22
+	 * The input data.
23
+	 *
24
+	 * @var array
25
+	 */
26
+	protected $input;
27
+
28
+	/**
29
+	 * Root component.
30
+	 *
31
+	 * @var Document
32
+	 */
33
+	protected $root;
34
+
35
+	/**
36
+	 * This method starts the parsing process.
37
+	 *
38
+	 * If the input was not supplied during construction, it's possible to pass
39
+	 * it here instead.
40
+	 *
41
+	 * If either input or options are not supplied, the defaults will be used.
42
+	 *
43
+	 * @param resource|string|array|null $input
44
+	 * @param int $options
45
+	 *
46
+	 * @return Sabre\VObject\Document
47
+	 */
48
+	public function parse($input = null, $options = 0) {
49
+
50
+		if (!is_null($input)) {
51
+			$this->setInput($input);
52
+		}
53
+		if (is_null($this->input)) {
54
+			throw new EofException('End of input stream, or no input supplied');
55
+		}
56
+
57
+		if (0 !== $options) {
58
+			$this->options = $options;
59
+		}
60
+
61
+		switch ($this->input[0]) {
62
+			case 'vcalendar' :
63
+				$this->root = new VCalendar([], false);
64
+				break;
65
+			case 'vcard' :
66
+				$this->root = new VCard([], false);
67
+				break;
68
+			default :
69
+				throw new ParseException('The root component must either be a vcalendar, or a vcard');
70
+
71
+		}
72
+		foreach ($this->input[1] as $prop) {
73
+			$this->root->add($this->parseProperty($prop));
74
+		}
75
+		if (isset($this->input[2])) foreach ($this->input[2] as $comp) {
76
+			$this->root->add($this->parseComponent($comp));
77
+		}
78
+
79
+		// Resetting the input so we can throw an feof exception the next time.
80
+		$this->input = null;
81
+
82
+		return $this->root;
83
+
84
+	}
85
+
86
+	/**
87
+	 * Parses a component.
88
+	 *
89
+	 * @param array $jComp
90
+	 *
91
+	 * @return \Sabre\VObject\Component
92
+	 */
93
+	public function parseComponent(array $jComp) {
94
+
95
+		// We can remove $self from PHP 5.4 onward.
96
+		$self = $this;
97
+
98
+		$properties = array_map(
99
+			function($jProp) use ($self) {
100
+				return $self->parseProperty($jProp);
101
+			},
102
+			$jComp[1]
103
+		);
104
+
105
+		if (isset($jComp[2])) {
106
+
107
+			$components = array_map(
108
+				public function($jComp) use ($self) {
109
+					return $self->parseComponent($jComp);
110
+				},
111
+				$jComp[2]
112
+			);
113
+
114
+		} else $components = [];
115
+
116
+		return $this->root->createComponent(
117
+			$jComp[0],
118
+			array_merge($properties, $components),
119
+			$defaults = false
120
+		);
121
+
122
+	}
123
+
124
+	/**
125
+	 * Parses properties.
126
+	 *
127
+	 * @param array $jProp
128
+	 *
129
+	 * @return \Sabre\VObject\Property
130
+	 */
131
+	public function parseProperty(array $jProp) {
132
+
133
+		list(
134
+			$propertyName,
135
+			$parameters,
136
+			$valueType
137
+		) = $jProp;
138
+
139
+		$propertyName = strtoupper($propertyName);
140
+
141
+		// This is the default class we would be using if we didn't know the
142
+		// value type. We're using this value later in this function.
143
+		$defaultPropertyClass = $this->root->getClassNameForPropertyName($propertyName);
144
+
145
+		$parameters = (array)$parameters;
146
+
147
+		$value = array_slice($jProp, 3);
148
+
149
+		$valueType = strtoupper($valueType);
150
+
151
+		if (isset($parameters['group'])) {
152
+			$propertyName = $parameters['group'] . '.' . $propertyName;
153
+			unset($parameters['group']);
154
+		}
155
+
156
+		$prop = $this->root->createProperty($propertyName, null, $parameters, $valueType);
157
+		$prop->setJsonValue($value);
158
+
159
+		// We have to do something awkward here. FlatText as well as Text
160
+		// represents TEXT values. We have to normalize these here. In the
161
+		// future we can get rid of FlatText once we're allowed to break BC
162
+		// again.
163
+		if ($defaultPropertyClass === 'Sabre\VObject\Property\FlatText') {
164
+			$defaultPropertyClass = 'Sabre\VObject\Property\Text';
165
+		}
166
+
167
+		// If the value type we received (e.g.: TEXT) was not the default value
168
+		// type for the given property (e.g.: BDAY), we need to add a VALUE=
169
+		// parameter.
170
+		if ($defaultPropertyClass !== get_class($prop)) {
171
+			$prop["VALUE"] = $valueType;
172
+		}
173
+
174
+		return $prop;
175
+
176
+	}
177
+
178
+	/**
179
+	 * Sets the input data.
180
+	 *
181
+	 * @param resource|string|array $input
182
+	 *
183
+	 * @return void
184
+	 */
185
+	public function setInput($input) {
186
+
187
+		if (is_resource($input)) {
188
+			$input = stream_get_contents($input);
189
+		}
190
+		if (is_string($input)) {
191
+			$input = json_decode($input);
192
+		}
193
+		$this->input = $input;
194
+
195
+	}
196 196
 
197 197
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Component.php 1 patch
Indentation   +680 added lines, -680 removed lines patch added patch discarded remove patch
@@ -16,685 +16,685 @@
 block discarded – undo
16 16
  */
17 17
 class Component extends Node {
18 18
 
19
-    /**
20
-     * Component name.
21
-     *
22
-     * This will contain a string such as VEVENT, VTODO, VCALENDAR, VCARD.
23
-     *
24
-     * @var string
25
-     */
26
-    public $name;
27
-
28
-    /**
29
-     * A list of properties and/or sub-components.
30
-     *
31
-     * @var array
32
-     */
33
-    protected $children = [];
34
-
35
-    /**
36
-     * Creates a new component.
37
-     *
38
-     * You can specify the children either in key=>value syntax, in which case
39
-     * properties will automatically be created, or you can just pass a list of
40
-     * Component and Property object.
41
-     *
42
-     * By default, a set of sensible values will be added to the component. For
43
-     * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
44
-     * ensure that this does not happen, set $defaults to false.
45
-     *
46
-     * @param Document $root
47
-     * @param string $name such as VCALENDAR, VEVENT.
48
-     * @param array $children
49
-     * @param bool $defaults
50
-     *
51
-     * @return void
52
-     */
53
-    public function __construct(Document $root, $name, array $children = [], $defaults = true) {
54
-
55
-        $this->name = strtoupper($name);
56
-        $this->root = $root;
57
-
58
-        if ($defaults) {
59
-            // This is a terribly convoluted way to do this, but this ensures
60
-            // that the order of properties as they are specified in both
61
-            // defaults and the childrens list, are inserted in the object in a
62
-            // natural way.
63
-            $list = $this->getDefaults();
64
-            $nodes = [];
65
-            foreach ($children as $key => $value) {
66
-                if ($value instanceof Node) {
67
-                    if (isset($list[$value->name])) {
68
-                        unset($list[$value->name]);
69
-                    }
70
-                    $nodes[] = $value;
71
-                } else {
72
-                    $list[$key] = $value;
73
-                }
74
-            }
75
-            foreach ($list as $key => $value) {
76
-                $this->add($key, $value);
77
-            }
78
-            foreach ($nodes as $node) {
79
-                $this->add($node);
80
-            }
81
-        } else {
82
-            foreach ($children as $k => $child) {
83
-                if ($child instanceof Node) {
84
-                    // Component or Property
85
-                    $this->add($child);
86
-                } else {
87
-
88
-                    // Property key=>value
89
-                    $this->add($k, $child);
90
-                }
91
-            }
92
-        }
93
-
94
-    }
95
-
96
-    /**
97
-     * Adds a new property or component, and returns the new item.
98
-     *
99
-     * This method has 3 possible signatures:
100
-     *
101
-     * add(Component $comp) // Adds a new component
102
-     * add(Property $prop)  // Adds a new property
103
-     * add($name, $value, array $parameters = []) // Adds a new property
104
-     * add($name, array $children = []) // Adds a new component
105
-     * by name.
106
-     *
107
-     * @return Node
108
-     */
109
-    public function add() {
110
-
111
-        $arguments = func_get_args();
112
-
113
-        if ($arguments[0] instanceof Node) {
114
-            if (isset($arguments[1])) {
115
-                throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node');
116
-            }
117
-            $arguments[0]->parent = $this;
118
-            $newNode = $arguments[0];
119
-
120
-        } elseif (is_string($arguments[0])) {
121
-
122
-            $newNode = call_user_func_array([$this->root, 'create'], $arguments);
123
-
124
-        } else {
125
-
126
-            throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string');
127
-
128
-        }
129
-
130
-        $name = $newNode->name;
131
-        if (isset($this->children[$name])) {
132
-            $this->children[$name][] = $newNode;
133
-        } else {
134
-            $this->children[$name] = [$newNode];
135
-        }
136
-        return $newNode;
137
-
138
-    }
139
-
140
-    /**
141
-     * This method removes a component or property from this component.
142
-     *
143
-     * You can either specify the item by name (like DTSTART), in which case
144
-     * all properties/components with that name will be removed, or you can
145
-     * pass an instance of a property or component, in which case only that
146
-     * exact item will be removed.
147
-     *
148
-     * @param string|Property|Component $item
149
-     * @return void
150
-     */
151
-    public function remove($item) {
152
-
153
-        if (is_string($item)) {
154
-            // If there's no dot in the name, it's an exact property name and
155
-            // we can just wipe out all those properties.
156
-            //
157
-            if (strpos($item, '.') === false) {
158
-                unset($this->children[strtoupper($item)]);
159
-                return;
160
-            }
161
-            // If there was a dot, we need to ask select() to help us out and
162
-            // then we just call remove recursively.
163
-            foreach ($this->select($item) as $child) {
164
-
165
-                $this->remove($child);
166
-
167
-            }
168
-        } else {
169
-            foreach ($this->select($item->name) as $k => $child) {
170
-                if ($child === $item) {
171
-                    unset($this->children[$item->name][$k]);
172
-                    return;
173
-                }
174
-            }
175
-        }
176
-
177
-        throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component');
178
-
179
-    }
180
-
181
-    /**
182
-     * Returns a flat list of all the properties and components in this
183
-     * component.
184
-     *
185
-     * @return array
186
-     */
187
-    public function children() {
188
-
189
-        $result = [];
190
-        foreach ($this->children as $childGroup) {
191
-            $result = array_merge($result, $childGroup);
192
-        }
193
-        return $result;
194
-
195
-    }
196
-
197
-    /**
198
-     * This method only returns a list of sub-components. Properties are
199
-     * ignored.
200
-     *
201
-     * @return array
202
-     */
203
-    public function getComponents() {
204
-
205
-        $result = [];
206
-
207
-        foreach ($this->children as $childGroup) {
208
-            foreach ($childGroup as $child) {
209
-                if ($child instanceof self) {
210
-                    $result[] = $child;
211
-                }
212
-            }
213
-        }
214
-        return $result;
215
-
216
-    }
217
-
218
-    /**
219
-     * Returns an array with elements that match the specified name.
220
-     *
221
-     * This function is also aware of MIME-Directory groups (as they appear in
222
-     * vcards). This means that if a property is grouped as "HOME.EMAIL", it
223
-     * will also be returned when searching for just "EMAIL". If you want to
224
-     * search for a property in a specific group, you can select on the entire
225
-     * string ("HOME.EMAIL"). If you want to search on a specific property that
226
-     * has not been assigned a group, specify ".EMAIL".
227
-     *
228
-     * @param string $name
229
-     * @return array
230
-     */
231
-    public function select($name) {
232
-
233
-        $group = null;
234
-        $name = strtoupper($name);
235
-        if (strpos($name, '.') !== false) {
236
-            list($group, $name) = explode('.', $name, 2);
237
-        }
238
-        if ($name === '') $name = null;
239
-
240
-        if (!is_null($name)) {
241
-
242
-            $result = isset($this->children[$name]) ? $this->children[$name] : [];
243
-
244
-            if (is_null($group)) {
245
-                return $result;
246
-            } else {
247
-                // If we have a group filter as well, we need to narrow it down
248
-                // more.
249
-                return array_filter(
250
-                    $result,
251
-                    function($child) use ($group) {
252
-
253
-                        return $child instanceof Property && strtoupper($child->group) === $group;
254
-
255
-                    }
256
-                );
257
-            }
258
-
259
-        }
260
-
261
-        // If we got to this point, it means there was no 'name' specified for
262
-        // searching, implying that this is a group-only search.
263
-        $result = [];
264
-        foreach ($this->children as $childGroup) {
265
-
266
-            foreach ($childGroup as $child) {
267
-
268
-                if ($child instanceof Property && strtoupper($child->group) === $group) {
269
-                    $result[] = $child;
270
-                }
271
-
272
-            }
273
-
274
-        }
275
-        return $result;
276
-
277
-    }
278
-
279
-    /**
280
-     * Turns the object back into a serialized blob.
281
-     *
282
-     * @return string
283
-     */
284
-    public function serialize() {
285
-
286
-        $str = "BEGIN:" . $this->name . "\r\n";
287
-
288
-        /**
289
-         * Gives a component a 'score' for sorting purposes.
290
-         *
291
-         * This is solely used by the childrenSort method.
292
-         *
293
-         * A higher score means the item will be lower in the list.
294
-         * To avoid score collisions, each "score category" has a reasonable
295
-         * space to accomodate elements. The $key is added to the $score to
296
-         * preserve the original relative order of elements.
297
-         *
298
-         * @param int $key
299
-         * @param array $array
300
-         *
301
-         * @return int
302
-         */
303
-        $sortScore = function($key, $array) {
304
-
305
-            if ($array[$key] instanceof Component) {
306
-
307
-                // We want to encode VTIMEZONE first, this is a personal
308
-                // preference.
309
-                if ($array[$key]->name === 'VTIMEZONE') {
310
-                    $score = 300000000;
311
-                    return $score + $key;
312
-                } else {
313
-                    $score = 400000000;
314
-                    return $score + $key;
315
-                }
316
-            } else {
317
-                // Properties get encoded first
318
-                // VCARD version 4.0 wants the VERSION property to appear first
319
-                if ($array[$key] instanceof Property) {
320
-                    if ($array[$key]->name === 'VERSION') {
321
-                        $score = 100000000;
322
-                        return $score + $key;
323
-                    } else {
324
-                        // All other properties
325
-                        $score = 200000000;
326
-                        return $score + $key;
327
-                    }
328
-                }
329
-            }
330
-
331
-        };
332
-
333
-        $children = $this->children();
334
-        $tmp = $children;
335
-        uksort(
336
-            $children,
337
-           function($a, $b) use ($sortScore, $tmp) {
338
-
339
-                $sA = $sortScore($a, $tmp);
340
-                $sB = $sortScore($b, $tmp);
341
-
342
-                return $sA - $sB;
343
-
344
-            }
345
-        );
346
-
347
-        foreach ($children as $child) $str .= $child->serialize();
348
-        $str .= "END:" . $this->name . "\r\n";
349
-
350
-        return $str;
351
-
352
-    }
353
-
354
-    /**
355
-     * This method returns an array, with the representation as it should be
356
-     * encoded in JSON. This is used to create jCard or jCal documents.
357
-     *
358
-     * @return array
359
-     */
360
-    public function jsonSerialize() {
361
-
362
-        $components = [];
363
-        $properties = [];
364
-
365
-        foreach ($this->children as $childGroup) {
366
-            foreach ($childGroup as $child) {
367
-                if ($child instanceof self) {
368
-                    $components[] = $child->jsonSerialize();
369
-                } else {
370
-                    $properties[] = $child->jsonSerialize();
371
-                }
372
-            }
373
-        }
374
-
375
-        return [
376
-            strtolower($this->name),
377
-            $properties,
378
-            $components
379
-        ];
380
-
381
-    }
382
-
383
-    /**
384
-     * This method serializes the data into XML. This is used to create xCard or
385
-     * xCal documents.
386
-     *
387
-     * @param Xml\Writer $writer  XML writer.
388
-     *
389
-     * @return void
390
-     */
391
-    public function xmlSerialize(Xml\Writer $writer) {
392
-
393
-        $components = [];
394
-        $properties = [];
395
-
396
-        foreach ($this->children as $childGroup) {
397
-            foreach ($childGroup as $child) {
398
-                if ($child instanceof self) {
399
-                    $components[] = $child;
400
-                } else {
401
-                    $properties[] = $child;
402
-                }
403
-            }
404
-        }
405
-
406
-        $writer->startElement(strtolower($this->name));
407
-
408
-        if (!empty($properties)) {
409
-
410
-            $writer->startElement('properties');
411
-
412
-            foreach ($properties as $property) {
413
-                $property->xmlSerialize($writer);
414
-            }
415
-
416
-            $writer->endElement();
417
-
418
-        }
419
-
420
-        if (!empty($components)) {
421
-
422
-            $writer->startElement('components');
423
-
424
-            foreach ($components as $component) {
425
-                $component->xmlSerialize($writer);
426
-            }
427
-
428
-            $writer->endElement();
429
-        }
430
-
431
-        $writer->endElement();
432
-
433
-    }
434
-
435
-    /**
436
-     * This method should return a list of default property values.
437
-     *
438
-     * @return array
439
-     */
440
-    protected function getDefaults() {
441
-
442
-        return [];
443
-
444
-    }
445
-
446
-    /* Magic property accessors {{{ */
447
-
448
-    /**
449
-     * Using 'get' you will either get a property or component.
450
-     *
451
-     * If there were no child-elements found with the specified name,
452
-     * null is returned.
453
-     *
454
-     * To use this, this may look something like this:
455
-     *
456
-     * $event = $calendar->VEVENT;
457
-     *
458
-     * @param string $name
459
-     *
460
-     * @return Property
461
-     */
462
-    public function __get($name) {
463
-
464
-        if ($name === 'children') {
465
-
466
-            throw new \RuntimeException('Starting sabre/vobject 4.0 the children property is now protected. You should use the children() method instead');
467
-
468
-        }
469
-
470
-        $matches = $this->select($name);
471
-        if (count($matches) === 0) {
472
-            return;
473
-        } else {
474
-            $firstMatch = current($matches);
475
-            /** @var $firstMatch Property */
476
-            $firstMatch->setIterator(new ElementList(array_values($matches)));
477
-            return $firstMatch;
478
-        }
479
-
480
-    }
481
-
482
-    /**
483
-     * This method checks if a sub-element with the specified name exists.
484
-     *
485
-     * @param string $name
486
-     *
487
-     * @return bool
488
-     */
489
-    public function __isset($name) {
490
-
491
-        $matches = $this->select($name);
492
-        return count($matches) > 0;
493
-
494
-    }
495
-
496
-    /**
497
-     * Using the setter method you can add properties or subcomponents.
498
-     *
499
-     * You can either pass a Component, Property
500
-     * object, or a string to automatically create a Property.
501
-     *
502
-     * If the item already exists, it will be removed. If you want to add
503
-     * a new item with the same name, always use the add() method.
504
-     *
505
-     * @param string $name
506
-     * @param mixed $value
507
-     *
508
-     * @return void
509
-     */
510
-    public function __set($name, $value) {
511
-
512
-        $name = strtoupper($name);
513
-        $this->remove($name);
514
-        if ($value instanceof self || $value instanceof Property) {
515
-            $this->add($value);
516
-        } else {
517
-            $this->add($name, $value);
518
-        }
519
-    }
520
-
521
-    /**
522
-     * Removes all properties and components within this component with the
523
-     * specified name.
524
-     *
525
-     * @param string $name
526
-     *
527
-     * @return void
528
-     */
529
-    public function __unset($name) {
530
-
531
-        $this->remove($name);
532
-
533
-    }
534
-
535
-    /* }}} */
536
-
537
-    /**
538
-     * This method is automatically called when the object is cloned.
539
-     * Specifically, this will ensure all child elements are also cloned.
540
-     *
541
-     * @return void
542
-     */
543
-    public function __clone() {
544
-
545
-        foreach ($this->children as $childName => $childGroup) {
546
-            foreach ($childGroup as $key => $child) {
547
-                $clonedChild = clone $child;
548
-                $clonedChild->parent = $this;
549
-                $clonedChild->root = $this->root;
550
-                $this->children[$childName][$key] = $clonedChild;
551
-            }
552
-        }
553
-
554
-    }
555
-
556
-    /**
557
-     * A simple list of validation rules.
558
-     *
559
-     * This is simply a list of properties, and how many times they either
560
-     * must or must not appear.
561
-     *
562
-     * Possible values per property:
563
-     *   * 0 - Must not appear.
564
-     *   * 1 - Must appear exactly once.
565
-     *   * + - Must appear at least once.
566
-     *   * * - Can appear any number of times.
567
-     *   * ? - May appear, but not more than once.
568
-     *
569
-     * It is also possible to specify defaults and severity levels for
570
-     * violating the rule.
571
-     *
572
-     * See the VEVENT implementation for getValidationRules for a more complex
573
-     * example.
574
-     *
575
-     * @var array
576
-     */
577
-    public function getValidationRules() {
578
-
579
-        return [];
580
-
581
-    }
582
-
583
-    /**
584
-     * Validates the node for correctness.
585
-     *
586
-     * The following options are supported:
587
-     *   Node::REPAIR - May attempt to automatically repair the problem.
588
-     *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
589
-     *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
590
-     *
591
-     * This method returns an array with detected problems.
592
-     * Every element has the following properties:
593
-     *
594
-     *  * level - problem level.
595
-     *  * message - A human-readable string describing the issue.
596
-     *  * node - A reference to the problematic node.
597
-     *
598
-     * The level means:
599
-     *   1 - The issue was repaired (only happens if REPAIR was turned on).
600
-     *   2 - A warning.
601
-     *   3 - An error.
602
-     *
603
-     * @param int $options
604
-     *
605
-     * @return array
606
-     */
607
-    public function validate($options = 0) {
608
-
609
-        $rules = $this->getValidationRules();
610
-        $defaults = $this->getDefaults();
611
-
612
-        $propertyCounters = [];
613
-
614
-        $messages = [];
615
-
616
-        foreach ($this->children() as $child) {
617
-            $name = strtoupper($child->name);
618
-            if (!isset($propertyCounters[$name])) {
619
-                $propertyCounters[$name] = 1;
620
-            } else {
621
-                $propertyCounters[$name]++;
622
-            }
623
-            $messages = array_merge($messages, $child->validate($options));
624
-        }
625
-
626
-        foreach ($rules as $propName => $rule) {
627
-
628
-            switch ($rule) {
629
-                case '0' :
630
-                    if (isset($propertyCounters[$propName])) {
631
-                        $messages[] = [
632
-                            'level'   => 3,
633
-                            'message' => $propName . ' MUST NOT appear in a ' . $this->name . ' component',
634
-                            'node'    => $this,
635
-                        ];
636
-                    }
637
-                    break;
638
-                case '1' :
639
-                    if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] !== 1) {
640
-                        $repaired = false;
641
-                        if ($options & self::REPAIR && isset($defaults[$propName])) {
642
-                            $this->add($propName, $defaults[$propName]);
643
-                            $repaired = true;
644
-                        }
645
-                        $messages[] = [
646
-                            'level'   => $repaired ? 1 : 3,
647
-                            'message' => $propName . ' MUST appear exactly once in a ' . $this->name . ' component',
648
-                            'node'    => $this,
649
-                        ];
650
-                    }
651
-                    break;
652
-                case '+' :
653
-                    if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] < 1) {
654
-                        $messages[] = [
655
-                            'level'   => 3,
656
-                            'message' => $propName . ' MUST appear at least once in a ' . $this->name . ' component',
657
-                            'node'    => $this,
658
-                        ];
659
-                    }
660
-                    break;
661
-                case '*' :
662
-                    break;
663
-                case '?' :
664
-                    if (isset($propertyCounters[$propName]) && $propertyCounters[$propName] > 1) {
665
-                        $messages[] = [
666
-                            'level'   => 3,
667
-                            'message' => $propName . ' MUST NOT appear more than once in a ' . $this->name . ' component',
668
-                            'node'    => $this,
669
-                        ];
670
-                    }
671
-                    break;
672
-
673
-            }
674
-
675
-        }
676
-        return $messages;
677
-
678
-    }
679
-
680
-    /**
681
-     * Call this method on a document if you're done using it.
682
-     *
683
-     * It's intended to remove all circular references, so PHP can easily clean
684
-     * it up.
685
-     *
686
-     * @return void
687
-     */
688
-    public function destroy() {
689
-
690
-        parent::destroy();
691
-        foreach ($this->children as $childGroup) {
692
-            foreach ($childGroup as $child) {
693
-                $child->destroy();
694
-            }
695
-        }
696
-        $this->children = [];
697
-
698
-    }
19
+	/**
20
+	 * Component name.
21
+	 *
22
+	 * This will contain a string such as VEVENT, VTODO, VCALENDAR, VCARD.
23
+	 *
24
+	 * @var string
25
+	 */
26
+	public $name;
27
+
28
+	/**
29
+	 * A list of properties and/or sub-components.
30
+	 *
31
+	 * @var array
32
+	 */
33
+	protected $children = [];
34
+
35
+	/**
36
+	 * Creates a new component.
37
+	 *
38
+	 * You can specify the children either in key=>value syntax, in which case
39
+	 * properties will automatically be created, or you can just pass a list of
40
+	 * Component and Property object.
41
+	 *
42
+	 * By default, a set of sensible values will be added to the component. For
43
+	 * an iCalendar object, this may be something like CALSCALE:GREGORIAN. To
44
+	 * ensure that this does not happen, set $defaults to false.
45
+	 *
46
+	 * @param Document $root
47
+	 * @param string $name such as VCALENDAR, VEVENT.
48
+	 * @param array $children
49
+	 * @param bool $defaults
50
+	 *
51
+	 * @return void
52
+	 */
53
+	public function __construct(Document $root, $name, array $children = [], $defaults = true) {
54
+
55
+		$this->name = strtoupper($name);
56
+		$this->root = $root;
57
+
58
+		if ($defaults) {
59
+			// This is a terribly convoluted way to do this, but this ensures
60
+			// that the order of properties as they are specified in both
61
+			// defaults and the childrens list, are inserted in the object in a
62
+			// natural way.
63
+			$list = $this->getDefaults();
64
+			$nodes = [];
65
+			foreach ($children as $key => $value) {
66
+				if ($value instanceof Node) {
67
+					if (isset($list[$value->name])) {
68
+						unset($list[$value->name]);
69
+					}
70
+					$nodes[] = $value;
71
+				} else {
72
+					$list[$key] = $value;
73
+				}
74
+			}
75
+			foreach ($list as $key => $value) {
76
+				$this->add($key, $value);
77
+			}
78
+			foreach ($nodes as $node) {
79
+				$this->add($node);
80
+			}
81
+		} else {
82
+			foreach ($children as $k => $child) {
83
+				if ($child instanceof Node) {
84
+					// Component or Property
85
+					$this->add($child);
86
+				} else {
87
+
88
+					// Property key=>value
89
+					$this->add($k, $child);
90
+				}
91
+			}
92
+		}
93
+
94
+	}
95
+
96
+	/**
97
+	 * Adds a new property or component, and returns the new item.
98
+	 *
99
+	 * This method has 3 possible signatures:
100
+	 *
101
+	 * add(Component $comp) // Adds a new component
102
+	 * add(Property $prop)  // Adds a new property
103
+	 * add($name, $value, array $parameters = []) // Adds a new property
104
+	 * add($name, array $children = []) // Adds a new component
105
+	 * by name.
106
+	 *
107
+	 * @return Node
108
+	 */
109
+	public function add() {
110
+
111
+		$arguments = func_get_args();
112
+
113
+		if ($arguments[0] instanceof Node) {
114
+			if (isset($arguments[1])) {
115
+				throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node');
116
+			}
117
+			$arguments[0]->parent = $this;
118
+			$newNode = $arguments[0];
119
+
120
+		} elseif (is_string($arguments[0])) {
121
+
122
+			$newNode = call_user_func_array([$this->root, 'create'], $arguments);
123
+
124
+		} else {
125
+
126
+			throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string');
127
+
128
+		}
129
+
130
+		$name = $newNode->name;
131
+		if (isset($this->children[$name])) {
132
+			$this->children[$name][] = $newNode;
133
+		} else {
134
+			$this->children[$name] = [$newNode];
135
+		}
136
+		return $newNode;
137
+
138
+	}
139
+
140
+	/**
141
+	 * This method removes a component or property from this component.
142
+	 *
143
+	 * You can either specify the item by name (like DTSTART), in which case
144
+	 * all properties/components with that name will be removed, or you can
145
+	 * pass an instance of a property or component, in which case only that
146
+	 * exact item will be removed.
147
+	 *
148
+	 * @param string|Property|Component $item
149
+	 * @return void
150
+	 */
151
+	public function remove($item) {
152
+
153
+		if (is_string($item)) {
154
+			// If there's no dot in the name, it's an exact property name and
155
+			// we can just wipe out all those properties.
156
+			//
157
+			if (strpos($item, '.') === false) {
158
+				unset($this->children[strtoupper($item)]);
159
+				return;
160
+			}
161
+			// If there was a dot, we need to ask select() to help us out and
162
+			// then we just call remove recursively.
163
+			foreach ($this->select($item) as $child) {
164
+
165
+				$this->remove($child);
166
+
167
+			}
168
+		} else {
169
+			foreach ($this->select($item->name) as $k => $child) {
170
+				if ($child === $item) {
171
+					unset($this->children[$item->name][$k]);
172
+					return;
173
+				}
174
+			}
175
+		}
176
+
177
+		throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component');
178
+
179
+	}
180
+
181
+	/**
182
+	 * Returns a flat list of all the properties and components in this
183
+	 * component.
184
+	 *
185
+	 * @return array
186
+	 */
187
+	public function children() {
188
+
189
+		$result = [];
190
+		foreach ($this->children as $childGroup) {
191
+			$result = array_merge($result, $childGroup);
192
+		}
193
+		return $result;
194
+
195
+	}
196
+
197
+	/**
198
+	 * This method only returns a list of sub-components. Properties are
199
+	 * ignored.
200
+	 *
201
+	 * @return array
202
+	 */
203
+	public function getComponents() {
204
+
205
+		$result = [];
206
+
207
+		foreach ($this->children as $childGroup) {
208
+			foreach ($childGroup as $child) {
209
+				if ($child instanceof self) {
210
+					$result[] = $child;
211
+				}
212
+			}
213
+		}
214
+		return $result;
215
+
216
+	}
217
+
218
+	/**
219
+	 * Returns an array with elements that match the specified name.
220
+	 *
221
+	 * This function is also aware of MIME-Directory groups (as they appear in
222
+	 * vcards). This means that if a property is grouped as "HOME.EMAIL", it
223
+	 * will also be returned when searching for just "EMAIL". If you want to
224
+	 * search for a property in a specific group, you can select on the entire
225
+	 * string ("HOME.EMAIL"). If you want to search on a specific property that
226
+	 * has not been assigned a group, specify ".EMAIL".
227
+	 *
228
+	 * @param string $name
229
+	 * @return array
230
+	 */
231
+	public function select($name) {
232
+
233
+		$group = null;
234
+		$name = strtoupper($name);
235
+		if (strpos($name, '.') !== false) {
236
+			list($group, $name) = explode('.', $name, 2);
237
+		}
238
+		if ($name === '') $name = null;
239
+
240
+		if (!is_null($name)) {
241
+
242
+			$result = isset($this->children[$name]) ? $this->children[$name] : [];
243
+
244
+			if (is_null($group)) {
245
+				return $result;
246
+			} else {
247
+				// If we have a group filter as well, we need to narrow it down
248
+				// more.
249
+				return array_filter(
250
+					$result,
251
+					function($child) use ($group) {
252
+
253
+						return $child instanceof Property && strtoupper($child->group) === $group;
254
+
255
+					}
256
+				);
257
+			}
258
+
259
+		}
260
+
261
+		// If we got to this point, it means there was no 'name' specified for
262
+		// searching, implying that this is a group-only search.
263
+		$result = [];
264
+		foreach ($this->children as $childGroup) {
265
+
266
+			foreach ($childGroup as $child) {
267
+
268
+				if ($child instanceof Property && strtoupper($child->group) === $group) {
269
+					$result[] = $child;
270
+				}
271
+
272
+			}
273
+
274
+		}
275
+		return $result;
276
+
277
+	}
278
+
279
+	/**
280
+	 * Turns the object back into a serialized blob.
281
+	 *
282
+	 * @return string
283
+	 */
284
+	public function serialize() {
285
+
286
+		$str = "BEGIN:" . $this->name . "\r\n";
287
+
288
+		/**
289
+		 * Gives a component a 'score' for sorting purposes.
290
+		 *
291
+		 * This is solely used by the childrenSort method.
292
+		 *
293
+		 * A higher score means the item will be lower in the list.
294
+		 * To avoid score collisions, each "score category" has a reasonable
295
+		 * space to accomodate elements. The $key is added to the $score to
296
+		 * preserve the original relative order of elements.
297
+		 *
298
+		 * @param int $key
299
+		 * @param array $array
300
+		 *
301
+		 * @return int
302
+		 */
303
+		$sortScore = function($key, $array) {
304
+
305
+			if ($array[$key] instanceof Component) {
306
+
307
+				// We want to encode VTIMEZONE first, this is a personal
308
+				// preference.
309
+				if ($array[$key]->name === 'VTIMEZONE') {
310
+					$score = 300000000;
311
+					return $score + $key;
312
+				} else {
313
+					$score = 400000000;
314
+					return $score + $key;
315
+				}
316
+			} else {
317
+				// Properties get encoded first
318
+				// VCARD version 4.0 wants the VERSION property to appear first
319
+				if ($array[$key] instanceof Property) {
320
+					if ($array[$key]->name === 'VERSION') {
321
+						$score = 100000000;
322
+						return $score + $key;
323
+					} else {
324
+						// All other properties
325
+						$score = 200000000;
326
+						return $score + $key;
327
+					}
328
+				}
329
+			}
330
+
331
+		};
332
+
333
+		$children = $this->children();
334
+		$tmp = $children;
335
+		uksort(
336
+			$children,
337
+		   function($a, $b) use ($sortScore, $tmp) {
338
+
339
+				$sA = $sortScore($a, $tmp);
340
+				$sB = $sortScore($b, $tmp);
341
+
342
+				return $sA - $sB;
343
+
344
+			}
345
+		);
346
+
347
+		foreach ($children as $child) $str .= $child->serialize();
348
+		$str .= "END:" . $this->name . "\r\n";
349
+
350
+		return $str;
351
+
352
+	}
353
+
354
+	/**
355
+	 * This method returns an array, with the representation as it should be
356
+	 * encoded in JSON. This is used to create jCard or jCal documents.
357
+	 *
358
+	 * @return array
359
+	 */
360
+	public function jsonSerialize() {
361
+
362
+		$components = [];
363
+		$properties = [];
364
+
365
+		foreach ($this->children as $childGroup) {
366
+			foreach ($childGroup as $child) {
367
+				if ($child instanceof self) {
368
+					$components[] = $child->jsonSerialize();
369
+				} else {
370
+					$properties[] = $child->jsonSerialize();
371
+				}
372
+			}
373
+		}
374
+
375
+		return [
376
+			strtolower($this->name),
377
+			$properties,
378
+			$components
379
+		];
380
+
381
+	}
382
+
383
+	/**
384
+	 * This method serializes the data into XML. This is used to create xCard or
385
+	 * xCal documents.
386
+	 *
387
+	 * @param Xml\Writer $writer  XML writer.
388
+	 *
389
+	 * @return void
390
+	 */
391
+	public function xmlSerialize(Xml\Writer $writer) {
392
+
393
+		$components = [];
394
+		$properties = [];
395
+
396
+		foreach ($this->children as $childGroup) {
397
+			foreach ($childGroup as $child) {
398
+				if ($child instanceof self) {
399
+					$components[] = $child;
400
+				} else {
401
+					$properties[] = $child;
402
+				}
403
+			}
404
+		}
405
+
406
+		$writer->startElement(strtolower($this->name));
407
+
408
+		if (!empty($properties)) {
409
+
410
+			$writer->startElement('properties');
411
+
412
+			foreach ($properties as $property) {
413
+				$property->xmlSerialize($writer);
414
+			}
415
+
416
+			$writer->endElement();
417
+
418
+		}
419
+
420
+		if (!empty($components)) {
421
+
422
+			$writer->startElement('components');
423
+
424
+			foreach ($components as $component) {
425
+				$component->xmlSerialize($writer);
426
+			}
427
+
428
+			$writer->endElement();
429
+		}
430
+
431
+		$writer->endElement();
432
+
433
+	}
434
+
435
+	/**
436
+	 * This method should return a list of default property values.
437
+	 *
438
+	 * @return array
439
+	 */
440
+	protected function getDefaults() {
441
+
442
+		return [];
443
+
444
+	}
445
+
446
+	/* Magic property accessors {{{ */
447
+
448
+	/**
449
+	 * Using 'get' you will either get a property or component.
450
+	 *
451
+	 * If there were no child-elements found with the specified name,
452
+	 * null is returned.
453
+	 *
454
+	 * To use this, this may look something like this:
455
+	 *
456
+	 * $event = $calendar->VEVENT;
457
+	 *
458
+	 * @param string $name
459
+	 *
460
+	 * @return Property
461
+	 */
462
+	public function __get($name) {
463
+
464
+		if ($name === 'children') {
465
+
466
+			throw new \RuntimeException('Starting sabre/vobject 4.0 the children property is now protected. You should use the children() method instead');
467
+
468
+		}
469
+
470
+		$matches = $this->select($name);
471
+		if (count($matches) === 0) {
472
+			return;
473
+		} else {
474
+			$firstMatch = current($matches);
475
+			/** @var $firstMatch Property */
476
+			$firstMatch->setIterator(new ElementList(array_values($matches)));
477
+			return $firstMatch;
478
+		}
479
+
480
+	}
481
+
482
+	/**
483
+	 * This method checks if a sub-element with the specified name exists.
484
+	 *
485
+	 * @param string $name
486
+	 *
487
+	 * @return bool
488
+	 */
489
+	public function __isset($name) {
490
+
491
+		$matches = $this->select($name);
492
+		return count($matches) > 0;
493
+
494
+	}
495
+
496
+	/**
497
+	 * Using the setter method you can add properties or subcomponents.
498
+	 *
499
+	 * You can either pass a Component, Property
500
+	 * object, or a string to automatically create a Property.
501
+	 *
502
+	 * If the item already exists, it will be removed. If you want to add
503
+	 * a new item with the same name, always use the add() method.
504
+	 *
505
+	 * @param string $name
506
+	 * @param mixed $value
507
+	 *
508
+	 * @return void
509
+	 */
510
+	public function __set($name, $value) {
511
+
512
+		$name = strtoupper($name);
513
+		$this->remove($name);
514
+		if ($value instanceof self || $value instanceof Property) {
515
+			$this->add($value);
516
+		} else {
517
+			$this->add($name, $value);
518
+		}
519
+	}
520
+
521
+	/**
522
+	 * Removes all properties and components within this component with the
523
+	 * specified name.
524
+	 *
525
+	 * @param string $name
526
+	 *
527
+	 * @return void
528
+	 */
529
+	public function __unset($name) {
530
+
531
+		$this->remove($name);
532
+
533
+	}
534
+
535
+	/* }}} */
536
+
537
+	/**
538
+	 * This method is automatically called when the object is cloned.
539
+	 * Specifically, this will ensure all child elements are also cloned.
540
+	 *
541
+	 * @return void
542
+	 */
543
+	public function __clone() {
544
+
545
+		foreach ($this->children as $childName => $childGroup) {
546
+			foreach ($childGroup as $key => $child) {
547
+				$clonedChild = clone $child;
548
+				$clonedChild->parent = $this;
549
+				$clonedChild->root = $this->root;
550
+				$this->children[$childName][$key] = $clonedChild;
551
+			}
552
+		}
553
+
554
+	}
555
+
556
+	/**
557
+	 * A simple list of validation rules.
558
+	 *
559
+	 * This is simply a list of properties, and how many times they either
560
+	 * must or must not appear.
561
+	 *
562
+	 * Possible values per property:
563
+	 *   * 0 - Must not appear.
564
+	 *   * 1 - Must appear exactly once.
565
+	 *   * + - Must appear at least once.
566
+	 *   * * - Can appear any number of times.
567
+	 *   * ? - May appear, but not more than once.
568
+	 *
569
+	 * It is also possible to specify defaults and severity levels for
570
+	 * violating the rule.
571
+	 *
572
+	 * See the VEVENT implementation for getValidationRules for a more complex
573
+	 * example.
574
+	 *
575
+	 * @var array
576
+	 */
577
+	public function getValidationRules() {
578
+
579
+		return [];
580
+
581
+	}
582
+
583
+	/**
584
+	 * Validates the node for correctness.
585
+	 *
586
+	 * The following options are supported:
587
+	 *   Node::REPAIR - May attempt to automatically repair the problem.
588
+	 *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
589
+	 *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
590
+	 *
591
+	 * This method returns an array with detected problems.
592
+	 * Every element has the following properties:
593
+	 *
594
+	 *  * level - problem level.
595
+	 *  * message - A human-readable string describing the issue.
596
+	 *  * node - A reference to the problematic node.
597
+	 *
598
+	 * The level means:
599
+	 *   1 - The issue was repaired (only happens if REPAIR was turned on).
600
+	 *   2 - A warning.
601
+	 *   3 - An error.
602
+	 *
603
+	 * @param int $options
604
+	 *
605
+	 * @return array
606
+	 */
607
+	public function validate($options = 0) {
608
+
609
+		$rules = $this->getValidationRules();
610
+		$defaults = $this->getDefaults();
611
+
612
+		$propertyCounters = [];
613
+
614
+		$messages = [];
615
+
616
+		foreach ($this->children() as $child) {
617
+			$name = strtoupper($child->name);
618
+			if (!isset($propertyCounters[$name])) {
619
+				$propertyCounters[$name] = 1;
620
+			} else {
621
+				$propertyCounters[$name]++;
622
+			}
623
+			$messages = array_merge($messages, $child->validate($options));
624
+		}
625
+
626
+		foreach ($rules as $propName => $rule) {
627
+
628
+			switch ($rule) {
629
+				case '0' :
630
+					if (isset($propertyCounters[$propName])) {
631
+						$messages[] = [
632
+							'level'   => 3,
633
+							'message' => $propName . ' MUST NOT appear in a ' . $this->name . ' component',
634
+							'node'    => $this,
635
+						];
636
+					}
637
+					break;
638
+				case '1' :
639
+					if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] !== 1) {
640
+						$repaired = false;
641
+						if ($options & self::REPAIR && isset($defaults[$propName])) {
642
+							$this->add($propName, $defaults[$propName]);
643
+							$repaired = true;
644
+						}
645
+						$messages[] = [
646
+							'level'   => $repaired ? 1 : 3,
647
+							'message' => $propName . ' MUST appear exactly once in a ' . $this->name . ' component',
648
+							'node'    => $this,
649
+						];
650
+					}
651
+					break;
652
+				case '+' :
653
+					if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] < 1) {
654
+						$messages[] = [
655
+							'level'   => 3,
656
+							'message' => $propName . ' MUST appear at least once in a ' . $this->name . ' component',
657
+							'node'    => $this,
658
+						];
659
+					}
660
+					break;
661
+				case '*' :
662
+					break;
663
+				case '?' :
664
+					if (isset($propertyCounters[$propName]) && $propertyCounters[$propName] > 1) {
665
+						$messages[] = [
666
+							'level'   => 3,
667
+							'message' => $propName . ' MUST NOT appear more than once in a ' . $this->name . ' component',
668
+							'node'    => $this,
669
+						];
670
+					}
671
+					break;
672
+
673
+			}
674
+
675
+		}
676
+		return $messages;
677
+
678
+	}
679
+
680
+	/**
681
+	 * Call this method on a document if you're done using it.
682
+	 *
683
+	 * It's intended to remove all circular references, so PHP can easily clean
684
+	 * it up.
685
+	 *
686
+	 * @return void
687
+	 */
688
+	public function destroy() {
689
+
690
+		parent::destroy();
691
+		foreach ($this->children as $childGroup) {
692
+			foreach ($childGroup as $child) {
693
+				$child->destroy();
694
+			}
695
+		}
696
+		$this->children = [];
697
+
698
+	}
699 699
 
700 700
 }
Please login to merge, or discard this patch.