1 | <?php |
||
13 | class ICalParser |
||
14 | { |
||
15 | |||
16 | /** @var ICalTimeZone **/ |
||
17 | protected $timezone; |
||
18 | |||
19 | protected $events = array(); |
||
20 | |||
21 | public function __construct() { |
||
22 | $this->timezone = new ICalTimeZone(); |
||
23 | } |
||
24 | |||
25 | /** |
||
26 | * @param {string} $filename The path to the iCal-file |
||
27 | * @return Object The iCal-Object |
||
28 | */ |
||
29 | public function parseFromFile($filename) |
||
30 | { |
||
31 | |||
32 | if (!$filename) { |
||
33 | return false; |
||
34 | } |
||
35 | |||
36 | $lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); |
||
37 | if (count($lines) < 1) { |
||
38 | return false; |
||
39 | } |
||
40 | |||
41 | if (stristr($lines[0], 'BEGIN:VCALENDAR') === false) { |
||
42 | return false; |
||
43 | } else { |
||
44 | |||
45 | # pass1: put multi lines back into one line. |
||
46 | $linesCompacted = array(); |
||
47 | foreach($lines as $line) { |
||
48 | if (substr($line,0,1) == ' ') { |
||
49 | $linesCompacted[count($linesCompacted)-1] .= substr($line, 1); |
||
50 | } else { |
||
51 | $linesCompacted[] = trim($line); |
||
52 | } |
||
53 | }; |
||
54 | |||
55 | #pass2: turn lines into formatted data |
||
56 | $rawLines = array(); |
||
57 | foreach($linesCompacted as $line) { |
||
58 | $bits = explode(":", $line, 2); |
||
59 | $kbits = explode(";", $bits[0], 2); |
||
60 | $value = count($bits) == 2 ? $bits[1] : ''; |
||
61 | // http://www.ietf.org/rfc/rfc2445.txt section 4.3.11 |
||
62 | $value = str_replace('\\\\', '\\', $value); |
||
63 | $value = str_replace('\\N', "\n", $value); |
||
64 | $value = str_replace('\\n', "\n", $value); |
||
65 | $value = str_replace('\\;', ';', $value); |
||
66 | $value = str_replace('\,', ',', $value); |
||
67 | $rawLines[] = array( |
||
68 | 'KEYWORD'=>$kbits[0], |
||
69 | 'KEYWORDOPTIONS'=>count($kbits) == 2 ? $kbits[1] : '', |
||
70 | 'VALUE'=>$value, |
||
71 | ); |
||
72 | } |
||
73 | |||
74 | # pass3: finally parse lines |
||
75 | $stack = array(); |
||
76 | foreach($rawLines as $line) { |
||
77 | if ($line['KEYWORD'] == 'BEGIN') { |
||
78 | $stack[] = $line['VALUE']; |
||
79 | if ($line['VALUE'] == 'VEVENT') { |
||
80 | $this->events[] = new ICalEvent($this->timezone->getTimeZone()); |
||
81 | } |
||
82 | } else if ($line['KEYWORD'] == 'END') { |
||
83 | // TODO check VALUE and last stack match |
||
84 | array_pop($stack); |
||
85 | } else { |
||
86 | $currentlyIn = $stack[count($stack)-1]; |
||
87 | //print $currentlyIn." with K ".$line['KEYWORD']."\n"; |
||
88 | if ($currentlyIn == 'VEVENT') { |
||
89 | $this->events[count($this->events)-1]->processLine($line['KEYWORD'],$line['VALUE'], $line['KEYWORDOPTIONS']); |
||
90 | } elseif ($currentlyIn == 'VTIMEZONE') { |
||
91 | $this->timezone->processLine($line['KEYWORD'],$line['VALUE'], $line['KEYWORDOPTIONS']); |
||
92 | } |
||
93 | } |
||
94 | } |
||
95 | |||
96 | //die(); |
||
97 | |||
98 | return true; |
||
99 | } |
||
100 | } |
||
101 | |||
102 | /** |
||
103 | * This is only for debugging. |
||
104 | * This class ensures all dates returned are in UTC, whatever the input time zone was. |
||
105 | * @return type |
||
106 | */ |
||
107 | public function getTimeZoneIdentifier() |
||
108 | { |
||
109 | return $this->timezone->getTimeZoneIdentifier(); |
||
110 | } |
||
111 | |||
112 | public function getEvents() |
||
113 | { |
||
114 | return $this->events; |
||
115 | } |
||
116 | |||
117 | } |
||
118 | |||
120 |