3 namespace Sabre\VObject;
6 * VCALENDAR/VCARD reader
8 * This class reads the vobject file, and returns a full element tree.
10 * TODO: this class currently completely works 'statically'. This is pointless,
11 * and defeats OOP principals. Needs refactoring in a future version.
13 * @copyright Copyright (C) 2007-2012 Rooftop Solutions. All rights reserved.
14 * @author Evert Pot (http://www.rooftopsolutions.nl/)
15 * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
20 * Parses the file and returns the top component
25 static function read($data) {
27 // Normalizing newlines
28 $data = str_replace(array("\r","\n\n"), array("\n","\n"), $data);
30 $lines = explode("\n", $data);
34 foreach($lines as $line) {
36 // Skipping empty lines
39 if ($line[0]===" " || $line[0]==="\t") {
40 $lines2[count($lines2)-1].=substr($line,1);
51 return self::readLine($lines2);
56 * Reads and parses a single line.
58 * This method receives the full array of lines. The array pointer is used
64 static private function readLine(&$lines) {
66 $line = current($lines);
67 $lineNr = key($lines);
71 if (stripos($line,"BEGIN:")===0) {
73 $componentName = strtoupper(substr($line,6));
74 $obj = Component::create($componentName);
76 $nextLine = current($lines);
78 while(stripos($nextLine,"END:")!==0) {
80 $obj->add(self::readLine($lines));
82 $nextLine = current($lines);
84 if ($nextLine===false)
85 throw new ParseException('Invalid VObject. Document ended prematurely.');
89 // Checking component name of the 'END:' line.
90 if (substr($nextLine,4)!==$obj->name) {
91 throw new ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"');
100 //$result = preg_match('/(?P<name>[A-Z0-9-]+)(?:;(?P<parameters>^(?<!:):))(.*)$/',$line,$matches);
103 $token = '[A-Z0-9-\.]+';
104 $parameters = "(?:;(?P<parameters>([^:^\"]|\"([^\"]*)\")*))?";
105 $regex = "/^(?P<name>$token)$parameters:(?P<value>.*)$/i";
107 $result = preg_match($regex,$line,$matches);
110 throw new ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format');
113 $propertyName = strtoupper($matches['name']);
114 $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
115 if ($matches[2]==='n' || $matches[2]==='N') {
120 }, $matches['value']);
122 $obj = Property::create($propertyName, $propertyValue);
124 if ($matches['parameters']) {
126 foreach(self::readParameters($matches['parameters']) as $param) {
138 * Reads a parameter list from a property
140 * This method returns an array of Parameter
142 * @param string $parameters
145 static private function readParameters($parameters) {
147 $token = '[A-Z0-9-]+';
149 $paramValue = '(?P<paramValue>[^\"^;]*|"[^"]*")';
151 $regex = "/(?<=^|;)(?P<paramName>$token)(=$paramValue(?=$|;))?/i";
152 preg_match_all($regex, $parameters, $matches, PREG_SET_ORDER);
155 foreach($matches as $match) {
157 $value = isset($match['paramValue'])?$match['paramValue']:null;
159 if (isset($value[0])) {
160 // Stripping quotes, if needed
161 if ($value[0] === '"') $value = substr($value,1,strlen($value)-2);
166 $value = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
167 if ($matches[2]==='n' || $matches[2]==='N') {
174 $params[] = new Parameter($match['paramName'], $value);