PluginProbe
Import Social Events / 1.8.5
Import Social Events v1.8.5
1.9.1 1.9.0 trunk 1.0.0 1.0.1 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0 1.3.0 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 All 61 releases
import-facebook-events / includes / lib / icalcreator / src / Util / IcalXMLFactory.php

IcalXMLFactory.php in Import Social Events 1.8.5, at includes/lib/icalcreator/src/Util/IcalXMLFactory.php

1,813 lines 77.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * iCalcreator, the PHP class package managing iCal (rfc2445/rfc5445) calendar information.
4 *
5 * copyright (c) 2007-2021 Kjell-Inge Gustafsson, kigkonsult, All rights reserved
6 * Link https://kigkonsult.se
7 * Package iCalcreator
8 * Version 2.30.3
9 * License Subject matter of licence is the software iCalcreator.
10 * The above copyright, link, package and version notices,
11 * this licence notice and the invariant [rfc5545] PRODID result use
12 * as implemented and invoked in iCalcreator shall be included in
13 * all copies or substantial portions of the iCalcreator.
14 *
15 * iCalcreator is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License as published
17 * by the Free Software Foundation, either version 3 of the License,
18 * or (at your option) any later version.
19 *
20 * iCalcreator is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public License
26 * along with iCalcreator. If not, see <https://www.gnu.org/licenses/>.
27 *
28 * This file is a part of iCalcreator.
29 */
30
31 namespace Kigkonsult\Icalcreator\Util;
32
33 use DateInterval;
34 use DateTime;
35 use Exception;
36 use InvalidArgumentException;
37 use Kigkonsult\Icalcreator\IcalInterface;
38 use Kigkonsult\Icalcreator\Vcalendar;
39 use SimpleXMLElement;
40
41 use function array_change_key_case;
42 use function html_entity_decode;
43 use function htmlspecialchars;
44 use function implode;
45 use function in_array;
46 use function is_null;
47 use function is_array;
48 use function number_format;
49 use function sprintf;
50 use function str_replace;
51 use function strcasecmp;
52 use function stripos;
53 use function strlen;
54 use function strtolower;
55 use function strtoupper;
56 use function substr;
57 use function trim;
58 use function ucfirst;
59
60 /**
61 * iCalcreator XML (rfc6321) support class
62 *
63 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
64 * @since 2.30.3 - 2021-02-14
65 */
66 class IcalXMLFactory
67 {
68 private static $Vcalendar = 'vcalendar';
69 private static $calProps = [
70 Vcalendar::VERSION,
71 Vcalendar::PRODID,
72 Vcalendar::CALSCALE,
73 Vcalendar::METHOD,
74 ];
75 private static $calPropsrfc7986Single = [
76 Vcalendar::UID,
77 Vcalendar::LAST_MODIFIED,
78 Vcalendar::URL,
79 Vcalendar::REFRESH_INTERVAL,
80 Vcalendar::SOURCE,
81 Vcalendar::COLOR
82 ];
83 private static $calPropsrfc7986Multi = [
84 Vcalendar::NAME,
85 Vcalendar::DESCRIPTION,
86 Vcalendar::CATEGORIES,
87 Vcalendar::IMAGE
88 ];
89 private static $properties = 'properties';
90 private static $PARAMETERS = 'parameters';
91 private static $components = 'components';
92
93 private static $text = 'text';
94 private static $binary = 'binary';
95 private static $uri = 'uri';
96 private static $date = 'date';
97 private static $date_time = 'date-time';
98 private static $period = 'period';
99 private static $rstatus = 'rstatus';
100 private static $unknown = 'unknown';
101 private static $recur = 'recur';
102 private static $cal_address = 'cal-address';
103 private static $integer = 'integer';
104 private static $utc_offset = 'utc-offset';
105 private static $code = 'code';
106 private static $description = 'description';
107 private static $data = 'data';
108 private static $time = 'time';
109
110 private static $altrep = 'altrep';
111 private static $dir = 'dir';
112 private static $delegated_from = 'delegated-from';
113 private static $delegated_to = 'delegated-to';
114 private static $member = 'member';
115 private static $sent_by = 'sent-by';
116 private static $rsvp = 'rsvp';
117 private static $bysecond = 'bysecond';
118 private static $byminute = 'byminute';
119 private static $byhour = 'byhour';
120 private static $bymonthday = 'bymonthday';
121 private static $byyearday = 'byyearday';
122 private static $byweekno = 'byweekno';
123 private static $bymonth = 'bymonth';
124 private static $bysetpos = 'bysetpos';
125 private static $byday = 'byday';
126 private static $freq = 'freq';
127 private static $count = 'count';
128 private static $interval = 'interval';
129 private static $wkst = 'wkst';
130
131 public static $XMLstart =
132 '<?xml version="1.0" encoding="utf-8"?><icalendar xmlns="urn:ietf:params:xml:ns:icalendar-2.0"><!-- kigkonsult.se %s, iCal2XMl (rfc6321), %s --></icalendar>';
133
134 /**
135 * Return iCal XML (rfc6321) output, using PHP SimpleXMLElement
136 *
137 * @param Vcalendar $calendar iCalcreator Vcalendar instance reference
138 * @return string
139 * @static
140 * @since 2.29.6 - 2019-07-03
141 * @throws Exception
142 * @throws InvalidArgumentException
143 */
144 public static function iCal2XML( Vcalendar $calendar )
145 {
146 static $YMDTHISZ = 'Ymd\THis\Z';
147 /** fix an SimpleXMLElement instance and create root element */
148 $xml = new SimpleXMLElement(
149 sprintf( self::$XMLstart, ICALCREATOR_VERSION, gmdate( $YMDTHISZ ))
150 );
151 $Vcalendar = $xml->addChild( self::$Vcalendar );
152 $langCal = $calendar->getConfig( Vcalendar::LANGUAGE );
153 /** fix calendar properties */
154 $properties = $Vcalendar->addChild( self::$properties );
155 foreach( self::$calProps as $propName ) {
156 $method = Vcalendar::getGetMethodName( $propName );
157 if( false !== ( $content = $calendar->{$method}())) {
158 self::addXMLchildText($properties, $propName, $content );
159 }
160 }
161 foreach( self::$calPropsrfc7986Single as $propName ) {
162 $method = Vcalendar::getGetMethodName( $propName );
163 switch( strtoupper( $propName )) {
164 case Vcalendar::UID : // fall through
165 case Vcalendar::COLOR :
166 if( false !== ( $content = $calendar->{$method}( true ))) {
167 self::addXMLchildText(
168 $properties,
169 $propName,
170 $content[Util::$LCvalue],
171 $content[Util::$LCparams]
172 );
173 }
174 break;
175 case Vcalendar::LAST_MODIFIED :
176 if( false !== ( $content = $calendar->{$method}( true ))) {
177 unset( $content[Util::$LCparams][Vcalendar::VALUE] );
178 self::addXMLchildDateTime(
179 $properties,
180 $propName,
181 $content[Util::$LCvalue],
182 $content[Util::$LCparams]
183 );
184 }
185 break;
186 case Vcalendar::SOURCE : // fall through
187 case Vcalendar::URL :
188 if( false !== ( $content = $calendar->{$method}( true ))) {
189 self::addXMLchildUri(
190 $properties,
191 $propName,
192 $content[Util::$LCvalue],
193 $content[Util::$LCparams]
194 );
195 }
196 break;
197 case Vcalendar::REFRESH_INTERVAL :
198 if( false !== ( $content = $calendar->{$method}( true ))) {
199 self::addXMLchildDuration(
200 $properties,
201 $propName,
202 $content[Util::$LCvalue],
203 $content[Util::$LCparams]
204 );
205 }
206 break;
207 } // end switch
208 } // end foreach
209 foreach( self::$calPropsrfc7986Multi as $propName ) {
210 $method = Vcalendar::getGetMethodName( $propName );
211 switch( strtoupper( $propName )) {
212 case Vcalendar::NAME : // fall through
213 case Vcalendar::CATEGORIES : // fall through
214 case Vcalendar::DESCRIPTION :
215 while( false !== ( $content = $calendar->{$method}( null, true ))) {
216 if( ! isset( $content[Util::$LCparams][Vcalendar::LANGUAGE] ) &&
217 $langCal ) {
218 $content[Util::$LCparams][Vcalendar::LANGUAGE] = $langCal;
219 }
220 self::addXMLchildText(
221 $properties,
222 $propName,
223 $content[Util::$LCvalue],
224 $content[Util::$LCparams]
225 );
226 } // end while
227 break;
228 case Vcalendar::IMAGE :
229 while( false !== ( $content = $calendar->{$method}( null, true ))) {
230 self::addXMLchildBinaryUri( $properties, $propName, $content );
231 }
232 break;
233 } // end switch
234 } // end foreach
235 while( false !== ( $content = $calendar->getXprop( null, null, true ))) {
236 self::addXMLchild(
237 $properties,
238 $content[0],
239 self::$unknown,
240 $content[1][Util::$LCvalue],
241 $content[1][Util::$LCparams]
242 );
243 } // end while
244 /** prepare to fix components with properties */
245 $components = $Vcalendar->addChild( self::$components );
246 /** fix component properties */
247 while( false !== ( $component = $calendar->getComponent())) {
248 $compName = $component->getCompType();
249 $child = $components->addChild( strtolower( $compName ));
250 $properties = $child->addChild( self::$properties );
251 $langComp = $component->getConfig( Vcalendar::LANGUAGE );
252 $props = $component->getConfig( Vcalendar::SETPROPERTYNAMES );
253 foreach( $props as $pix => $propName ) {
254 switch( strtoupper( $propName )) {
255 case Vcalendar::ATTACH : // may occur multiple times
256 case Vcalendar::IMAGE :
257 $method = Vcalendar::getGetMethodName( $propName );
258 while( false !== ( $content = $component->{$method }( null, true ))) {
259 self::addXMLchildBinaryUri(
260 $properties,
261 $propName,
262 $content
263 );
264 } // end while
265 break;
266 case Vcalendar::ATTENDEE :
267 while( false !== ( $content = $component->getAttendee( null, true ))) {
268 if( isset( $content[Util::$LCparams][Vcalendar::CN] )) {
269 self::addLanguage(
270 $content[Util::$LCparams],
271 $langComp,
272 $langCal
273 );
274 }
275 self::addXMLchildCalAddress(
276 $properties,
277 $propName,
278 $content[Util::$LCvalue],
279 $content[Util::$LCparams]
280 );
281 } // end while
282 break;
283 case Vcalendar::EXDATE :
284 while( false !== ( $content = $component->getExdate( null, true ))) {
285 $isDateSet =
286 ParameterFactory::isParamsValueSet(
287 $content,
288 Vcalendar::DATE
289 );
290 unset( $content[Util::$LCparams][Vcalendar::VALUE] );
291 if( $isDateSet ) {
292 self::addXMLchildDate(
293 $properties,
294 $propName,
295 $content[Util::$LCvalue],
296 $content[Util::$LCparams]
297 );
298 }
299 else {
300 self::addXMLchildDateTime(
301 $properties,
302 $propName,
303 $content[Util::$LCvalue],
304 $content[Util::$LCparams]
305 );
306 }
307 } // end while
308 break;
309 case Vcalendar::FREEBUSY :
310 while( false !==
311 ( $content = $component->getFreebusy( null, true ))
312 ) {
313 if( is_array( $content ) &&
314 isset( $content[Util::$LCvalue][Vcalendar::FBTYPE] )) {
315 $content[Util::$LCparams][Vcalendar::FBTYPE] =
316 $content[Util::$LCvalue][Vcalendar::FBTYPE];
317 unset( $content[Util::$LCvalue][Vcalendar::FBTYPE] );
318 }
319 self::addXMLchild(
320 $properties,
321 $propName,
322 self::$period,
323 $content[Util::$LCvalue],
324 $content[Util::$LCparams]
325 );
326 } // end while
327 break;
328 case Vcalendar::REQUEST_STATUS :
329 while( false !==
330 ( $content = $component->getRequeststatus( null, true ))
331 ) {
332 self::addLanguage(
333 $content[Util::$LCparams],
334 $langComp,
335 $langCal
336 );
337 self::addXMLchild(
338 $properties,
339 $propName,
340 self::$rstatus,
341 $content[Util::$LCvalue],
342 $content[Util::$LCparams]
343 );
344 } // end while
345 break;
346 case Vcalendar::RDATE :
347 while( false !==
348 ( $content = $component->getRdate( null, true ))
349 ) {
350 $type = self::$date_time;
351 if( isset( $content[Util::$LCparams][Vcalendar::VALUE] )) {
352 if( ParameterFactory::isParamsValueSet(
353 $content,
354 Vcalendar::DATE
355 )) {
356 $type = self::$date;
357 }
358 elseif( ParameterFactory::isParamsValueSet(
359 $content,
360 Vcalendar::PERIOD
361 )) {
362 $type = self::$period;
363 }
364 } // end if
365 unset( $content[Util::$LCparams][Vcalendar::VALUE] );
366 self::addXMLchild(
367 $properties,
368 $propName,
369 $type,
370 $content[Util::$LCvalue],
371 $content[Util::$LCparams]
372 );
373 } // end while
374 break;
375 case Vcalendar::DESCRIPTION :
376 $method = Vcalendar::getGetMethodName( $propName );
377 while( false !==
378 ( $content = $component->{$method}( null, true ))
379 ) {
380 self::addXMLchildText(
381 $properties,
382 $propName,
383 $content[Util::$LCvalue],
384 $content[Util::$LCparams]
385 );
386 if( Vcalendar::VJOURNAL != $compName ) {
387 break;
388 }
389 } // end while
390 break;
391 case Vcalendar::CATEGORIES : // fall through
392 case Vcalendar::COMMENT : // fall through
393 case Vcalendar::CONTACT : // fall through
394 case Vcalendar::RELATED_TO : // fall through
395 case Vcalendar::RESOURCES :
396 $method = Vcalendar::getGetMethodName( $propName );
397 while( false !==
398 ( $content = $component->{$method}( null, true )
399 )) {
400 if(( Vcalendar::RELATED_TO != $propName )) {
401 self::addLanguage(
402 $content[Util::$LCparams],
403 $langComp,
404 $langCal
405 );
406 } // end if
407 self::addXMLchildText(
408 $properties,
409 $propName,
410 $content[Util::$LCvalue],
411 $content[Util::$LCparams]
412 );
413 } // end while
414 break;
415 case Vcalendar::CREATED : // single occurrence below, if set
416 case Vcalendar::COMPLETED : // fall through
417 case Vcalendar::DTSTAMP : // fall through
418 case Vcalendar::LAST_MODIFIED : // fall through
419 case Vcalendar::DTSTART : // fall through
420 case Vcalendar::DTEND : // fall through
421 case Vcalendar::DUE : // fall through
422 case Vcalendar::RECURRENCE_ID : // fall through
423 $method = Vcalendar::getGetMethodName( $propName );
424 if( false !== ( $content = $component->{$method}( true ))) {
425 $isDateSet =
426 ParameterFactory::isParamsValueSet(
427 $content,
428 Vcalendar::DATE
429 );
430 unset( $content[Util::$LCparams][Vcalendar::VALUE] );
431 if( $isDateSet ) {
432 self::addXMLchildDate(
433 $properties,
434 $propName,
435 $content[Util::$LCvalue],
436 $content[Util::$LCparams]
437 );
438 }
439 else {
440 self::addXMLchildDateTime(
441 $properties,
442 $propName,
443 $content[Util::$LCvalue],
444 $content[Util::$LCparams]
445 );
446 }
447 } // end if
448 break;
449 case Vcalendar::DURATION :
450 if( false !== ( $content = $component->getDuration( true ))) {
451 self::addXMLchildDuration(
452 $properties,
453 $propName,
454 $content[Util::$LCvalue],
455 $content[Util::$LCparams]
456 );
457 }
458 break;
459 case Vcalendar::EXRULE :
460 case Vcalendar::RRULE :
461 $method = Vcalendar::getGetMethodName( $propName );
462 if( false !== ( $content = $component->{$method}( true ))) {
463 self::addXMLchildRecur(
464 $properties,
465 $propName,
466 $content[Util::$LCvalue],
467 $content[Util::$LCparams]
468 );
469 }
470 break;
471 case Vcalendar::COLOR : // fall through
472 case Vcalendar::KLASS : // fall through
473 case Vcalendar::LOCATION : // fall through
474 case Vcalendar::STATUS : // fall through
475 case Vcalendar::SUMMARY : // fall through
476 case Vcalendar::TRANSP : // fall through
477 case Vcalendar::TZID : // fall through
478 case Vcalendar::UID :
479 $method = Vcalendar::getGetMethodName( $propName );
480 if( false !== ( $content = $component->{$method}( true ))) {
481 if(( Vcalendar::LOCATION == $propName ) ||
482 ( Vcalendar::SUMMARY == $propName )) {
483 self::addLanguage(
484 $content[Util::$LCparams],
485 $langComp,
486 $langCal
487 );
488 }
489 self::addXMLchildText(
490 $properties,
491 $propName,
492 $content[Util::$LCvalue],
493 $content[Util::$LCparams]
494 );
495 }
496 break;
497 case Vcalendar::GEO :
498 if( false !== ( $content = $component->getGeo( true ))) {
499 self::addXMLchild(
500 $properties,
501 $propName,
502 strtolower( Vcalendar::GEO ),
503 $content[Util::$LCvalue],
504 $content[Util::$LCparams]
505 );
506 }
507 break;
508 case Vcalendar::ORGANIZER :
509 if( false !== ( $content = $component->getOrganizer( true ))) {
510 if( isset( $content[Util::$LCparams][Vcalendar::CN] )) {
511 self::addLanguage(
512 $content[Util::$LCparams],
513 $langComp,
514 $langCal
515 );
516 }
517 self::addXMLchildCalAddress(
518 $properties,
519 $propName,
520 $content[Util::$LCvalue],
521 $content[Util::$LCparams]
522 );
523 }
524 break;
525 case Vcalendar::PERCENT_COMPLETE : // fall through
526 case Vcalendar::PRIORITY : // fall through
527 case Vcalendar::SEQUENCE :
528 $method = Vcalendar::getGetMethodName( $propName );
529 if( false !== ( $content = $component->{$method}( true ))) {
530 self::addXMLchildInteger(
531 $properties,
532 $propName,
533 $content[Util::$LCvalue],
534 $content[Util::$LCparams]
535 );
536 }
537 break;
538 case Vcalendar::CONFERENCE :
539 $method = Vcalendar::getGetMethodName( $propName );
540 while( false !==
541 ( $content = $component->{$method}( null, true )
542 )) {
543 self::addXMLchildUri(
544 $properties,
545 $propName,
546 $content[Util::$LCvalue],
547 $content[Util::$LCparams]
548 );
549 } // end while
550 break;
551 case Vcalendar::TZURL : // fall through
552 case Vcalendar::URL :
553 $method = Vcalendar::getGetMethodName( $propName );
554 if( false !== ( $content = $component->{$method}( true ))) {
555 self::addXMLchildUri(
556 $properties,
557 $propName,
558 $content[Util::$LCvalue],
559 $content[Util::$LCparams]
560 );
561 }
562 break;
563 default :
564 if( ! StringFactory::isXprefixed( $propName )) {
565 break;
566 }
567 if( false !==
568 ( $content = $component->getXprop( $propName, null, true )
569 )) {
570 self::addXMLchild(
571 $properties,
572 $content[0],
573 self::$unknown,
574 $content[1][Util::$LCvalue],
575 $content[1][Util::$LCparams]
576 );
577 }
578 break;
579 } // end switch( $propName )
580 } // end foreach( $props as $pix => $propName )
581 /** fix subComponent properties, if any */
582 while( false !== ( $subcomp = $component->getComponent())) {
583 $subCompName = $subcomp->getCompType();
584 $child2 = $child->addChild( strtolower( $subCompName ));
585 $properties = $child2->addChild( self::$properties );
586 $langComp = $subcomp->getConfig( Vcalendar::LANGUAGE );
587 $subCompProps = $subcomp->getConfig( Vcalendar::SETPROPERTYNAMES );
588 foreach( $subCompProps as $pix2 => $propName ) {
589 switch( strtoupper( $propName )) {
590 case Vcalendar::ATTACH : // may occur multiple times, below
591 while( false !==
592 ( $content = $subcomp->getAttach( null, true )
593 )) {
594 self::addXMLchildBinaryUri(
595 $properties,
596 $propName,
597 $content
598 );
599 } // end while
600 break;
601 case Vcalendar::ATTENDEE :
602 while( false !==
603 ( $content = $subcomp->getAttendee( null, true )
604 )) {
605 if( isset( $content[Util::$LCparams][Vcalendar::CN] )) {
606 self::addLanguage(
607 $content[Util::$LCparams],
608 $langComp,
609 $langCal
610 );
611 }
612 self::addXMLchildCalAddress(
613 $properties,
614 $propName,
615 $content[Util::$LCvalue],
616 $content[Util::$LCparams]
617 );
618 } // end while
619 break;
620 case Vcalendar::COMMENT : // fall through
621 case Vcalendar::TZNAME :
622 $method = Vcalendar::getGetMethodName( $propName );
623 while( false !==
624 ( $content = $subcomp->{$method}( null, true )
625 )) {
626 self::addLanguage(
627 $content[Util::$LCparams],
628 $langComp,
629 $langCal
630 );
631 self::addXMLchildText(
632 $properties,
633 $propName,
634 $content[Util::$LCvalue],
635 $content[Util::$LCparams]
636 );
637 } // end while
638 break;
639 case Vcalendar::RDATE :
640 while( false !==
641 ( $content = $subcomp->getRdate( null, true )
642 )) {
643 $type = self::$date_time;
644 if( isset( $content[Util::$LCparams][Vcalendar::VALUE] )) {
645 if( ParameterFactory::isParamsValueSet(
646 $content,
647 Vcalendar::DATE
648 )) {
649 $type = self::$date;
650 }
651 elseif( ParameterFactory::isParamsValueSet(
652 $content,
653 Vcalendar::PERIOD
654 )) {
655 $type = self::$period;
656 }
657 } // end if
658 unset( $content[Util::$LCparams][Vcalendar::VALUE] );
659 self::addXMLchild(
660 $properties,
661 $propName,
662 $type,
663 $content[Util::$LCvalue],
664 $content[Util::$LCparams]
665 );
666 } // end while
667 break;
668 case Vcalendar::ACTION : // single occurrence below, if set
669 case Vcalendar::DESCRIPTION : // fall through
670 case Vcalendar::SUMMARY :
671 $method = Vcalendar::getGetMethodName( $propName );
672 if( false !== ( $content = $subcomp->{$method}( true ))) {
673 if(( Vcalendar::ACTION != $propName ) ) {
674 self::addLanguage(
675 $content[Util::$LCparams],
676 $langComp,
677 $langCal
678 );
679 }
680 self::addXMLchildText(
681 $properties,
682 $propName,
683 $content[Util::$LCvalue],
684 $content[Util::$LCparams]
685 );
686 }
687 break;
688 case Vcalendar::DTSTART :
689 if( false !== ( $content = $subcomp->getDtstart( true ))) {
690 unset( $content[Util::$LCparams][Vcalendar::VALUE] );
691 self::addXMLchildDateTime(
692 $properties,
693 $propName,
694 $content[Util::$LCvalue],
695 $content[Util::$LCparams]
696 );
697 }
698 break;
699 case Vcalendar::DURATION :
700 if( false !== ( $content = $subcomp->getDuration( true ))) {
701 self::addXMLchildDuration(
702 $properties,
703 $propName,
704 $content[Util::$LCvalue],
705 $content[Util::$LCparams]
706 );
707 }
708 break;
709 case Vcalendar::REPEAT :
710 if( false !== ( $content = $subcomp->getRepeat( true ))) {
711 self::addXMLchildInteger(
712 $properties,
713 $propName,
714 $content[Util::$LCvalue],
715 $content[Util::$LCparams]
716 );
717 }
718 break;
719 case Vcalendar::TRIGGER :
720 if( false !== ( $content = $subcomp->getTrigger( true ))) {
721 if( DateIntervalFactory::isDateIntervalArrayInvertSet(
722 $content[Util::$LCvalue]
723 )) {
724 $content[Util::$LCvalue] =
725 DateIntervalFactory::DateIntervalArr2DateInterval(
726 $content[Util::$LCvalue]
727 );
728 }
729 if( $content[Util::$LCvalue] instanceof DateInterval ) {
730 self::addXMLchildDuration(
731 $properties,
732 $propName,
733 $content[Util::$LCvalue],
734 $content[Util::$LCparams]
735 );
736 }
737 else {
738 self::addXMLchildDateTime(
739 $properties,
740 $propName,
741 $content[Util::$LCvalue],
742 $content[Util::$LCparams]
743 );
744 }
745 } // end if
746 break;
747 case Vcalendar::TZOFFSETFROM : // fall through
748 case Vcalendar::TZOFFSETTO :
749 $method = Vcalendar::getGetMethodName( $propName );
750 if( false !== ( $content = $subcomp->{$method}( true ))) {
751 self::addXMLchild(
752 $properties,
753 $propName,
754 self::$utc_offset,
755 $content[Util::$LCvalue],
756 $content[Util::$LCparams]
757 );
758 }
759 break;
760 case Vcalendar::RRULE :
761 // rfc5545 restriction: .. SHOULD NOT occur more than once
762 if( false !== ( $content = $subcomp->getRrule( true ))) {
763 self::addXMLchildRecur(
764 $properties,
765 $propName,
766 $content[Util::$LCvalue],
767 $content[Util::$LCparams]
768 );
769 }
770 break;
771 default :
772 if( ! StringFactory::isXprefixed( $propName )) {
773 break;
774 }
775 if( false !==
776 ( $content = $subcomp->getXprop( $propName, null, true )
777 )) {
778 self::addXMLchild(
779 $properties,
780 $content[0],
781 self::$unknown,
782 $content[1][Util::$LCvalue],
783 $content[1][Util::$LCparams]
784 );
785 }
786 break;
787 } // switch( $propName )
788 } // end foreach( $subCompProps as $pix2 => $propName )
789 } // end while( false !== ( $subcomp = $component->getComponent()))
790 } // end while( false !== ( $component = $calendar->getComponent()))
791 return $xml->asXML();
792 }
793
794 /**
795 * Add parameter language if not set
796 *
797 * @param array $params
798 * @param string $langComp
799 * @param string $langCal
800 */
801 private static function addLanguage( & $params, $langComp, $langCal )
802 {
803 switch( true ) {
804 case isset( $params[Vcalendar::LANGUAGE] ) :
805 break;
806 case ( ! empty( $langComp )) :
807 $params[Vcalendar::LANGUAGE] = $langComp;
808 break;
809 case ( ! empty( $langCal )) :
810 $params[Vcalendar::LANGUAGE] = $langCal;
811 break;
812 } // end switch
813 }
814
815 /**
816 * Add XML (rfc6321) binary/uri children to a SimpleXMLelement
817 *
818 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
819 * @param string $name new element node name
820 * @param array $content
821 * @throws Exception
822 * @throws InvalidArgumentException
823 * @static
824 * @since 2.29.2 - 2019-06-29
825 */
826 private static function addXMLchildBinaryUri(
827 SimpleXMLElement $parent,
828 $name,
829 $content
830 ) {
831 $type = ( ParameterFactory::isParamsValueSet( $content, Vcalendar::BINARY ))
832 ? self::$binary
833 : self::$uri;
834 unset( $content[Util::$LCparams][Vcalendar::VALUE] );
835 self::addXMLchild(
836 $parent,
837 $name,
838 $type,
839 $content[Util::$LCvalue],
840 $content[Util::$LCparams]
841 );
842 }
843
844 /**
845 * Add XML (rfc6321) cal-address children to a SimpleXMLelement
846 *
847 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
848 * @param string $name new element node name
849 * @param string|array|DateTime|DateInterval $content new subelement content
850 * @param array $params new element 'attributes'
851 * @throws Exception
852 * @throws InvalidArgumentException
853 * @static
854 * @since 2.29.2 - 2019-06-29
855 */
856 private static function addXMLchildCalAddress(
857 SimpleXMLElement $parent,
858 $name,
859 $content,
860 $params = []
861 ) {
862 self::addXMLchild($parent, $name, self::$cal_address, $content, $params );
863 }
864 /**
865 * Add XML (rfc6321) date children to a SimpleXMLelement
866 *
867 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
868 * @param string $name new element node name
869 * @param string|array|DateTime|DateInterval $content new subelement content
870 * @param array $params new element 'attributes'
871 * @throws Exception
872 * @throws InvalidArgumentException
873 * @static
874 * @since 2.29.2 - 2019-06-29
875 */
876 private static function addXMLchildDate(
877 SimpleXMLElement $parent,
878 $name,
879 $content,
880 $params = []
881 ) {
882 self::addXMLchild($parent, $name, self::$date, $content, $params );
883 }
884
885 /**
886 * Add XML (rfc6321) date-time children to a SimpleXMLelement
887 *
888 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
889 * @param string $name new element node name
890 * @param string|array|DateTime|DateInterval $content new subelement content
891 * @param array $params new element 'attributes'
892 * @throws Exception
893 * @throws InvalidArgumentException
894 * @static
895 * @since 2.29.2 - 2019-06-29
896 */
897 private static function addXMLchildDateTime(
898 SimpleXMLElement $parent,
899 $name,
900 $content,
901 $params = []
902 ) {
903 self::addXMLchild($parent, $name, self::$date_time, $content, $params );
904 }
905
906 /**
907 * Add XML (rfc6321) duration children to a SimpleXMLelement
908 *
909 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
910 * @param string $name new element node name
911 * @param string|array|DateTime|DateInterval $content new subelement content
912 * @param array $params new element 'attributes'
913 * @throws Exception
914 * @throws InvalidArgumentException
915 * @static
916 * @since 2.29.2 - 2019-06-29
917 */
918 private static function addXMLchildDuration(
919 SimpleXMLElement $parent,
920 $name,
921 $content,
922 $params = []
923 ) {
924 self::addXMLchild(
925 $parent,
926 $name,
927 strtolower( Vcalendar::DURATION ),
928 $content,
929 $params
930 );
931 }
932
933 /**
934 * Add XML (rfc6321) integer children to a SimpleXMLelement
935 *
936 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
937 * @param string $name new element node name
938 * @param string|array|DateTime|DateInterval $content new subelement content
939 * @param array $params new element 'attributes'
940 * @throws Exception
941 * @throws InvalidArgumentException
942 * @static
943 * @since 2.29.2 - 2019-06-29
944 */
945 private static function addXMLchildInteger(
946 SimpleXMLElement $parent,
947 $name,
948 $content,
949 $params = []
950 ) {
951 self::addXMLchild($parent, $name, self::$integer, $content, $params );
952 }
953
954 /**
955 * Add XML (rfc6321) recur children to a SimpleXMLelement
956 *
957 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
958 * @param string $name new element node name
959 * @param string|array|DateTime|DateInterval $content new subelement content
960 * @param array $params new element 'attributes'
961 * @throws Exception
962 * @throws InvalidArgumentException
963 * @static
964 * @since 2.29.2 - 2019-06-29
965 */
966 private static function addXMLchildRecur(
967 SimpleXMLElement $parent,
968 $name,
969 $content,
970 $params = []
971 ) {
972 self::addXMLchild($parent, $name, self::$recur, $content, $params );
973 }
974
975 /**
976 * Add XML (rfc6321) text children to a SimpleXMLelement
977 *
978 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
979 * @param string $name new element node name
980 * @param string|array|DateTime|DateInterval $content new subelement content
981 * @param array $params new element 'attributes'
982 * @throws Exception
983 * @throws InvalidArgumentException
984 * @static
985 * @since 2.29.2 - 2019-06-29
986 */
987 private static function addXMLchildText(
988 SimpleXMLElement $parent,
989 $name,
990 $content,
991 $params = []
992 ) {
993 self::addXMLchild($parent, $name, self::$text, $content, $params );
994 }
995
996 /**
997 * Add XML (rfc6321) uri children to a SimpleXMLelement
998 *
999 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
1000 * @param string $name new element node name
1001 * @param string|array|DateTime|DateInterval $content new subelement content
1002 * @param array $params new element 'attributes'
1003 * @throws Exception
1004 * @throws InvalidArgumentException
1005 * @static
1006 * @since 2.30.3 - 2021-02-15
1007 */
1008 private static function addXMLchildUri(
1009 SimpleXMLElement $parent,
1010 $name,
1011 $content,
1012 $params = []
1013 ) {
1014 self::addXMLchild( $parent, $name, self::$uri, htmlentities( $content ), $params );
1015 }
1016
1017 /**
1018 * Add XML (rfc6321) children to a SimpleXMLelement
1019 *
1020 * @param SimpleXMLElement $parent a SimpleXMLelement class instance
1021 * @param string $name new element node name
1022 * @param string $type content type, subelement(-s) name
1023 * @param string|array|DateTime|DateInterval $content new subelement content
1024 * @param array $params new element 'attributes'
1025 * @throws Exception
1026 * @throws InvalidArgumentException
1027 * @static
1028 * @since 2.29.30 - 2020-12-09
1029 */
1030 private static function addXMLchild(
1031 SimpleXMLElement $parent,
1032 $name,
1033 $type,
1034 $content,
1035 $params = []
1036 ) {
1037 static $BOOLEAN = 'boolean';
1038 static $UNTIL = 'until';
1039 static $START = 'start';
1040 static $END = 'end';
1041 static $SP0 = '';
1042 /** create new child node */
1043 $name = strtolower( $name );
1044 $child = $parent->addChild( $name );
1045 if(( empty( $content ) && ( Util::$ZERO != $content )) ||
1046 ( is_string( $content) &&
1047 ( Util::$MINUS != substr( $content, 0, 1 )) &&
1048 ( 0 > $content ))
1049 ) { // ??
1050 $v = $child->addChild( $type );
1051 return;
1052 }
1053 $recurDateIsSet = false;
1054 switch( true ) {
1055 case empty( $params ) :
1056 break;
1057 case (( 1 == count( $params )) && isset( $params[Util::$ISLOCALTIME] )) :
1058 break;
1059 case ( self::$recur == $type ) :
1060 $recurDateIsSet = ParameterFactory::isParamsValueSet(
1061 [ Util::$LCparams => $params ],
1062 Vcalendar::DATE
1063 );
1064 if(( 1 == count( $params )) && isset( $params[Vcalendar::VALUE ] )) {
1065 break;
1066 }
1067 unset( $params[Vcalendar::VALUE ] );
1068 // fall through
1069 default :
1070 $parameters = $child->addChild( self::$PARAMETERS );
1071 foreach( $params as $param => $parVal ) {
1072 if( Vcalendar::VALUE === $param ) {
1073 if( false !== strpos( $parVal, Util::$COLON )) {
1074 $p1 = $parameters->addChild( strtolower( $param ));
1075 $p2 = $p1->addChild( self::$unknown, htmlspecialchars( $parVal ));
1076 $type = strtolower( StringFactory::before( Util::$COLON, $parVal ));
1077 }
1078 elseif( 0 != strcasecmp( $type, $parVal )) {
1079 $type = strtolower( $parVal );
1080 }
1081 continue;
1082 }
1083 elseif( Util::$ISLOCALTIME == $param ) {
1084 continue;
1085 }
1086 $param = strtolower( $param );
1087 if( StringFactory::isXprefixed( $param )) {
1088 $p1 = $parameters->addChild( $param );
1089 $p2 = $p1->addChild( self::$unknown, htmlspecialchars( $parVal ));
1090 continue;
1091 }
1092 $p1 = $parameters->addChild( $param );
1093 switch( $param ) {
1094 case self::$altrep :
1095 case self::$dir :
1096 $ptype = self::$uri;
1097 break;
1098 case self::$delegated_from :
1099 case self::$delegated_to :
1100 case self::$member :
1101 case self::$sent_by :
1102 $ptype = self::$cal_address;
1103 break;
1104 case self::$rsvp :
1105 $ptype = $BOOLEAN;
1106 break;
1107 default :
1108 $ptype = self::$text;
1109 break;
1110 } // end switch
1111 if( is_array( $parVal )) {
1112 foreach( $parVal as $pV ) {
1113 $p2 = $p1->addChild( $ptype, htmlspecialchars( $pV ));
1114 }
1115 }
1116 else {
1117 $p2 = $p1->addChild( $ptype, htmlspecialchars( $parVal ));
1118 }
1119 } // end foreach
1120 break;
1121 } // end switch
1122 /** store content */
1123 switch( $type ) {
1124 case self::$binary :
1125 $v = $child->addChild( $type, $content );
1126 break;
1127 case $BOOLEAN :
1128 break;
1129 case self::$cal_address :
1130 $v = $child->addChild( $type, $content );
1131 break;
1132 case self::$date :
1133 if( $content instanceof DateTime ) {
1134 $content = [ $content ];
1135 }
1136 foreach( $content as $date ) {
1137 $v = $child->addChild(
1138 $type,
1139 DateTimeFactory::dateTime2Str( $date, true )
1140 );
1141 }
1142 break;
1143 case self::$date_time :
1144 if( $content instanceof DateTime ) {
1145 $content = [ $content ];
1146 }
1147 $isLocalTime = isset( $params[Util::$ISLOCALTIME] );
1148 foreach( $content as $dt ) {
1149 $v = $child->addChild(
1150 $type,
1151 DateTimeFactory::dateTime2Str( $dt, false, $isLocalTime )
1152 );
1153 } // end foreach
1154 break;
1155 case strtolower( Vcalendar::DURATION ) :
1156 $v = $child->addChild(
1157 $type,
1158 DateIntervalFactory::dateInterval2String( $content, true )
1159 );
1160 break;
1161 case strtolower( Vcalendar::GEO ) :
1162 if( ! empty( $content )) {
1163 $v1 = $child->addChild(
1164 Vcalendar::LATITUDE,
1165 GeoFactory::geo2str2(
1166 $content[Vcalendar::LATITUDE],
1167 GeoFactory::$geoLatFmt
1168 )
1169 );
1170 $v1 = $child->addChild(
1171 Vcalendar::LONGITUDE,
1172 GeoFactory::geo2str2(
1173 $content[Vcalendar::LONGITUDE],
1174 GeoFactory::$geoLongFmt
1175 )
1176 );
1177 }
1178 break;
1179 case self::$integer :
1180 $v = $child->addChild( $type, (string) $content );
1181 break;
1182 case self::$period :
1183 if( ! is_array( $content )) {
1184 break;
1185 }
1186 $isLocalTime = isset( $params[Util::$ISLOCALTIME] );
1187 foreach( $content as $period ) {
1188 $v1 = $child->addChild( $type );
1189 $str = DateTimeFactory::dateTime2Str(
1190 $period[0],
1191 false,
1192 $isLocalTime
1193 );
1194 $v2 = $v1->addChild( $START, $str );
1195 if( $period[1] instanceof DateInterval ) {
1196 $v2 = $v1->addChild(
1197 strtolower( Vcalendar::DURATION ),
1198 DateIntervalFactory::dateInterval2String( $period[1] )
1199 );
1200 }
1201 elseif( $period[1] instanceof DateTime ) {
1202 $str = DateTimeFactory::dateTime2Str(
1203 $period[1],
1204 false,
1205 $isLocalTime
1206 );
1207 $v2 = $v1->addChild( $END, $str );
1208 }
1209 } // end foreach
1210 break;
1211 case self::$recur :
1212 $content = array_change_key_case( $content );
1213 foreach( $content as $ruleLabel => $ruleValue ) {
1214 switch( $ruleLabel ) {
1215 case $UNTIL :
1216 $v = $child->addChild(
1217 $ruleLabel,
1218 DateTimeFactory::dateTime2Str(
1219 $ruleValue,
1220 $recurDateIsSet
1221 )
1222 );
1223 break;
1224 case self::$bysecond :
1225 case self::$byminute :
1226 case self::$byhour :
1227 case self::$bymonthday :
1228 case self::$byyearday :
1229 case self::$byweekno :
1230 case self::$bymonth :
1231 case self::$bysetpos :
1232 if( is_array( $ruleValue )) {
1233 foreach( $ruleValue as $vix => $valuePart ) {
1234 $v = $child->addChild( $ruleLabel, $valuePart );
1235 }
1236 }
1237 else {
1238 $v = $child->addChild( $ruleLabel, $ruleValue );
1239 }
1240 break;
1241 case self::$byday :
1242 if( isset( $ruleValue[Vcalendar::DAY] )) {
1243 $str = ( isset( $ruleValue[0] )) ? $ruleValue[0] : null;
1244 $str .= $ruleValue[Vcalendar::DAY];
1245 $p = $child->addChild( $ruleLabel, $str );
1246 }
1247 else {
1248 foreach( $ruleValue as $valuePart ) {
1249 if( isset( $valuePart[Vcalendar::DAY] )) {
1250 $str = ( isset( $valuePart[0] ))
1251 ? $valuePart[0]
1252 : null;
1253 $str .= $valuePart[Vcalendar::DAY];
1254 $p = $child->addChild( $ruleLabel, $str );
1255 }
1256 else {
1257 $p = $child->addChild( $ruleLabel, $valuePart );
1258 }
1259 } // end foreach
1260 }
1261 break;
1262 case self::$freq :
1263 case self::$count :
1264 case self::$interval :
1265 case self::$wkst :
1266 default:
1267 $p = $child->addChild( $ruleLabel, $ruleValue );
1268 break;
1269 } // end switch( $ruleLabel )
1270 } // end foreach( $content as $ruleLabel => $ruleValue )
1271 break;
1272 case self::$rstatus :
1273 $v = $child->addChild(
1274 self::$code,
1275 number_format(
1276 (float) $content[Vcalendar::STATCODE],
1277 2,
1278 Util::$DOT,
1279 $SP0
1280 )
1281 );
1282 $v = $child->addChild(
1283 self::$description,
1284 htmlspecialchars( $content[Vcalendar::STATDESC] )
1285 );
1286 if( isset( $content[Vcalendar::EXTDATA] )) {
1287 $v = $child->addChild(
1288 self::$data,
1289 htmlspecialchars( $content[Vcalendar::EXTDATA] )
1290 );
1291 }
1292 break;
1293 case self::$text :
1294 if( ! is_array( $content )) {
1295 $content = [ $content ];
1296 }
1297 foreach( $content as $part ) {
1298 $v = $child->addChild( $type, htmlspecialchars( $part ));
1299 }
1300 break;
1301 case self::$time :
1302 break;
1303 case self::$uri :
1304 $v = $child->addChild( $type, $content );
1305 break;
1306 case self::$utc_offset :
1307 if( DateIntervalFactory::hasPlusMinusPrefix( $content )) {
1308 $str = substr( $content, 0, 1 );
1309 $content = substr( $content, 1 );
1310 }
1311 else {
1312 $str = Util::$PLUS;
1313 }
1314 $str .= substr( $content, 0, 2 ) .
1315 Util::$COLON . substr( $content, 2, 2 );
1316 if( 4 < strlen( $content )) {
1317 $str .= Util::$COLON . substr( $content, 4 );
1318 }
1319 $v = $child->addChild( $type, $str );
1320 break;
1321 case self::$unknown :
1322 default:
1323 if( is_array( $content )) {
1324 $content = implode( $content );
1325 }
1326 $v = $child->addChild( self::$unknown, htmlspecialchars( $content ));
1327 break;
1328 } // end switch
1329 }
1330
1331 /**
1332 * Parse (rfc6321) XML string into iCalcreator instance
1333 *
1334 * @param string $xmlStr
1335 * @param array $iCalcfg iCalcreator config array (opt)
1336 * @return mixed iCalcreator instance or false on error
1337 * @static
1338 * @since 2.20.23 - 2017-02-25
1339 */
1340 public static function XML2iCal( $xmlStr, $iCalcfg = [] )
1341 {
1342 static $CRLF = [ "\r\n", "\n\r", "\n", "\r" ];
1343 $xmlStr = str_replace( $CRLF, null, $xmlStr );
1344 $xml = self::XMLgetTagContent1( $xmlStr, self::$Vcalendar, $endIx );
1345 $iCal = new Vcalendar( $iCalcfg );
1346 if( false === self::XMLgetComps( $iCal, $xmlStr ))
1347 return false;
1348 return $iCal;
1349 }
1350
1351 /**
1352 * Parse (rfc6321) XML string into iCalcreator components
1353 *
1354 * @param IcalInterface $iCal
1355 * @param string $xml
1356 * @return mixed Vcalendar|bool
1357 * @static
1358 * @since 2.27.14 - 2019-03-09
1359 */
1360 private static function XMLgetComps( IcalInterface $iCal, $xml )
1361 {
1362 static $PROPSTAGempty = '<properties/>';
1363 static $PROPSTAGstart = '<properties>';
1364 static $COMPSTAGempty = '<components/>';
1365 static $COMPSTAGstart = '<components>';
1366 static $NEW = 'new';
1367 static $ALLCOMPS = [
1368 Vcalendar::VTIMEZONE,
1369 Vcalendar::STANDARD,
1370 Vcalendar::DAYLIGHT,
1371 Vcalendar::VEVENT,
1372 Vcalendar::VTODO,
1373 Vcalendar::VJOURNAL,
1374 Vcalendar::VFREEBUSY,
1375 Vcalendar::VALARM
1376 ];
1377 $len = strlen( $xml );
1378 $sx = 0;
1379 while(
1380 ((( $sx + 12 ) < $len ) &&
1381 ! StringFactory::startsWith( substr( $xml, $sx ), $PROPSTAGstart ) &&
1382 ! StringFactory::startsWith( substr( $xml, $sx ), $COMPSTAGstart )
1383 ) &&
1384 ((( $sx + 13 ) < $len ) &&
1385 ! StringFactory::startsWith( substr( $xml, $sx ), $PROPSTAGempty ) &&
1386 ! StringFactory::startsWith( substr( $xml, $sx ), $COMPSTAGempty ))) {
1387 $sx += 1;
1388 } // end while
1389 if(( $sx + 11 ) >= $len ) {
1390 return false;
1391 }
1392 if( StringFactory::startsWith( $xml, $PROPSTAGempty, $pos )) {
1393 $xml = substr( $xml, $pos );
1394 }
1395 elseif( StringFactory::startsWith( substr( $xml, $sx ), $PROPSTAGstart )) {
1396 $xml2 = self::XMLgetTagContent1( $xml, self::$properties, $endIx );
1397 self::XMLgetProps( $iCal, $xml2 );
1398 $xml = substr( $xml, $endIx );
1399 }
1400 if( StringFactory::startsWith( $xml, $COMPSTAGempty, $pos )) {
1401 $xml = substr( $xml, $pos );
1402 }
1403 elseif( StringFactory::startsWith( $xml, $COMPSTAGstart )) {
1404 $xml = self::XMLgetTagContent1( $xml, self::$components, $endIx );
1405 }
1406 while( ! empty( $xml )) {
1407 $xml2 = self::XMLgetTagContent2( $xml, $tagName, $endIx );
1408 $newCompMethod = $NEW . ucfirst( strtolower( $tagName ));
1409 if( Util::isCompInList( $tagName, $ALLCOMPS )) {
1410 $iCalComp = $iCal->{$newCompMethod}();
1411 self::XMLgetComps( $iCalComp, $xml2 );
1412 }
1413 $xml = substr( $xml, $endIx );
1414 } // end while( ! empty( $xml ))
1415 return $iCal;
1416 }
1417
1418 /**
1419 * Parse (rfc6321) XML into iCalcreator properties
1420 *
1421 * @param IcalInterface $iCalComp iCalcreator calendar/component instance
1422 * @param string $xml
1423 * @static
1424 * @since 2.30.3 - 2021-02-15
1425 */
1426 private static function XMLgetProps( IcalInterface $iCalComp, $xml )
1427 {
1428 static $VERSIONPRODID = null;
1429 static $PARAMENDTAG = '<parameters/>';
1430 static $PARAMTAG = '<parameters>';
1431 static $DATETAGST = '<date';
1432 static $PERIODTAG = '<period>';
1433 static $ATTENDEEPARKEYS = [
1434 Vcalendar::DELEGATED_FROM,
1435 Vcalendar::DELEGATED_TO,
1436 Vcalendar::MEMBER
1437 ];
1438 if( is_null( $VERSIONPRODID )) {
1439 $VERSIONPRODID = [
1440 Vcalendar::VERSION,
1441 Vcalendar::PRODID,
1442 ];
1443 }
1444 while( ! empty( $xml )) {
1445 $xml2 = self::XMLgetTagContent2( $xml, $propName, $endIx );
1446 $propName = strtoupper( $propName );
1447 if( empty( $xml2 ) && ( Util::$ZERO != $xml2 )) {
1448 if( StringFactory::isXprefixed( $propName )) {
1449 $iCalComp->setXprop( $propName );
1450 }
1451 else {
1452 $method = Vcalendar::getSetMethodName( $propName );
1453 $iCalComp->{$method}();
1454 }
1455 $xml = substr( $xml, $endIx );
1456 continue;
1457 }
1458 $params = [];
1459 if( StringFactory::startsWith( $xml2, $PARAMENDTAG, $pos )) {
1460 $xml2 = substr( $xml2, 13 );
1461 }
1462 elseif( StringFactory::startsWith( $xml2, $PARAMTAG )) {
1463 $xml3 = self::XMLgetTagContent1( $xml2, self::$PARAMETERS, $endIx2 );
1464 $endIx3 = 0;
1465 while( ! empty( $xml3 )) {
1466 $xml4 = self::XMLgetTagContent2( $xml3, $paramKey, $endIx3 );
1467 $pType = false; // skip parameter valueType
1468 $paramKey = strtoupper( $paramKey );
1469 if( in_array( $paramKey, $ATTENDEEPARKEYS )) {
1470 while( ! empty( $xml4 )) {
1471 $paramValue = self::XMLgetTagContent1(
1472 $xml4,
1473 self::$cal_address,
1474 $endIx4
1475 );
1476 if( ! isset( $params[$paramKey] )) {
1477 $params[$paramKey] = [ $paramValue ];
1478 }
1479 else {
1480 $params[$paramKey][] = $paramValue;
1481 }
1482 $xml4 = substr( $xml4, $endIx4 );
1483 } // end while
1484 } // end if( in_array( $paramKey, Util::$ATTENDEEPARKEYS ))
1485 else {
1486 $paramValue = html_entity_decode(
1487 self::XMLgetTagContent2(
1488 $xml4,
1489 $pType,
1490 $endIx4
1491 )
1492 );
1493 if( ! isset( $params[$paramKey] )) {
1494 $params[$paramKey] = $paramValue;
1495 }
1496 else {
1497 $params[$paramKey] .= Util::$COMMA . $paramValue;
1498 }
1499 }
1500 $xml3 = substr( $xml3, $endIx3 );
1501 } // end while
1502 $xml2 = substr( $xml2, $endIx2 );
1503 } // end elseif - parameters
1504 $valueType = false;
1505 $value = ( ! empty( $xml2 ) || ( Util::$ZERO == $xml2 ))
1506 ? self::XMLgetTagContent2( $xml2, $valueType, $endIx3 )
1507 : null;
1508 switch( $propName ) {
1509 case Vcalendar::URL : // fall through
1510 case Vcalendar::TZURL :
1511 $value = html_entity_decode( $value );
1512 break;
1513 case Vcalendar::EXDATE : // multiple single-date(-times) may exist
1514 // fall through
1515 case Vcalendar::RDATE :
1516 if( self::$period != $valueType ) {
1517 if( self::$date == $valueType ) {
1518 $params[Vcalendar::VALUE] = Vcalendar::DATE;
1519 }
1520 $t = [];
1521 while( ! empty( $xml2 ) &&
1522 ( StringFactory::startsWith( $xml2, $DATETAGST ))) {
1523 $t[] = self::XMLgetTagContent2( $xml2, $pType, $endIx4);
1524 $xml2 = substr( $xml2, $endIx4 );
1525 } // end while
1526 $value = $t;
1527 break;
1528 }
1529 // fall through
1530 case Vcalendar::FREEBUSY :
1531 if( Vcalendar::RDATE == $propName ) {
1532 $params[Vcalendar::VALUE] = Vcalendar::PERIOD;
1533 }
1534 $value = [];
1535 while( ! empty( $xml2 ) &&
1536 ( StringFactory::startsWith( $xml2, $PERIODTAG ))) {
1537 $xml3 = self::XMLgetTagContent1( $xml2, self::$period, $endIx4);
1538 $t = [];
1539 while( ! empty( $xml3 )) { // start - end/duration
1540 $t[] = self::XMLgetTagContent2( $xml3, $pType, $endIx5 );
1541 $xml3 = substr( $xml3, $endIx5 );
1542 } // end while
1543 $value[] = $t;
1544 $xml2 = substr( $xml2, $endIx4 );
1545 } // end while
1546 break;
1547 case Vcalendar::TZOFFSETTO :
1548 // fall through
1549 case Vcalendar::TZOFFSETFROM :
1550 $value = str_replace( Util::$COLON, null, $value );
1551 break;
1552 case Vcalendar::GEO :
1553 $tValue = [ Vcalendar::LATITUDE => $value ];
1554 $tValue[Vcalendar::LONGITUDE] = self::XMLgetTagContent1(
1555 substr( $xml2, $endIx3 ),
1556 Vcalendar::LONGITUDE,
1557 $endIx3
1558 );
1559 $value = $tValue;
1560 break;
1561 case Vcalendar::EXRULE :
1562 // fall through
1563 case Vcalendar::RRULE :
1564 $tValue = [ $valueType => $value ];
1565 $xml2 = substr( $xml2, $endIx3 );
1566 $valueType = false;
1567 while( ! empty( $xml2 )) {
1568 $t = self::XMLgetTagContent2( $xml2, $valueType, $endIx4 );
1569 switch( strtoupper( $valueType )) {
1570 case Vcalendar::FREQ :
1571 case Vcalendar::COUNT :
1572 case Vcalendar::UNTIL :
1573 case Vcalendar::INTERVAL :
1574 case Vcalendar::WKST :
1575 $tValue[$valueType] = $t;
1576 break;
1577 case Vcalendar::BYDAY :
1578 if( 2 == strlen( $t )) {
1579 $tValue[$valueType][] = [ Vcalendar::DAY => $t ];
1580 }
1581 else {
1582 $day = substr( $t, -2 );
1583 $key = substr( $t, 0, ( strlen( $t ) - 2 ));
1584 $tValue[$valueType][] = [ $key, Vcalendar::DAY => $day ];
1585 }
1586 break;
1587 default:
1588 $tValue[$valueType][] = $t;
1589 } // end switch
1590 $xml2 = substr( $xml2, $endIx4 );
1591 } // end while
1592 $value = $tValue;
1593 break;
1594 case Vcalendar::REQUEST_STATUS :
1595 $value = [
1596 self::$code => null,
1597 self::$description => null,
1598 self::$data => null
1599 ];
1600 while( ! empty( $xml2 )) {
1601 $t = html_entity_decode(
1602 self::XMLgetTagContent2(
1603 $xml2,
1604 $valueType,
1605 $endIx4 )
1606 );
1607 $value[$valueType] = $t;
1608 $xml2 = substr( $xml2, $endIx4 );
1609 } // end while
1610 break;
1611 default:
1612 switch( $valueType ) {
1613 case self::$uri :
1614 $value = html_entity_decode( $value );
1615 if( in_array( $propName, [ Vcalendar::ATTACH, Vcalendar::SOURCE ] )) {
1616 break;
1617 }
1618 $params[Vcalendar::VALUE] = Vcalendar::URI;
1619 break;
1620 case self::$binary :
1621 $params[Vcalendar::VALUE] = Vcalendar::BINARY;
1622 break;
1623 case self::$date :
1624 $params[Vcalendar::VALUE] = Vcalendar::DATE;
1625 break;
1626 case self::$date_time :
1627 $params[Vcalendar::VALUE] = Vcalendar::DATE_TIME;
1628 break;
1629 case self::$text :
1630 // fall through
1631 case self::$unknown :
1632 $value = html_entity_decode( $value );
1633 break;
1634 default :
1635 if( StringFactory::isXprefixed( $propName ) &&
1636 ( self::$unknown != strtolower( $valueType ))) {
1637 $params[Vcalendar::VALUE] = strtoupper( $valueType );
1638 }
1639 break;
1640 } // end switch
1641 break;
1642 } // end switch( $propName )
1643 $method = Vcalendar::getSetMethodName( $propName );
1644 switch( true ) {
1645 case ( Util::isPropInList( $propName, $VERSIONPRODID )) :
1646 break;
1647 case ( StringFactory::isXprefixed( $propName )) :
1648 $iCalComp->setXprop( $propName, $value, $params );
1649 break;
1650 case ( Vcalendar::FREEBUSY == $propName ) :
1651 $fbtype = isset( $params[Vcalendar::FBTYPE] )
1652 ? $params[Vcalendar::FBTYPE]
1653 : null;
1654 unset( $params[Vcalendar::FBTYPE] );
1655 $iCalComp->{$method}( $fbtype, $value, $params );
1656 break;
1657 case ( Vcalendar::GEO == $propName ) :
1658 $iCalComp->{$method}(
1659 $value[Vcalendar::LATITUDE],
1660 $value[Vcalendar::LONGITUDE],
1661 $params
1662 );
1663 break;
1664 case ( Vcalendar::REQUEST_STATUS == $propName ) :
1665 $iCalComp->{$method}(
1666 $value[self::$code],
1667 $value[self::$description],
1668 $value[self::$data],
1669 $params
1670 );
1671 break;
1672 default :
1673 if( empty( $value )
1674 && ( is_array( $value ) || ( Util::$ZERO > $value ))) {
1675 $value = null;
1676 }
1677 $iCalComp->{$method}( $value, $params );
1678 break;
1679 } // end switch
1680 $xml = substr( $xml, $endIx );
1681 } // end while( ! empty( $xml ))
1682 }
1683
1684 /**
1685 * Fetch a specific XML tag content
1686 *
1687 * @param string $xml
1688 * @param string $tagName
1689 * @param int $endIx
1690 * @return mixed
1691 * @static
1692 * @since 2.23.8 - 2017-04-17
1693 */
1694 private static function XMLgetTagContent1( $xml, $tagName, & $endIx = 0 ) {
1695 static $FMT0 = '<%s>';
1696 static $FMT1 = '<%s />';
1697 static $FMT2 = '<%s/>';
1698 static $FMT3 = '</%s>';
1699 $tagName = strtolower( $tagName );
1700 $strLen = strlen( $tagName );
1701 $xmlLen = strlen( $xml );
1702 $sx1 = 0;
1703 while( $sx1 < $xmlLen ) {
1704 if((( $sx1 + $strLen + 1 ) < $xmlLen ) &&
1705 ( sprintf( $FMT0, $tagName ) ==
1706 strtolower( substr( $xml, $sx1, ( $strLen + 2 ))))
1707 ) {
1708 break;
1709 }
1710 if((( $sx1 + $strLen + 3 ) < $xmlLen ) &&
1711 ( sprintf( $FMT1, $tagName ) ==
1712 strtolower( substr( $xml, $sx1, ( $strLen + 4 ))))
1713 ) {
1714 $endIx = $strLen + 5;
1715 return null; // empty tag
1716 }
1717 if((( $sx1 + $strLen + 2 ) < $xmlLen ) &&
1718 ( sprintf( $FMT2, $tagName ) ==
1719 strtolower( substr( $xml, $sx1, ( $strLen + 3 ))))
1720 ) {
1721 $endIx = $strLen + 4;
1722 return null; // empty tag
1723 }
1724 $sx1 += 1;
1725 } // end while...
1726 if( false === substr( $xml, $sx1, 1 )) {
1727 $endIx = ( empty( $sx )) ? 0 : $sx - 1; // ??
1728 return null;
1729 }
1730 $endTag = sprintf( $FMT3, $tagName );
1731 if( false === ( $pos = stripos( $xml, $endTag ))) { // missing end tag??
1732 $endIx = $xmlLen + 1;
1733 return null;
1734 }
1735 $endIx = $pos + $strLen + 3;
1736 return substr( $xml, ( $sx1 + $strLen + 2 ), ( $pos - $sx1 - 2 - $strLen ));
1737 }
1738
1739 /**
1740 * Fetch next (unknown) XML tagname AND content
1741 *
1742 * @param string $xml
1743 * @param string $tagName
1744 * @param int $endIx
1745 * @return mixed
1746 * @static
1747 * @since 2.23.8 - 2017-04-17
1748 */
1749 private static function XMLgetTagContent2( $xml, & $tagName, & $endIx )
1750 {
1751 static $LT = '<';
1752 static $CMTSTART = '<!--';
1753 static $EMPTYTAGEND = '/>';
1754 static $GT = '>';
1755 static $DURATION = 'duration';
1756 static $DURATIONTAG = '<duration>';
1757 static $DURENDTAG = '</duration>';
1758 static $FMTTAG = '</%s>';
1759 $xmlLen = strlen( $xml );
1760 $endIx = $xmlLen + 1; // just in case.. .
1761 $sx1 = 0;
1762 while( $sx1 < $xmlLen ) {
1763 if( $LT == substr( $xml, $sx1, 1 )) {
1764 if((( $sx1 + 3 ) < $xmlLen ) &&
1765 ( StringFactory::startsWith( substr( $xml, $sx1 ), $CMTSTART ))
1766 ) { // skip comment
1767 $sx1 += 1;
1768 }
1769 else {
1770 break;
1771 } // tagname start here
1772 }
1773 else {
1774 $sx1 += 1;
1775 }
1776 } // end while...
1777 $sx2 = $sx1;
1778 while( $sx2 < $xmlLen ) {
1779 if((( $sx2 + 1 ) < $xmlLen ) &&
1780 ( StringFactory::startsWith( substr( $xml, $sx2 ), $EMPTYTAGEND ))
1781 ) { // tag with no content
1782 $tagName = trim( substr( $xml, ( $sx1 + 1 ), ( $sx2 - $sx1 - 1 )));
1783 $endIx = $sx2 + 2;
1784 return null;
1785 }
1786 if( $GT == substr( $xml, $sx2, 1 )) // tagname ends here
1787 {
1788 break;
1789 }
1790 $sx2 += 1;
1791 } // end while...
1792 $tagName = substr( $xml, ( $sx1 + 1 ), ( $sx2 - $sx1 - 1 ));
1793 $endIx = $sx2 + 1;
1794 if( $sx2 >= $xmlLen ) {
1795 return null;
1796 }
1797 $strLen = strlen( $tagName );
1798 if(( $DURATION == $tagName ) &&
1799 ( false !== ( $pos1 = stripos( $xml, $DURATIONTAG, $sx1 + 1 ))) &&
1800 ( false !== ( $pos2 = stripos( $xml, $DURENDTAG, $pos1 + 1 ))) &&
1801 ( false !== ( $pos3 = stripos( $xml, $DURENDTAG, $pos2 + 1 ))) &&
1802 ( $pos1 < $pos2 ) && ( $pos2 < $pos3 )) {
1803 $pos = $pos3;
1804 }
1805 elseif( false === ( $pos = stripos( $xml, sprintf( $FMTTAG, $tagName ), $sx2 ))) {
1806 return null;
1807 }
1808 $endIx = $pos + $strLen + 3;
1809 return substr( $xml, ( $sx1 + $strLen + 2 ), ( $pos - $strLen - 2 ));
1810 }
1811 }
1812
1813