From c85702aeb61a0d496d123c63b322b728b78ff0f7 Mon Sep 17 00:00:00 2001 From: friendica Date: Mon, 22 Dec 2014 16:28:30 -0800 Subject: basic ical parser --- library/ical.php | 321 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100644 library/ical.php diff --git a/library/ical.php b/library/ical.php new file mode 100644 index 000000000..d49c71460 --- /dev/null +++ b/library/ical.php @@ -0,0 +1,321 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @version SVN: + * @link http://code.google.com/p/ics-parser/ + * @example $ical = new ical('MyCal.ics'); + * print_r( $ical->events() ); + */ + +error_reporting(E_ALL); + +/** + * This is the iCal-class + * + * @category Parser + * @package Ics-parser + * @author Martin Thoma + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @link http://code.google.com/p/ics-parser/ + * + * @param {string} filename The name of the file which should be parsed + * @constructor + */ +class ICal +{ + /* How many ToDos are in this ical? */ + public /** @type {int} */ $todo_count = 0; + + /* How many events are in this ical? */ + public /** @type {int} */ $event_count = 0; + + /* The parsed calendar */ + public /** @type {Array} */ $cal; + + /* Which keyword has been added to cal at last? */ + private /** @type {string} */ $_lastKeyWord; + + /** + * Creates the iCal-Object + * + * @param {string} $filename The path to the iCal-file + * + * @return Object The iCal-Object + */ + public function __construct($filename) + { + if (!$filename) { + return false; + } + + $lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (stristr($lines[0], 'BEGIN:VCALENDAR') === false) { + return false; + } else { + // TODO: Fix multiline-description problem (see http://tools.ietf.org/html/rfc2445#section-4.8.1.5) + foreach ($lines as $line) { + $line = trim($line); + $add = $this->keyValueFromString($line); + if ($add === false) { + $this->addCalendarComponentWithKeyAndValue($type, false, $line); + continue; + } + + list($keyword, $value) = $add; + + switch ($line) { + // http://www.kanzaki.com/docs/ical/vtodo.html + case "BEGIN:VTODO": + $this->todo_count++; + $type = "VTODO"; + break; + + // http://www.kanzaki.com/docs/ical/vevent.html + case "BEGIN:VEVENT": + //echo "vevent gematcht"; + $this->event_count++; + $type = "VEVENT"; + break; + + //all other special strings + case "BEGIN:VCALENDAR": + case "BEGIN:DAYLIGHT": + // http://www.kanzaki.com/docs/ical/vtimezone.html + case "BEGIN:VTIMEZONE": + case "BEGIN:STANDARD": + $type = $value; + break; + case "END:VTODO": // end special text - goto VCALENDAR key + case "END:VEVENT": + case "END:VCALENDAR": + case "END:DAYLIGHT": + case "END:VTIMEZONE": + case "END:STANDARD": + $type = "VCALENDAR"; + break; + default: + $this->addCalendarComponentWithKeyAndValue($type, + $keyword, + $value); + break; + } + } + return $this->cal; + } + } + + /** + * Add to $this->ical array one value and key. + * + * @param {string} $component This could be VTODO, VEVENT, VCALENDAR, ... + * @param {string} $keyword The keyword, for example DTSTART + * @param {string} $value The value, for example 20110105T090000Z + * + * @return {None} + */ + public function addCalendarComponentWithKeyAndValue($component, + $keyword, + $value) + { + if ($keyword == false) { + $keyword = $this->last_keyword; + switch ($component) { + case 'VEVENT': + $value = $this->cal[$component][$this->event_count - 1] + [$keyword].$value; + break; + case 'VTODO' : + $value = $this->cal[$component][$this->todo_count - 1] + [$keyword].$value; + break; + } + } + + if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) { + $keyword = explode(";", $keyword); + $keyword = $keyword[0]; + } + + switch ($component) { + case "VTODO": + $this->cal[$component][$this->todo_count - 1][$keyword] = $value; + //$this->cal[$component][$this->todo_count]['Unix'] = $unixtime; + break; + case "VEVENT": + $this->cal[$component][$this->event_count - 1][$keyword] = $value; + break; + default: + $this->cal[$component][$keyword] = $value; + break; + } + $this->last_keyword = $keyword; + } + + /** + * Get a key-value pair of a string. + * + * @param {string} $text which is like "VCALENDAR:Begin" or "LOCATION:" + * + * @return {array} array("VCALENDAR", "Begin") + */ + public function keyValueFromString($text) + { + preg_match("/([^:]+)[:]([\w\W]*)/", $text, $matches); + if (count($matches) == 0) { + return false; + } + $matches = array_splice($matches, 1, 2); + return $matches; + } + + /** + * Return Unix timestamp from ical date time format + * + * @param {string} $icalDate A Date in the format YYYYMMDD[T]HHMMSS[Z] or + * YYYYMMDD[T]HHMMSS + * + * @return {int} + */ + public function iCalDateToUnixTimestamp($icalDate) + { + $icalDate = str_replace('T', '', $icalDate); + $icalDate = str_replace('Z', '', $icalDate); + + $pattern = '/([0-9]{4})'; // 1: YYYY + $pattern .= '([0-9]{2})'; // 2: MM + $pattern .= '([0-9]{2})'; // 3: DD + $pattern .= '([0-9]{0,2})'; // 4: HH + $pattern .= '([0-9]{0,2})'; // 5: MM + $pattern .= '([0-9]{0,2})/'; // 6: SS + preg_match($pattern, $icalDate, $date); + + // Unix timestamp can't represent dates before 1970 + if ($date[1] <= 1970) { + return false; + } + // Unix timestamps after 03:14:07 UTC 2038-01-19 might cause an overflow + // if 32 bit integers are used. + $timestamp = mktime((int)$date[4], + (int)$date[5], + (int)$date[6], + (int)$date[2], + (int)$date[3], + (int)$date[1]); + return $timestamp; + } + + /** + * Returns an array of arrays with all events. Every event is an associative + * array and each property is an element it. + * + * @return {array} + */ + public function events() + { + $array = $this->cal; + return $array['VEVENT']; + } + + /** + * Returns a boolean value whether thr current calendar has events or not + * + * @return {boolean} + */ + public function hasEvents() + { + return ( count($this->events()) > 0 ? true : false ); + } + + /** + * Returns false when the current calendar has no events in range, else the + * events. + * + * Note that this function makes use of a UNIX timestamp. This might be a + * problem on January the 29th, 2038. + * See http://en.wikipedia.org/wiki/Unix_time#Representing_the_number + * + * @param {boolean} $rangeStart Either true or false + * @param {boolean} $rangeEnd Either true or false + * + * @return {mixed} + */ + public function eventsFromRange($rangeStart = false, $rangeEnd = false) + { + $events = $this->sortEventsWithOrder($this->events(), SORT_ASC); + + if (!$events) { + return false; + } + + $extendedEvents = array(); + + if ($rangeStart !== false) { + $rangeStart = new DateTime(); + } + + if ($rangeEnd !== false or $rangeEnd <= 0) { + $rangeEnd = new DateTime('2038/01/18'); + } else { + $rangeEnd = new DateTime($rangeEnd); + } + + $rangeStart = $rangeStart->format('U'); + $rangeEnd = $rangeEnd->format('U'); + + + + // loop through all events by adding two new elements + foreach ($events as $anEvent) { + $timestamp = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']); + if ($timestamp >= $rangeStart && $timestamp <= $rangeEnd) { + $extendedEvents[] = $anEvent; + } + } + + return $extendedEvents; + } + + /** + * Returns a boolean value whether thr current calendar has events or not + * + * @param {array} $events An array with events. + * @param {array} $sortOrder Either SORT_ASC, SORT_DESC, SORT_REGULAR, + * SORT_NUMERIC, SORT_STRING + * + * @return {boolean} + */ + public function sortEventsWithOrder($events, $sortOrder = SORT_ASC) + { + $extendedEvents = array(); + + // loop through all events by adding two new elements + foreach ($events as $anEvent) { + if (!array_key_exists('UNIX_TIMESTAMP', $anEvent)) { + $anEvent['UNIX_TIMESTAMP'] = + $this->iCalDateToUnixTimestamp($anEvent['DTSTART']); + } + + if (!array_key_exists('REAL_DATETIME', $anEvent)) { + $anEvent['REAL_DATETIME'] = + date("d.m.Y", $anEvent['UNIX_TIMESTAMP']); + } + + $extendedEvents[] = $anEvent; + } + + foreach ($extendedEvents as $key => $value) { + $timestamp[$key] = $value['UNIX_TIMESTAMP']; + } + array_multisort($timestamp, $sortOrder, $extendedEvents); + + return $extendedEvents; + } +} -- cgit v1.2.3