PluginProbe
Import Social Events / 1.8.7
Import Social Events v1.8.7
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 / IcalBase.php

IcalBase.php in Import Social Events 1.8.7, at includes/lib/icalcreator/src/IcalBase.php

937 lines 29.9 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.10
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 namespace Kigkonsult\Icalcreator;
31
32 use InvalidArgumentException;
33 use Kigkonsult\Icalcreator\Util\ParameterFactory;
34 use Kigkonsult\Icalcreator\Util\Util;
35
36 use function define;
37 use function defined;
38 use function array_change_key_case;
39 use function array_filter;
40 use function array_keys;
41 use function array_slice;
42 use function count;
43 use function ctype_digit;
44 use function get_object_vars;
45 use function is_array;
46 use function is_null;
47 use function is_object;
48 use function key;
49 use function ksort;
50 use function property_exists;
51 use function sprintf;
52 use function strtolower;
53 use function strtoupper;
54 use function trim;
55 use function ucfirst;
56
57 /**
58 * Do NOT alter or remove the constant!!
59 */
60 if( ! defined( 'ICALCREATOR_VERSION' )) {
61 define( 'ICALCREATOR_VERSION', 'iCalcreator 2.30.10' );
62 }
63
64 /**
65 * iCalcreator base class
66 *
67 * Properties and methods shared by Vcalendar and CalendarComponents
68 *
69 * @author Kjell-Inge Gustafsson, kigkonsult <ical@kigkonsult.se>
70 * @since 2.29.4 - 2019-07-01
71 */
72 abstract class IcalBase implements IcalInterface
73 {
74 use Traits\X_PROPtrait;
75
76 /**
77 * @var string
78 */
79 protected static $INDEX = 'INDEX';
80
81 /**
82 * @var array iCal V*-component collection
83 * @usedby IcalBase+Vcalendar+SelectFactory
84 */
85 public static $VCOMPS = [
86 Vcalendar::VEVENT,
87 Vcalendar::VTODO,
88 Vcalendar::VJOURNAL,
89 Vcalendar::VFREEBUSY
90 ];
91
92 /**
93 * @var array iCal timezone component collection
94 * @usedby DTSTARTtrait+RexdateFactory
95 */
96 public static $TZCOMPS = [
97 Vcalendar::VTIMEZONE,
98 Vcalendar::STANDARD,
99 Vcalendar::DAYLIGHT
100 ];
101
102 /**
103 * @var array iCal component collection
104 */
105 protected static $CALCOMPS = [
106 Vcalendar::VEVENT,
107 Vcalendar::VTODO,
108 Vcalendar::VJOURNAL,
109 Vcalendar::VFREEBUSY,
110 Vcalendar::VALARM,
111 Vcalendar::VTIMEZONE
112 ];
113
114 /**
115 * @var array iCal sub-component collection
116 * @usedby CalendarComponent+IcalBase+DTSTAMPtrait
117 */
118 protected static $SUBCOMPS = [
119 Vcalendar::VALARM,
120 Vcalendar::VTIMEZONE,
121 Vcalendar::STANDARD,
122 Vcalendar::DAYLIGHT
123 ];
124
125 /**
126 * @var array iCal component multiple property sub-collection
127 * @usedby CalendarComponent + Vcalendar + SelectFactory + SortFactory
128 */
129 public static $MPROPS1 = [
130 Vcalendar::ATTENDEE, Vcalendar::CATEGORIES, Vcalendar::CONTACT,
131 Vcalendar::RELATED_TO, Vcalendar::RESOURCES,
132 ];
133
134 /**
135 * @var array iCal component multiple property collection
136 * @since 2.27.20 - 2019-05-20
137 * @usedby IcalBase
138 */
139 protected static $MPROPS2 = [
140 Vcalendar::ATTACH, Vcalendar::ATTENDEE, Vcalendar::CATEGORIES,
141 Vcalendar::COMMENT, Vcalendar::CONTACT, Vcalendar::DESCRIPTION,
142 Vcalendar::EXDATE, Vcalendar::FREEBUSY, Vcalendar::RDATE,
143 Vcalendar::RELATED_TO, Vcalendar::RESOURCES,
144 Vcalendar::REQUEST_STATUS, Vcalendar::TZNAME, Vcalendar::X_PROP,
145 ];
146
147 /**
148 * @var array iCal component misc. property collection
149 * @usedby Vcalendar + SelectFactory
150 */
151 public static $OTHERPROPS = [
152 Vcalendar::ATTENDEE, Vcalendar::CATEGORIES, Vcalendar::CONTACT, Vcalendar::LOCATION,
153 Vcalendar::ORGANIZER, Vcalendar::PRIORITY, Vcalendar::RELATED_TO, Vcalendar::RESOURCES,
154 Vcalendar::STATUS, Vcalendar::SUMMARY, Vcalendar::UID, Vcalendar::URL,
155 ];
156
157 /**
158 * @var array iCal component TEXT properties
159 * @usedby Vcalendar + CalendarComponent
160 */
161 protected static $TEXTPROPS = [
162 self::CATEGORIES,
163 self::COLOR,
164 self::COMMENT,
165 self::DESCRIPTION,
166 self::NAME,
167 self::SUMMARY,
168 ];
169
170 /**
171 * @var string
172 */
173 protected static $FMTERRPROPFMT = 'Invalid %s input format (%s)';
174
175 /**
176 * @var array
177 */
178 protected static $ALTRPLANGARR = [ self::ALTREP, self::LANGUAGE ];
179
180 /**
181 * @var array container for sub-components
182 */
183 protected $components = [];
184
185 /**
186 * @var array $unparsed calendar/components in 'raw' text...
187 */
188 protected $unparsed = null;
189
190 /**
191 * @var array $config configuration with defaults
192 */
193 protected $config = [
194 self::ALLOWEMPTY => true,
195 ];
196
197 /**
198 * @var string component type
199 */
200 protected $compType = null;
201
202 /**
203 * @var array component index
204 */
205 protected $compix = [];
206
207 /**
208 * @var array get multi property index
209 */
210 protected $propIx = [];
211
212 /**
213 * @var array delete multi property index
214 */
215 protected $propDelIx = [];
216
217 /**
218 * __clone method
219 *
220 * @link https://php.net/manual/en/language.oop5.cloning.php#116329
221 * @since 2.26 - 2018-11-10
222 */
223 public function __clone()
224 {
225 $object_vars = get_object_vars( $this );
226 foreach( $object_vars as $attr_name => $attr_value ) {
227 if( is_object( $this->$attr_name )) {
228 $this->$attr_name = clone $this->$attr_name;
229 }
230 else if( is_array( $this->$attr_name )) {
231 // Note: This copies only one dimension arrays
232 foreach( $this->$attr_name as & $attr_array_value ) {
233 if( is_object( $attr_array_value )) {
234 $attr_array_value = clone $attr_array_value;
235 }
236 unset( $attr_array_value );
237 }
238 }
239 } // end foreach
240 $this->compix = [];
241 $this->propIx = [];
242 $this->propDelIx = [];
243 }
244
245 /**
246 * Reset internal counters
247 *
248 * @return static
249 * @since 2.27.14 - 2019-03-11
250 */
251 public function reset()
252 {
253 $this->compix = [];
254 return $this;
255 }
256
257 /**
258 * Return component type
259 *
260 * @return string
261 * @since 2.27.2 - 2018-12-21
262 */
263 public function getCompType()
264 {
265 return $this->compType;
266 }
267
268 /**
269 * Remove Vcalendar/component config key value
270 *
271 * @param string $key
272 * @return static
273 * @since 2.27.14 - 2019-02-04
274 */
275 public function deleteConfig( $key )
276 {
277 $key = strtoupper( $key );
278 if( isset( $this->config[$key] )) {
279 unset( $this->config[$key] );
280 }
281 return $this;
282 }
283
284 /**
285 * Return Vcalendar/component config value, false on not found
286 *
287 * @param mixed $config
288 * @return mixed bool false on not found or empty
289 * @since 2.29.4 - 2019-07-02
290 */
291 public function getConfig( $config = null )
292 {
293 static $LCORDNO = 'ordno';
294 static $LCTYPE = 'type';
295 static $LCUID = 'uid';
296 static $LCPROPS = 'props';
297 static $LCSUB = 'sub';
298 if( is_null( $config )) {
299 $output = [];
300 $output[self::ALLOWEMPTY] = $this->getConfig( self::ALLOWEMPTY );
301 if( false !== ( $cfg = $this->getConfig( self::LANGUAGE ))) {
302 $output[self::LANGUAGE] = $cfg;
303 }
304 $output[self::UNIQUE_ID] = $this->getConfig( self::UNIQUE_ID );
305 return $output;
306 }
307 switch( strtoupper( $config )) {
308 case self::ALLOWEMPTY:
309 if( ! isset( $this->config[self::ALLOWEMPTY] )) { // default
310 $this->config[self::ALLOWEMPTY] = true;
311 }
312 return $this->config[self::ALLOWEMPTY];
313 break;
314 case self::UNIQUE_ID:
315 if( isset( $this->config[self::UNIQUE_ID] )) {
316 return $this->config[self::UNIQUE_ID];
317 }
318 break;
319 case self::LANGUAGE: // get language for calendar component as defined in [RFC 1766]
320 if( isset( $this->config[self::LANGUAGE] )) {
321 return $this->config[self::LANGUAGE];
322 }
323 break;
324 case self::COMPSINFO:
325 $this->compix = [];
326 $info = [];
327 if( ! empty( $this->components )) {
328 foreach( $this->components as $cix => $component ) {
329 if( empty( $component )) {
330 continue;
331 }
332 $info[$cix][$LCORDNO] = $cix + 1;
333 $info[$cix][$LCTYPE] = $component->getCompType();
334 if( ! Util::isCompInList( $component->getCompType(), self::$SUBCOMPS )) {
335 $info[$cix][$LCUID] = $component->getUid();
336 }
337 $info[$cix][$LCPROPS] = $component->getConfig( self::PROPINFO );
338 $info[$cix][$LCSUB] = $component->getConfig( self::COMPSINFO );
339 } // end foreach
340 }
341 return $info;
342 break;
343 case self::PROPINFO:
344 return $this->getpropInfo();
345 break;
346 case self::SETPROPERTYNAMES:
347 return array_keys( $this->getConfig( self::PROPINFO ));
348 break;
349 default :
350 break;
351 } // end switch
352 return false;
353 }
354
355 /**
356 * Return array( propertyName => count )
357 *
358 * @return array
359 * @since 2.29.05 - 2019-06-20
360 */
361 protected function getpropInfo()
362 {
363 static $PROPNAMES = [
364 self::ACTION, self::ATTACH, self::ATTENDEE, self::CATEGORIES,
365 self::KLASS, self::COLOR, self::COMMENT,
366 self::COMPLETED, self::CONFERENCE, self::CONTACT,
367 self::CREATED, self::DESCRIPTION, self::DTEND, self::DTSTAMP,
368 self::DTSTART, self::DUE, self::DURATION, self::EXDATE, self::EXRULE,
369 self::FREEBUSY, self::GEO, self::IMAGE,
370 self::LAST_MODIFIED, self::LOCATION, self::NAME,
371 self::ORGANIZER, self::PERCENT_COMPLETE, self::PRIORITY,
372 self::RECURRENCE_ID, self::REFRESH_INTERVAL, self::RELATED_TO, self::REPEAT,
373 self::REQUEST_STATUS, self::RESOURCES, self::RRULE, self::RDATE,
374 self::SEQUENCE, self::SOURCE, self::STATUS, self::SUMMARY, self::TRANSP,
375 self::TRIGGER, self::TZNAME, self::TZID, self::TZOFFSETFROM,
376 self::TZOFFSETTO, self::TZURL, self::UID, self::URL, self::X_PROP,
377 ];
378 if( ! Util::isCompInList( $this->getCompType(), self::$SUBCOMPS )) {
379 $this->getUid();
380 $this->getDtstamp();
381 }
382 $output = [];
383 foreach( $PROPNAMES as $propName ) {
384 $propName2 = IcalBase::getInternalPropName( $propName );
385 switch( true ) {
386 case ( ! property_exists( $this, $propName2 )) :
387 break;
388 case ( empty( $this->{$propName2} )) :
389 break;
390 case ( self::X_PROP == $propName ) :
391 foreach( array_keys( $this->{$propName2}) as $propName3 ) {
392 $output[$propName3] = 1;
393 }
394 break;
395 case ( Util::isPropInList( $propName, self::$MPROPS2 )) :
396 $output[$propName] = count( $this->{$propName2} );
397 break;
398 default :
399 $output[$propName] = 1;
400 } // end switch
401 } // end foreach
402 return $output;
403 }
404
405 /**
406 * Set Vcalendar/component config
407 *
408 * @param mixed $config
409 * @param string $value
410 * @param bool $softUpdate
411 * @return static
412 * @throws InvalidArgumentException
413 * @since 2.29.4 - 2019-07-02
414 */
415 public function setConfig( $config, $value = null, $softUpdate = null )
416 {
417 static $ERRMSG9 = 'Invalid config value %s';
418 $isComponent = ( ! property_exists(
419 $this,
420 self::getInternalPropName( self::PRODID )
421 ));
422 if( is_array( $config )) {
423 $config = array_change_key_case( $config, CASE_UPPER );
424 foreach( $config as $cKey => $cValue ) {
425 $this->setConfig( $cKey, $cValue );
426 }
427 return $this;
428 }
429 $key = strtoupper( $config );
430 $subCfg = null;
431 switch( true ) {
432 case ( self::ALLOWEMPTY == $key ) :
433 $this->config[self::ALLOWEMPTY] = $value;
434 $subCfg = [ self::ALLOWEMPTY => $value ];
435 break;
436 case ( self::LANGUAGE == $key ) :
437 // set language for calendar component as defined in [RFC 1766]
438 $value = trim( $value );
439 if( empty( $this->config[self::LANGUAGE] ) || ! $softUpdate ) { // ??
440 $this->config[self::LANGUAGE] = $value;
441 }
442 $subCfg = [ self::LANGUAGE => $value ];
443 break;
444 case ( self::UNIQUE_ID == $key ) :
445 $value = trim( $value );
446 $this->config[self::UNIQUE_ID] = $value;
447 $subCfg = [ self::UNIQUE_ID => $value ];
448 break;
449 case ( $isComponent ) :
450 break;
451 default: // any invalid config key.. .
452 throw new InvalidArgumentException( sprintf( $ERRMSG9, $config )); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
453 } // end switch
454 if( ! empty( $subCfg ) && ! empty( $this->components )) {
455 foreach( $subCfg as $cfgkey => $cfgValue ) {
456 foreach( $this->components as $cix => $component ) {
457 $this->components[$cix]->setConfig( $cfgkey, $cfgValue, true );
458 }
459 }
460 }
461 return $this;
462 }
463
464 /**
465 * Assert empty value
466 *
467 * @param mixed $value
468 * @param string $propName
469 * @throws InvalidArgumentException
470 * @since 2.27.1 - 2018-12-12
471 */
472 protected function assertEmptyValue( $value, $propName )
473 {
474 static $ERRMSG = 'Empty %s value not allowed';
475 if( empty( $value ) && ! $this->getConfig( self::ALLOWEMPTY )) {
476 throw new InvalidArgumentException( sprintf( $ERRMSG, $propName )); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
477 }
478 }
479
480 /**
481 * Return index
482 *
483 * @param array $indexArr
484 * @param mixed $propName
485 * @param int $index
486 * @return bool true on success
487 * @since 2.27.1 - 2018-12-15
488 */
489 protected static function getIndex( array & $indexArr, $propName, $index = null )
490 {
491 if( is_null( $index )) {
492 $index = ( isset( $indexArr[$propName] )) ? $indexArr[$propName] + 2 : 1;
493 }
494 $index -= 1;
495 $indexArr[$propName] = $index;
496 return $index;
497 }
498
499 /**
500 * Return internal name for property
501 *
502 * @param string $propName
503 * @return string
504 * @since 2.27.1 - 2018-12-16
505 */
506 protected static function getInternalPropName( $propName )
507 {
508 $internalName = strtolower( $propName );
509 if( false !== strpos( $internalName, Util::$MINUS )) {
510 $internalName = implode( explode( Util::$MINUS, $internalName ));
511 }
512 return $internalName;
513 }
514
515 /**
516 * Return method from format and propName
517 *
518 * @param string $format
519 * @param string $propName
520 * @return string
521 * @since 2.27.14 - 2019-02-18
522 */
523 private static function getMethodName( $format, $propName )
524 {
525 return sprintf( $format, ucfirst( self::getInternalPropName( $propName )));
526 }
527
528 /**
529 * Return name for property delete-method
530 *
531 * @param string $propName
532 * @return string
533 * @since 2.27.1 - 2019-01-17
534 */
535 public static function getCreateMethodName( $propName )
536 {
537 static $FMT = 'create%s';
538 return self::getMethodName( $FMT, $propName );
539 }
540
541 /**
542 * Return name for property delete-method
543 *
544 * @param string $propName
545 * @return string
546 * @since 2.27.1 - 2018-12-12
547 */
548 public static function getDeleteMethodName( $propName )
549 {
550 static $FMT = 'delete%s';
551 return self::getMethodName( $FMT, $propName );
552 }
553
554 /**
555 * Return name for property get-method
556 *
557 * @param string $propName
558 * @return string
559 * @since 2.27.1 - 2018-12-12
560 */
561 public static function getGetMethodName( $propName )
562 {
563 static $FMT = 'get%s';
564 return self::getMethodName( $FMT, $propName );
565 }
566
567 /**
568 * Return name for property set-method
569 *
570 * @param string $propName
571 * @return string
572 * @since 2.27.1 - 2018-12-16
573 */
574 public static function getSetMethodName( $propName )
575 {
576 static $FMT = 'set%s';
577 return self::getMethodName( $FMT, $propName );
578 }
579
580 /**
581 * Recount property propIx, used at consecutive getProperty calls
582 *
583 * @param array $propArr component (multi-)property
584 * @param int $propIx getter counter
585 * @return bool true
586 * @since 2.27.1 - 2018-12-15
587 */
588 protected static function recountMvalPropix( array $propArr, & $propIx )
589 {
590 if( empty( $propArr )) {
591 return false;
592 }
593 $last = key( array_slice( $propArr, -1, 1, true ));
594 while( ! isset( $propArr[$propIx] ) && ( $last > $propIx )) {
595 $propIx++;
596 }
597 return true;
598 }
599
600 /**
601 * Check index and set (an indexed) content in a multiple value array
602 *
603 * @param array $valArr
604 * @param mixed $value
605 * @param array $params
606 * @param array $defaults
607 * @param int $index
608 * @since 2.22.23 - 2017-04-08
609 */
610 protected static function setMval(
611 & $valArr,
612 $value,
613 $params = null,
614 $defaults = null,
615 $index = null
616 ) {
617 if( ! is_array( $valArr )) {
618 $valArr = [];
619 }
620 if( empty( $params )) {
621 $params = [];
622 }
623 $params = ParameterFactory::setParams( $params, $defaults );
624 if( is_null( $index )) { // i.e. next
625 $valArr[] = [
626 Util::$LCvalue => $value,
627 Util::$LCparams => $params,
628 ];
629 return;
630 }
631 $index = $index - 1;
632 if( isset( $valArr[$index] )) { // replace
633 $valArr[$index] = [
634 Util::$LCvalue => $value,
635 Util::$LCparams => $params,
636 ];
637 return;
638 }
639 $valArr[$index] = [
640 Util::$LCvalue => $value,
641 Util::$LCparams => $params,
642 ];
643 ksort( $valArr ); // order
644 }
645
646 /**
647 * Delete calendar component multiProp property[ix]
648 *
649 * @param array $multiProp component (multi-)property
650 * @param mixed $propName
651 * @param int $propDelIx specific property in case of multiply occurrence
652 * @return bool true on success
653 * @since 2.27.1 - 2018-12-15
654 */
655 protected function deletePropertyM( & $multiProp, $propName, $propDelIx = null )
656 {
657 if( empty( $multiProp )) {
658 unset( $this->propDelIx[$propName] );
659 return false;
660 }
661 if( false === $propDelIx ) {
662 $propDelIx = null; // tidy up, altered default value
663 }
664 $propDelIx = self::getIndex( $this->propDelIx, $propName, $propDelIx );
665 if( isset( $multiProp[$propDelIx] )) {
666 unset( $multiProp[$propDelIx] );
667 }
668 if( empty( $multiProp )) {
669 $multiProp = null;
670 unset( $this->propDelIx[$propName] );
671 return false;
672 }
673 return true;
674 }
675
676 /**
677 * Get calendar component multpProp property
678 *
679 * @param array $multiProp component (multi-)property
680 * @param string $propName
681 * @param int $propIx specific property in case of multiply occurrence
682 * @param bool $inclParam
683 * @return bool|array
684 * @since 2.27.1 - 2018-12-15
685 */
686 protected function getPropertyM(
687 $multiProp,
688 $propName,
689 $propIx = null,
690 $inclParam = false
691 ) {
692 if( empty( $multiProp )) {
693 unset( $this->propIx[$propName] );
694 return false;
695 }
696 if( false === $propIx ) {
697 $propIx = null; // tidy up, altered default value
698 }
699 $propIx = self::getIndex( $this->propIx, $propName, $propIx );
700 if( ! self::recountMvalPropix( $multiProp, $propIx )) {
701 unset( $this->propIx[$propName] );
702 return false;
703 }
704 $this->propIx[$propName] = $propIx;
705 if( ! isset( $multiProp[$propIx] )) {
706 unset( $this->propIx[$propName] );
707 return false;
708 }
709 return ( $inclParam )
710 ? $multiProp[$propIx]
711 : $multiProp[$propIx][Util::$LCvalue];
712 }
713
714 /**
715 * Return number of components
716 *
717 * @return int
718 * @since 2.23.5 - 2017-04-13
719 */
720 public function countComponents()
721 {
722 return ( empty( $this->components )) ? 0 : count( $this->components );
723 }
724
725
726 /**
727 * Delete calendar subcomponent from component container
728 *
729 * @param mixed $arg1 ordno / component type / component uid
730 * @param mixed $arg2 ordno if arg1 = component type
731 * @return bool true on success, false on not found (last one deleted)
732 * @since 2.26.14 - 2019-02-25
733 * @todo Exception mgnt on unknown component
734 */
735 public function deleteComponent( $arg1, $arg2 = false )
736 {
737 if( ! isset( $this->components )) {
738 return false;
739 }
740 $argType = $index = null;
741 if( ctype_digit((string) $arg1 )) {
742 $argType = self::$INDEX;
743 $index = (int) $arg1 - 1;
744 }
745 elseif( property_exists( $this, self::getInternalPropName( self::PRODID )) &&
746 ( Util::isCompInList( $arg1, self::$CALCOMPS ) &&
747 ( 0 != strcasecmp( $arg1, self::VALARM )))) {
748 $argType = ucfirst( strtolower( $arg1 ));
749 $index = ( ! empty( $arg2 ) && ctype_digit((string) $arg2 ))
750 ? (( int ) $arg2 - 1 )
751 : 0;
752 }
753 $cix2dC = 0;
754 $remove = false;
755 foreach( $this->components as $cix => $component ) {
756 if(( self::$INDEX == $argType ) && ( $index == $cix )) {
757 unset( $this->components[$cix] );
758 $remove = true;
759 break;
760 }
761 elseif( $argType == $component->getCompType()) {
762 if( $index == $cix2dC ) {
763 unset( $this->components[$cix] );
764 $argType = strtolower( $argType );
765 if( isset( $this->compix[$argType] )) {
766 unset( $this->compix[$argType] );
767 }
768 $remove = true;
769 break;
770 }
771 $cix2dC++;
772 }
773 elseif( ! $argType &&
774 ! Util::isCompInList( $component->getCompType(), self::$SUBCOMPS ) &&
775 ( $arg1 == $component->getUid())) {
776 unset( $this->components[$cix] );
777 $remove = true;
778 break;
779 }
780 } // end foreach( $this->components as $cix => $component )
781 if( $remove ) {
782 $this->components = array_filter( $this->components );
783 return true;
784 }
785 return false;
786 }
787
788 /**
789 * Return next component index
790 *
791 * @return int
792 * @since 2.27.2 - 2018-12-21
793 */
794 protected function getNextComponentIndex()
795 {
796 return ( empty( $this->components ))
797 ? 0
798 : key( array_slice( $this->components, -1, 1, true )) + 1;
799 }
800
801 /**
802 * Add calendar component as subcomponent to container for subcomponents
803 *
804 * @param CalendarComponent $component
805 * @param mixed $arg1 ordno/component type/ component uid
806 * @param mixed $arg2 ordno if arg1 = component type
807 * @throws InvalidArgumentException
808 * @return static
809 * @since 2.27.3 - 2018-12-21
810 */
811 public function setComponent(
812 CalendarComponent $component,
813 $arg1 = false,
814 $arg2 = false
815 ) {
816 $component->setConfig( $this->getConfig(), false, true );
817 if( ! Util::isCompInList( $component->getCompType(), self::$SUBCOMPS )) {
818 /* make sure dtstamp and uid is set */
819 $component->getUid();
820 $component->getDtstamp();
821 }
822 if( ! $arg1 ) { // plain insert, last in chain
823 self::assertComponents( $this, $component );
824 $this->components[] = clone $component;
825 return $this;
826 }
827 $argType = $index = null;
828 if( ctype_digit((string) $arg1 )) { // index insert/replace
829 $argType = self::$INDEX;
830 $index = (int) $arg1 - 1;
831 }
832 elseif( Util::isCompInList( $arg1, self::$CALCOMPS )) {
833 $argType = ucfirst( strtolower( $arg1 ));
834 $index = ( ctype_digit((string) $arg2 )) ? ((int) $arg2 ) - 1 : 0;
835 }
836 // else if arg1 is set, arg1 must be an UID
837 $cix2sC = 0;
838 foreach( $this->components as $cix => $component2 ) {
839 if( empty( $component2 )) {
840 continue;
841 }
842 if(( self::$INDEX == $argType ) && ( $index == $cix )) {
843 // index insert/replace
844 $this->components[$cix] = clone $component;
845 return $this;
846 }
847 elseif( $argType == $component2->getCompType()) {
848 // component Type index insert/replace
849 if( $index == $cix2sC ) {
850 $this->components[$cix] = clone $component;
851 return $this;
852 }
853 $cix2sC++;
854 }
855 elseif( ! $argType &&
856 ! Util::isCompInList( $component2->getCompType(), self::$SUBCOMPS ) &&
857 ( $arg1 == $component2->getUid())) { // UID insert/replace
858 $this->components[$cix] = clone $component;
859 return $this;
860 }
861 } // end foreach
862 if( self::$INDEX == $argType ) { // arg1=index and not found.. . insert at index .. .
863 $this->components[$index] = clone $component;
864 ksort( $this->components, SORT_NUMERIC );
865 }
866 else { /* not found.. . insert last in chain anyway .. .*/
867 $this->components[] = clone $component;
868 }
869 return $this;
870 }
871
872 /**
873 * Assert base components
874 *
875 * @param IcalBase $component
876 * @throws InvalidArgumentException
877 * @since 2.27.6 - 2018-12-28
878 */
879 protected static function assertBaseComponents( IcalBase $component )
880 {
881 static $ERRMSG = 'Invalid component type \'%s\'';
882 $compType = $component->getCompType();
883 if( ! Util::isCompInList( $compType, self::$VCOMPS ) &&
884 ( self::VTIMEZONE != $compType )) {
885 throw new InvalidArgumentException( sprintf( $ERRMSG, $compType )); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
886 }
887 }
888
889 /**
890 * Assert components
891 *
892 * @param IcalBase $comp
893 * @param CalendarComponent $subComp
894 * @throws InvalidArgumentException
895 * @since 2.27.6 - 2018-12-28
896 */
897 private static function assertComponents(
898 IcalBase $comp,
899 CalendarComponent $subComp
900 ) {
901 static $MSG = 'Unknown component %s';
902 $type = $comp->getCompType();
903 $subType = $subComp->getCompType();
904 switch( $type ) {
905 case Vcalendar::VCALENDAR :
906 self::assertBaseComponents( $subComp );
907 break;
908 case self::VTIMEZONE :
909 switch( $subType ) {
910 case self::STANDARD :
911 break 2;
912 case self::DAYLIGHT :
913 break 2;
914 default :
915 throw new InvalidArgumentException( sprintf( $MSG, $subType )); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
916 }
917 break;
918 case self::VEVENT :
919 // fall through
920 case self::VTODO :
921 switch( $subType ) {
922 case self::VALARM :
923 break 2;
924 default :
925 throw new InvalidArgumentException( sprintf( $MSG, $subType )); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
926 }
927 break;
928 case self::VFREEBUSY :
929 break;
930 case self::VJOURNAL :
931 break;
932 default :
933 throw new InvalidArgumentException( sprintf( $MSG, $type )); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
934 } // end switch
935 }
936 }
937