Completed
Branch develop (37f7b7)
by
unknown
24:41
created
htdocs/includes/sabre/sabre/vobject/lib/FreeBusyData.php 1 patch
Indentation   +171 added lines, -171 removed lines patch added patch discarded remove patch
@@ -11,175 +11,175 @@
 block discarded – undo
11 11
  */
12 12
 class FreeBusyData
13 13
 {
14
-    /**
15
-     * Start timestamp.
16
-     *
17
-     * @var int
18
-     */
19
-    protected $start;
20
-
21
-    /**
22
-     * End timestamp.
23
-     *
24
-     * @var int
25
-     */
26
-    protected $end;
27
-
28
-    /**
29
-     * A list of free-busy times.
30
-     *
31
-     * @var array
32
-     */
33
-    protected $data;
34
-
35
-    public function __construct($start, $end)
36
-    {
37
-        $this->start = $start;
38
-        $this->end = $end;
39
-        $this->data = [];
40
-
41
-        $this->data[] = [
42
-            'start' => $this->start,
43
-            'end' => $this->end,
44
-            'type' => 'FREE',
45
-        ];
46
-    }
47
-
48
-    /**
49
-     * Adds free or busytime to the data.
50
-     *
51
-     * @param int    $start
52
-     * @param int    $end
53
-     * @param string $type  FREE, BUSY, BUSY-UNAVAILABLE or BUSY-TENTATIVE
54
-     */
55
-    public function add($start, $end, $type)
56
-    {
57
-        if ($start > $this->end || $end < $this->start) {
58
-            // This new data is outside our timerange.
59
-            return;
60
-        }
61
-
62
-        if ($start < $this->start) {
63
-            // The item starts before our requested time range
64
-            $start = $this->start;
65
-        }
66
-        if ($end > $this->end) {
67
-            // The item ends after our requested time range
68
-            $end = $this->end;
69
-        }
70
-
71
-        // Finding out where we need to insert the new item.
72
-        $currentIndex = 0;
73
-        while ($start > $this->data[$currentIndex]['end']) {
74
-            ++$currentIndex;
75
-        }
76
-
77
-        // The standard insertion point will be one _after_ the first
78
-        // overlapping item.
79
-        $insertStartIndex = $currentIndex + 1;
80
-
81
-        $newItem = [
82
-            'start' => $start,
83
-            'end' => $end,
84
-            'type' => $type,
85
-        ];
86
-
87
-        $precedingItem = $this->data[$insertStartIndex - 1];
88
-        if ($this->data[$insertStartIndex - 1]['start'] === $start) {
89
-            // The old item starts at the exact same point as the new item.
90
-            --$insertStartIndex;
91
-        }
92
-
93
-        // Now we know where to insert the item, we need to know where it
94
-        // starts overlapping with items on the tail end. We need to start
95
-        // looking one item before the insertStartIndex, because it's possible
96
-        // that the new item 'sits inside' the previous old item.
97
-        if ($insertStartIndex > 0) {
98
-            $currentIndex = $insertStartIndex - 1;
99
-        } else {
100
-            $currentIndex = 0;
101
-        }
102
-
103
-        while ($end > $this->data[$currentIndex]['end']) {
104
-            ++$currentIndex;
105
-        }
106
-
107
-        // What we are about to insert into the array
108
-        $newItems = [
109
-            $newItem,
110
-        ];
111
-
112
-        // This is the amount of items that are completely overwritten by the
113
-        // new item.
114
-        $itemsToDelete = $currentIndex - $insertStartIndex;
115
-        if ($this->data[$currentIndex]['end'] <= $end) {
116
-            ++$itemsToDelete;
117
-        }
118
-
119
-        // If itemsToDelete was -1, it means that the newly inserted item is
120
-        // actually sitting inside an existing one. This means we need to split
121
-        // the item at the current position in two and insert the new item in
122
-        // between.
123
-        if (-1 === $itemsToDelete) {
124
-            $itemsToDelete = 0;
125
-            if ($newItem['end'] < $precedingItem['end']) {
126
-                $newItems[] = [
127
-                    'start' => $newItem['end'] + 1,
128
-                    'end' => $precedingItem['end'],
129
-                    'type' => $precedingItem['type'],
130
-                ];
131
-            }
132
-        }
133
-
134
-        array_splice(
135
-            $this->data,
136
-            $insertStartIndex,
137
-            $itemsToDelete,
138
-            $newItems
139
-        );
140
-
141
-        $doMerge = false;
142
-        $mergeOffset = $insertStartIndex;
143
-        $mergeItem = $newItem;
144
-        $mergeDelete = 1;
145
-
146
-        if (isset($this->data[$insertStartIndex - 1])) {
147
-            // Updating the start time of the previous item.
148
-            $this->data[$insertStartIndex - 1]['end'] = $start;
149
-
150
-            // If the previous and the current are of the same type, we can
151
-            // merge them into one item.
152
-            if ($this->data[$insertStartIndex - 1]['type'] === $this->data[$insertStartIndex]['type']) {
153
-                $doMerge = true;
154
-                --$mergeOffset;
155
-                ++$mergeDelete;
156
-                $mergeItem['start'] = $this->data[$insertStartIndex - 1]['start'];
157
-            }
158
-        }
159
-        if (isset($this->data[$insertStartIndex + 1])) {
160
-            // Updating the start time of the next item.
161
-            $this->data[$insertStartIndex + 1]['start'] = $end;
162
-
163
-            // If the next and the current are of the same type, we can
164
-            // merge them into one item.
165
-            if ($this->data[$insertStartIndex + 1]['type'] === $this->data[$insertStartIndex]['type']) {
166
-                $doMerge = true;
167
-                ++$mergeDelete;
168
-                $mergeItem['end'] = $this->data[$insertStartIndex + 1]['end'];
169
-            }
170
-        }
171
-        if ($doMerge) {
172
-            array_splice(
173
-                $this->data,
174
-                $mergeOffset,
175
-                $mergeDelete,
176
-                [$mergeItem]
177
-            );
178
-        }
179
-    }
180
-
181
-    public function getData()
182
-    {
183
-        return $this->data;
184
-    }
14
+	/**
15
+	 * Start timestamp.
16
+	 *
17
+	 * @var int
18
+	 */
19
+	protected $start;
20
+
21
+	/**
22
+	 * End timestamp.
23
+	 *
24
+	 * @var int
25
+	 */
26
+	protected $end;
27
+
28
+	/**
29
+	 * A list of free-busy times.
30
+	 *
31
+	 * @var array
32
+	 */
33
+	protected $data;
34
+
35
+	public function __construct($start, $end)
36
+	{
37
+		$this->start = $start;
38
+		$this->end = $end;
39
+		$this->data = [];
40
+
41
+		$this->data[] = [
42
+			'start' => $this->start,
43
+			'end' => $this->end,
44
+			'type' => 'FREE',
45
+		];
46
+	}
47
+
48
+	/**
49
+	 * Adds free or busytime to the data.
50
+	 *
51
+	 * @param int    $start
52
+	 * @param int    $end
53
+	 * @param string $type  FREE, BUSY, BUSY-UNAVAILABLE or BUSY-TENTATIVE
54
+	 */
55
+	public function add($start, $end, $type)
56
+	{
57
+		if ($start > $this->end || $end < $this->start) {
58
+			// This new data is outside our timerange.
59
+			return;
60
+		}
61
+
62
+		if ($start < $this->start) {
63
+			// The item starts before our requested time range
64
+			$start = $this->start;
65
+		}
66
+		if ($end > $this->end) {
67
+			// The item ends after our requested time range
68
+			$end = $this->end;
69
+		}
70
+
71
+		// Finding out where we need to insert the new item.
72
+		$currentIndex = 0;
73
+		while ($start > $this->data[$currentIndex]['end']) {
74
+			++$currentIndex;
75
+		}
76
+
77
+		// The standard insertion point will be one _after_ the first
78
+		// overlapping item.
79
+		$insertStartIndex = $currentIndex + 1;
80
+
81
+		$newItem = [
82
+			'start' => $start,
83
+			'end' => $end,
84
+			'type' => $type,
85
+		];
86
+
87
+		$precedingItem = $this->data[$insertStartIndex - 1];
88
+		if ($this->data[$insertStartIndex - 1]['start'] === $start) {
89
+			// The old item starts at the exact same point as the new item.
90
+			--$insertStartIndex;
91
+		}
92
+
93
+		// Now we know where to insert the item, we need to know where it
94
+		// starts overlapping with items on the tail end. We need to start
95
+		// looking one item before the insertStartIndex, because it's possible
96
+		// that the new item 'sits inside' the previous old item.
97
+		if ($insertStartIndex > 0) {
98
+			$currentIndex = $insertStartIndex - 1;
99
+		} else {
100
+			$currentIndex = 0;
101
+		}
102
+
103
+		while ($end > $this->data[$currentIndex]['end']) {
104
+			++$currentIndex;
105
+		}
106
+
107
+		// What we are about to insert into the array
108
+		$newItems = [
109
+			$newItem,
110
+		];
111
+
112
+		// This is the amount of items that are completely overwritten by the
113
+		// new item.
114
+		$itemsToDelete = $currentIndex - $insertStartIndex;
115
+		if ($this->data[$currentIndex]['end'] <= $end) {
116
+			++$itemsToDelete;
117
+		}
118
+
119
+		// If itemsToDelete was -1, it means that the newly inserted item is
120
+		// actually sitting inside an existing one. This means we need to split
121
+		// the item at the current position in two and insert the new item in
122
+		// between.
123
+		if (-1 === $itemsToDelete) {
124
+			$itemsToDelete = 0;
125
+			if ($newItem['end'] < $precedingItem['end']) {
126
+				$newItems[] = [
127
+					'start' => $newItem['end'] + 1,
128
+					'end' => $precedingItem['end'],
129
+					'type' => $precedingItem['type'],
130
+				];
131
+			}
132
+		}
133
+
134
+		array_splice(
135
+			$this->data,
136
+			$insertStartIndex,
137
+			$itemsToDelete,
138
+			$newItems
139
+		);
140
+
141
+		$doMerge = false;
142
+		$mergeOffset = $insertStartIndex;
143
+		$mergeItem = $newItem;
144
+		$mergeDelete = 1;
145
+
146
+		if (isset($this->data[$insertStartIndex - 1])) {
147
+			// Updating the start time of the previous item.
148
+			$this->data[$insertStartIndex - 1]['end'] = $start;
149
+
150
+			// If the previous and the current are of the same type, we can
151
+			// merge them into one item.
152
+			if ($this->data[$insertStartIndex - 1]['type'] === $this->data[$insertStartIndex]['type']) {
153
+				$doMerge = true;
154
+				--$mergeOffset;
155
+				++$mergeDelete;
156
+				$mergeItem['start'] = $this->data[$insertStartIndex - 1]['start'];
157
+			}
158
+		}
159
+		if (isset($this->data[$insertStartIndex + 1])) {
160
+			// Updating the start time of the next item.
161
+			$this->data[$insertStartIndex + 1]['start'] = $end;
162
+
163
+			// If the next and the current are of the same type, we can
164
+			// merge them into one item.
165
+			if ($this->data[$insertStartIndex + 1]['type'] === $this->data[$insertStartIndex]['type']) {
166
+				$doMerge = true;
167
+				++$mergeDelete;
168
+				$mergeItem['end'] = $this->data[$insertStartIndex + 1]['end'];
169
+			}
170
+		}
171
+		if ($doMerge) {
172
+			array_splice(
173
+				$this->data,
174
+				$mergeOffset,
175
+				$mergeDelete,
176
+				[$mergeItem]
177
+			);
178
+		}
179
+	}
180
+
181
+	public function getData()
182
+	{
183
+		return $this->data;
184
+	}
185 185
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/vobject/lib/Cli.php 3 patches
Indentation   +683 added lines, -683 removed lines patch added patch discarded remove patch
@@ -13,693 +13,693 @@
 block discarded – undo
13 13
  */
14 14
 class Cli
15 15
 {
16
-    /**
17
-     * No output.
18
-     *
19
-     * @var bool
20
-     */
21
-    protected $quiet = false;
22
-
23
-    /**
24
-     * Help display.
25
-     *
26
-     * @var bool
27
-     */
28
-    protected $showHelp = false;
29
-
30
-    /**
31
-     * Whether to spit out 'mimedir' or 'json' format.
32
-     *
33
-     * @var string
34
-     */
35
-    protected $format;
36
-
37
-    /**
38
-     * JSON pretty print.
39
-     *
40
-     * @var bool
41
-     */
42
-    protected $pretty;
43
-
44
-    /**
45
-     * Source file.
46
-     *
47
-     * @var string
48
-     */
49
-    protected $inputPath;
50
-
51
-    /**
52
-     * Destination file.
53
-     *
54
-     * @var string
55
-     */
56
-    protected $outputPath;
57
-
58
-    /**
59
-     * output stream.
60
-     *
61
-     * @var resource
62
-     */
63
-    protected $stdout;
64
-
65
-    /**
66
-     * stdin.
67
-     *
68
-     * @var resource
69
-     */
70
-    protected $stdin;
71
-
72
-    /**
73
-     * stderr.
74
-     *
75
-     * @var resource
76
-     */
77
-    protected $stderr;
78
-
79
-    /**
80
-     * Input format (one of json or mimedir).
81
-     *
82
-     * @var string
83
-     */
84
-    protected $inputFormat;
85
-
86
-    /**
87
-     * Makes the parser less strict.
88
-     *
89
-     * @var bool
90
-     */
91
-    protected $forgiving = false;
92
-
93
-    /**
94
-     * Main function.
95
-     *
96
-     * @return int
97
-     */
98
-    public function main(array $argv)
99
-    {
100
-        // @codeCoverageIgnoreStart
101
-        // We cannot easily test this, so we'll skip it. Pretty basic anyway.
102
-
103
-        if (!$this->stderr) {
104
-            $this->stderr = fopen('php://stderr', 'w');
105
-        }
106
-        if (!$this->stdout) {
107
-            $this->stdout = fopen('php://stdout', 'w');
108
-        }
109
-        if (!$this->stdin) {
110
-            $this->stdin = fopen('php://stdin', 'r');
111
-        }
112
-
113
-        // @codeCoverageIgnoreEnd
114
-
115
-        try {
116
-            list($options, $positional) = $this->parseArguments($argv);
117
-
118
-            if (isset($options['q'])) {
119
-                $this->quiet = true;
120
-            }
121
-            $this->log($this->colorize('green', 'sabre/vobject ').$this->colorize('yellow', Version::VERSION));
122
-
123
-            foreach ($options as $name => $value) {
124
-                switch ($name) {
125
-                    case 'q':
126
-                        // Already handled earlier.
127
-                        break;
128
-                    case 'h':
129
-                    case 'help':
130
-                        $this->showHelp();
131
-
132
-                        return 0;
133
-                        break;
134
-                    case 'format':
135
-                        switch ($value) {
136
-                            // jcard/jcal documents
137
-                            case 'jcard':
138
-                            case 'jcal':
139
-                            // specific document versions
140
-                            case 'vcard21':
141
-                            case 'vcard30':
142
-                            case 'vcard40':
143
-                            case 'icalendar20':
144
-                            // specific formats
145
-                            case 'json':
146
-                            case 'mimedir':
147
-                            // icalendar/vcad
148
-                            case 'icalendar':
149
-                            case 'vcard':
150
-                                $this->format = $value;
151
-                                break;
152
-
153
-                            default:
154
-                                throw new InvalidArgumentException('Unknown format: '.$value);
155
-                        }
156
-                        break;
157
-                    case 'pretty':
158
-                        if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
159
-                            $this->pretty = true;
160
-                        }
161
-                        break;
162
-                    case 'forgiving':
163
-                        $this->forgiving = true;
164
-                        break;
165
-                    case 'inputformat':
166
-                        switch ($value) {
167
-                            // json formats
168
-                            case 'jcard':
169
-                            case 'jcal':
170
-                            case 'json':
171
-                                $this->inputFormat = 'json';
172
-                                break;
173
-
174
-                            // mimedir formats
175
-                            case 'mimedir':
176
-                            case 'icalendar':
177
-                            case 'vcard':
178
-                            case 'vcard21':
179
-                            case 'vcard30':
180
-                            case 'vcard40':
181
-                            case 'icalendar20':
182
-                                $this->inputFormat = 'mimedir';
183
-                                break;
184
-
185
-                            default:
186
-                                throw new InvalidArgumentException('Unknown format: '.$value);
187
-                        }
188
-                        break;
189
-                    default:
190
-                        throw new InvalidArgumentException('Unknown option: '.$name);
191
-                }
192
-            }
193
-
194
-            if (0 === count($positional)) {
195
-                $this->showHelp();
196
-
197
-                return 1;
198
-            }
199
-
200
-            if (1 === count($positional)) {
201
-                throw new InvalidArgumentException('Inputfile is a required argument');
202
-            }
203
-
204
-            if (count($positional) > 3) {
205
-                throw new InvalidArgumentException('Too many arguments');
206
-            }
207
-
208
-            if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'])) {
209
-                throw new InvalidArgumentException('Unknown command: '.$positional[0]);
210
-            }
211
-        } catch (InvalidArgumentException $e) {
212
-            $this->showHelp();
213
-            $this->log('Error: '.$e->getMessage(), 'red');
214
-
215
-            return 1;
216
-        }
217
-
218
-        $command = $positional[0];
219
-
220
-        $this->inputPath = $positional[1];
221
-        $this->outputPath = isset($positional[2]) ? $positional[2] : '-';
222
-
223
-        if ('-' !== $this->outputPath) {
224
-            $this->stdout = fopen($this->outputPath, 'w');
225
-        }
226
-
227
-        if (!$this->inputFormat) {
228
-            if ('.json' === substr($this->inputPath, -5)) {
229
-                $this->inputFormat = 'json';
230
-            } else {
231
-                $this->inputFormat = 'mimedir';
232
-            }
233
-        }
234
-        if (!$this->format) {
235
-            if ('.json' === substr($this->outputPath, -5)) {
236
-                $this->format = 'json';
237
-            } else {
238
-                $this->format = 'mimedir';
239
-            }
240
-        }
241
-
242
-        $realCode = 0;
243
-
244
-        try {
245
-            while ($input = $this->readInput()) {
246
-                $returnCode = $this->$command($input);
247
-                if (0 !== $returnCode) {
248
-                    $realCode = $returnCode;
249
-                }
250
-            }
251
-        } catch (EofException $e) {
252
-            // end of file
253
-        } catch (\Exception $e) {
254
-            $this->log('Error: '.$e->getMessage(), 'red');
255
-
256
-            return 2;
257
-        }
258
-
259
-        return $realCode;
260
-    }
261
-
262
-    /**
263
-     * Shows the help message.
264
-     */
265
-    protected function showHelp()
266
-    {
267
-        $this->log('Usage:', 'yellow');
268
-        $this->log('  vobject [options] command [arguments]');
269
-        $this->log('');
270
-        $this->log('Options:', 'yellow');
271
-        $this->log($this->colorize('green', '  -q            ')."Don't output anything.");
272
-        $this->log($this->colorize('green', '  -help -h      ').'Display this help message.');
273
-        $this->log($this->colorize('green', '  --format      ').'Convert to a specific format. Must be one of: vcard, vcard21,');
274
-        $this->log($this->colorize('green', '  --forgiving   ').'Makes the parser less strict.');
275
-        $this->log('                vcard30, vcard40, icalendar20, jcal, jcard, json, mimedir.');
276
-        $this->log($this->colorize('green', '  --inputformat ').'If the input format cannot be guessed from the extension, it');
277
-        $this->log('                must be specified here.');
278
-        // Only PHP 5.4 and up
279
-        if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
280
-            $this->log($this->colorize('green', '  --pretty      ').'json pretty-print.');
281
-        }
282
-        $this->log('');
283
-        $this->log('Commands:', 'yellow');
284
-        $this->log($this->colorize('green', '  validate').' source_file              Validates a file for correctness.');
285
-        $this->log($this->colorize('green', '  repair').' source_file [output_file]  Repairs a file.');
286
-        $this->log($this->colorize('green', '  convert').' source_file [output_file] Converts a file.');
287
-        $this->log($this->colorize('green', '  color').' source_file                 Colorize a file, useful for debugging.');
288
-        $this->log(
289
-        <<<HELP
16
+	/**
17
+	 * No output.
18
+	 *
19
+	 * @var bool
20
+	 */
21
+	protected $quiet = false;
22
+
23
+	/**
24
+	 * Help display.
25
+	 *
26
+	 * @var bool
27
+	 */
28
+	protected $showHelp = false;
29
+
30
+	/**
31
+	 * Whether to spit out 'mimedir' or 'json' format.
32
+	 *
33
+	 * @var string
34
+	 */
35
+	protected $format;
36
+
37
+	/**
38
+	 * JSON pretty print.
39
+	 *
40
+	 * @var bool
41
+	 */
42
+	protected $pretty;
43
+
44
+	/**
45
+	 * Source file.
46
+	 *
47
+	 * @var string
48
+	 */
49
+	protected $inputPath;
50
+
51
+	/**
52
+	 * Destination file.
53
+	 *
54
+	 * @var string
55
+	 */
56
+	protected $outputPath;
57
+
58
+	/**
59
+	 * output stream.
60
+	 *
61
+	 * @var resource
62
+	 */
63
+	protected $stdout;
64
+
65
+	/**
66
+	 * stdin.
67
+	 *
68
+	 * @var resource
69
+	 */
70
+	protected $stdin;
71
+
72
+	/**
73
+	 * stderr.
74
+	 *
75
+	 * @var resource
76
+	 */
77
+	protected $stderr;
78
+
79
+	/**
80
+	 * Input format (one of json or mimedir).
81
+	 *
82
+	 * @var string
83
+	 */
84
+	protected $inputFormat;
85
+
86
+	/**
87
+	 * Makes the parser less strict.
88
+	 *
89
+	 * @var bool
90
+	 */
91
+	protected $forgiving = false;
92
+
93
+	/**
94
+	 * Main function.
95
+	 *
96
+	 * @return int
97
+	 */
98
+	public function main(array $argv)
99
+	{
100
+		// @codeCoverageIgnoreStart
101
+		// We cannot easily test this, so we'll skip it. Pretty basic anyway.
102
+
103
+		if (!$this->stderr) {
104
+			$this->stderr = fopen('php://stderr', 'w');
105
+		}
106
+		if (!$this->stdout) {
107
+			$this->stdout = fopen('php://stdout', 'w');
108
+		}
109
+		if (!$this->stdin) {
110
+			$this->stdin = fopen('php://stdin', 'r');
111
+		}
112
+
113
+		// @codeCoverageIgnoreEnd
114
+
115
+		try {
116
+			list($options, $positional) = $this->parseArguments($argv);
117
+
118
+			if (isset($options['q'])) {
119
+				$this->quiet = true;
120
+			}
121
+			$this->log($this->colorize('green', 'sabre/vobject ').$this->colorize('yellow', Version::VERSION));
122
+
123
+			foreach ($options as $name => $value) {
124
+				switch ($name) {
125
+					case 'q':
126
+						// Already handled earlier.
127
+						break;
128
+					case 'h':
129
+					case 'help':
130
+						$this->showHelp();
131
+
132
+						return 0;
133
+						break;
134
+					case 'format':
135
+						switch ($value) {
136
+							// jcard/jcal documents
137
+							case 'jcard':
138
+							case 'jcal':
139
+							// specific document versions
140
+							case 'vcard21':
141
+							case 'vcard30':
142
+							case 'vcard40':
143
+							case 'icalendar20':
144
+							// specific formats
145
+							case 'json':
146
+							case 'mimedir':
147
+							// icalendar/vcad
148
+							case 'icalendar':
149
+							case 'vcard':
150
+								$this->format = $value;
151
+								break;
152
+
153
+							default:
154
+								throw new InvalidArgumentException('Unknown format: '.$value);
155
+						}
156
+						break;
157
+					case 'pretty':
158
+						if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
159
+							$this->pretty = true;
160
+						}
161
+						break;
162
+					case 'forgiving':
163
+						$this->forgiving = true;
164
+						break;
165
+					case 'inputformat':
166
+						switch ($value) {
167
+							// json formats
168
+							case 'jcard':
169
+							case 'jcal':
170
+							case 'json':
171
+								$this->inputFormat = 'json';
172
+								break;
173
+
174
+							// mimedir formats
175
+							case 'mimedir':
176
+							case 'icalendar':
177
+							case 'vcard':
178
+							case 'vcard21':
179
+							case 'vcard30':
180
+							case 'vcard40':
181
+							case 'icalendar20':
182
+								$this->inputFormat = 'mimedir';
183
+								break;
184
+
185
+							default:
186
+								throw new InvalidArgumentException('Unknown format: '.$value);
187
+						}
188
+						break;
189
+					default:
190
+						throw new InvalidArgumentException('Unknown option: '.$name);
191
+				}
192
+			}
193
+
194
+			if (0 === count($positional)) {
195
+				$this->showHelp();
196
+
197
+				return 1;
198
+			}
199
+
200
+			if (1 === count($positional)) {
201
+				throw new InvalidArgumentException('Inputfile is a required argument');
202
+			}
203
+
204
+			if (count($positional) > 3) {
205
+				throw new InvalidArgumentException('Too many arguments');
206
+			}
207
+
208
+			if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'])) {
209
+				throw new InvalidArgumentException('Unknown command: '.$positional[0]);
210
+			}
211
+		} catch (InvalidArgumentException $e) {
212
+			$this->showHelp();
213
+			$this->log('Error: '.$e->getMessage(), 'red');
214
+
215
+			return 1;
216
+		}
217
+
218
+		$command = $positional[0];
219
+
220
+		$this->inputPath = $positional[1];
221
+		$this->outputPath = isset($positional[2]) ? $positional[2] : '-';
222
+
223
+		if ('-' !== $this->outputPath) {
224
+			$this->stdout = fopen($this->outputPath, 'w');
225
+		}
226
+
227
+		if (!$this->inputFormat) {
228
+			if ('.json' === substr($this->inputPath, -5)) {
229
+				$this->inputFormat = 'json';
230
+			} else {
231
+				$this->inputFormat = 'mimedir';
232
+			}
233
+		}
234
+		if (!$this->format) {
235
+			if ('.json' === substr($this->outputPath, -5)) {
236
+				$this->format = 'json';
237
+			} else {
238
+				$this->format = 'mimedir';
239
+			}
240
+		}
241
+
242
+		$realCode = 0;
243
+
244
+		try {
245
+			while ($input = $this->readInput()) {
246
+				$returnCode = $this->$command($input);
247
+				if (0 !== $returnCode) {
248
+					$realCode = $returnCode;
249
+				}
250
+			}
251
+		} catch (EofException $e) {
252
+			// end of file
253
+		} catch (\Exception $e) {
254
+			$this->log('Error: '.$e->getMessage(), 'red');
255
+
256
+			return 2;
257
+		}
258
+
259
+		return $realCode;
260
+	}
261
+
262
+	/**
263
+	 * Shows the help message.
264
+	 */
265
+	protected function showHelp()
266
+	{
267
+		$this->log('Usage:', 'yellow');
268
+		$this->log('  vobject [options] command [arguments]');
269
+		$this->log('');
270
+		$this->log('Options:', 'yellow');
271
+		$this->log($this->colorize('green', '  -q            ')."Don't output anything.");
272
+		$this->log($this->colorize('green', '  -help -h      ').'Display this help message.');
273
+		$this->log($this->colorize('green', '  --format      ').'Convert to a specific format. Must be one of: vcard, vcard21,');
274
+		$this->log($this->colorize('green', '  --forgiving   ').'Makes the parser less strict.');
275
+		$this->log('                vcard30, vcard40, icalendar20, jcal, jcard, json, mimedir.');
276
+		$this->log($this->colorize('green', '  --inputformat ').'If the input format cannot be guessed from the extension, it');
277
+		$this->log('                must be specified here.');
278
+		// Only PHP 5.4 and up
279
+		if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
280
+			$this->log($this->colorize('green', '  --pretty      ').'json pretty-print.');
281
+		}
282
+		$this->log('');
283
+		$this->log('Commands:', 'yellow');
284
+		$this->log($this->colorize('green', '  validate').' source_file              Validates a file for correctness.');
285
+		$this->log($this->colorize('green', '  repair').' source_file [output_file]  Repairs a file.');
286
+		$this->log($this->colorize('green', '  convert').' source_file [output_file] Converts a file.');
287
+		$this->log($this->colorize('green', '  color').' source_file                 Colorize a file, useful for debugging.');
288
+		$this->log(
289
+		<<<HELP
290 290
 
291 291
 If source_file is set as '-', STDIN will be used.
292 292
 If output_file is omitted, STDOUT will be used.
293 293
 All other output is sent to STDERR.
294 294
 
295 295
 HELP
296
-        );
297
-
298
-        $this->log('Examples:', 'yellow');
299
-        $this->log('   vobject convert contact.vcf contact.json');
300
-        $this->log('   vobject convert --format=vcard40 old.vcf new.vcf');
301
-        $this->log('   vobject convert --inputformat=json --format=mimedir - -');
302
-        $this->log('   vobject color calendar.ics');
303
-        $this->log('');
304
-        $this->log('https://github.com/fruux/sabre-vobject', 'purple');
305
-    }
306
-
307
-    /**
308
-     * Validates a VObject file.
309
-     *
310
-     * @return int
311
-     */
312
-    protected function validate(Component $vObj)
313
-    {
314
-        $returnCode = 0;
315
-
316
-        switch ($vObj->name) {
317
-            case 'VCALENDAR':
318
-                $this->log('iCalendar: '.(string) $vObj->VERSION);
319
-                break;
320
-            case 'VCARD':
321
-                $this->log('vCard: '.(string) $vObj->VERSION);
322
-                break;
323
-        }
324
-
325
-        $warnings = $vObj->validate();
326
-        if (!count($warnings)) {
327
-            $this->log('  No warnings!');
328
-        } else {
329
-            $levels = [
330
-                1 => 'REPAIRED',
331
-                2 => 'WARNING',
332
-                3 => 'ERROR',
333
-            ];
334
-            $returnCode = 2;
335
-            foreach ($warnings as $warn) {
336
-                $extra = '';
337
-                if ($warn['node'] instanceof Property) {
338
-                    $extra = ' (property: "'.$warn['node']->name.'")';
339
-                }
340
-                $this->log('  ['.$levels[$warn['level']].'] '.$warn['message'].$extra);
341
-            }
342
-        }
343
-
344
-        return $returnCode;
345
-    }
346
-
347
-    /**
348
-     * Repairs a VObject file.
349
-     *
350
-     * @return int
351
-     */
352
-    protected function repair(Component $vObj)
353
-    {
354
-        $returnCode = 0;
355
-
356
-        switch ($vObj->name) {
357
-            case 'VCALENDAR':
358
-                $this->log('iCalendar: '.(string) $vObj->VERSION);
359
-                break;
360
-            case 'VCARD':
361
-                $this->log('vCard: '.(string) $vObj->VERSION);
362
-                break;
363
-        }
364
-
365
-        $warnings = $vObj->validate(Node::REPAIR);
366
-        if (!count($warnings)) {
367
-            $this->log('  No warnings!');
368
-        } else {
369
-            $levels = [
370
-                1 => 'REPAIRED',
371
-                2 => 'WARNING',
372
-                3 => 'ERROR',
373
-            ];
374
-            $returnCode = 2;
375
-            foreach ($warnings as $warn) {
376
-                $extra = '';
377
-                if ($warn['node'] instanceof Property) {
378
-                    $extra = ' (property: "'.$warn['node']->name.'")';
379
-                }
380
-                $this->log('  ['.$levels[$warn['level']].'] '.$warn['message'].$extra);
381
-            }
382
-        }
383
-        fwrite($this->stdout, $vObj->serialize());
384
-
385
-        return $returnCode;
386
-    }
387
-
388
-    /**
389
-     * Converts a vObject file to a new format.
390
-     *
391
-     * @param Component $vObj
392
-     *
393
-     * @return int
394
-     */
395
-    protected function convert($vObj)
396
-    {
397
-        $json = false;
398
-        $convertVersion = null;
399
-        $forceInput = null;
400
-
401
-        switch ($this->format) {
402
-            case 'json':
403
-                $json = true;
404
-                if ('VCARD' === $vObj->name) {
405
-                    $convertVersion = Document::VCARD40;
406
-                }
407
-                break;
408
-            case 'jcard':
409
-                $json = true;
410
-                $forceInput = 'VCARD';
411
-                $convertVersion = Document::VCARD40;
412
-                break;
413
-            case 'jcal':
414
-                $json = true;
415
-                $forceInput = 'VCALENDAR';
416
-                break;
417
-            case 'mimedir':
418
-            case 'icalendar':
419
-            case 'icalendar20':
420
-            case 'vcard':
421
-                break;
422
-            case 'vcard21':
423
-                $convertVersion = Document::VCARD21;
424
-                break;
425
-            case 'vcard30':
426
-                $convertVersion = Document::VCARD30;
427
-                break;
428
-            case 'vcard40':
429
-                $convertVersion = Document::VCARD40;
430
-                break;
431
-        }
432
-
433
-        if ($forceInput && $vObj->name !== $forceInput) {
434
-            throw new \Exception('You cannot convert a '.strtolower($vObj->name).' to '.$this->format);
435
-        }
436
-        if ($convertVersion) {
437
-            $vObj = $vObj->convert($convertVersion);
438
-        }
439
-        if ($json) {
440
-            $jsonOptions = 0;
441
-            if ($this->pretty) {
442
-                $jsonOptions = JSON_PRETTY_PRINT;
443
-            }
444
-            fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions));
445
-        } else {
446
-            fwrite($this->stdout, $vObj->serialize());
447
-        }
448
-
449
-        return 0;
450
-    }
451
-
452
-    /**
453
-     * Colorizes a file.
454
-     *
455
-     * @param Component $vObj
456
-     */
457
-    protected function color($vObj)
458
-    {
459
-        $this->serializeComponent($vObj);
460
-    }
461
-
462
-    /**
463
-     * Returns an ansi color string for a color name.
464
-     *
465
-     * @param string $color
466
-     *
467
-     * @return string
468
-     */
469
-    protected function colorize($color, $str, $resetTo = 'default')
470
-    {
471
-        $colors = [
472
-            'cyan' => '1;36',
473
-            'red' => '1;31',
474
-            'yellow' => '1;33',
475
-            'blue' => '0;34',
476
-            'green' => '0;32',
477
-            'default' => '0',
478
-            'purple' => '0;35',
479
-        ];
480
-
481
-        return "\033[".$colors[$color].'m'.$str."\033[".$colors[$resetTo].'m';
482
-    }
483
-
484
-    /**
485
-     * Writes out a string in specific color.
486
-     *
487
-     * @param string $color
488
-     * @param string $str
489
-     */
490
-    protected function cWrite($color, $str)
491
-    {
492
-        fwrite($this->stdout, $this->colorize($color, $str));
493
-    }
494
-
495
-    protected function serializeComponent(Component $vObj)
496
-    {
497
-        $this->cWrite('cyan', 'BEGIN');
498
-        $this->cWrite('red', ':');
499
-        $this->cWrite('yellow', $vObj->name."\n");
500
-
501
-        /**
502
-         * Gives a component a 'score' for sorting purposes.
503
-         *
504
-         * This is solely used by the childrenSort method.
505
-         *
506
-         * A higher score means the item will be lower in the list.
507
-         * To avoid score collisions, each "score category" has a reasonable
508
-         * space to accommodate elements. The $key is added to the $score to
509
-         * preserve the original relative order of elements.
510
-         *
511
-         * @param int   $key
512
-         * @param array $array
513
-         *
514
-         * @return int
515
-         */
516
-        $sortScore = function ($key, $array) {
517
-            if ($array[$key] instanceof Component) {
518
-                // We want to encode VTIMEZONE first, this is a personal
519
-                // preference.
520
-                if ('VTIMEZONE' === $array[$key]->name) {
521
-                    $score = 300000000;
522
-
523
-                    return $score + $key;
524
-                } else {
525
-                    $score = 400000000;
526
-
527
-                    return $score + $key;
528
-                }
529
-            } else {
530
-                // Properties get encoded first
531
-                // VCARD version 4.0 wants the VERSION property to appear first
532
-                if ($array[$key] instanceof Property) {
533
-                    if ('VERSION' === $array[$key]->name) {
534
-                        $score = 100000000;
535
-
536
-                        return $score + $key;
537
-                    } else {
538
-                        // All other properties
539
-                        $score = 200000000;
540
-
541
-                        return $score + $key;
542
-                    }
543
-                }
544
-            }
545
-        };
546
-
547
-        $children = $vObj->children();
548
-        $tmp = $children;
549
-        uksort(
550
-            $children,
551
-            function ($a, $b) use ($sortScore, $tmp) {
552
-                $sA = $sortScore($a, $tmp);
553
-                $sB = $sortScore($b, $tmp);
554
-
555
-                return $sA - $sB;
556
-            }
557
-        );
558
-
559
-        foreach ($children as $child) {
560
-            if ($child instanceof Component) {
561
-                $this->serializeComponent($child);
562
-            } else {
563
-                $this->serializeProperty($child);
564
-            }
565
-        }
566
-
567
-        $this->cWrite('cyan', 'END');
568
-        $this->cWrite('red', ':');
569
-        $this->cWrite('yellow', $vObj->name."\n");
570
-    }
571
-
572
-    /**
573
-     * Colorizes a property.
574
-     */
575
-    protected function serializeProperty(Property $property)
576
-    {
577
-        if ($property->group) {
578
-            $this->cWrite('default', $property->group);
579
-            $this->cWrite('red', '.');
580
-        }
581
-
582
-        $this->cWrite('yellow', $property->name);
583
-
584
-        foreach ($property->parameters as $param) {
585
-            $this->cWrite('red', ';');
586
-            $this->cWrite('blue', $param->serialize());
587
-        }
588
-        $this->cWrite('red', ':');
589
-
590
-        if ($property instanceof Property\Binary) {
591
-            $this->cWrite('default', 'embedded binary stripped. ('.strlen($property->getValue()).' bytes)');
592
-        } else {
593
-            $parts = $property->getParts();
594
-            $first1 = true;
595
-            // Looping through property values
596
-            foreach ($parts as $part) {
597
-                if ($first1) {
598
-                    $first1 = false;
599
-                } else {
600
-                    $this->cWrite('red', $property->delimiter);
601
-                }
602
-                $first2 = true;
603
-                // Looping through property sub-values
604
-                foreach ((array) $part as $subPart) {
605
-                    if ($first2) {
606
-                        $first2 = false;
607
-                    } else {
608
-                        // The sub-value delimiter is always comma
609
-                        $this->cWrite('red', ',');
610
-                    }
611
-
612
-                    $subPart = strtr(
613
-                        $subPart,
614
-                        [
615
-                            '\\' => $this->colorize('purple', '\\\\', 'green'),
616
-                            ';' => $this->colorize('purple', '\;', 'green'),
617
-                            ',' => $this->colorize('purple', '\,', 'green'),
618
-                            "\n" => $this->colorize('purple', "\\n\n\t", 'green'),
619
-                            "\r" => '',
620
-                        ]
621
-                    );
622
-
623
-                    $this->cWrite('green', $subPart);
624
-                }
625
-            }
626
-        }
627
-        $this->cWrite('default', "\n");
628
-    }
629
-
630
-    /**
631
-     * Parses the list of arguments.
632
-     */
633
-    protected function parseArguments(array $argv)
634
-    {
635
-        $positional = [];
636
-        $options = [];
637
-
638
-        for ($ii = 0; $ii < count($argv); ++$ii) {
639
-            // Skipping the first argument.
640
-            if (0 === $ii) {
641
-                continue;
642
-            }
643
-
644
-            $v = $argv[$ii];
645
-
646
-            if ('--' === substr($v, 0, 2)) {
647
-                // This is a long-form option.
648
-                $optionName = substr($v, 2);
649
-                $optionValue = true;
650
-                if (strpos($optionName, '=')) {
651
-                    list($optionName, $optionValue) = explode('=', $optionName);
652
-                }
653
-                $options[$optionName] = $optionValue;
654
-            } elseif ('-' === substr($v, 0, 1) && strlen($v) > 1) {
655
-                // This is a short-form option.
656
-                foreach (str_split(substr($v, 1)) as $option) {
657
-                    $options[$option] = true;
658
-                }
659
-            } else {
660
-                $positional[] = $v;
661
-            }
662
-        }
663
-
664
-        return [$options, $positional];
665
-    }
666
-
667
-    protected $parser;
668
-
669
-    /**
670
-     * Reads the input file.
671
-     *
672
-     * @return Component
673
-     */
674
-    protected function readInput()
675
-    {
676
-        if (!$this->parser) {
677
-            if ('-' !== $this->inputPath) {
678
-                $this->stdin = fopen($this->inputPath, 'r');
679
-            }
680
-
681
-            if ('mimedir' === $this->inputFormat) {
682
-                $this->parser = new Parser\MimeDir($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
683
-            } else {
684
-                $this->parser = new Parser\Json($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
685
-            }
686
-        }
687
-
688
-        return $this->parser->parse();
689
-    }
690
-
691
-    /**
692
-     * Sends a message to STDERR.
693
-     *
694
-     * @param string $msg
695
-     */
696
-    protected function log($msg, $color = 'default')
697
-    {
698
-        if (!$this->quiet) {
699
-            if ('default' !== $color) {
700
-                $msg = $this->colorize($color, $msg);
701
-            }
702
-            fwrite($this->stderr, $msg."\n");
703
-        }
704
-    }
296
+		);
297
+
298
+		$this->log('Examples:', 'yellow');
299
+		$this->log('   vobject convert contact.vcf contact.json');
300
+		$this->log('   vobject convert --format=vcard40 old.vcf new.vcf');
301
+		$this->log('   vobject convert --inputformat=json --format=mimedir - -');
302
+		$this->log('   vobject color calendar.ics');
303
+		$this->log('');
304
+		$this->log('https://github.com/fruux/sabre-vobject', 'purple');
305
+	}
306
+
307
+	/**
308
+	 * Validates a VObject file.
309
+	 *
310
+	 * @return int
311
+	 */
312
+	protected function validate(Component $vObj)
313
+	{
314
+		$returnCode = 0;
315
+
316
+		switch ($vObj->name) {
317
+			case 'VCALENDAR':
318
+				$this->log('iCalendar: '.(string) $vObj->VERSION);
319
+				break;
320
+			case 'VCARD':
321
+				$this->log('vCard: '.(string) $vObj->VERSION);
322
+				break;
323
+		}
324
+
325
+		$warnings = $vObj->validate();
326
+		if (!count($warnings)) {
327
+			$this->log('  No warnings!');
328
+		} else {
329
+			$levels = [
330
+				1 => 'REPAIRED',
331
+				2 => 'WARNING',
332
+				3 => 'ERROR',
333
+			];
334
+			$returnCode = 2;
335
+			foreach ($warnings as $warn) {
336
+				$extra = '';
337
+				if ($warn['node'] instanceof Property) {
338
+					$extra = ' (property: "'.$warn['node']->name.'")';
339
+				}
340
+				$this->log('  ['.$levels[$warn['level']].'] '.$warn['message'].$extra);
341
+			}
342
+		}
343
+
344
+		return $returnCode;
345
+	}
346
+
347
+	/**
348
+	 * Repairs a VObject file.
349
+	 *
350
+	 * @return int
351
+	 */
352
+	protected function repair(Component $vObj)
353
+	{
354
+		$returnCode = 0;
355
+
356
+		switch ($vObj->name) {
357
+			case 'VCALENDAR':
358
+				$this->log('iCalendar: '.(string) $vObj->VERSION);
359
+				break;
360
+			case 'VCARD':
361
+				$this->log('vCard: '.(string) $vObj->VERSION);
362
+				break;
363
+		}
364
+
365
+		$warnings = $vObj->validate(Node::REPAIR);
366
+		if (!count($warnings)) {
367
+			$this->log('  No warnings!');
368
+		} else {
369
+			$levels = [
370
+				1 => 'REPAIRED',
371
+				2 => 'WARNING',
372
+				3 => 'ERROR',
373
+			];
374
+			$returnCode = 2;
375
+			foreach ($warnings as $warn) {
376
+				$extra = '';
377
+				if ($warn['node'] instanceof Property) {
378
+					$extra = ' (property: "'.$warn['node']->name.'")';
379
+				}
380
+				$this->log('  ['.$levels[$warn['level']].'] '.$warn['message'].$extra);
381
+			}
382
+		}
383
+		fwrite($this->stdout, $vObj->serialize());
384
+
385
+		return $returnCode;
386
+	}
387
+
388
+	/**
389
+	 * Converts a vObject file to a new format.
390
+	 *
391
+	 * @param Component $vObj
392
+	 *
393
+	 * @return int
394
+	 */
395
+	protected function convert($vObj)
396
+	{
397
+		$json = false;
398
+		$convertVersion = null;
399
+		$forceInput = null;
400
+
401
+		switch ($this->format) {
402
+			case 'json':
403
+				$json = true;
404
+				if ('VCARD' === $vObj->name) {
405
+					$convertVersion = Document::VCARD40;
406
+				}
407
+				break;
408
+			case 'jcard':
409
+				$json = true;
410
+				$forceInput = 'VCARD';
411
+				$convertVersion = Document::VCARD40;
412
+				break;
413
+			case 'jcal':
414
+				$json = true;
415
+				$forceInput = 'VCALENDAR';
416
+				break;
417
+			case 'mimedir':
418
+			case 'icalendar':
419
+			case 'icalendar20':
420
+			case 'vcard':
421
+				break;
422
+			case 'vcard21':
423
+				$convertVersion = Document::VCARD21;
424
+				break;
425
+			case 'vcard30':
426
+				$convertVersion = Document::VCARD30;
427
+				break;
428
+			case 'vcard40':
429
+				$convertVersion = Document::VCARD40;
430
+				break;
431
+		}
432
+
433
+		if ($forceInput && $vObj->name !== $forceInput) {
434
+			throw new \Exception('You cannot convert a '.strtolower($vObj->name).' to '.$this->format);
435
+		}
436
+		if ($convertVersion) {
437
+			$vObj = $vObj->convert($convertVersion);
438
+		}
439
+		if ($json) {
440
+			$jsonOptions = 0;
441
+			if ($this->pretty) {
442
+				$jsonOptions = JSON_PRETTY_PRINT;
443
+			}
444
+			fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions));
445
+		} else {
446
+			fwrite($this->stdout, $vObj->serialize());
447
+		}
448
+
449
+		return 0;
450
+	}
451
+
452
+	/**
453
+	 * Colorizes a file.
454
+	 *
455
+	 * @param Component $vObj
456
+	 */
457
+	protected function color($vObj)
458
+	{
459
+		$this->serializeComponent($vObj);
460
+	}
461
+
462
+	/**
463
+	 * Returns an ansi color string for a color name.
464
+	 *
465
+	 * @param string $color
466
+	 *
467
+	 * @return string
468
+	 */
469
+	protected function colorize($color, $str, $resetTo = 'default')
470
+	{
471
+		$colors = [
472
+			'cyan' => '1;36',
473
+			'red' => '1;31',
474
+			'yellow' => '1;33',
475
+			'blue' => '0;34',
476
+			'green' => '0;32',
477
+			'default' => '0',
478
+			'purple' => '0;35',
479
+		];
480
+
481
+		return "\033[".$colors[$color].'m'.$str."\033[".$colors[$resetTo].'m';
482
+	}
483
+
484
+	/**
485
+	 * Writes out a string in specific color.
486
+	 *
487
+	 * @param string $color
488
+	 * @param string $str
489
+	 */
490
+	protected function cWrite($color, $str)
491
+	{
492
+		fwrite($this->stdout, $this->colorize($color, $str));
493
+	}
494
+
495
+	protected function serializeComponent(Component $vObj)
496
+	{
497
+		$this->cWrite('cyan', 'BEGIN');
498
+		$this->cWrite('red', ':');
499
+		$this->cWrite('yellow', $vObj->name."\n");
500
+
501
+		/**
502
+		 * Gives a component a 'score' for sorting purposes.
503
+		 *
504
+		 * This is solely used by the childrenSort method.
505
+		 *
506
+		 * A higher score means the item will be lower in the list.
507
+		 * To avoid score collisions, each "score category" has a reasonable
508
+		 * space to accommodate elements. The $key is added to the $score to
509
+		 * preserve the original relative order of elements.
510
+		 *
511
+		 * @param int   $key
512
+		 * @param array $array
513
+		 *
514
+		 * @return int
515
+		 */
516
+		$sortScore = function ($key, $array) {
517
+			if ($array[$key] instanceof Component) {
518
+				// We want to encode VTIMEZONE first, this is a personal
519
+				// preference.
520
+				if ('VTIMEZONE' === $array[$key]->name) {
521
+					$score = 300000000;
522
+
523
+					return $score + $key;
524
+				} else {
525
+					$score = 400000000;
526
+
527
+					return $score + $key;
528
+				}
529
+			} else {
530
+				// Properties get encoded first
531
+				// VCARD version 4.0 wants the VERSION property to appear first
532
+				if ($array[$key] instanceof Property) {
533
+					if ('VERSION' === $array[$key]->name) {
534
+						$score = 100000000;
535
+
536
+						return $score + $key;
537
+					} else {
538
+						// All other properties
539
+						$score = 200000000;
540
+
541
+						return $score + $key;
542
+					}
543
+				}
544
+			}
545
+		};
546
+
547
+		$children = $vObj->children();
548
+		$tmp = $children;
549
+		uksort(
550
+			$children,
551
+			function ($a, $b) use ($sortScore, $tmp) {
552
+				$sA = $sortScore($a, $tmp);
553
+				$sB = $sortScore($b, $tmp);
554
+
555
+				return $sA - $sB;
556
+			}
557
+		);
558
+
559
+		foreach ($children as $child) {
560
+			if ($child instanceof Component) {
561
+				$this->serializeComponent($child);
562
+			} else {
563
+				$this->serializeProperty($child);
564
+			}
565
+		}
566
+
567
+		$this->cWrite('cyan', 'END');
568
+		$this->cWrite('red', ':');
569
+		$this->cWrite('yellow', $vObj->name."\n");
570
+	}
571
+
572
+	/**
573
+	 * Colorizes a property.
574
+	 */
575
+	protected function serializeProperty(Property $property)
576
+	{
577
+		if ($property->group) {
578
+			$this->cWrite('default', $property->group);
579
+			$this->cWrite('red', '.');
580
+		}
581
+
582
+		$this->cWrite('yellow', $property->name);
583
+
584
+		foreach ($property->parameters as $param) {
585
+			$this->cWrite('red', ';');
586
+			$this->cWrite('blue', $param->serialize());
587
+		}
588
+		$this->cWrite('red', ':');
589
+
590
+		if ($property instanceof Property\Binary) {
591
+			$this->cWrite('default', 'embedded binary stripped. ('.strlen($property->getValue()).' bytes)');
592
+		} else {
593
+			$parts = $property->getParts();
594
+			$first1 = true;
595
+			// Looping through property values
596
+			foreach ($parts as $part) {
597
+				if ($first1) {
598
+					$first1 = false;
599
+				} else {
600
+					$this->cWrite('red', $property->delimiter);
601
+				}
602
+				$first2 = true;
603
+				// Looping through property sub-values
604
+				foreach ((array) $part as $subPart) {
605
+					if ($first2) {
606
+						$first2 = false;
607
+					} else {
608
+						// The sub-value delimiter is always comma
609
+						$this->cWrite('red', ',');
610
+					}
611
+
612
+					$subPart = strtr(
613
+						$subPart,
614
+						[
615
+							'\\' => $this->colorize('purple', '\\\\', 'green'),
616
+							';' => $this->colorize('purple', '\;', 'green'),
617
+							',' => $this->colorize('purple', '\,', 'green'),
618
+							"\n" => $this->colorize('purple', "\\n\n\t", 'green'),
619
+							"\r" => '',
620
+						]
621
+					);
622
+
623
+					$this->cWrite('green', $subPart);
624
+				}
625
+			}
626
+		}
627
+		$this->cWrite('default', "\n");
628
+	}
629
+
630
+	/**
631
+	 * Parses the list of arguments.
632
+	 */
633
+	protected function parseArguments(array $argv)
634
+	{
635
+		$positional = [];
636
+		$options = [];
637
+
638
+		for ($ii = 0; $ii < count($argv); ++$ii) {
639
+			// Skipping the first argument.
640
+			if (0 === $ii) {
641
+				continue;
642
+			}
643
+
644
+			$v = $argv[$ii];
645
+
646
+			if ('--' === substr($v, 0, 2)) {
647
+				// This is a long-form option.
648
+				$optionName = substr($v, 2);
649
+				$optionValue = true;
650
+				if (strpos($optionName, '=')) {
651
+					list($optionName, $optionValue) = explode('=', $optionName);
652
+				}
653
+				$options[$optionName] = $optionValue;
654
+			} elseif ('-' === substr($v, 0, 1) && strlen($v) > 1) {
655
+				// This is a short-form option.
656
+				foreach (str_split(substr($v, 1)) as $option) {
657
+					$options[$option] = true;
658
+				}
659
+			} else {
660
+				$positional[] = $v;
661
+			}
662
+		}
663
+
664
+		return [$options, $positional];
665
+	}
666
+
667
+	protected $parser;
668
+
669
+	/**
670
+	 * Reads the input file.
671
+	 *
672
+	 * @return Component
673
+	 */
674
+	protected function readInput()
675
+	{
676
+		if (!$this->parser) {
677
+			if ('-' !== $this->inputPath) {
678
+				$this->stdin = fopen($this->inputPath, 'r');
679
+			}
680
+
681
+			if ('mimedir' === $this->inputFormat) {
682
+				$this->parser = new Parser\MimeDir($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
683
+			} else {
684
+				$this->parser = new Parser\Json($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
685
+			}
686
+		}
687
+
688
+		return $this->parser->parse();
689
+	}
690
+
691
+	/**
692
+	 * Sends a message to STDERR.
693
+	 *
694
+	 * @param string $msg
695
+	 */
696
+	protected function log($msg, $color = 'default')
697
+	{
698
+		if (!$this->quiet) {
699
+			if ('default' !== $color) {
700
+				$msg = $this->colorize($color, $msg);
701
+			}
702
+			fwrite($this->stderr, $msg."\n");
703
+		}
704
+	}
705 705
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -513,7 +513,7 @@  discard block
 block discarded – undo
513 513
          *
514 514
          * @return int
515 515
          */
516
-        $sortScore = function ($key, $array) {
516
+        $sortScore = function($key, $array) {
517 517
             if ($array[$key] instanceof Component) {
518 518
                 // We want to encode VTIMEZONE first, this is a personal
519 519
                 // preference.
@@ -548,7 +548,7 @@  discard block
 block discarded – undo
548 548
         $tmp = $children;
549 549
         uksort(
550 550
             $children,
551
-            function ($a, $b) use ($sortScore, $tmp) {
551
+            function($a, $b) use ($sortScore, $tmp) {
552 552
                 $sA = $sortScore($a, $tmp);
553 553
                 $sB = $sortScore($b, $tmp);
554 554
 
Please login to merge, or discard this patch.
Upper-Lower-Casing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -292,7 +292,7 @@
 block discarded – undo
292 292
 If output_file is omitted, STDOUT will be used.
293 293
 All other output is sent to STDERR.
294 294
 
295
-HELP
295
+help
296 296
         );
297 297
 
298 298
         $this->log('Examples:', 'yellow');
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/vobject/lib/Component.php 2 patches
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -237,7 +237,7 @@  discard block
 block discarded – undo
237 237
                 // more.
238 238
                 return array_filter(
239 239
                     $result,
240
-                    function ($child) use ($group) {
240
+                    function($child) use ($group) {
241 241
                         return $child instanceof Property && (null !== $child->group ? strtoupper($child->group) : '') === $group;
242 242
                     }
243 243
                 );
@@ -282,7 +282,7 @@  discard block
 block discarded – undo
282 282
          *
283 283
          * @return int
284 284
          */
285
-        $sortScore = function ($key, $array) {
285
+        $sortScore = function($key, $array) {
286 286
             if ($array[$key] instanceof Component) {
287 287
                 // We want to encode VTIMEZONE first, this is a personal
288 288
                 // preference.
@@ -317,7 +317,7 @@  discard block
 block discarded – undo
317 317
         $tmp = $children;
318 318
         uksort(
319 319
             $children,
320
-            function ($a, $b) use ($sortScore, $tmp) {
320
+            function($a, $b) use ($sortScore, $tmp) {
321 321
                 $sA = $sortScore($a, $tmp);
322 322
                 $sB = $sortScore($b, $tmp);
323 323
 
Please login to merge, or discard this patch.
Indentation   +653 added lines, -653 removed lines patch added patch discarded remove patch
@@ -16,657 +16,657 @@
 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<string, Component|Property>
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 string|null $name     such as VCALENDAR, VEVENT
47
-     * @param bool        $defaults
48
-     */
49
-    public function __construct(Document $root, $name, array $children = [], $defaults = true)
50
-    {
51
-        $this->name = isset($name) ? strtoupper($name) : '';
52
-        $this->root = $root;
53
-
54
-        if ($defaults) {
55
-            // This is a terribly convoluted way to do this, but this ensures
56
-            // that the order of properties as they are specified in both
57
-            // defaults and the childrens list, are inserted in the object in a
58
-            // natural way.
59
-            $list = $this->getDefaults();
60
-            $nodes = [];
61
-            foreach ($children as $key => $value) {
62
-                if ($value instanceof Node) {
63
-                    if (isset($list[$value->name])) {
64
-                        unset($list[$value->name]);
65
-                    }
66
-                    $nodes[] = $value;
67
-                } else {
68
-                    $list[$key] = $value;
69
-                }
70
-            }
71
-            foreach ($list as $key => $value) {
72
-                $this->add($key, $value);
73
-            }
74
-            foreach ($nodes as $node) {
75
-                $this->add($node);
76
-            }
77
-        } else {
78
-            foreach ($children as $k => $child) {
79
-                if ($child instanceof Node) {
80
-                    // Component or Property
81
-                    $this->add($child);
82
-                } else {
83
-                    // Property key=>value
84
-                    $this->add($k, $child);
85
-                }
86
-            }
87
-        }
88
-    }
89
-
90
-    /**
91
-     * Adds a new property or component, and returns the new item.
92
-     *
93
-     * This method has 3 possible signatures:
94
-     *
95
-     * add(Component $comp) // Adds a new component
96
-     * add(Property $prop)  // Adds a new property
97
-     * add($name, $value, array $parameters = []) // Adds a new property
98
-     * add($name, array $children = []) // Adds a new component
99
-     * by name.
100
-     *
101
-     * @return Node
102
-     */
103
-    public function add()
104
-    {
105
-        $arguments = func_get_args();
106
-
107
-        if ($arguments[0] instanceof Node) {
108
-            if (isset($arguments[1])) {
109
-                throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node');
110
-            }
111
-            $arguments[0]->parent = $this;
112
-            $newNode = $arguments[0];
113
-        } elseif (is_string($arguments[0])) {
114
-            $newNode = call_user_func_array([$this->root, 'create'], $arguments);
115
-        } else {
116
-            throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string');
117
-        }
118
-
119
-        $name = $newNode->name;
120
-        if (isset($this->children[$name])) {
121
-            $this->children[$name][] = $newNode;
122
-        } else {
123
-            $this->children[$name] = [$newNode];
124
-        }
125
-
126
-        return $newNode;
127
-    }
128
-
129
-    /**
130
-     * This method removes a component or property from this component.
131
-     *
132
-     * You can either specify the item by name (like DTSTART), in which case
133
-     * all properties/components with that name will be removed, or you can
134
-     * pass an instance of a property or component, in which case only that
135
-     * exact item will be removed.
136
-     *
137
-     * @param string|Property|Component $item
138
-     */
139
-    public function remove($item)
140
-    {
141
-        if (is_string($item)) {
142
-            // If there's no dot in the name, it's an exact property name and
143
-            // we can just wipe out all those properties.
144
-            //
145
-            if (false === strpos($item, '.')) {
146
-                unset($this->children[strtoupper($item)]);
147
-
148
-                return;
149
-            }
150
-            // If there was a dot, we need to ask select() to help us out and
151
-            // then we just call remove recursively.
152
-            foreach ($this->select($item) as $child) {
153
-                $this->remove($child);
154
-            }
155
-        } else {
156
-            foreach ($this->select($item->name) as $k => $child) {
157
-                if ($child === $item) {
158
-                    unset($this->children[$item->name][$k]);
159
-
160
-                    return;
161
-                }
162
-            }
163
-
164
-            throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component');
165
-        }
166
-    }
167
-
168
-    /**
169
-     * Returns a flat list of all the properties and components in this
170
-     * component.
171
-     *
172
-     * @return array
173
-     */
174
-    public function children()
175
-    {
176
-        $result = [];
177
-        foreach ($this->children as $childGroup) {
178
-            $result = array_merge($result, $childGroup);
179
-        }
180
-
181
-        return $result;
182
-    }
183
-
184
-    /**
185
-     * This method only returns a list of sub-components. Properties are
186
-     * ignored.
187
-     *
188
-     * @return array
189
-     */
190
-    public function getComponents()
191
-    {
192
-        $result = [];
193
-
194
-        foreach ($this->children as $childGroup) {
195
-            foreach ($childGroup as $child) {
196
-                if ($child instanceof self) {
197
-                    $result[] = $child;
198
-                }
199
-            }
200
-        }
201
-
202
-        return $result;
203
-    }
204
-
205
-    /**
206
-     * Returns an array with elements that match the specified name.
207
-     *
208
-     * This function is also aware of MIME-Directory groups (as they appear in
209
-     * vcards). This means that if a property is grouped as "HOME.EMAIL", it
210
-     * will also be returned when searching for just "EMAIL". If you want to
211
-     * search for a property in a specific group, you can select on the entire
212
-     * string ("HOME.EMAIL"). If you want to search on a specific property that
213
-     * has not been assigned a group, specify ".EMAIL".
214
-     *
215
-     * @param string $name
216
-     *
217
-     * @return array
218
-     */
219
-    public function select($name)
220
-    {
221
-        $group = null;
222
-        $name = strtoupper($name);
223
-        if (false !== strpos($name, '.')) {
224
-            list($group, $name) = explode('.', $name, 2);
225
-        }
226
-        if ('' === $name) {
227
-            $name = null;
228
-        }
229
-
230
-        if (!is_null($name)) {
231
-            $result = isset($this->children[$name]) ? $this->children[$name] : [];
232
-
233
-            if (is_null($group)) {
234
-                return $result;
235
-            } else {
236
-                // If we have a group filter as well, we need to narrow it down
237
-                // more.
238
-                return array_filter(
239
-                    $result,
240
-                    function ($child) use ($group) {
241
-                        return $child instanceof Property && (null !== $child->group ? strtoupper($child->group) : '') === $group;
242
-                    }
243
-                );
244
-            }
245
-        }
246
-
247
-        // If we got to this point, it means there was no 'name' specified for
248
-        // searching, implying that this is a group-only search.
249
-        $result = [];
250
-        foreach ($this->children as $childGroup) {
251
-            foreach ($childGroup as $child) {
252
-                if ($child instanceof Property && (null !== $child->group ? strtoupper($child->group) : '') === $group) {
253
-                    $result[] = $child;
254
-                }
255
-            }
256
-        }
257
-
258
-        return $result;
259
-    }
260
-
261
-    /**
262
-     * Turns the object back into a serialized blob.
263
-     *
264
-     * @return string
265
-     */
266
-    public function serialize()
267
-    {
268
-        $str = 'BEGIN:'.$this->name."\r\n";
269
-
270
-        /**
271
-         * Gives a component a 'score' for sorting purposes.
272
-         *
273
-         * This is solely used by the childrenSort method.
274
-         *
275
-         * A higher score means the item will be lower in the list.
276
-         * To avoid score collisions, each "score category" has a reasonable
277
-         * space to accommodate elements. The $key is added to the $score to
278
-         * preserve the original relative order of elements.
279
-         *
280
-         * @param int   $key
281
-         * @param array $array
282
-         *
283
-         * @return int
284
-         */
285
-        $sortScore = function ($key, $array) {
286
-            if ($array[$key] instanceof Component) {
287
-                // We want to encode VTIMEZONE first, this is a personal
288
-                // preference.
289
-                if ('VTIMEZONE' === $array[$key]->name) {
290
-                    $score = 300000000;
291
-
292
-                    return $score + $key;
293
-                } else {
294
-                    $score = 400000000;
295
-
296
-                    return $score + $key;
297
-                }
298
-            } else {
299
-                // Properties get encoded first
300
-                // VCARD version 4.0 wants the VERSION property to appear first
301
-                if ($array[$key] instanceof Property) {
302
-                    if ('VERSION' === $array[$key]->name) {
303
-                        $score = 100000000;
304
-
305
-                        return $score + $key;
306
-                    } else {
307
-                        // All other properties
308
-                        $score = 200000000;
309
-
310
-                        return $score + $key;
311
-                    }
312
-                }
313
-            }
314
-        };
315
-
316
-        $children = $this->children();
317
-        $tmp = $children;
318
-        uksort(
319
-            $children,
320
-            function ($a, $b) use ($sortScore, $tmp) {
321
-                $sA = $sortScore($a, $tmp);
322
-                $sB = $sortScore($b, $tmp);
323
-
324
-                return $sA - $sB;
325
-            }
326
-        );
327
-
328
-        foreach ($children as $child) {
329
-            $str .= $child->serialize();
330
-        }
331
-        $str .= 'END:'.$this->name."\r\n";
332
-
333
-        return $str;
334
-    }
335
-
336
-    /**
337
-     * This method returns an array, with the representation as it should be
338
-     * encoded in JSON. This is used to create jCard or jCal documents.
339
-     *
340
-     * @return array
341
-     */
342
-    #[\ReturnTypeWillChange]
343
-    public function jsonSerialize()
344
-    {
345
-        $components = [];
346
-        $properties = [];
347
-
348
-        foreach ($this->children as $childGroup) {
349
-            foreach ($childGroup as $child) {
350
-                if ($child instanceof self) {
351
-                    $components[] = $child->jsonSerialize();
352
-                } else {
353
-                    $properties[] = $child->jsonSerialize();
354
-                }
355
-            }
356
-        }
357
-
358
-        return [
359
-            strtolower($this->name),
360
-            $properties,
361
-            $components,
362
-        ];
363
-    }
364
-
365
-    /**
366
-     * This method serializes the data into XML. This is used to create xCard or
367
-     * xCal documents.
368
-     *
369
-     * @param Xml\Writer $writer XML writer
370
-     */
371
-    public function xmlSerialize(Xml\Writer $writer): void
372
-    {
373
-        $components = [];
374
-        $properties = [];
375
-
376
-        foreach ($this->children as $childGroup) {
377
-            foreach ($childGroup as $child) {
378
-                if ($child instanceof self) {
379
-                    $components[] = $child;
380
-                } else {
381
-                    $properties[] = $child;
382
-                }
383
-            }
384
-        }
385
-
386
-        $writer->startElement(strtolower($this->name));
387
-
388
-        if (!empty($properties)) {
389
-            $writer->startElement('properties');
390
-
391
-            foreach ($properties as $property) {
392
-                $property->xmlSerialize($writer);
393
-            }
394
-
395
-            $writer->endElement();
396
-        }
397
-
398
-        if (!empty($components)) {
399
-            $writer->startElement('components');
400
-
401
-            foreach ($components as $component) {
402
-                $component->xmlSerialize($writer);
403
-            }
404
-
405
-            $writer->endElement();
406
-        }
407
-
408
-        $writer->endElement();
409
-    }
410
-
411
-    /**
412
-     * This method should return a list of default property values.
413
-     *
414
-     * @return array
415
-     */
416
-    protected function getDefaults()
417
-    {
418
-        return [];
419
-    }
420
-
421
-    /* Magic property accessors {{{ */
422
-
423
-    /**
424
-     * Using 'get' you will either get a property or component.
425
-     *
426
-     * If there were no child-elements found with the specified name,
427
-     * null is returned.
428
-     *
429
-     * To use this, this may look something like this:
430
-     *
431
-     * $event = $calendar->VEVENT;
432
-     *
433
-     * @param string $name
434
-     *
435
-     * @return Property|null
436
-     */
437
-    public function __get($name)
438
-    {
439
-        if ('children' === $name) {
440
-            throw new \RuntimeException('Starting sabre/vobject 4.0 the children property is now protected. You should use the children() method instead');
441
-        }
442
-
443
-        $matches = $this->select($name);
444
-        if (0 === count($matches)) {
445
-            return;
446
-        } else {
447
-            $firstMatch = current($matches);
448
-            /* @var $firstMatch Property */
449
-            $firstMatch->setIterator(new ElementList(array_values($matches)));
450
-
451
-            return $firstMatch;
452
-        }
453
-    }
454
-
455
-    /**
456
-     * This method checks if a sub-element with the specified name exists.
457
-     *
458
-     * @param string $name
459
-     *
460
-     * @return bool
461
-     */
462
-    public function __isset($name)
463
-    {
464
-        $matches = $this->select($name);
465
-
466
-        return count($matches) > 0;
467
-    }
468
-
469
-    /**
470
-     * Using the setter method you can add properties or subcomponents.
471
-     *
472
-     * You can either pass a Component, Property
473
-     * object, or a string to automatically create a Property.
474
-     *
475
-     * If the item already exists, it will be removed. If you want to add
476
-     * a new item with the same name, always use the add() method.
477
-     *
478
-     * @param string $name
479
-     * @param mixed  $value
480
-     */
481
-    public function __set($name, $value)
482
-    {
483
-        $name = strtoupper($name);
484
-        $this->remove($name);
485
-        if ($value instanceof self || $value instanceof Property) {
486
-            $this->add($value);
487
-        } else {
488
-            $this->add($name, $value);
489
-        }
490
-    }
491
-
492
-    /**
493
-     * Removes all properties and components within this component with the
494
-     * specified name.
495
-     *
496
-     * @param string $name
497
-     */
498
-    public function __unset($name)
499
-    {
500
-        $this->remove($name);
501
-    }
502
-
503
-    /* }}} */
504
-
505
-    /**
506
-     * This method is automatically called when the object is cloned.
507
-     * Specifically, this will ensure all child elements are also cloned.
508
-     */
509
-    public function __clone()
510
-    {
511
-        foreach ($this->children as $childName => $childGroup) {
512
-            foreach ($childGroup as $key => $child) {
513
-                $clonedChild = clone $child;
514
-                $clonedChild->parent = $this;
515
-                $clonedChild->root = $this->root;
516
-                $this->children[$childName][$key] = $clonedChild;
517
-            }
518
-        }
519
-    }
520
-
521
-    /**
522
-     * A simple list of validation rules.
523
-     *
524
-     * This is simply a list of properties, and how many times they either
525
-     * must or must not appear.
526
-     *
527
-     * Possible values per property:
528
-     *   * 0 - Must not appear.
529
-     *   * 1 - Must appear exactly once.
530
-     *   * + - Must appear at least once.
531
-     *   * * - Can appear any number of times.
532
-     *   * ? - May appear, but not more than once.
533
-     *
534
-     * It is also possible to specify defaults and severity levels for
535
-     * violating the rule.
536
-     *
537
-     * See the VEVENT implementation for getValidationRules for a more complex
538
-     * example.
539
-     *
540
-     * @var array
541
-     */
542
-    public function getValidationRules()
543
-    {
544
-        return [];
545
-    }
546
-
547
-    /**
548
-     * Validates the node for correctness.
549
-     *
550
-     * The following options are supported:
551
-     *   Node::REPAIR - May attempt to automatically repair the problem.
552
-     *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
553
-     *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
554
-     *
555
-     * This method returns an array with detected problems.
556
-     * Every element has the following properties:
557
-     *
558
-     *  * level - problem level.
559
-     *  * message - A human-readable string describing the issue.
560
-     *  * node - A reference to the problematic node.
561
-     *
562
-     * The level means:
563
-     *   1 - The issue was repaired (only happens if REPAIR was turned on).
564
-     *   2 - A warning.
565
-     *   3 - An error.
566
-     *
567
-     * @param int $options
568
-     *
569
-     * @return array
570
-     */
571
-    public function validate($options = 0)
572
-    {
573
-        $rules = $this->getValidationRules();
574
-        $defaults = $this->getDefaults();
575
-
576
-        $propertyCounters = [];
577
-
578
-        $messages = [];
579
-
580
-        foreach ($this->children() as $child) {
581
-            $name = strtoupper($child->name);
582
-            if (!isset($propertyCounters[$name])) {
583
-                $propertyCounters[$name] = 1;
584
-            } else {
585
-                ++$propertyCounters[$name];
586
-            }
587
-            $messages = array_merge($messages, $child->validate($options));
588
-        }
589
-
590
-        foreach ($rules as $propName => $rule) {
591
-            switch ($rule) {
592
-                case '0':
593
-                    if (isset($propertyCounters[$propName])) {
594
-                        $messages[] = [
595
-                            'level' => 3,
596
-                            'message' => $propName.' MUST NOT appear in a '.$this->name.' component',
597
-                            'node' => $this,
598
-                        ];
599
-                    }
600
-                    break;
601
-                case '1':
602
-                    if (!isset($propertyCounters[$propName]) || 1 !== $propertyCounters[$propName]) {
603
-                        $repaired = false;
604
-                        if ($options & self::REPAIR && isset($defaults[$propName])) {
605
-                            $this->add($propName, $defaults[$propName]);
606
-                            $repaired = true;
607
-                        }
608
-                        $messages[] = [
609
-                            'level' => $repaired ? 1 : 3,
610
-                            'message' => $propName.' MUST appear exactly once in a '.$this->name.' component',
611
-                            'node' => $this,
612
-                        ];
613
-                    }
614
-                    break;
615
-                case '+':
616
-                    if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] < 1) {
617
-                        $messages[] = [
618
-                            'level' => 3,
619
-                            'message' => $propName.' MUST appear at least once in a '.$this->name.' component',
620
-                            'node' => $this,
621
-                        ];
622
-                    }
623
-                    break;
624
-                case '*':
625
-                    break;
626
-                case '?':
627
-                    if (isset($propertyCounters[$propName]) && $propertyCounters[$propName] > 1) {
628
-                        $level = 3;
629
-
630
-                        // We try to repair the same property appearing multiple times with the exact same value
631
-                        // by removing the duplicates and keeping only one property
632
-                        if ($options & self::REPAIR) {
633
-                            $properties = array_unique($this->select($propName), SORT_REGULAR);
634
-
635
-                            if (1 === count($properties)) {
636
-                                $this->remove($propName);
637
-                                $this->add($properties[0]);
638
-
639
-                                $level = 1;
640
-                            }
641
-                        }
642
-
643
-                        $messages[] = [
644
-                            'level' => $level,
645
-                            'message' => $propName.' MUST NOT appear more than once in a '.$this->name.' component',
646
-                            'node' => $this,
647
-                        ];
648
-                    }
649
-                    break;
650
-            }
651
-        }
652
-
653
-        return $messages;
654
-    }
655
-
656
-    /**
657
-     * Call this method on a document if you're done using it.
658
-     *
659
-     * It's intended to remove all circular references, so PHP can easily clean
660
-     * it up.
661
-     */
662
-    public function destroy()
663
-    {
664
-        parent::destroy();
665
-        foreach ($this->children as $childGroup) {
666
-            foreach ($childGroup as $child) {
667
-                $child->destroy();
668
-            }
669
-        }
670
-        $this->children = [];
671
-    }
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<string, Component|Property>
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 string|null $name     such as VCALENDAR, VEVENT
47
+	 * @param bool        $defaults
48
+	 */
49
+	public function __construct(Document $root, $name, array $children = [], $defaults = true)
50
+	{
51
+		$this->name = isset($name) ? strtoupper($name) : '';
52
+		$this->root = $root;
53
+
54
+		if ($defaults) {
55
+			// This is a terribly convoluted way to do this, but this ensures
56
+			// that the order of properties as they are specified in both
57
+			// defaults and the childrens list, are inserted in the object in a
58
+			// natural way.
59
+			$list = $this->getDefaults();
60
+			$nodes = [];
61
+			foreach ($children as $key => $value) {
62
+				if ($value instanceof Node) {
63
+					if (isset($list[$value->name])) {
64
+						unset($list[$value->name]);
65
+					}
66
+					$nodes[] = $value;
67
+				} else {
68
+					$list[$key] = $value;
69
+				}
70
+			}
71
+			foreach ($list as $key => $value) {
72
+				$this->add($key, $value);
73
+			}
74
+			foreach ($nodes as $node) {
75
+				$this->add($node);
76
+			}
77
+		} else {
78
+			foreach ($children as $k => $child) {
79
+				if ($child instanceof Node) {
80
+					// Component or Property
81
+					$this->add($child);
82
+				} else {
83
+					// Property key=>value
84
+					$this->add($k, $child);
85
+				}
86
+			}
87
+		}
88
+	}
89
+
90
+	/**
91
+	 * Adds a new property or component, and returns the new item.
92
+	 *
93
+	 * This method has 3 possible signatures:
94
+	 *
95
+	 * add(Component $comp) // Adds a new component
96
+	 * add(Property $prop)  // Adds a new property
97
+	 * add($name, $value, array $parameters = []) // Adds a new property
98
+	 * add($name, array $children = []) // Adds a new component
99
+	 * by name.
100
+	 *
101
+	 * @return Node
102
+	 */
103
+	public function add()
104
+	{
105
+		$arguments = func_get_args();
106
+
107
+		if ($arguments[0] instanceof Node) {
108
+			if (isset($arguments[1])) {
109
+				throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node');
110
+			}
111
+			$arguments[0]->parent = $this;
112
+			$newNode = $arguments[0];
113
+		} elseif (is_string($arguments[0])) {
114
+			$newNode = call_user_func_array([$this->root, 'create'], $arguments);
115
+		} else {
116
+			throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string');
117
+		}
118
+
119
+		$name = $newNode->name;
120
+		if (isset($this->children[$name])) {
121
+			$this->children[$name][] = $newNode;
122
+		} else {
123
+			$this->children[$name] = [$newNode];
124
+		}
125
+
126
+		return $newNode;
127
+	}
128
+
129
+	/**
130
+	 * This method removes a component or property from this component.
131
+	 *
132
+	 * You can either specify the item by name (like DTSTART), in which case
133
+	 * all properties/components with that name will be removed, or you can
134
+	 * pass an instance of a property or component, in which case only that
135
+	 * exact item will be removed.
136
+	 *
137
+	 * @param string|Property|Component $item
138
+	 */
139
+	public function remove($item)
140
+	{
141
+		if (is_string($item)) {
142
+			// If there's no dot in the name, it's an exact property name and
143
+			// we can just wipe out all those properties.
144
+			//
145
+			if (false === strpos($item, '.')) {
146
+				unset($this->children[strtoupper($item)]);
147
+
148
+				return;
149
+			}
150
+			// If there was a dot, we need to ask select() to help us out and
151
+			// then we just call remove recursively.
152
+			foreach ($this->select($item) as $child) {
153
+				$this->remove($child);
154
+			}
155
+		} else {
156
+			foreach ($this->select($item->name) as $k => $child) {
157
+				if ($child === $item) {
158
+					unset($this->children[$item->name][$k]);
159
+
160
+					return;
161
+				}
162
+			}
163
+
164
+			throw new \InvalidArgumentException('The item you passed to remove() was not a child of this component');
165
+		}
166
+	}
167
+
168
+	/**
169
+	 * Returns a flat list of all the properties and components in this
170
+	 * component.
171
+	 *
172
+	 * @return array
173
+	 */
174
+	public function children()
175
+	{
176
+		$result = [];
177
+		foreach ($this->children as $childGroup) {
178
+			$result = array_merge($result, $childGroup);
179
+		}
180
+
181
+		return $result;
182
+	}
183
+
184
+	/**
185
+	 * This method only returns a list of sub-components. Properties are
186
+	 * ignored.
187
+	 *
188
+	 * @return array
189
+	 */
190
+	public function getComponents()
191
+	{
192
+		$result = [];
193
+
194
+		foreach ($this->children as $childGroup) {
195
+			foreach ($childGroup as $child) {
196
+				if ($child instanceof self) {
197
+					$result[] = $child;
198
+				}
199
+			}
200
+		}
201
+
202
+		return $result;
203
+	}
204
+
205
+	/**
206
+	 * Returns an array with elements that match the specified name.
207
+	 *
208
+	 * This function is also aware of MIME-Directory groups (as they appear in
209
+	 * vcards). This means that if a property is grouped as "HOME.EMAIL", it
210
+	 * will also be returned when searching for just "EMAIL". If you want to
211
+	 * search for a property in a specific group, you can select on the entire
212
+	 * string ("HOME.EMAIL"). If you want to search on a specific property that
213
+	 * has not been assigned a group, specify ".EMAIL".
214
+	 *
215
+	 * @param string $name
216
+	 *
217
+	 * @return array
218
+	 */
219
+	public function select($name)
220
+	{
221
+		$group = null;
222
+		$name = strtoupper($name);
223
+		if (false !== strpos($name, '.')) {
224
+			list($group, $name) = explode('.', $name, 2);
225
+		}
226
+		if ('' === $name) {
227
+			$name = null;
228
+		}
229
+
230
+		if (!is_null($name)) {
231
+			$result = isset($this->children[$name]) ? $this->children[$name] : [];
232
+
233
+			if (is_null($group)) {
234
+				return $result;
235
+			} else {
236
+				// If we have a group filter as well, we need to narrow it down
237
+				// more.
238
+				return array_filter(
239
+					$result,
240
+					function ($child) use ($group) {
241
+						return $child instanceof Property && (null !== $child->group ? strtoupper($child->group) : '') === $group;
242
+					}
243
+				);
244
+			}
245
+		}
246
+
247
+		// If we got to this point, it means there was no 'name' specified for
248
+		// searching, implying that this is a group-only search.
249
+		$result = [];
250
+		foreach ($this->children as $childGroup) {
251
+			foreach ($childGroup as $child) {
252
+				if ($child instanceof Property && (null !== $child->group ? strtoupper($child->group) : '') === $group) {
253
+					$result[] = $child;
254
+				}
255
+			}
256
+		}
257
+
258
+		return $result;
259
+	}
260
+
261
+	/**
262
+	 * Turns the object back into a serialized blob.
263
+	 *
264
+	 * @return string
265
+	 */
266
+	public function serialize()
267
+	{
268
+		$str = 'BEGIN:'.$this->name."\r\n";
269
+
270
+		/**
271
+		 * Gives a component a 'score' for sorting purposes.
272
+		 *
273
+		 * This is solely used by the childrenSort method.
274
+		 *
275
+		 * A higher score means the item will be lower in the list.
276
+		 * To avoid score collisions, each "score category" has a reasonable
277
+		 * space to accommodate elements. The $key is added to the $score to
278
+		 * preserve the original relative order of elements.
279
+		 *
280
+		 * @param int   $key
281
+		 * @param array $array
282
+		 *
283
+		 * @return int
284
+		 */
285
+		$sortScore = function ($key, $array) {
286
+			if ($array[$key] instanceof Component) {
287
+				// We want to encode VTIMEZONE first, this is a personal
288
+				// preference.
289
+				if ('VTIMEZONE' === $array[$key]->name) {
290
+					$score = 300000000;
291
+
292
+					return $score + $key;
293
+				} else {
294
+					$score = 400000000;
295
+
296
+					return $score + $key;
297
+				}
298
+			} else {
299
+				// Properties get encoded first
300
+				// VCARD version 4.0 wants the VERSION property to appear first
301
+				if ($array[$key] instanceof Property) {
302
+					if ('VERSION' === $array[$key]->name) {
303
+						$score = 100000000;
304
+
305
+						return $score + $key;
306
+					} else {
307
+						// All other properties
308
+						$score = 200000000;
309
+
310
+						return $score + $key;
311
+					}
312
+				}
313
+			}
314
+		};
315
+
316
+		$children = $this->children();
317
+		$tmp = $children;
318
+		uksort(
319
+			$children,
320
+			function ($a, $b) use ($sortScore, $tmp) {
321
+				$sA = $sortScore($a, $tmp);
322
+				$sB = $sortScore($b, $tmp);
323
+
324
+				return $sA - $sB;
325
+			}
326
+		);
327
+
328
+		foreach ($children as $child) {
329
+			$str .= $child->serialize();
330
+		}
331
+		$str .= 'END:'.$this->name."\r\n";
332
+
333
+		return $str;
334
+	}
335
+
336
+	/**
337
+	 * This method returns an array, with the representation as it should be
338
+	 * encoded in JSON. This is used to create jCard or jCal documents.
339
+	 *
340
+	 * @return array
341
+	 */
342
+	#[\ReturnTypeWillChange]
343
+	public function jsonSerialize()
344
+	{
345
+		$components = [];
346
+		$properties = [];
347
+
348
+		foreach ($this->children as $childGroup) {
349
+			foreach ($childGroup as $child) {
350
+				if ($child instanceof self) {
351
+					$components[] = $child->jsonSerialize();
352
+				} else {
353
+					$properties[] = $child->jsonSerialize();
354
+				}
355
+			}
356
+		}
357
+
358
+		return [
359
+			strtolower($this->name),
360
+			$properties,
361
+			$components,
362
+		];
363
+	}
364
+
365
+	/**
366
+	 * This method serializes the data into XML. This is used to create xCard or
367
+	 * xCal documents.
368
+	 *
369
+	 * @param Xml\Writer $writer XML writer
370
+	 */
371
+	public function xmlSerialize(Xml\Writer $writer): void
372
+	{
373
+		$components = [];
374
+		$properties = [];
375
+
376
+		foreach ($this->children as $childGroup) {
377
+			foreach ($childGroup as $child) {
378
+				if ($child instanceof self) {
379
+					$components[] = $child;
380
+				} else {
381
+					$properties[] = $child;
382
+				}
383
+			}
384
+		}
385
+
386
+		$writer->startElement(strtolower($this->name));
387
+
388
+		if (!empty($properties)) {
389
+			$writer->startElement('properties');
390
+
391
+			foreach ($properties as $property) {
392
+				$property->xmlSerialize($writer);
393
+			}
394
+
395
+			$writer->endElement();
396
+		}
397
+
398
+		if (!empty($components)) {
399
+			$writer->startElement('components');
400
+
401
+			foreach ($components as $component) {
402
+				$component->xmlSerialize($writer);
403
+			}
404
+
405
+			$writer->endElement();
406
+		}
407
+
408
+		$writer->endElement();
409
+	}
410
+
411
+	/**
412
+	 * This method should return a list of default property values.
413
+	 *
414
+	 * @return array
415
+	 */
416
+	protected function getDefaults()
417
+	{
418
+		return [];
419
+	}
420
+
421
+	/* Magic property accessors {{{ */
422
+
423
+	/**
424
+	 * Using 'get' you will either get a property or component.
425
+	 *
426
+	 * If there were no child-elements found with the specified name,
427
+	 * null is returned.
428
+	 *
429
+	 * To use this, this may look something like this:
430
+	 *
431
+	 * $event = $calendar->VEVENT;
432
+	 *
433
+	 * @param string $name
434
+	 *
435
+	 * @return Property|null
436
+	 */
437
+	public function __get($name)
438
+	{
439
+		if ('children' === $name) {
440
+			throw new \RuntimeException('Starting sabre/vobject 4.0 the children property is now protected. You should use the children() method instead');
441
+		}
442
+
443
+		$matches = $this->select($name);
444
+		if (0 === count($matches)) {
445
+			return;
446
+		} else {
447
+			$firstMatch = current($matches);
448
+			/* @var $firstMatch Property */
449
+			$firstMatch->setIterator(new ElementList(array_values($matches)));
450
+
451
+			return $firstMatch;
452
+		}
453
+	}
454
+
455
+	/**
456
+	 * This method checks if a sub-element with the specified name exists.
457
+	 *
458
+	 * @param string $name
459
+	 *
460
+	 * @return bool
461
+	 */
462
+	public function __isset($name)
463
+	{
464
+		$matches = $this->select($name);
465
+
466
+		return count($matches) > 0;
467
+	}
468
+
469
+	/**
470
+	 * Using the setter method you can add properties or subcomponents.
471
+	 *
472
+	 * You can either pass a Component, Property
473
+	 * object, or a string to automatically create a Property.
474
+	 *
475
+	 * If the item already exists, it will be removed. If you want to add
476
+	 * a new item with the same name, always use the add() method.
477
+	 *
478
+	 * @param string $name
479
+	 * @param mixed  $value
480
+	 */
481
+	public function __set($name, $value)
482
+	{
483
+		$name = strtoupper($name);
484
+		$this->remove($name);
485
+		if ($value instanceof self || $value instanceof Property) {
486
+			$this->add($value);
487
+		} else {
488
+			$this->add($name, $value);
489
+		}
490
+	}
491
+
492
+	/**
493
+	 * Removes all properties and components within this component with the
494
+	 * specified name.
495
+	 *
496
+	 * @param string $name
497
+	 */
498
+	public function __unset($name)
499
+	{
500
+		$this->remove($name);
501
+	}
502
+
503
+	/* }}} */
504
+
505
+	/**
506
+	 * This method is automatically called when the object is cloned.
507
+	 * Specifically, this will ensure all child elements are also cloned.
508
+	 */
509
+	public function __clone()
510
+	{
511
+		foreach ($this->children as $childName => $childGroup) {
512
+			foreach ($childGroup as $key => $child) {
513
+				$clonedChild = clone $child;
514
+				$clonedChild->parent = $this;
515
+				$clonedChild->root = $this->root;
516
+				$this->children[$childName][$key] = $clonedChild;
517
+			}
518
+		}
519
+	}
520
+
521
+	/**
522
+	 * A simple list of validation rules.
523
+	 *
524
+	 * This is simply a list of properties, and how many times they either
525
+	 * must or must not appear.
526
+	 *
527
+	 * Possible values per property:
528
+	 *   * 0 - Must not appear.
529
+	 *   * 1 - Must appear exactly once.
530
+	 *   * + - Must appear at least once.
531
+	 *   * * - Can appear any number of times.
532
+	 *   * ? - May appear, but not more than once.
533
+	 *
534
+	 * It is also possible to specify defaults and severity levels for
535
+	 * violating the rule.
536
+	 *
537
+	 * See the VEVENT implementation for getValidationRules for a more complex
538
+	 * example.
539
+	 *
540
+	 * @var array
541
+	 */
542
+	public function getValidationRules()
543
+	{
544
+		return [];
545
+	}
546
+
547
+	/**
548
+	 * Validates the node for correctness.
549
+	 *
550
+	 * The following options are supported:
551
+	 *   Node::REPAIR - May attempt to automatically repair the problem.
552
+	 *   Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
553
+	 *   Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
554
+	 *
555
+	 * This method returns an array with detected problems.
556
+	 * Every element has the following properties:
557
+	 *
558
+	 *  * level - problem level.
559
+	 *  * message - A human-readable string describing the issue.
560
+	 *  * node - A reference to the problematic node.
561
+	 *
562
+	 * The level means:
563
+	 *   1 - The issue was repaired (only happens if REPAIR was turned on).
564
+	 *   2 - A warning.
565
+	 *   3 - An error.
566
+	 *
567
+	 * @param int $options
568
+	 *
569
+	 * @return array
570
+	 */
571
+	public function validate($options = 0)
572
+	{
573
+		$rules = $this->getValidationRules();
574
+		$defaults = $this->getDefaults();
575
+
576
+		$propertyCounters = [];
577
+
578
+		$messages = [];
579
+
580
+		foreach ($this->children() as $child) {
581
+			$name = strtoupper($child->name);
582
+			if (!isset($propertyCounters[$name])) {
583
+				$propertyCounters[$name] = 1;
584
+			} else {
585
+				++$propertyCounters[$name];
586
+			}
587
+			$messages = array_merge($messages, $child->validate($options));
588
+		}
589
+
590
+		foreach ($rules as $propName => $rule) {
591
+			switch ($rule) {
592
+				case '0':
593
+					if (isset($propertyCounters[$propName])) {
594
+						$messages[] = [
595
+							'level' => 3,
596
+							'message' => $propName.' MUST NOT appear in a '.$this->name.' component',
597
+							'node' => $this,
598
+						];
599
+					}
600
+					break;
601
+				case '1':
602
+					if (!isset($propertyCounters[$propName]) || 1 !== $propertyCounters[$propName]) {
603
+						$repaired = false;
604
+						if ($options & self::REPAIR && isset($defaults[$propName])) {
605
+							$this->add($propName, $defaults[$propName]);
606
+							$repaired = true;
607
+						}
608
+						$messages[] = [
609
+							'level' => $repaired ? 1 : 3,
610
+							'message' => $propName.' MUST appear exactly once in a '.$this->name.' component',
611
+							'node' => $this,
612
+						];
613
+					}
614
+					break;
615
+				case '+':
616
+					if (!isset($propertyCounters[$propName]) || $propertyCounters[$propName] < 1) {
617
+						$messages[] = [
618
+							'level' => 3,
619
+							'message' => $propName.' MUST appear at least once in a '.$this->name.' component',
620
+							'node' => $this,
621
+						];
622
+					}
623
+					break;
624
+				case '*':
625
+					break;
626
+				case '?':
627
+					if (isset($propertyCounters[$propName]) && $propertyCounters[$propName] > 1) {
628
+						$level = 3;
629
+
630
+						// We try to repair the same property appearing multiple times with the exact same value
631
+						// by removing the duplicates and keeping only one property
632
+						if ($options & self::REPAIR) {
633
+							$properties = array_unique($this->select($propName), SORT_REGULAR);
634
+
635
+							if (1 === count($properties)) {
636
+								$this->remove($propName);
637
+								$this->add($properties[0]);
638
+
639
+								$level = 1;
640
+							}
641
+						}
642
+
643
+						$messages[] = [
644
+							'level' => $level,
645
+							'message' => $propName.' MUST NOT appear more than once in a '.$this->name.' component',
646
+							'node' => $this,
647
+						];
648
+					}
649
+					break;
650
+			}
651
+		}
652
+
653
+		return $messages;
654
+	}
655
+
656
+	/**
657
+	 * Call this method on a document if you're done using it.
658
+	 *
659
+	 * It's intended to remove all circular references, so PHP can easily clean
660
+	 * it up.
661
+	 */
662
+	public function destroy()
663
+	{
664
+		parent::destroy();
665
+		foreach ($this->children as $childGroup) {
666
+			foreach ($childGroup as $child) {
667
+				$child->destroy();
668
+			}
669
+		}
670
+		$this->children = [];
671
+	}
672 672
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/vobject/lib/TimezoneGuesser/TimezoneFinder.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -6,5 +6,5 @@
 block discarded – undo
6 6
 
7 7
 interface TimezoneFinder
8 8
 {
9
-    public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone;
9
+	public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone;
10 10
 }
Please login to merge, or discard this patch.
sabre/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneIdentifier.php 1 patch
Indentation   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -12,60 +12,60 @@
 block discarded – undo
12 12
  */
13 13
 class FindFromTimezoneIdentifier implements TimezoneFinder
14 14
 {
15
-    public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone
16
-    {
17
-        // First we will just see if the tzid is a support timezone identifier.
18
-        //
19
-        // The only exception is if the timezone starts with (. This is to
20
-        // handle cases where certain microsoft products generate timezone
21
-        // identifiers that for instance look like:
22
-        //
23
-        // (GMT+01.00) Sarajevo/Warsaw/Zagreb
24
-        //
25
-        // Since PHP 5.5.10, the first bit will be used as the timezone and
26
-        // this method will return just GMT+01:00. This is wrong, because it
27
-        // doesn't take DST into account
28
-        if (!isset($tzid[0])) {
29
-            return null;
30
-        }
31
-        if ('(' === $tzid[0]) {
32
-            return null;
33
-        }
34
-        // PHP has a bug that logs PHP warnings even it shouldn't:
35
-        // https://bugs.php.net/bug.php?id=67881
36
-        //
37
-        // That's why we're checking if we'll be able to successfully instantiate
38
-        // \DateTimeZone() before doing so. Otherwise we could simply instantiate
39
-        // and catch the exception.
40
-        $tzIdentifiers = DateTimeZone::listIdentifiers();
15
+	public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone
16
+	{
17
+		// First we will just see if the tzid is a support timezone identifier.
18
+		//
19
+		// The only exception is if the timezone starts with (. This is to
20
+		// handle cases where certain microsoft products generate timezone
21
+		// identifiers that for instance look like:
22
+		//
23
+		// (GMT+01.00) Sarajevo/Warsaw/Zagreb
24
+		//
25
+		// Since PHP 5.5.10, the first bit will be used as the timezone and
26
+		// this method will return just GMT+01:00. This is wrong, because it
27
+		// doesn't take DST into account
28
+		if (!isset($tzid[0])) {
29
+			return null;
30
+		}
31
+		if ('(' === $tzid[0]) {
32
+			return null;
33
+		}
34
+		// PHP has a bug that logs PHP warnings even it shouldn't:
35
+		// https://bugs.php.net/bug.php?id=67881
36
+		//
37
+		// That's why we're checking if we'll be able to successfully instantiate
38
+		// \DateTimeZone() before doing so. Otherwise we could simply instantiate
39
+		// and catch the exception.
40
+		$tzIdentifiers = DateTimeZone::listIdentifiers();
41 41
 
42
-        try {
43
-            if (
44
-                (in_array($tzid, $tzIdentifiers)) ||
45
-                (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) ||
46
-                (in_array($tzid, $this->getIdentifiersBC()))
47
-            ) {
48
-                return new DateTimeZone($tzid);
49
-            }
50
-        } catch (Exception $e) {
51
-        }
42
+		try {
43
+			if (
44
+				(in_array($tzid, $tzIdentifiers)) ||
45
+				(preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) ||
46
+				(in_array($tzid, $this->getIdentifiersBC()))
47
+			) {
48
+				return new DateTimeZone($tzid);
49
+			}
50
+		} catch (Exception $e) {
51
+		}
52 52
 
53
-        return null;
54
-    }
53
+		return null;
54
+	}
55 55
 
56
-    /**
57
-     * This method returns an array of timezone identifiers, that are supported
58
-     * by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers().
59
-     *
60
-     * We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because:
61
-     * - It's not supported by some PHP versions as well as HHVM.
62
-     * - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions.
63
-     * (See timezonedata/php-bc.php and timezonedata php-workaround.php)
64
-     *
65
-     * @return array
66
-     */
67
-    private function getIdentifiersBC()
68
-    {
69
-        return include __DIR__.'/../timezonedata/php-bc.php';
70
-    }
56
+	/**
57
+	 * This method returns an array of timezone identifiers, that are supported
58
+	 * by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers().
59
+	 *
60
+	 * We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because:
61
+	 * - It's not supported by some PHP versions as well as HHVM.
62
+	 * - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions.
63
+	 * (See timezonedata/php-bc.php and timezonedata php-workaround.php)
64
+	 *
65
+	 * @return array
66
+	 */
67
+	private function getIdentifiersBC()
68
+	{
69
+		return include __DIR__.'/../timezonedata/php-bc.php';
70
+	}
71 71
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/vobject/lib/TimezoneGuesser/GuessFromMsTzId.php 1 patch
Indentation   +102 added lines, -102 removed lines patch added patch discarded remove patch
@@ -9,111 +9,111 @@
 block discarded – undo
9 9
 
10 10
 class GuessFromMsTzId implements TimezoneGuesser
11 11
 {
12
-    /**
13
-     * List of microsoft exchange timezone ids.
14
-     *
15
-     * Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx
16
-     */
17
-    public static $microsoftExchangeMap = [
18
-        0 => 'UTC',
19
-        31 => 'Africa/Casablanca',
12
+	/**
13
+	 * List of microsoft exchange timezone ids.
14
+	 *
15
+	 * Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx
16
+	 */
17
+	public static $microsoftExchangeMap = [
18
+		0 => 'UTC',
19
+		31 => 'Africa/Casablanca',
20 20
 
21
-        // Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo.
22
-        // I'm not even kidding.. We handle this special case in the
23
-        // getTimeZone method.
24
-        2 => 'Europe/Lisbon',
25
-        1 => 'Europe/London',
26
-        4 => 'Europe/Berlin',
27
-        6 => 'Europe/Prague',
28
-        3 => 'Europe/Paris',
29
-        69 => 'Africa/Luanda', // This was a best guess
30
-        7 => 'Europe/Athens',
31
-        5 => 'Europe/Bucharest',
32
-        49 => 'Africa/Cairo',
33
-        50 => 'Africa/Harare',
34
-        59 => 'Europe/Helsinki',
35
-        27 => 'Asia/Jerusalem',
36
-        26 => 'Asia/Baghdad',
37
-        74 => 'Asia/Kuwait',
38
-        51 => 'Europe/Moscow',
39
-        56 => 'Africa/Nairobi',
40
-        25 => 'Asia/Tehran',
41
-        24 => 'Asia/Muscat', // Best guess
42
-        54 => 'Asia/Baku',
43
-        48 => 'Asia/Kabul',
44
-        58 => 'Asia/Yekaterinburg',
45
-        47 => 'Asia/Karachi',
46
-        23 => 'Asia/Calcutta',
47
-        62 => 'Asia/Kathmandu',
48
-        46 => 'Asia/Almaty',
49
-        71 => 'Asia/Dhaka',
50
-        66 => 'Asia/Colombo',
51
-        61 => 'Asia/Rangoon',
52
-        22 => 'Asia/Bangkok',
53
-        64 => 'Asia/Krasnoyarsk',
54
-        45 => 'Asia/Shanghai',
55
-        63 => 'Asia/Irkutsk',
56
-        21 => 'Asia/Singapore',
57
-        73 => 'Australia/Perth',
58
-        75 => 'Asia/Taipei',
59
-        20 => 'Asia/Tokyo',
60
-        72 => 'Asia/Seoul',
61
-        70 => 'Asia/Yakutsk',
62
-        19 => 'Australia/Adelaide',
63
-        44 => 'Australia/Darwin',
64
-        18 => 'Australia/Brisbane',
65
-        76 => 'Australia/Sydney',
66
-        43 => 'Pacific/Guam',
67
-        42 => 'Australia/Hobart',
68
-        68 => 'Asia/Vladivostok',
69
-        41 => 'Asia/Magadan',
70
-        17 => 'Pacific/Auckland',
71
-        40 => 'Pacific/Fiji',
72
-        67 => 'Pacific/Tongatapu',
73
-        29 => 'Atlantic/Azores',
74
-        53 => 'Atlantic/Cape_Verde',
75
-        30 => 'America/Noronha',
76
-        8 => 'America/Sao_Paulo', // Best guess
77
-        32 => 'America/Argentina/Buenos_Aires',
78
-        60 => 'America/Godthab',
79
-        28 => 'America/St_Johns',
80
-        9 => 'America/Halifax',
81
-        33 => 'America/Caracas',
82
-        65 => 'America/Santiago',
83
-        35 => 'America/Bogota',
84
-        10 => 'America/New_York',
85
-        34 => 'America/Indiana/Indianapolis',
86
-        55 => 'America/Guatemala',
87
-        11 => 'America/Chicago',
88
-        37 => 'America/Mexico_City',
89
-        36 => 'America/Edmonton',
90
-        38 => 'America/Phoenix',
91
-        12 => 'America/Denver', // Best guess
92
-        13 => 'America/Los_Angeles', // Best guess
93
-        14 => 'America/Anchorage',
94
-        15 => 'Pacific/Honolulu',
95
-        16 => 'Pacific/Midway',
96
-        39 => 'Pacific/Kwajalein',
97
-    ];
21
+		// Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo.
22
+		// I'm not even kidding.. We handle this special case in the
23
+		// getTimeZone method.
24
+		2 => 'Europe/Lisbon',
25
+		1 => 'Europe/London',
26
+		4 => 'Europe/Berlin',
27
+		6 => 'Europe/Prague',
28
+		3 => 'Europe/Paris',
29
+		69 => 'Africa/Luanda', // This was a best guess
30
+		7 => 'Europe/Athens',
31
+		5 => 'Europe/Bucharest',
32
+		49 => 'Africa/Cairo',
33
+		50 => 'Africa/Harare',
34
+		59 => 'Europe/Helsinki',
35
+		27 => 'Asia/Jerusalem',
36
+		26 => 'Asia/Baghdad',
37
+		74 => 'Asia/Kuwait',
38
+		51 => 'Europe/Moscow',
39
+		56 => 'Africa/Nairobi',
40
+		25 => 'Asia/Tehran',
41
+		24 => 'Asia/Muscat', // Best guess
42
+		54 => 'Asia/Baku',
43
+		48 => 'Asia/Kabul',
44
+		58 => 'Asia/Yekaterinburg',
45
+		47 => 'Asia/Karachi',
46
+		23 => 'Asia/Calcutta',
47
+		62 => 'Asia/Kathmandu',
48
+		46 => 'Asia/Almaty',
49
+		71 => 'Asia/Dhaka',
50
+		66 => 'Asia/Colombo',
51
+		61 => 'Asia/Rangoon',
52
+		22 => 'Asia/Bangkok',
53
+		64 => 'Asia/Krasnoyarsk',
54
+		45 => 'Asia/Shanghai',
55
+		63 => 'Asia/Irkutsk',
56
+		21 => 'Asia/Singapore',
57
+		73 => 'Australia/Perth',
58
+		75 => 'Asia/Taipei',
59
+		20 => 'Asia/Tokyo',
60
+		72 => 'Asia/Seoul',
61
+		70 => 'Asia/Yakutsk',
62
+		19 => 'Australia/Adelaide',
63
+		44 => 'Australia/Darwin',
64
+		18 => 'Australia/Brisbane',
65
+		76 => 'Australia/Sydney',
66
+		43 => 'Pacific/Guam',
67
+		42 => 'Australia/Hobart',
68
+		68 => 'Asia/Vladivostok',
69
+		41 => 'Asia/Magadan',
70
+		17 => 'Pacific/Auckland',
71
+		40 => 'Pacific/Fiji',
72
+		67 => 'Pacific/Tongatapu',
73
+		29 => 'Atlantic/Azores',
74
+		53 => 'Atlantic/Cape_Verde',
75
+		30 => 'America/Noronha',
76
+		8 => 'America/Sao_Paulo', // Best guess
77
+		32 => 'America/Argentina/Buenos_Aires',
78
+		60 => 'America/Godthab',
79
+		28 => 'America/St_Johns',
80
+		9 => 'America/Halifax',
81
+		33 => 'America/Caracas',
82
+		65 => 'America/Santiago',
83
+		35 => 'America/Bogota',
84
+		10 => 'America/New_York',
85
+		34 => 'America/Indiana/Indianapolis',
86
+		55 => 'America/Guatemala',
87
+		11 => 'America/Chicago',
88
+		37 => 'America/Mexico_City',
89
+		36 => 'America/Edmonton',
90
+		38 => 'America/Phoenix',
91
+		12 => 'America/Denver', // Best guess
92
+		13 => 'America/Los_Angeles', // Best guess
93
+		14 => 'America/Anchorage',
94
+		15 => 'Pacific/Honolulu',
95
+		16 => 'Pacific/Midway',
96
+		39 => 'Pacific/Kwajalein',
97
+	];
98 98
 
99
-    public function guess(VTimeZone $vtimezone, bool $throwIfUnsure = false): ?DateTimeZone
100
-    {
101
-        // Microsoft may add a magic number, which we also have an
102
-        // answer for.
103
-        if (!isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
104
-            return null;
105
-        }
106
-        $cdoId = (int) $vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue();
99
+	public function guess(VTimeZone $vtimezone, bool $throwIfUnsure = false): ?DateTimeZone
100
+	{
101
+		// Microsoft may add a magic number, which we also have an
102
+		// answer for.
103
+		if (!isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
104
+			return null;
105
+		}
106
+		$cdoId = (int) $vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue();
107 107
 
108
-        // 2 can mean both Europe/Lisbon and Europe/Sarajevo.
109
-        if (2 === $cdoId && false !== strpos((string) $vtimezone->TZID, 'Sarajevo')) {
110
-            return new DateTimeZone('Europe/Sarajevo');
111
-        }
108
+		// 2 can mean both Europe/Lisbon and Europe/Sarajevo.
109
+		if (2 === $cdoId && false !== strpos((string) $vtimezone->TZID, 'Sarajevo')) {
110
+			return new DateTimeZone('Europe/Sarajevo');
111
+		}
112 112
 
113
-        if (isset(self::$microsoftExchangeMap[$cdoId])) {
114
-            return new DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
115
-        }
113
+		if (isset(self::$microsoftExchangeMap[$cdoId])) {
114
+			return new DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
115
+		}
116 116
 
117
-        return null;
118
-    }
117
+		return null;
118
+	}
119 119
 }
Please login to merge, or discard this patch.
includes/sabre/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneMap.php 1 patch
Indentation   +56 added lines, -56 removed lines patch added patch discarded remove patch
@@ -11,68 +11,68 @@
 block discarded – undo
11 11
  */
12 12
 class FindFromTimezoneMap implements TimezoneFinder
13 13
 {
14
-    private $map = [];
14
+	private $map = [];
15 15
 
16
-    private $patterns = [
17
-        '/^\((UTC|GMT)(\+|\-)[\d]{2}\:[\d]{2}\) (.*)/',
18
-        '/^\((UTC|GMT)(\+|\-)[\d]{2}\.[\d]{2}\) (.*)/',
19
-    ];
16
+	private $patterns = [
17
+		'/^\((UTC|GMT)(\+|\-)[\d]{2}\:[\d]{2}\) (.*)/',
18
+		'/^\((UTC|GMT)(\+|\-)[\d]{2}\.[\d]{2}\) (.*)/',
19
+	];
20 20
 
21
-    public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone
22
-    {
23
-        // Next, we check if the tzid is somewhere in our tzid map.
24
-        if ($this->hasTzInMap($tzid)) {
25
-            return new DateTimeZone($this->getTzFromMap($tzid));
26
-        }
21
+	public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone
22
+	{
23
+		// Next, we check if the tzid is somewhere in our tzid map.
24
+		if ($this->hasTzInMap($tzid)) {
25
+			return new DateTimeZone($this->getTzFromMap($tzid));
26
+		}
27 27
 
28
-        // Some Microsoft products prefix the offset first, so let's strip that off
29
-        // and see if it is our tzid map.  We don't want to check for this first just
30
-        // in case there are overrides in our tzid map.
31
-        foreach ($this->patterns as $pattern) {
32
-            if (!preg_match($pattern, $tzid, $matches)) {
33
-                continue;
34
-            }
35
-            $tzidAlternate = $matches[3];
36
-            if ($this->hasTzInMap($tzidAlternate)) {
37
-                return new DateTimeZone($this->getTzFromMap($tzidAlternate));
38
-            }
39
-        }
28
+		// Some Microsoft products prefix the offset first, so let's strip that off
29
+		// and see if it is our tzid map.  We don't want to check for this first just
30
+		// in case there are overrides in our tzid map.
31
+		foreach ($this->patterns as $pattern) {
32
+			if (!preg_match($pattern, $tzid, $matches)) {
33
+				continue;
34
+			}
35
+			$tzidAlternate = $matches[3];
36
+			if ($this->hasTzInMap($tzidAlternate)) {
37
+				return new DateTimeZone($this->getTzFromMap($tzidAlternate));
38
+			}
39
+		}
40 40
 
41
-        return null;
42
-    }
41
+		return null;
42
+	}
43 43
 
44
-    /**
45
-     * This method returns an array of timezone identifiers, that are supported
46
-     * by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers().
47
-     *
48
-     * We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because:
49
-     * - It's not supported by some PHP versions as well as HHVM.
50
-     * - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions.
51
-     * (See timezonedata/php-bc.php and timezonedata php-workaround.php)
52
-     *
53
-     * @return array
54
-     */
55
-    private function getTzMaps()
56
-    {
57
-        if ([] === $this->map) {
58
-            $this->map = array_merge(
59
-                include __DIR__.'/../timezonedata/windowszones.php',
60
-                include __DIR__.'/../timezonedata/lotuszones.php',
61
-                include __DIR__.'/../timezonedata/exchangezones.php',
62
-                include __DIR__.'/../timezonedata/php-workaround.php'
63
-            );
64
-        }
44
+	/**
45
+	 * This method returns an array of timezone identifiers, that are supported
46
+	 * by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers().
47
+	 *
48
+	 * We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because:
49
+	 * - It's not supported by some PHP versions as well as HHVM.
50
+	 * - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions.
51
+	 * (See timezonedata/php-bc.php and timezonedata php-workaround.php)
52
+	 *
53
+	 * @return array
54
+	 */
55
+	private function getTzMaps()
56
+	{
57
+		if ([] === $this->map) {
58
+			$this->map = array_merge(
59
+				include __DIR__.'/../timezonedata/windowszones.php',
60
+				include __DIR__.'/../timezonedata/lotuszones.php',
61
+				include __DIR__.'/../timezonedata/exchangezones.php',
62
+				include __DIR__.'/../timezonedata/php-workaround.php'
63
+			);
64
+		}
65 65
 
66
-        return $this->map;
67
-    }
66
+		return $this->map;
67
+	}
68 68
 
69
-    private function getTzFromMap(string $tzid): string
70
-    {
71
-        return $this->getTzMaps()[$tzid];
72
-    }
69
+	private function getTzFromMap(string $tzid): string
70
+	{
71
+		return $this->getTzMaps()[$tzid];
72
+	}
73 73
 
74
-    private function hasTzInMap(string $tzid): bool
75
-    {
76
-        return isset($this->getTzMaps()[$tzid]);
77
-    }
74
+	private function hasTzInMap(string $tzid): bool
75
+	{
76
+		return isset($this->getTzMaps()[$tzid]);
77
+	}
78 78
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/vobject/lib/TimezoneGuesser/FindFromOffset.php 1 patch
Indentation   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -11,21 +11,21 @@
 block discarded – undo
11 11
  */
12 12
 class FindFromOffset implements TimezoneFinder
13 13
 {
14
-    public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone
15
-    {
16
-        // Maybe the author was hyper-lazy and just included an offset. We
17
-        // support it, but we aren't happy about it.
18
-        if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
19
-            // Note that the path in the source will never be taken from PHP 5.5.10
20
-            // onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it
21
-            // already gets returned early in this function. Once we drop support
22
-            // for versions under PHP 5.5.10, this bit can be taken out of the
23
-            // source.
24
-            // @codeCoverageIgnoreStart
25
-            return new DateTimeZone('Etc/GMT'.$matches[1].ltrim(substr($matches[2], 0, 2), '0'));
26
-            // @codeCoverageIgnoreEnd
27
-        }
14
+	public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone
15
+	{
16
+		// Maybe the author was hyper-lazy and just included an offset. We
17
+		// support it, but we aren't happy about it.
18
+		if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
19
+			// Note that the path in the source will never be taken from PHP 5.5.10
20
+			// onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it
21
+			// already gets returned early in this function. Once we drop support
22
+			// for versions under PHP 5.5.10, this bit can be taken out of the
23
+			// source.
24
+			// @codeCoverageIgnoreStart
25
+			return new DateTimeZone('Etc/GMT'.$matches[1].ltrim(substr($matches[2], 0, 2), '0'));
26
+			// @codeCoverageIgnoreEnd
27
+		}
28 28
 
29
-        return null;
30
-    }
29
+		return null;
30
+	}
31 31
 }
Please login to merge, or discard this patch.
htdocs/includes/sabre/sabre/vobject/lib/TimezoneGuesser/TimezoneGuesser.php 1 patch
Indentation   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -7,5 +7,5 @@
 block discarded – undo
7 7
 
8 8
 interface TimezoneGuesser
9 9
 {
10
-    public function guess(VTimeZone $vtimezone, bool $failIfUncertain = false): ?DateTimeZone;
10
+	public function guess(VTimeZone $vtimezone, bool $failIfUncertain = false): ?DateTimeZone;
11 11
 }
Please login to merge, or discard this patch.