Update libs
This commit is contained in:
parent
3d1ca79209
commit
b95de25ee8
2 changed files with 230 additions and 204 deletions
|
@ -32,7 +32,7 @@ class Event
|
||||||
/**
|
/**
|
||||||
* https://www.kanzaki.com/docs/ical/duration.html
|
* https://www.kanzaki.com/docs/ical/duration.html
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
public $duration;
|
public $duration;
|
||||||
|
|
||||||
|
@ -81,14 +81,14 @@ class Event
|
||||||
/**
|
/**
|
||||||
* https://www.kanzaki.com/docs/ical/description.html
|
* https://www.kanzaki.com/docs/ical/description.html
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
public $description;
|
public $description;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://www.kanzaki.com/docs/ical/location.html
|
* https://www.kanzaki.com/docs/ical/location.html
|
||||||
*
|
*
|
||||||
* @var string
|
* @var string|null
|
||||||
*/
|
*/
|
||||||
public $location;
|
public $location;
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ class Event
|
||||||
*
|
*
|
||||||
* @var array<string, mixed>
|
* @var array<string, mixed>
|
||||||
*/
|
*/
|
||||||
private $additionalProperties = [];
|
public $additionalProperties = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the Event object
|
* Creates the Event object
|
||||||
|
@ -250,10 +250,15 @@ class Event
|
||||||
*/
|
*/
|
||||||
protected static function snakeCase($input, $glue = '_', $separator = '-')
|
protected static function snakeCase($input, $glue = '_', $separator = '-')
|
||||||
{
|
{
|
||||||
$input = preg_split('/(?<=[a-z])(?=[A-Z])/x', $input);
|
$inputSplit = preg_split('/(?<=[a-z])(?=[A-Z])/x', $input);
|
||||||
$input = implode($glue, $input);
|
|
||||||
$input = str_replace($separator, $glue, $input);
|
|
||||||
|
|
||||||
return strtolower($input);
|
if ($inputSplit === false) {
|
||||||
|
return $input;
|
||||||
|
}
|
||||||
|
|
||||||
|
$inputSplit = implode($glue, $inputSplit);
|
||||||
|
$inputSplit = str_replace($separator, $glue, $inputSplit);
|
||||||
|
|
||||||
|
return strtolower($inputSplit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
411
ICal/ICal.php
411
ICal/ICal.php
|
@ -517,7 +517,7 @@ class ICal
|
||||||
|
|
||||||
// Fallback to use the system default time zone
|
// Fallback to use the system default time zone
|
||||||
if (!isset($this->defaultTimeZone) || !$this->isValidTimeZoneId($this->defaultTimeZone)) {
|
if (!isset($this->defaultTimeZone) || !$this->isValidTimeZoneId($this->defaultTimeZone)) {
|
||||||
$this->defaultTimeZone = date_default_timezone_get();
|
$this->defaultTimeZone = $this->getDefaultTimeZone(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ideally you would use `PHP_INT_MIN` from PHP 7
|
// Ideally you would use `PHP_INT_MIN` from PHP 7
|
||||||
|
@ -553,7 +553,7 @@ class ICal
|
||||||
{
|
{
|
||||||
$string = str_replace(array("\r\n", "\n\r", "\r"), "\n", $string);
|
$string = str_replace(array("\r\n", "\n\r", "\r"), "\n", $string);
|
||||||
|
|
||||||
if (empty($this->cal)) {
|
if ($this->cal === array()) {
|
||||||
$lines = explode("\n", $string);
|
$lines = explode("\n", $string);
|
||||||
|
|
||||||
$this->initLines($lines);
|
$this->initLines($lines);
|
||||||
|
@ -572,7 +572,7 @@ class ICal
|
||||||
*/
|
*/
|
||||||
public function initFile($file)
|
public function initFile($file)
|
||||||
{
|
{
|
||||||
if (empty($this->cal)) {
|
if ($this->cal === array()) {
|
||||||
$lines = $this->fileOrUrl($file);
|
$lines = $this->fileOrUrl($file);
|
||||||
|
|
||||||
$this->initLines($lines);
|
$this->initLines($lines);
|
||||||
|
@ -640,15 +640,16 @@ class ICal
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->disableCharacterReplacement) {
|
if (!$this->disableCharacterReplacement) {
|
||||||
$line = $this->cleanData($line);
|
$line = str_replace(array(
|
||||||
}
|
' ',
|
||||||
|
"\t",
|
||||||
$add = $this->keyValueFromString($line);
|
"\xc2\xa0", // Non-breaking space
|
||||||
|
), ' ', $line);
|
||||||
if ($add === false) {
|
|
||||||
continue;
|
$line = $this->cleanCharacters($line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$add = $this->keyValueFromString($line);
|
||||||
$keyword = $add[0];
|
$keyword = $add[0];
|
||||||
$values = $add[1]; // May be an array containing multiple values
|
$values = $add[1]; // May be an array containing multiple values
|
||||||
|
|
||||||
|
@ -679,8 +680,8 @@ class ICal
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// https://www.kanzaki.com/docs/ical/vevent.html
|
|
||||||
case 'BEGIN:VEVENT':
|
case 'BEGIN:VEVENT':
|
||||||
|
// https://www.kanzaki.com/docs/ical/vevent.html
|
||||||
if (!is_array($value)) {
|
if (!is_array($value)) {
|
||||||
$this->eventCount++;
|
$this->eventCount++;
|
||||||
}
|
}
|
||||||
|
@ -689,8 +690,8 @@ class ICal
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// https://www.kanzaki.com/docs/ical/vfreebusy.html
|
|
||||||
case 'BEGIN:VFREEBUSY':
|
case 'BEGIN:VFREEBUSY':
|
||||||
|
// https://www.kanzaki.com/docs/ical/vfreebusy.html
|
||||||
if (!is_array($value)) {
|
if (!is_array($value)) {
|
||||||
$this->freeBusyIndex++;
|
$this->freeBusyIndex++;
|
||||||
}
|
}
|
||||||
|
@ -754,7 +755,7 @@ class ICal
|
||||||
$this->processRecurrences();
|
$this->processRecurrences();
|
||||||
|
|
||||||
// Apply changes to altered recurrence instances
|
// Apply changes to altered recurrence instances
|
||||||
if (!empty($this->alteredRecurrenceInstances)) {
|
if ($this->alteredRecurrenceInstances !== array()) {
|
||||||
$events = $this->cal['VEVENT'];
|
$events = $this->cal['VEVENT'];
|
||||||
|
|
||||||
foreach ($this->alteredRecurrenceInstances as $alteredRecurrenceInstance) {
|
foreach ($this->alteredRecurrenceInstances as $alteredRecurrenceInstance) {
|
||||||
|
@ -787,7 +788,7 @@ class ICal
|
||||||
{
|
{
|
||||||
$events = $this->cal['VEVENT'];
|
$events = $this->cal['VEVENT'];
|
||||||
|
|
||||||
if (!empty($events)) {
|
if ($events !== array()) {
|
||||||
$lastIndex = count($events) - 1;
|
$lastIndex = count($events) - 1;
|
||||||
$lastEvent = $events[$lastIndex];
|
$lastEvent = $events[$lastIndex];
|
||||||
|
|
||||||
|
@ -810,7 +811,7 @@ class ICal
|
||||||
{
|
{
|
||||||
$events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
|
$events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
|
||||||
|
|
||||||
if (!empty($events)) {
|
if ($events !== array()) {
|
||||||
foreach ($events as $key => $anEvent) {
|
foreach ($events as $key => $anEvent) {
|
||||||
if ($anEvent === null) {
|
if ($anEvent === null) {
|
||||||
unset($events[$key]);
|
unset($events[$key]);
|
||||||
|
@ -869,9 +870,10 @@ class ICal
|
||||||
{
|
{
|
||||||
$string = implode(PHP_EOL, $lines);
|
$string = implode(PHP_EOL, $lines);
|
||||||
$string = str_ireplace(' ', ' ', $string);
|
$string = str_ireplace(' ', ' ', $string);
|
||||||
$string = preg_replace('/' . PHP_EOL . '[ \t]/', '', $string);
|
|
||||||
|
|
||||||
$lines = explode(PHP_EOL, $string);
|
$cleanedString = preg_replace('/' . PHP_EOL . '[ \t]/', '', $string);
|
||||||
|
|
||||||
|
$lines = explode(PHP_EOL, $cleanedString ?: $string);
|
||||||
|
|
||||||
return $lines;
|
return $lines;
|
||||||
}
|
}
|
||||||
|
@ -912,7 +914,6 @@ class ICal
|
||||||
$this->cal[$key1][$key2][$key3][$keyword] .= ',' . $value;
|
$this->cal[$key1][$key2][$key3][$keyword] .= ',' . $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'VEVENT':
|
case 'VEVENT':
|
||||||
|
@ -948,11 +949,10 @@ class ICal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->cal[$key1][$key2][$keyword] !== $value) {
|
if (!is_array($value) && $this->cal[$key1][$key2][$keyword] !== $value) {
|
||||||
$this->cal[$key1][$key2][$keyword] .= ',' . $value;
|
$this->cal[$key1][$key2][$keyword] .= ',' . $value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'VFREEBUSY':
|
case 'VFREEBUSY':
|
||||||
|
@ -975,7 +975,6 @@ class ICal
|
||||||
} else {
|
} else {
|
||||||
$this->cal[$key1][$key2][$key3][] = $value;
|
$this->cal[$key1][$key2][$key3][] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'VTODO':
|
case 'VTODO':
|
||||||
|
@ -989,14 +988,16 @@ class ICal
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->lastKeyword = $keyword;
|
if (is_string($keyword)) {
|
||||||
|
$this->lastKeyword = $keyword;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the key value pair from an iCal string
|
* Gets the key value pair from an iCal string
|
||||||
*
|
*
|
||||||
* @param string $text
|
* @param string $text
|
||||||
* @return array|boolean
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function keyValueFromString($text)
|
public function keyValueFromString($text)
|
||||||
{
|
{
|
||||||
|
@ -1011,6 +1012,7 @@ class ICal
|
||||||
if ($i === 0) {
|
if ($i === 0) {
|
||||||
$object[0] = $splitLine[$i];
|
$object[0] = $splitLine[$i];
|
||||||
$i++;
|
$i++;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1052,7 +1054,7 @@ class ICal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object construction
|
// Object construction
|
||||||
if ($paramObj !== []) {
|
if ($paramObj !== array()) {
|
||||||
$object[1][0] = $valueObj;
|
$object[1][0] = $valueObj;
|
||||||
$object[1][1] = $paramObj;
|
$object[1][1] = $paramObj;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1103,11 +1105,29 @@ class ICal
|
||||||
return $words;
|
return $words;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default time zone if set.
|
||||||
|
* Falls back to the system default if not set.
|
||||||
|
*
|
||||||
|
* @param boolean $forceReturnSystemDefault
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getDefaultTimeZone($forceReturnSystemDefault = false)
|
||||||
|
{
|
||||||
|
$systemDefault = date_default_timezone_get();
|
||||||
|
|
||||||
|
if ($forceReturnSystemDefault) {
|
||||||
|
return $systemDefault;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->defaultTimeZone ?: $systemDefault;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a `DateTime` object from an iCal date time format
|
* Returns a `DateTime` object from an iCal date time format
|
||||||
*
|
*
|
||||||
* @param string $icalDate
|
* @param string $icalDate
|
||||||
* @return \DateTime
|
* @return \DateTime|false
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function iCalDateToDateTime($icalDate)
|
public function iCalDateToDateTime($icalDate)
|
||||||
|
@ -1131,7 +1151,7 @@ class ICal
|
||||||
|
|
||||||
preg_match($pattern, $icalDate, $date);
|
preg_match($pattern, $icalDate, $date);
|
||||||
|
|
||||||
if (empty($date)) {
|
if ($date === array()) {
|
||||||
throw new \Exception('Invalid iCal date format.');
|
throw new \Exception('Invalid iCal date format.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1141,10 +1161,10 @@ class ICal
|
||||||
|
|
||||||
if ($date[4] === 'Z') {
|
if ($date[4] === 'Z') {
|
||||||
$dateTimeZone = new \DateTimeZone(self::TIME_ZONE_UTC);
|
$dateTimeZone = new \DateTimeZone(self::TIME_ZONE_UTC);
|
||||||
} elseif (!empty($date[1])) {
|
} elseif (isset($date[1]) && $date[1] !== '') {
|
||||||
$dateTimeZone = $this->timeZoneStringToDateTimeZone($date[1]);
|
$dateTimeZone = $this->timeZoneStringToDateTimeZone($date[1]);
|
||||||
} else {
|
} else {
|
||||||
$dateTimeZone = new \DateTimeZone($this->defaultTimeZone);
|
$dateTimeZone = new \DateTimeZone($this->getDefaultTimeZone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The exclamation mark at the start of the format string indicates that if a
|
// The exclamation mark at the start of the format string indicates that if a
|
||||||
|
@ -1152,7 +1172,7 @@ class ICal
|
||||||
// set to 00:00:00. Without it, the time would be set to the current system time.
|
// set to 00:00:00. Without it, the time would be set to the current system time.
|
||||||
$dateFormat = '!Ymd';
|
$dateFormat = '!Ymd';
|
||||||
$dateBasic = $date[2];
|
$dateBasic = $date[2];
|
||||||
if (!empty($date[3])) {
|
if (isset($date[3]) && $date[3] !== '') {
|
||||||
$dateBasic .= "T{$date[3]}";
|
$dateBasic .= "T{$date[3]}";
|
||||||
$dateFormat .= '\THis';
|
$dateFormat .= '\THis';
|
||||||
}
|
}
|
||||||
|
@ -1168,7 +1188,15 @@ class ICal
|
||||||
*/
|
*/
|
||||||
public function iCalDateToUnixTimestamp($icalDate)
|
public function iCalDateToUnixTimestamp($icalDate)
|
||||||
{
|
{
|
||||||
return $this->iCalDateToDateTime($icalDate)->getTimestamp();
|
$iCalDateToDateTime = $this->iCalDateToDateTime($icalDate);
|
||||||
|
|
||||||
|
if ($iCalDateToDateTime === false) {
|
||||||
|
trigger_error("ICal::iCalDateToUnixTimestamp: Invalid date passed ({$icalDate})", E_USER_NOTICE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $iCalDateToDateTime->getTimestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1177,7 +1205,7 @@ class ICal
|
||||||
* @param array $event
|
* @param array $event
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @param string|null $format
|
* @param string|null $format
|
||||||
* @return string|boolean|\DateTime
|
* @return string|integer|boolean|\DateTime
|
||||||
*/
|
*/
|
||||||
public function iCalDateWithTimeZone(array $event, $key, $format = self::DATE_TIME_FORMAT)
|
public function iCalDateWithTimeZone(array $event, $key, $format = self::DATE_TIME_FORMAT)
|
||||||
{
|
{
|
||||||
|
@ -1188,14 +1216,24 @@ class ICal
|
||||||
$dateArray = $event["{$key}_array"];
|
$dateArray = $event["{$key}_array"];
|
||||||
|
|
||||||
if ($key === 'DURATION') {
|
if ($key === 'DURATION') {
|
||||||
$dateTime = $this->parseDuration($event['DTSTART'], $dateArray[2], null);
|
$dateTime = $this->parseDuration($event['DTSTART'], $dateArray[2]);
|
||||||
|
|
||||||
|
if ($dateTime instanceof \DateTime === false) {
|
||||||
|
trigger_error("ICal::iCalDateWithTimeZone: Invalid date passed ({$event['DTSTART']})", E_USER_NOTICE);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// When constructing from a Unix Timestamp, no time zone needs passing.
|
// When constructing from a Unix Timestamp, no time zone needs passing.
|
||||||
$dateTime = new \DateTime("@{$dateArray[2]}");
|
$dateTime = new \DateTime("@{$dateArray[2]}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the time zone we wish to use when running `$dateTime->format`.
|
$calendarTimeZone = $this->calendarTimeZone();
|
||||||
$dateTime->setTimezone(new \DateTimeZone($this->calendarTimeZone()));
|
|
||||||
|
if (!is_null($calendarTimeZone)) {
|
||||||
|
// Set the time zone we wish to use when running `$dateTime->format`.
|
||||||
|
$dateTime->setTimezone(new \DateTimeZone($calendarTimeZone));
|
||||||
|
}
|
||||||
|
|
||||||
if (is_null($format)) {
|
if (is_null($format)) {
|
||||||
return $dateTime;
|
return $dateTime;
|
||||||
|
@ -1216,7 +1254,7 @@ class ICal
|
||||||
$checks = null;
|
$checks = null;
|
||||||
$events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
|
$events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
|
||||||
|
|
||||||
if (!empty($events)) {
|
if ($events !== array()) {
|
||||||
foreach ($events as $key => $anEvent) {
|
foreach ($events as $key => $anEvent) {
|
||||||
foreach (array('DTSTART', 'DTEND', 'RECURRENCE-ID') as $type) {
|
foreach (array('DTSTART', 'DTEND', 'RECURRENCE-ID') as $type) {
|
||||||
if (isset($anEvent[$type])) {
|
if (isset($anEvent[$type])) {
|
||||||
|
@ -1286,7 +1324,7 @@ class ICal
|
||||||
$events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
|
$events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
|
||||||
|
|
||||||
// If there are no events, then we have nothing to process.
|
// If there are no events, then we have nothing to process.
|
||||||
if (empty($events)) {
|
if ($events === array()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1304,6 +1342,12 @@ class ICal
|
||||||
// Create new initial starting point.
|
// Create new initial starting point.
|
||||||
$initialEventDate = $this->icalDateToDateTime($anEvent['DTSTART_array'][3]);
|
$initialEventDate = $this->icalDateToDateTime($anEvent['DTSTART_array'][3]);
|
||||||
|
|
||||||
|
if ($initialEventDate === false) {
|
||||||
|
trigger_error("ICal::processRecurrences: Invalid date passed ({$anEvent['DTSTART_array'][3]})", E_USER_NOTICE);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Separate the RRULE stanzas, and explode the values that are lists.
|
// Separate the RRULE stanzas, and explode the values that are lists.
|
||||||
$rrules = array();
|
$rrules = array();
|
||||||
foreach (array_filter(explode(';', $anEvent['RRULE'])) as $s) {
|
foreach (array_filter(explode(';', $anEvent['RRULE'])) as $s) {
|
||||||
|
@ -1315,9 +1359,14 @@ class ICal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get frequency
|
|
||||||
$frequency = $rrules['FREQ'];
|
$frequency = $rrules['FREQ'];
|
||||||
|
|
||||||
|
if (!is_string($frequency)) {
|
||||||
|
trigger_error('ICal::processRecurrences: Invalid frequency passed', E_USER_NOTICE);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Reject RRULE if BYDAY stanza is invalid:
|
// Reject RRULE if BYDAY stanza is invalid:
|
||||||
// > The BYDAY rule part MUST NOT be specified with a numeric value
|
// > The BYDAY rule part MUST NOT be specified with a numeric value
|
||||||
// > when the FREQ rule part is not set to MONTHLY or YEARLY.
|
// > when the FREQ rule part is not set to MONTHLY or YEARLY.
|
||||||
|
@ -1329,21 +1378,20 @@ class ICal
|
||||||
return $carry && substr($weekday, -2) === $weekday;
|
return $carry && substr($weekday, -2) === $weekday;
|
||||||
};
|
};
|
||||||
if (!in_array($frequency, array('MONTHLY', 'YEARLY'))) {
|
if (!in_array($frequency, array('MONTHLY', 'YEARLY'))) {
|
||||||
if (!array_reduce($rrules['BYDAY'], $checkByDays, true)) {
|
if (is_array($rrules['BYDAY']) && !array_reduce($rrules['BYDAY'], $checkByDays, true)) {
|
||||||
error_log("ICal::ProcessRecurrences: A {$frequency} RRULE may not contain BYDAY values with numeric prefixes");
|
trigger_error("ICal::processRecurrences: A {$frequency} RRULE may not contain BYDAY values with numeric prefixes", E_USER_NOTICE);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} elseif ($frequency === 'YEARLY' && !empty($rrules['BYWEEKNO'])) {
|
} elseif ($frequency === 'YEARLY' && (isset($rrules['BYWEEKNO']) && ($rrules['BYWEEKNO'] !== '' && $rrules['BYWEEKNO'] !== array()))) {
|
||||||
if (!array_reduce($rrules['BYDAY'], $checkByDays, true)) {
|
if (is_array($rrules['BYDAY']) && !array_reduce($rrules['BYDAY'], $checkByDays, true)) {
|
||||||
error_log('ICal::ProcessRecurrences: A YEARLY RRULE with a BYWEEKNO part may not contain BYDAY values with numeric prefixes');
|
trigger_error('ICal::processRecurrences: A YEARLY RRULE with a BYWEEKNO part may not contain BYDAY values with numeric prefixes', E_USER_NOTICE);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Interval
|
|
||||||
$interval = (empty($rrules['INTERVAL'])) ? 1 : (int) $rrules['INTERVAL'];
|
$interval = (empty($rrules['INTERVAL'])) ? 1 : (int) $rrules['INTERVAL'];
|
||||||
|
|
||||||
// Throw an error if this isn't an integer.
|
// Throw an error if this isn't an integer.
|
||||||
|
@ -1380,22 +1428,45 @@ class ICal
|
||||||
*/
|
*/
|
||||||
$count = 1;
|
$count = 1;
|
||||||
$countLimit = (isset($rrules['COUNT'])) ? intval($rrules['COUNT']) : PHP_INT_MAX;
|
$countLimit = (isset($rrules['COUNT'])) ? intval($rrules['COUNT']) : PHP_INT_MAX;
|
||||||
$until = date_create()->modify("{$this->defaultSpan} years")->setTime(23, 59, 59)->getTimestamp();
|
$now = date_create();
|
||||||
|
|
||||||
if (isset($rrules['UNTIL'])) {
|
$until = $now === false
|
||||||
$until = min($until, $this->iCalDateToUnixTimestamp($rrules['UNTIL']));
|
? 0
|
||||||
|
: $now->modify("{$this->defaultSpan} years")->setTime(23, 59, 59)->getTimestamp();
|
||||||
|
|
||||||
|
$untilWhile = $until;
|
||||||
|
|
||||||
|
if (isset($rrules['UNTIL']) && is_string($rrules['UNTIL'])) {
|
||||||
|
$untilDT = $this->iCalDateToDateTime($rrules['UNTIL']);
|
||||||
|
$until = min($until, ($untilDT === false) ? $until : $untilDT->getTimestamp());
|
||||||
|
|
||||||
|
// There are certain edge cases where we need to go a little beyond the UNTIL to
|
||||||
|
// ensure we get all events. Consider:
|
||||||
|
//
|
||||||
|
// DTSTART:20200103
|
||||||
|
// RRULE:FREQ=MONTHLY;BYDAY=-5FR;UNTIL=20200502
|
||||||
|
//
|
||||||
|
// In this case the last occurrence should be 1st May, however when we transition
|
||||||
|
// from April to May:
|
||||||
|
//
|
||||||
|
// $until ~= 2nd May
|
||||||
|
// $frequencyRecurringDateTime ~= 3rd May
|
||||||
|
//
|
||||||
|
// And as the latter comes after the former, the while loop ends before any dates
|
||||||
|
// in May have the chance to be considered.
|
||||||
|
$untilWhile = min($untilWhile, ($untilDT === false) ? $untilWhile : $untilDT->modify("+1 {$this->frequencyConversion[$frequency]}")->getTimestamp());
|
||||||
}
|
}
|
||||||
|
|
||||||
$eventRecurrences = array();
|
$eventRecurrences = array();
|
||||||
|
|
||||||
$frequencyRecurringDateTime = clone $initialEventDate;
|
$frequencyRecurringDateTime = clone $initialEventDate;
|
||||||
while ($frequencyRecurringDateTime->getTimestamp() <= $until && $count < $countLimit) {
|
while ($frequencyRecurringDateTime->getTimestamp() <= $untilWhile && $count < $countLimit) {
|
||||||
$candidateDateTimes = array();
|
$candidateDateTimes = array();
|
||||||
|
|
||||||
// phpcs:ignore Squiz.ControlStructures.SwitchDeclaration.MissingDefault
|
// phpcs:ignore Squiz.ControlStructures.SwitchDeclaration.MissingDefault
|
||||||
switch ($frequency) {
|
switch ($frequency) {
|
||||||
case 'DAILY':
|
case 'DAILY':
|
||||||
if (!empty($rrules['BYMONTHDAY'])) {
|
if (isset($rrules['BYMONTHDAY']) && (is_array($rrules['BYMONTHDAY']) && $rrules['BYMONTHDAY'] !== array())) {
|
||||||
if (!isset($monthDays)) {
|
if (!isset($monthDays)) {
|
||||||
// This variable is unset when we change months (see below)
|
// This variable is unset when we change months (see below)
|
||||||
$monthDays = $this->getDaysOfMonthMatchingByMonthDayRRule($rrules['BYMONTHDAY'], $frequencyRecurringDateTime);
|
$monthDays = $this->getDaysOfMonthMatchingByMonthDayRRule($rrules['BYMONTHDAY'], $frequencyRecurringDateTime);
|
||||||
|
@ -1414,7 +1485,7 @@ class ICal
|
||||||
$initialDayOfWeek = $frequencyRecurringDateTime->format('N');
|
$initialDayOfWeek = $frequencyRecurringDateTime->format('N');
|
||||||
$matchingDays = array($initialDayOfWeek);
|
$matchingDays = array($initialDayOfWeek);
|
||||||
|
|
||||||
if (!empty($rrules['BYDAY'])) {
|
if (isset($rrules['BYDAY']) && (is_array($rrules['BYDAY']) && $rrules['BYDAY'] !== array())) {
|
||||||
// setISODate() below uses the ISO-8601 specification of weeks: start on
|
// setISODate() below uses the ISO-8601 specification of weeks: start on
|
||||||
// a Monday, end on a Sunday. However, RRULEs (or the caller of the
|
// a Monday, end on a Sunday. However, RRULEs (or the caller of the
|
||||||
// parser) may state an alternate WeeKSTart.
|
// parser) may state an alternate WeeKSTart.
|
||||||
|
@ -1459,18 +1530,17 @@ class ICal
|
||||||
$candidateDateTimes[] = $clonedDateTime->setISODate(
|
$candidateDateTimes[] = $clonedDateTime->setISODate(
|
||||||
(int) $frequencyRecurringDateTime->format('o'),
|
(int) $frequencyRecurringDateTime->format('o'),
|
||||||
(int) $frequencyRecurringDateTime->format('W'),
|
(int) $frequencyRecurringDateTime->format('W'),
|
||||||
$day
|
(int) $day
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'MONTHLY':
|
case 'MONTHLY':
|
||||||
$matchingDays = array();
|
$matchingDays = array();
|
||||||
|
|
||||||
if (!empty($rrules['BYMONTHDAY'])) {
|
if (isset($rrules['BYMONTHDAY']) && (is_array($rrules['BYMONTHDAY']) && $rrules['BYMONTHDAY'] !== array())) {
|
||||||
$matchingDays = $this->getDaysOfMonthMatchingByMonthDayRRule($rrules['BYMONTHDAY'], $frequencyRecurringDateTime);
|
$matchingDays = $this->getDaysOfMonthMatchingByMonthDayRRule($rrules['BYMONTHDAY'], $frequencyRecurringDateTime);
|
||||||
if (!empty($rrules['BYDAY'])) {
|
if (isset($rrules['BYDAY']) && (is_array($rrules['BYDAY']) && $rrules['BYDAY'] !== array())) {
|
||||||
$matchingDays = array_filter(
|
$matchingDays = array_filter(
|
||||||
$this->getDaysOfMonthMatchingByDayRRule($rrules['BYDAY'], $frequencyRecurringDateTime),
|
$this->getDaysOfMonthMatchingByDayRRule($rrules['BYDAY'], $frequencyRecurringDateTime),
|
||||||
function ($monthDay) use ($matchingDays) {
|
function ($monthDay) use ($matchingDays) {
|
||||||
|
@ -1478,13 +1548,13 @@ class ICal
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} elseif (!empty($rrules['BYDAY'])) {
|
} elseif (isset($rrules['BYDAY']) && (is_array($rrules['BYDAY']) && $rrules['BYDAY'] !== array())) {
|
||||||
$matchingDays = $this->getDaysOfMonthMatchingByDayRRule($rrules['BYDAY'], $frequencyRecurringDateTime);
|
$matchingDays = $this->getDaysOfMonthMatchingByDayRRule($rrules['BYDAY'], $frequencyRecurringDateTime);
|
||||||
} else {
|
} else {
|
||||||
$matchingDays[] = $frequencyRecurringDateTime->format('d');
|
$matchingDays[] = $frequencyRecurringDateTime->format('d');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($rrules['BYSETPOS'])) {
|
if (isset($rrules['BYSETPOS']) && (is_array($rrules['BYSETPOS']) && $rrules['BYSETPOS'] !== array())) {
|
||||||
$matchingDays = $this->filterValuesUsingBySetPosRRule($rrules['BYSETPOS'], $matchingDays);
|
$matchingDays = $this->filterValuesUsingBySetPosRRule($rrules['BYSETPOS'], $matchingDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1501,27 +1571,26 @@ class ICal
|
||||||
$day
|
$day
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'YEARLY':
|
case 'YEARLY':
|
||||||
$matchingDays = array();
|
$matchingDays = array();
|
||||||
|
|
||||||
if (!empty($rrules['BYMONTH'])) {
|
if (isset($rrules['BYMONTH']) && (is_array($rrules['BYMONTH']) && $rrules['BYMONTH'] !== array())) {
|
||||||
$bymonthRecurringDatetime = clone $frequencyRecurringDateTime;
|
$bymonthRecurringDatetime = clone $frequencyRecurringDateTime;
|
||||||
foreach ($rrules['BYMONTH'] as $byMonth) {
|
foreach ($rrules['BYMONTH'] as $byMonth) {
|
||||||
$bymonthRecurringDatetime->setDate(
|
$bymonthRecurringDatetime->setDate(
|
||||||
(int) $frequencyRecurringDateTime->format('Y'),
|
(int) $frequencyRecurringDateTime->format('Y'),
|
||||||
$byMonth,
|
(int) $byMonth,
|
||||||
(int) $frequencyRecurringDateTime->format('d')
|
(int) $frequencyRecurringDateTime->format('d')
|
||||||
);
|
);
|
||||||
|
|
||||||
// Determine the days of the month affected
|
// Determine the days of the month affected
|
||||||
// (The interaction between BYMONTHDAY and BYDAY is resolved later.)
|
// (The interaction between BYMONTHDAY and BYDAY is resolved later.)
|
||||||
$monthDays = array();
|
$monthDays = array();
|
||||||
if (!empty($rrules['BYMONTHDAY'])) {
|
if (isset($rrules['BYMONTHDAY']) && (is_array($rrules['BYMONTHDAY']) && $rrules['BYMONTHDAY'] !== array())) {
|
||||||
$monthDays = $this->getDaysOfMonthMatchingByMonthDayRRule($rrules['BYMONTHDAY'], $bymonthRecurringDatetime);
|
$monthDays = $this->getDaysOfMonthMatchingByMonthDayRRule($rrules['BYMONTHDAY'], $bymonthRecurringDatetime);
|
||||||
} elseif (!empty($rrules['BYDAY'])) {
|
} elseif (isset($rrules['BYDAY']) && (is_array($rrules['BYDAY']) && $rrules['BYDAY'] !== array())) {
|
||||||
$monthDays = $this->getDaysOfMonthMatchingByDayRRule($rrules['BYDAY'], $bymonthRecurringDatetime);
|
$monthDays = $this->getDaysOfMonthMatchingByDayRRule($rrules['BYDAY'], $bymonthRecurringDatetime);
|
||||||
} else {
|
} else {
|
||||||
$monthDays[] = $bymonthRecurringDatetime->format('d');
|
$monthDays[] = $bymonthRecurringDatetime->format('d');
|
||||||
|
@ -1536,34 +1605,34 @@ class ICal
|
||||||
)->format('z') + 1;
|
)->format('z') + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} elseif (!empty($rrules['BYWEEKNO'])) {
|
} elseif (isset($rrules['BYWEEKNO']) && (is_array($rrules['BYWEEKNO']) && $rrules['BYWEEKNO'] !== array())) {
|
||||||
$matchingDays = $this->getDaysOfYearMatchingByWeekNoRRule($rrules['BYWEEKNO'], $frequencyRecurringDateTime);
|
$matchingDays = $this->getDaysOfYearMatchingByWeekNoRRule($rrules['BYWEEKNO'], $frequencyRecurringDateTime);
|
||||||
} elseif (!empty($rrules['BYYEARDAY'])) {
|
} elseif (isset($rrules['BYYEARDAY']) && (is_array($rrules['BYYEARDAY']) && $rrules['BYYEARDAY'] !== array())) {
|
||||||
$matchingDays = $this->getDaysOfYearMatchingByYearDayRRule($rrules['BYYEARDAY'], $frequencyRecurringDateTime);
|
$matchingDays = $this->getDaysOfYearMatchingByYearDayRRule($rrules['BYYEARDAY'], $frequencyRecurringDateTime);
|
||||||
} elseif (!empty($rrules['BYMONTHDAY'])) {
|
} elseif (isset($rrules['BYMONTHDAY']) && (is_array($rrules['BYMONTHDAY']) && $rrules['BYMONTHDAY'] !== array())) {
|
||||||
$matchingDays = $this->getDaysOfYearMatchingByMonthDayRRule($rrules['BYMONTHDAY'], $frequencyRecurringDateTime);
|
$matchingDays = $this->getDaysOfYearMatchingByMonthDayRRule($rrules['BYMONTHDAY'], $frequencyRecurringDateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($rrules['BYDAY'])) {
|
if (isset($rrules['BYDAY']) && (is_array($rrules['BYDAY']) && $rrules['BYDAY'] !== array())) {
|
||||||
if (!empty($rrules['BYYEARDAY']) || !empty($rrules['BYMONTHDAY']) || !empty($rrules['BYWEEKNO'])) {
|
if (isset($rrules['BYYEARDAY']) && ($rrules['BYYEARDAY'] !== '' && $rrules['BYYEARDAY'] !== array()) || isset($rrules['BYMONTHDAY']) && ($rrules['BYMONTHDAY'] !== '' && $rrules['BYMONTHDAY'] !== array()) || isset($rrules['BYWEEKNO']) && ($rrules['BYWEEKNO'] !== '' && $rrules['BYWEEKNO'] !== array())) {
|
||||||
$matchingDays = array_filter(
|
$matchingDays = array_filter(
|
||||||
$this->getDaysOfYearMatchingByDayRRule($rrules['BYDAY'], $frequencyRecurringDateTime),
|
$this->getDaysOfYearMatchingByDayRRule($rrules['BYDAY'], $frequencyRecurringDateTime),
|
||||||
function ($yearDay) use ($matchingDays) {
|
function ($yearDay) use ($matchingDays) {
|
||||||
return in_array($yearDay, $matchingDays);
|
return in_array($yearDay, $matchingDays);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} elseif ($matchingDays === []) {
|
} elseif ($matchingDays === array()) {
|
||||||
$matchingDays = $this->getDaysOfYearMatchingByDayRRule($rrules['BYDAY'], $frequencyRecurringDateTime);
|
$matchingDays = $this->getDaysOfYearMatchingByDayRRule($rrules['BYDAY'], $frequencyRecurringDateTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($matchingDays === []) {
|
if ($matchingDays === array()) {
|
||||||
$matchingDays = array($frequencyRecurringDateTime->format('z') + 1);
|
$matchingDays = array($frequencyRecurringDateTime->format('z') + 1);
|
||||||
} else {
|
} else {
|
||||||
sort($matchingDays);
|
sort($matchingDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($rrules['BYSETPOS'])) {
|
if (isset($rrules['BYSETPOS']) && (is_array($rrules['BYSETPOS']) && $rrules['BYSETPOS'] !== array())) {
|
||||||
$matchingDays = $this->filterValuesUsingBySetPosRRule($rrules['BYSETPOS'], $matchingDays);
|
$matchingDays = $this->filterValuesUsingBySetPosRRule($rrules['BYSETPOS'], $matchingDays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1575,7 +1644,6 @@ class ICal
|
||||||
$day
|
$day
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1753,6 +1821,7 @@ class ICal
|
||||||
protected function getDaysOfMonthMatchingByDayRRule(array $byDays, $initialDateTime)
|
protected function getDaysOfMonthMatchingByDayRRule(array $byDays, $initialDateTime)
|
||||||
{
|
{
|
||||||
$matchingDays = array();
|
$matchingDays = array();
|
||||||
|
$currentMonth = $initialDateTime->format('n');
|
||||||
|
|
||||||
foreach ($byDays as $weekday) {
|
foreach ($byDays as $weekday) {
|
||||||
$bydayDateTime = clone $initialDateTime;
|
$bydayDateTime = clone $initialDateTime;
|
||||||
|
@ -1771,10 +1840,14 @@ class ICal
|
||||||
|
|
||||||
if ($ordwk < 0) { // -ve {ordwk}
|
if ($ordwk < 0) { // -ve {ordwk}
|
||||||
$bydayDateTime->modify((++$ordwk) . ' week');
|
$bydayDateTime->modify((++$ordwk) . ' week');
|
||||||
$matchingDays[] = $bydayDateTime->format('j');
|
if ($bydayDateTime->format('n') === $currentMonth) {
|
||||||
|
$matchingDays[] = $bydayDateTime->format('j');
|
||||||
|
}
|
||||||
} elseif ($ordwk > 0) { // +ve {ordwk}
|
} elseif ($ordwk > 0) { // +ve {ordwk}
|
||||||
$bydayDateTime->modify((--$ordwk) . ' week');
|
$bydayDateTime->modify((--$ordwk) . ' week');
|
||||||
$matchingDays[] = $bydayDateTime->format('j');
|
if ($bydayDateTime->format('n') === $currentMonth) {
|
||||||
|
$matchingDays[] = $bydayDateTime->format('j');
|
||||||
|
}
|
||||||
} else { // No {ordwk}
|
} else { // No {ordwk}
|
||||||
while ($bydayDateTime->format('n') === $initialDateTime->format('n')) {
|
while ($bydayDateTime->format('n') === $initialDateTime->format('n')) {
|
||||||
$matchingDays[] = $bydayDateTime->format('j');
|
$matchingDays[] = $bydayDateTime->format('j');
|
||||||
|
@ -1923,9 +1996,10 @@ class ICal
|
||||||
protected function getDaysOfYearMatchingByWeekNoRRule(array $byWeekNums, $initialDateTime)
|
protected function getDaysOfYearMatchingByWeekNoRRule(array $byWeekNums, $initialDateTime)
|
||||||
{
|
{
|
||||||
// `\DateTime::format('L')` returns 1 if leap year, 0 if not.
|
// `\DateTime::format('L')` returns 1 if leap year, 0 if not.
|
||||||
$isLeapYear = $initialDateTime->format('L');
|
$isLeapYear = $initialDateTime->format('L');
|
||||||
$firstDayOfTheYear = date_create("first day of January {$initialDateTime->format('Y')}")->format('D');
|
$initialYear = date_create("first day of January {$initialDateTime->format('Y')}");
|
||||||
$weeksInThisYear = ($firstDayOfTheYear === 'Thu' || $isLeapYear && $firstDayOfTheYear === 'Wed') ? 53 : 52;
|
$firstDayOfTheYear = ($initialYear === false) ? null : $initialYear->format('D');
|
||||||
|
$weeksInThisYear = ($firstDayOfTheYear === 'Thu' || $isLeapYear && $firstDayOfTheYear === 'Wed') ? 53 : 52;
|
||||||
|
|
||||||
$matchingWeeks = $this->resolveIndicesOfRange($byWeekNums, $weeksInThisYear);
|
$matchingWeeks = $this->resolveIndicesOfRange($byWeekNums, $weeksInThisYear);
|
||||||
$matchingDays = array();
|
$matchingDays = array();
|
||||||
|
@ -2039,7 +2113,7 @@ class ICal
|
||||||
{
|
{
|
||||||
$events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
|
$events = (isset($this->cal['VEVENT'])) ? $this->cal['VEVENT'] : array();
|
||||||
|
|
||||||
if (!empty($events)) {
|
if ($events !== array()) {
|
||||||
foreach ($events as $key => $anEvent) {
|
foreach ($events as $key => $anEvent) {
|
||||||
if (is_null($anEvent) || !$this->isValidDate($anEvent['DTSTART'])) {
|
if (is_null($anEvent) || !$this->isValidDate($anEvent['DTSTART'])) {
|
||||||
unset($events[$key]);
|
unset($events[$key]);
|
||||||
|
@ -2077,10 +2151,8 @@ class ICal
|
||||||
|
|
||||||
$events = array();
|
$events = array();
|
||||||
|
|
||||||
if (!empty($array)) {
|
foreach ($array as $event) {
|
||||||
foreach ($array as $event) {
|
$events[] = new Event($event);
|
||||||
$events[] = new Event($event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $events;
|
return $events;
|
||||||
|
@ -2154,7 +2226,7 @@ class ICal
|
||||||
*/
|
*/
|
||||||
public function hasEvents()
|
public function hasEvents()
|
||||||
{
|
{
|
||||||
return ($this->events() !== []) ?: false;
|
return ($this->events() !== array()) ?: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2185,7 +2257,7 @@ class ICal
|
||||||
// Sort events before processing range
|
// Sort events before processing range
|
||||||
$events = $this->sortEventsWithOrder($this->events());
|
$events = $this->sortEventsWithOrder($this->events());
|
||||||
|
|
||||||
if (empty($events)) {
|
if ($events === array()) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2193,34 +2265,36 @@ class ICal
|
||||||
|
|
||||||
if (!is_null($rangeStart)) {
|
if (!is_null($rangeStart)) {
|
||||||
try {
|
try {
|
||||||
$rangeStart = new \DateTime($rangeStart, new \DateTimeZone($this->defaultTimeZone));
|
$rangeStart = new \DateTime($rangeStart, new \DateTimeZone($this->getDefaultTimeZone()));
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
error_log("ICal::eventsFromRange: Invalid date passed ({$rangeStart})");
|
error_log("ICal::eventsFromRange: Invalid date passed ({$rangeStart})");
|
||||||
$rangeStart = false;
|
$rangeStart = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$rangeStart = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
|
$rangeStart = new \DateTime('now', new \DateTimeZone($this->getDefaultTimeZone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_null($rangeEnd)) {
|
if (!is_null($rangeEnd)) {
|
||||||
try {
|
try {
|
||||||
$rangeEnd = new \DateTime($rangeEnd, new \DateTimeZone($this->defaultTimeZone));
|
$rangeEnd = new \DateTime($rangeEnd, new \DateTimeZone($this->getDefaultTimeZone()));
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
error_log("ICal::eventsFromRange: Invalid date passed ({$rangeEnd})");
|
error_log("ICal::eventsFromRange: Invalid date passed ({$rangeEnd})");
|
||||||
$rangeEnd = false;
|
$rangeEnd = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$rangeEnd = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
|
$rangeEnd = new \DateTime('now', new \DateTimeZone($this->getDefaultTimeZone()));
|
||||||
$rangeEnd->modify('+20 years');
|
$rangeEnd->modify('+20 years');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If start and end are identical and are dates with no times...
|
if ($rangeEnd !== false && $rangeStart !== false) {
|
||||||
if ($rangeEnd->format('His') == 0 && $rangeStart->getTimestamp() === $rangeEnd->getTimestamp()) {
|
// If start and end are identical and are dates with no times...
|
||||||
$rangeEnd->modify('+1 day');
|
if ($rangeEnd->format('His') == 0 && $rangeStart->getTimestamp() === $rangeEnd->getTimestamp()) {
|
||||||
}
|
$rangeEnd->modify('+1 day');
|
||||||
|
}
|
||||||
|
|
||||||
$rangeStart = $rangeStart->getTimestamp();
|
$rangeStart = $rangeStart->getTimestamp();
|
||||||
$rangeEnd = $rangeEnd->getTimestamp();
|
$rangeEnd = $rangeEnd->getTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($events as $anEvent) {
|
foreach ($events as $anEvent) {
|
||||||
$eventStart = $anEvent->dtstart_array[2];
|
$eventStart = $anEvent->dtstart_array[2];
|
||||||
|
@ -2228,7 +2302,8 @@ class ICal
|
||||||
|
|
||||||
if (
|
if (
|
||||||
($eventStart >= $rangeStart && $eventStart < $rangeEnd) // Event start date contained in the range
|
($eventStart >= $rangeStart && $eventStart < $rangeEnd) // Event start date contained in the range
|
||||||
|| ($eventEnd !== null
|
|| (
|
||||||
|
$eventEnd !== null
|
||||||
&& (
|
&& (
|
||||||
($eventEnd > $rangeStart && $eventEnd <= $rangeEnd) // Event end date contained in the range
|
($eventEnd > $rangeStart && $eventEnd <= $rangeEnd) // Event end date contained in the range
|
||||||
|| ($eventStart < $rangeStart && $eventEnd > $rangeEnd) // Event starts before and finishes after range
|
|| ($eventStart < $rangeStart && $eventEnd > $rangeEnd) // Event starts before and finishes after range
|
||||||
|
@ -2239,27 +2314,26 @@ class ICal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($extendedEvents)) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $extendedEvents;
|
return $extendedEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a sorted array of the events following a given string,
|
* Returns a sorted array of the events following a given string
|
||||||
* or `false` if no events exist in the range.
|
|
||||||
*
|
*
|
||||||
* @param string $interval
|
* @param string $interval
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function eventsFromInterval($interval)
|
public function eventsFromInterval($interval)
|
||||||
{
|
{
|
||||||
$rangeStart = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
|
$timeZone = $this->getDefaultTimeZone();
|
||||||
$rangeEnd = new \DateTime('now', new \DateTimeZone($this->defaultTimeZone));
|
$rangeStart = new \DateTime('now', new \DateTimeZone($timeZone));
|
||||||
|
$rangeEnd = new \DateTime('now', new \DateTimeZone($timeZone));
|
||||||
|
|
||||||
$dateInterval = \DateInterval::createFromDateString($interval);
|
$dateInterval = \DateInterval::createFromDateString($interval);
|
||||||
$rangeEnd->add($dateInterval);
|
|
||||||
|
if ($dateInterval instanceof \DateInterval) {
|
||||||
|
$rangeEnd->add($dateInterval);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->eventsFromRange($rangeStart->format('Y-m-d'), $rangeEnd->format('Y-m-d'));
|
return $this->eventsFromRange($rangeStart->format('Y-m-d'), $rangeEnd->format('Y-m-d'));
|
||||||
}
|
}
|
||||||
|
@ -2358,12 +2432,16 @@ class ICal
|
||||||
*
|
*
|
||||||
* @param string $date
|
* @param string $date
|
||||||
* @param \DateInterval $duration
|
* @param \DateInterval $duration
|
||||||
* @param string|null $format
|
* @return \DateTime|false
|
||||||
* @return integer|\DateTime
|
|
||||||
*/
|
*/
|
||||||
protected function parseDuration($date, $duration, $format = self::UNIX_FORMAT)
|
protected function parseDuration($date, $duration)
|
||||||
{
|
{
|
||||||
$dateTime = date_create($date);
|
$dateTime = date_create($date);
|
||||||
|
|
||||||
|
if ($dateTime === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$dateTime->modify("{$duration->y} year");
|
$dateTime->modify("{$duration->y} year");
|
||||||
$dateTime->modify("{$duration->m} month");
|
$dateTime->modify("{$duration->m} month");
|
||||||
$dateTime->modify("{$duration->d} day");
|
$dateTime->modify("{$duration->d} day");
|
||||||
|
@ -2371,22 +2449,14 @@ class ICal
|
||||||
$dateTime->modify("{$duration->i} minute");
|
$dateTime->modify("{$duration->i} minute");
|
||||||
$dateTime->modify("{$duration->s} second");
|
$dateTime->modify("{$duration->s} second");
|
||||||
|
|
||||||
if (is_null($format)) {
|
return $dateTime;
|
||||||
$output = $dateTime;
|
|
||||||
} elseif ($format === self::UNIX_FORMAT) {
|
|
||||||
$output = $dateTime->getTimestamp();
|
|
||||||
} else {
|
|
||||||
$output = $dateTime->format($format);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $output;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes unprintable ASCII and UTF-8 characters
|
* Removes unprintable ASCII and UTF-8 characters
|
||||||
*
|
*
|
||||||
* @param string $data
|
* @param string $data
|
||||||
* @return string
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
protected function removeUnprintableChars($data)
|
protected function removeUnprintableChars($data)
|
||||||
{
|
{
|
||||||
|
@ -2419,53 +2489,6 @@ class ICal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace all occurrences of the search string with the replacement string.
|
|
||||||
* Multibyte safe.
|
|
||||||
*
|
|
||||||
* @param string|array $search
|
|
||||||
* @param string|array $replace
|
|
||||||
* @param string|array $subject
|
|
||||||
* @param string $encoding
|
|
||||||
* @param integer $count
|
|
||||||
* @return array|string
|
|
||||||
*/
|
|
||||||
protected static function mb_str_replace($search, $replace, $subject, $encoding = null, &$count = 0) // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
|
|
||||||
{
|
|
||||||
if (is_array($subject)) {
|
|
||||||
// Call `mb_str_replace()` for each subject in the array, recursively
|
|
||||||
foreach ($subject as $key => $value) {
|
|
||||||
$subject[$key] = self::mb_str_replace($search, $replace, $value, $encoding, $count);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Normalize $search and $replace so they are both arrays of the same length
|
|
||||||
$searches = is_array($search) ? array_values($search) : array($search);
|
|
||||||
$replacements = is_array($replace) ? array_values($replace) : array($replace);
|
|
||||||
$replacements = array_pad($replacements, count($searches), '');
|
|
||||||
|
|
||||||
foreach ($searches as $key => $search) {
|
|
||||||
if (is_null($encoding)) {
|
|
||||||
$encoding = mb_detect_encoding($search, 'UTF-8', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$replace = $replacements[$key];
|
|
||||||
$searchLen = mb_strlen($search, $encoding);
|
|
||||||
|
|
||||||
$sb = array();
|
|
||||||
while (($offset = mb_strpos($subject, $search, 0, $encoding)) !== false) {
|
|
||||||
$sb[] = mb_substr($subject, 0, $offset, $encoding);
|
|
||||||
$subject = mb_substr($subject, $offset + $searchLen, null, $encoding);
|
|
||||||
++$count;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sb[] = $subject;
|
|
||||||
$subject = implode($replace, $sb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $subject;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Places double-quotes around texts that have characters not permitted
|
* Places double-quotes around texts that have characters not permitted
|
||||||
* in parameter-texts, but are permitted in quoted-texts.
|
* in parameter-texts, but are permitted in quoted-texts.
|
||||||
|
@ -2483,39 +2506,37 @@ class ICal
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces curly quotes and other special characters
|
* Replace curly quotes and other special characters with their standard equivalents
|
||||||
* with their standard equivalents
|
* @see https://utf8-chartable.de/unicode-utf8-table.pl?start=8211&utf8=string-literal
|
||||||
*
|
*
|
||||||
* @param string $data
|
* @param string $input
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function cleanData($data)
|
protected function cleanCharacters($input)
|
||||||
{
|
{
|
||||||
$replacementChars = array(
|
return strtr(
|
||||||
"\t" => ' ',
|
$input,
|
||||||
"\xe2\x80\x98" => "'", // ‘
|
array(
|
||||||
"\xe2\x80\x99" => "'", // ’
|
"\xe2\x80\x98" => "'", // ‘
|
||||||
"\xe2\x80\x9a" => "'", // ‚
|
"\xe2\x80\x99" => "'", // ’
|
||||||
"\xe2\x80\x9b" => "'", // ‛
|
"\xe2\x80\x9a" => "'", // ‚
|
||||||
"\xe2\x80\x9c" => '"', // “
|
"\xe2\x80\x9b" => "'", // ‛
|
||||||
"\xe2\x80\x9d" => '"', // ”
|
"\xe2\x80\x9c" => '"', // “
|
||||||
"\xe2\x80\x9e" => '"', // „
|
"\xe2\x80\x9d" => '"', // ”
|
||||||
"\xe2\x80\x9f" => '"', // ‟
|
"\xe2\x80\x9e" => '"', // „
|
||||||
"\xe2\x80\x93" => '-', // –
|
"\xe2\x80\x9f" => '"', // ‟
|
||||||
"\xe2\x80\x94" => '--', // —
|
"\xe2\x80\x93" => '-', // –
|
||||||
"\xe2\x80\xa6" => '...', // …
|
"\xe2\x80\x94" => '--', // —
|
||||||
"\xc2\xa0" => ' ', // Non-breaking space
|
"\xe2\x80\xa6" => '...', // …
|
||||||
|
$this->mb_chr(145) => "'", // ‘
|
||||||
|
$this->mb_chr(146) => "'", // ’
|
||||||
|
$this->mb_chr(147) => '"', // “
|
||||||
|
$this->mb_chr(148) => '"', // ”
|
||||||
|
$this->mb_chr(150) => '-', // –
|
||||||
|
$this->mb_chr(151) => '--', // —
|
||||||
|
$this->mb_chr(133) => '...', // …
|
||||||
|
)
|
||||||
);
|
);
|
||||||
// Replace UTF-8 characters
|
|
||||||
$cleanedData = strtr($data, $replacementChars);
|
|
||||||
|
|
||||||
// Replace Windows-1252 equivalents
|
|
||||||
$charsToReplace = array_map(function ($code) {
|
|
||||||
return $this->mb_chr($code);
|
|
||||||
}, array(133, 145, 146, 147, 148, 150, 151, 194));
|
|
||||||
$cleanedData = $this->mb_str_replace($charsToReplace, $replacementChars, $cleanedData);
|
|
||||||
|
|
||||||
return $cleanedData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2534,7 +2555,7 @@ class ICal
|
||||||
}
|
}
|
||||||
|
|
||||||
$output = array();
|
$output = array();
|
||||||
$currentTimeZone = new \DateTimeZone($this->defaultTimeZone);
|
$currentTimeZone = new \DateTimeZone($this->getDefaultTimeZone());
|
||||||
|
|
||||||
foreach ($exdates as $subArray) {
|
foreach ($exdates as $subArray) {
|
||||||
end($subArray);
|
end($subArray);
|
||||||
|
@ -2554,7 +2575,7 @@ class ICal
|
||||||
|
|
||||||
if ($key === $finalKey) {
|
if ($key === $finalKey) {
|
||||||
// Reset to default
|
// Reset to default
|
||||||
$currentTimeZone = new \DateTimeZone($this->defaultTimeZone);
|
$currentTimeZone = new \DateTimeZone($this->getDefaultTimeZone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2609,8 +2630,8 @@ class ICal
|
||||||
$options['http'] = array();
|
$options['http'] = array();
|
||||||
$options['http']['header'] = array();
|
$options['http']['header'] = array();
|
||||||
|
|
||||||
if (!empty($this->httpBasicAuth) || !empty($this->httpUserAgent) || !empty($this->httpAcceptLanguage)) {
|
if ($this->httpBasicAuth === array() || !empty($this->httpUserAgent) || !empty($this->httpAcceptLanguage)) {
|
||||||
if (!empty($this->httpBasicAuth)) {
|
if ($this->httpBasicAuth !== array()) {
|
||||||
$username = $this->httpBasicAuth['username'];
|
$username = $this->httpBasicAuth['username'];
|
||||||
$password = $this->httpBasicAuth['password'];
|
$password = $this->httpBasicAuth['password'];
|
||||||
$basicAuth = base64_encode("{$username}:{$password}");
|
$basicAuth = base64_encode("{$username}:{$password}");
|
||||||
|
@ -2678,6 +2699,6 @@ class ICal
|
||||||
return new \DateTimeZone(self::$windowsTimeZonesMap[$timeZoneString]);
|
return new \DateTimeZone(self::$windowsTimeZonesMap[$timeZoneString]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new \DateTimeZone($this->defaultTimeZone);
|
return new \DateTimeZone($this->getDefaultTimeZone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue