Overview

Namespaces

  • intouch
    • ical

Classes

  • Duration
  • Factory
  • Freq
  • iCal
  • Line
  • Parser
  • Query
  • Recurrence
  • VCalendar
  • VEvent
  • VTimeZone
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php // BUILD: Remove line
  2: 
  3: namespace intouch\ical;
  4: 
  5: /**
  6:  * Class Parser
  7:  *
  8:  * @author Morten Fangel (C) 2008
  9:  * @author Michael Kahn (C) 2013
 10:  * @license http://creativecommons.org/licenses/by-sa/2.5/dk/deed.en_GB CC-BY-SA-DK
 11:  */
 12: class Parser {
 13:     /**
 14:      * Fetches $url and passes it on to be parsed
 15:      * @param string $url
 16:      * @param intouch\ical\iCal$ical
 17:      */
 18:     public static function Parse( $url, iCal $ical ) {
 19:         $content = self::Fetch( $url );
 20:         $content = self::UnfoldLines($content);
 21:         self::_Parse( $content, $ical );
 22:     }
 23: 
 24:     /**
 25:      * Passes a text string on to be parsed
 26:      * @param string $content
 27:      * @param intouch\ical\iCal $ical
 28:      */
 29:     public static function ParseString($content, iCal $ical ) {
 30:         $content = self::UnfoldLines($content);
 31:         self::_Parse( $content, $ical );
 32:     }
 33: 
 34:     /**
 35:      * Fetches a resource and tries to make sure it's UTF8
 36:      * encoded
 37:      * @return string
 38:      */
 39:     protected static function Fetch( $resource ) {
 40:         $is_utf8 = true;
 41: 
 42:         if( is_file( $resource ) ) {
 43:             // The resource is a local file
 44:             $content = file_get_contents($resource);
 45: 
 46:             if( ! self::_ValidUtf8( $content ) ) {
 47:                 // The file doesn't appear to be UTF8
 48:                 $is_utf8 = false;
 49:             }
 50:         } else {
 51:             // The resource isn't local, so it's assumed to
 52:             // be a URL
 53:             $c = curl_init();
 54:             curl_setopt($c, CURLOPT_URL, $resource);
 55:             curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
 56:             if( !ini_get('safe_mode') && !ini_get('open_basedir') ){
 57:                 curl_setopt($c, CURLOPT_FOLLOWLOCATION, true);
 58:             }
 59:             $content = curl_exec($c);
 60: 
 61:             $ct = curl_getinfo($c, CURLINFO_CONTENT_TYPE);
 62:             $enc = preg_replace('/^.*charset=([-a-zA-Z0-9]+).*$/', '$1', $ct);
 63:             if( $ct != '' && strtolower(str_replace('-','', $enc)) != 'utf8' ) {
 64:                 // Well, the encoding says it ain't utf-8
 65:                 $is_utf8 = false;
 66:             } elseif( ! self::_ValidUtf8( $content ) ) {
 67:                 // The data isn't utf-8
 68:                 $is_utf8 = false;
 69:             }
 70:         }
 71: 
 72:         if( !$is_utf8 ) {
 73:             $content = utf8_encode($content);
 74:         }
 75: 
 76:         return $content;
 77:     }
 78: 
 79:     /**
 80:      * Takes the string $content, and creates a array of iCal lines.
 81:      * This includes unfolding multi-line entries into a single line.
 82:      * @param $content string
 83:      */
 84:     protected static function UnfoldLines($content) {
 85:         $data = array();
 86:         $content = explode("\n", $content);
 87:         for( $i=0; $i < count($content); $i++) {
 88:             $line = rtrim($content[$i]);
 89:             while( isset($content[$i+1]) && strlen($content[$i+1]) > 0 && ($content[$i+1]{0} == ' ' || $content[$i+1]{0} == "\t" )) {
 90:                 $line .= rtrim(substr($content[++$i],1));
 91:             }
 92:             $data[] = $line;
 93:         }
 94:         return $data;
 95:     }
 96: 
 97:     /**
 98:      * Parses the feed found in content and calls storeSection to store
 99:      * parsed data
100:      * @param string $content
101:      * @param intouch\ical\iCal$ical
102:      */
103:     private static function _Parse( $content, iCal $ical ) {
104:         $main_sections = array('vevent', 'vjournal', 'vtodo', 'vtimezone', 'vcalendar');
105:         $array_idents = array('exdate','rdate');
106:         $sections = array();
107:         $section = '';
108:         $current_data = array();
109: 
110:         foreach( $content AS $line ) {
111:             $line = new Line($line);
112:             if( $line->isBegin() ) {
113:                 // New block of data, $section = new block
114:                 $section = strtolower($line->getData());
115:                 $sections[] = strtolower($line->getData());
116:             } elseif( $line->isEnd() ) {
117:                 // End of block of data ($removed = just ended block, $section = new top-block)
118:                 $removed = array_pop($sections);
119:                 $section = end($sections);
120: 
121:                 if( array_search($removed, $main_sections) !== false ) {
122:                     self::StoreSection( $removed, $current_data[$removed], $ical);
123:                     $current_data[$removed] = array();
124:                 }
125:             } else {
126:                 // Data line
127:                 foreach( $main_sections AS $s ) {
128:                     // Loops though the main sections
129:                     if( array_search($s, $sections) !== false ) {
130:                         // This section is in the main section
131:                         if( $section == $s ) {
132:                             // It _is_ the main section else
133:                             if (in_array($line->getIdent(), $array_idents))
134:                                 //exdate could appears more that once
135:                                 $current_data[$s][$line->getIdent()][] = $line;
136:                             else {
137:                                 $current_data[$s][$line->getIdent()] = $line;
138:                             }
139:                         } else {
140:                             // Sub section
141:                             $current_data[$s][$section][$line->getIdent()] = $line;
142:                         }
143:                         break;
144:                     }
145:                 }
146:             }
147:         }
148:         $current_data = array();
149:     }
150: 
151:     /**
152:      * Stores the data in provided intouch\ical\iCalobject
153:      * @param string $section eg 'vcalender', 'vevent' etc
154:      * @param string $data
155:      * @param intouch\ical\iCal$ical
156:      */
157:     protected static function storeSection( $section, $data, iCal $ical ) {
158:         $data = Factory::Factory($ical, $section, $data);
159:         switch( $section ) {
160:             case 'vcalendar':
161:                 return $ical->setCalendarInfo( $data );
162:             case 'vevent':
163:                 return $ical->addEvent( $data );
164:             case 'vjournal':
165:             case 'vtodo':
166:                 return true; // TODO: Implement
167:             case 'vtimezone':
168:                 return $ical->addTimeZone( $data );
169:         }
170:     }
171: 
172:     /**
173:      * This functions does some regexp checking to see if the value is
174:      * valid UTF-8.
175:      *
176:      * The function is from the book "Building Scalable Web Sites" by
177:      * Cal Henderson.
178:      *
179:      * @param string $data
180:      * @return bool
181:      */
182:     private static function _ValidUtf8( $data ) {
183:         $rx  = '[\xC0-\xDF]([^\x80-\xBF]|$)';
184:         $rx .= '|[\xE0-\xEF].{0,1}([^\x80-\xBF]|$)';
185:         $rx .= '|[\xF0-\xF7].{0,2}([^\x80-\xBF]|$)';
186:         $rx .= '|[\xF8-\xFB].{0,3}([^\x80-\xBF]|$)';
187:         $rx .= '|[\xFC-\xFD].{0,4}([^\x80-\xBF]|$)';
188:         $rx .= '|[\xFE-\xFE].{0,5}([^\x80-\xBF]|$)';
189:         $rx .= '|[\x00-\x7F][\x80-\xBF]';
190:         $rx .= '|[\xC0-\xDF].[\x80-\xBF]';
191:         $rx .= '|[\xE0-\xEF]..[\x80-\xBF]';
192:         $rx .= '|[\xF0-\xF7]...[\x80-\xBF]';
193:         $rx .= '|[\xF8-\xFB]....[\x80-\xBF]';
194:         $rx .= '|[\xFC-\xFD].....[\x80-\xBF]';
195:         $rx .= '|[\xFE-\xFE]......[\x80-\xBF]';
196:         $rx .= '|^[\x80-\xBF]';
197: 
198:         return ( ! (bool) preg_match('!'.$rx.'!', $data) );
199:     }
200: }
201: 
202: 
203: 
intouch-iCalendar API documentation generated by ApiGen 2.8.0