]> git.mxchange.org Git - friendica-addons.git/blob - dav/sabre-vobject/lib/Sabre/VObject/Reader.php
Second part of refactoring; should be runnable again, yet not thoroughly tested
[friendica-addons.git] / dav / sabre-vobject / lib / Sabre / VObject / Reader.php
1 <?php
2
3 namespace Sabre\VObject;
4
5 /**
6  * VCALENDAR/VCARD reader
7  *
8  * This class reads the vobject file, and returns a full element tree.
9  *
10  * TODO: this class currently completely works 'statically'. This is pointless,
11  * and defeats OOP principals. Needs refactoring in a future version.
12  *
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
16  */
17 class Reader {
18
19     /**
20      * Parses the file and returns the top component
21      *
22      * @param string $data
23      * @return Element
24      */
25     static function read($data) {
26
27         // Normalizing newlines
28         $data = str_replace(array("\r","\n\n"), array("\n","\n"), $data);
29
30         $lines = explode("\n", $data);
31
32         // Unfolding lines
33         $lines2 = array();
34         foreach($lines as $line) {
35
36             // Skipping empty lines
37             if (!$line) continue;
38
39             if ($line[0]===" " || $line[0]==="\t") {
40                 $lines2[count($lines2)-1].=substr($line,1);
41             } else {
42                 $lines2[] = $line;
43             }
44
45         }
46
47         unset($lines);
48
49         reset($lines2);
50
51         return self::readLine($lines2);
52
53     }
54
55     /**
56      * Reads and parses a single line.
57      *
58      * This method receives the full array of lines. The array pointer is used
59      * to traverse.
60      *
61      * @param array $lines
62      * @return Element
63      */
64     static private function readLine(&$lines) {
65
66         $line = current($lines);
67         $lineNr = key($lines);
68         next($lines);
69
70         // Components
71         if (stripos($line,"BEGIN:")===0) {
72
73             $componentName = strtoupper(substr($line,6));
74             $obj = Component::create($componentName);
75
76             $nextLine = current($lines);
77
78             while(stripos($nextLine,"END:")!==0) {
79
80                 $obj->add(self::readLine($lines));
81
82                 $nextLine = current($lines);
83
84                 if ($nextLine===false)
85                     throw new ParseException('Invalid VObject. Document ended prematurely.');
86
87             }
88
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 . '"');
92             }
93             next($lines);
94
95             return $obj;
96
97         }
98
99         // Properties
100         //$result = preg_match('/(?P<name>[A-Z0-9-]+)(?:;(?P<parameters>^(?<!:):))(.*)$/',$line,$matches);
101
102
103         $token = '[A-Z0-9-\.]+';
104         $parameters = "(?:;(?P<parameters>([^:^\"]|\"([^\"]*)\")*))?";
105         $regex = "/^(?P<name>$token)$parameters:(?P<value>.*)$/i";
106
107         $result = preg_match($regex,$line,$matches);
108
109         if (!$result) {
110             throw new ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format');
111         }
112
113         $propertyName = strtoupper($matches['name']);
114         $propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
115             if ($matches[2]==='n' || $matches[2]==='N') {
116                 return "\n";
117             } else {
118                 return $matches[2];
119             }
120         }, $matches['value']);
121
122         $obj = Property::create($propertyName, $propertyValue);
123
124         if ($matches['parameters']) {
125
126             foreach(self::readParameters($matches['parameters']) as $param) {
127                 $obj->add($param);
128             }
129
130         }
131
132         return $obj;
133
134
135     }
136
137     /**
138      * Reads a parameter list from a property
139      *
140      * This method returns an array of Parameter
141      *
142      * @param string $parameters
143      * @return array
144      */
145     static private function readParameters($parameters) {
146
147         $token = '[A-Z0-9-]+';
148
149         $paramValue = '(?P<paramValue>[^\"^;]*|"[^"]*")';
150
151         $regex = "/(?<=^|;)(?P<paramName>$token)(=$paramValue(?=$|;))?/i";
152         preg_match_all($regex, $parameters, $matches,  PREG_SET_ORDER);
153
154         $params = array();
155         foreach($matches as $match) {
156
157             $value = isset($match['paramValue'])?$match['paramValue']:null;
158
159             if (isset($value[0])) {
160                 // Stripping quotes, if needed
161                 if ($value[0] === '"') $value = substr($value,1,strlen($value)-2);
162             } else {
163                 $value = '';
164             }
165
166             $value = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
167                 if ($matches[2]==='n' || $matches[2]==='N') {
168                     return "\n";
169                 } else {
170                     return $matches[2];
171                 }
172             }, $value);
173
174             $params[] = new Parameter($match['paramName'], $value);
175
176         }
177
178         return $params;
179
180     }
181
182
183 }