Created
November 20, 2013 10:30
-
-
Save charlycoste/7560985 to your computer and use it in GitHub Desktop.
iCalendar parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* This PHP-Class should only read a iCal-File (*.ics), parse it and give an | |
* array with its content. | |
* | |
* PHP Version 5 | |
* | |
* @category Parser | |
* @package Ics-parser | |
* @author Martin Thoma <info@martin-thoma.de> | |
* @license http://www.opensource.org/licenses/mit-license.php MIT License | |
* @version SVN: <svn_id> | |
* @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 <info@martin-thoma.de> | |
* @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; | |
} | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment