Mercurial > hg > rc1
comparison vendor/sabre/vobject/lib/Property.php @ 7:430dbd5346f7
vendor sabre as distributed
author | Charlie Root |
---|---|
date | Sat, 13 Jan 2018 09:06:10 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
6:cec75ba50afc | 7:430dbd5346f7 |
---|---|
1 <?php | |
2 | |
3 namespace Sabre\VObject; | |
4 | |
5 /** | |
6 * Property | |
7 * | |
8 * A property is always in a KEY:VALUE structure, and may optionally contain | |
9 * parameters. | |
10 * | |
11 * @copyright Copyright (C) 2011-2015 fruux GmbH (https://fruux.com/). | |
12 * @author Evert Pot (http://evertpot.com/) | |
13 * @license http://sabre.io/license/ Modified BSD License | |
14 */ | |
15 abstract class Property extends Node { | |
16 | |
17 /** | |
18 * Property name. | |
19 * | |
20 * This will contain a string such as DTSTART, SUMMARY, FN. | |
21 * | |
22 * @var string | |
23 */ | |
24 public $name; | |
25 | |
26 /** | |
27 * Property group. | |
28 * | |
29 * This is only used in vcards | |
30 * | |
31 * @var string | |
32 */ | |
33 public $group; | |
34 | |
35 /** | |
36 * List of parameters | |
37 * | |
38 * @var array | |
39 */ | |
40 public $parameters = array(); | |
41 | |
42 /** | |
43 * Current value | |
44 * | |
45 * @var mixed | |
46 */ | |
47 protected $value; | |
48 | |
49 /** | |
50 * In case this is a multi-value property. This string will be used as a | |
51 * delimiter. | |
52 * | |
53 * @var string|null | |
54 */ | |
55 public $delimiter = ';'; | |
56 | |
57 /** | |
58 * Creates the generic property. | |
59 * | |
60 * Parameters must be specified in key=>value syntax. | |
61 * | |
62 * @param Component $root The root document | |
63 * @param string $name | |
64 * @param string|array|null $value | |
65 * @param array $parameters List of parameters | |
66 * @param string $group The vcard property group | |
67 * @return void | |
68 */ | |
69 public function __construct(Component $root, $name, $value = null, array $parameters = array(), $group = null) { | |
70 | |
71 $this->name = $name; | |
72 $this->group = $group; | |
73 | |
74 $this->root = $root; | |
75 | |
76 foreach($parameters as $k=>$v) { | |
77 $this->add($k, $v); | |
78 } | |
79 | |
80 if (!is_null($value)) { | |
81 $this->setValue($value); | |
82 } | |
83 | |
84 } | |
85 | |
86 /** | |
87 * Updates the current value. | |
88 * | |
89 * This may be either a single, or multiple strings in an array. | |
90 * | |
91 * @param string|array $value | |
92 * @return void | |
93 */ | |
94 public function setValue($value) { | |
95 | |
96 $this->value = $value; | |
97 | |
98 } | |
99 | |
100 /** | |
101 * Returns the current value. | |
102 * | |
103 * This method will always return a singular value. If this was a | |
104 * multi-value object, some decision will be made first on how to represent | |
105 * it as a string. | |
106 * | |
107 * To get the correct multi-value version, use getParts. | |
108 * | |
109 * @return string | |
110 */ | |
111 public function getValue() { | |
112 | |
113 if (is_array($this->value)) { | |
114 if (count($this->value)==0) { | |
115 return null; | |
116 } elseif (count($this->value)===1) { | |
117 return $this->value[0]; | |
118 } else { | |
119 return $this->getRawMimeDirValue($this->value); | |
120 } | |
121 } else { | |
122 return $this->value; | |
123 } | |
124 | |
125 } | |
126 | |
127 /** | |
128 * Sets a multi-valued property. | |
129 * | |
130 * @param array $parts | |
131 * @return void | |
132 */ | |
133 public function setParts(array $parts) { | |
134 | |
135 $this->value = $parts; | |
136 | |
137 } | |
138 | |
139 /** | |
140 * Returns a multi-valued property. | |
141 * | |
142 * This method always returns an array, if there was only a single value, | |
143 * it will still be wrapped in an array. | |
144 * | |
145 * @return array | |
146 */ | |
147 public function getParts() { | |
148 | |
149 if (is_null($this->value)) { | |
150 return array(); | |
151 } elseif (is_array($this->value)) { | |
152 return $this->value; | |
153 } else { | |
154 return array($this->value); | |
155 } | |
156 | |
157 } | |
158 | |
159 /** | |
160 * Adds a new parameter, and returns the new item. | |
161 * | |
162 * If a parameter with same name already existed, the values will be | |
163 * combined. | |
164 * If nameless parameter is added, we try to guess it's name. | |
165 * | |
166 * @param string $name | |
167 * @param string|null|array $value | |
168 * @return Node | |
169 */ | |
170 public function add($name, $value = null) { | |
171 $noName = false; | |
172 if ($name === null) { | |
173 $name = Parameter::guessParameterNameByValue($value); | |
174 $noName = true; | |
175 } | |
176 | |
177 if (isset($this->parameters[strtoupper($name)])) { | |
178 $this->parameters[strtoupper($name)]->addValue($value); | |
179 } | |
180 else { | |
181 $param = new Parameter($this->root, $name, $value); | |
182 $param->noName = $noName; | |
183 $this->parameters[$param->name] = $param; | |
184 } | |
185 } | |
186 | |
187 /** | |
188 * Returns an iterable list of children | |
189 * | |
190 * @return array | |
191 */ | |
192 public function parameters() { | |
193 | |
194 return $this->parameters; | |
195 | |
196 } | |
197 | |
198 /** | |
199 * Returns the type of value. | |
200 * | |
201 * This corresponds to the VALUE= parameter. Every property also has a | |
202 * 'default' valueType. | |
203 * | |
204 * @return string | |
205 */ | |
206 abstract public function getValueType(); | |
207 | |
208 /** | |
209 * Sets a raw value coming from a mimedir (iCalendar/vCard) file. | |
210 * | |
211 * This has been 'unfolded', so only 1 line will be passed. Unescaping is | |
212 * not yet done, but parameters are not included. | |
213 * | |
214 * @param string $val | |
215 * @return void | |
216 */ | |
217 abstract public function setRawMimeDirValue($val); | |
218 | |
219 /** | |
220 * Returns a raw mime-dir representation of the value. | |
221 * | |
222 * @return string | |
223 */ | |
224 abstract public function getRawMimeDirValue(); | |
225 | |
226 /** | |
227 * Turns the object back into a serialized blob. | |
228 * | |
229 * @return string | |
230 */ | |
231 public function serialize() { | |
232 | |
233 $str = $this->name; | |
234 if ($this->group) $str = $this->group . '.' . $this->name; | |
235 | |
236 foreach($this->parameters as $param) { | |
237 | |
238 $str.=';' . $param->serialize(); | |
239 | |
240 } | |
241 | |
242 $str.=':' . $this->getRawMimeDirValue(); | |
243 | |
244 $out = ''; | |
245 while(strlen($str)>0) { | |
246 if (strlen($str)>75) { | |
247 $out.= mb_strcut($str,0,75,'utf-8') . "\r\n"; | |
248 $str = ' ' . mb_strcut($str,75,strlen($str),'utf-8'); | |
249 } else { | |
250 $out.=$str . "\r\n"; | |
251 $str=''; | |
252 break; | |
253 } | |
254 } | |
255 | |
256 return $out; | |
257 | |
258 } | |
259 | |
260 /** | |
261 * Returns the value, in the format it should be encoded for json. | |
262 * | |
263 * This method must always return an array. | |
264 * | |
265 * @return array | |
266 */ | |
267 public function getJsonValue() { | |
268 | |
269 return $this->getParts(); | |
270 | |
271 } | |
272 | |
273 /** | |
274 * Sets the json value, as it would appear in a jCard or jCal object. | |
275 * | |
276 * The value must always be an array. | |
277 * | |
278 * @param array $value | |
279 * @return void | |
280 */ | |
281 public function setJsonValue(array $value) { | |
282 | |
283 if (count($value)===1) { | |
284 $this->setValue(reset($value)); | |
285 } else { | |
286 $this->setValue($value); | |
287 } | |
288 | |
289 } | |
290 | |
291 /** | |
292 * This method returns an array, with the representation as it should be | |
293 * encoded in json. This is used to create jCard or jCal documents. | |
294 * | |
295 * @return array | |
296 */ | |
297 public function jsonSerialize() { | |
298 | |
299 $parameters = array(); | |
300 | |
301 foreach($this->parameters as $parameter) { | |
302 if ($parameter->name === 'VALUE') { | |
303 continue; | |
304 } | |
305 $parameters[strtolower($parameter->name)] = $parameter->jsonSerialize(); | |
306 } | |
307 // In jCard, we need to encode the property-group as a separate 'group' | |
308 // parameter. | |
309 if ($this->group) { | |
310 $parameters['group'] = $this->group; | |
311 } | |
312 | |
313 return array_merge( | |
314 array( | |
315 strtolower($this->name), | |
316 (object)$parameters, | |
317 strtolower($this->getValueType()), | |
318 ), | |
319 $this->getJsonValue() | |
320 ); | |
321 } | |
322 | |
323 | |
324 /** | |
325 * Called when this object is being cast to a string. | |
326 * | |
327 * If the property only had a single value, you will get just that. In the | |
328 * case the property had multiple values, the contents will be escaped and | |
329 * combined with ,. | |
330 * | |
331 * @return string | |
332 */ | |
333 public function __toString() { | |
334 | |
335 return (string)$this->getValue(); | |
336 | |
337 } | |
338 | |
339 /* ArrayAccess interface {{{ */ | |
340 | |
341 /** | |
342 * Checks if an array element exists | |
343 * | |
344 * @param mixed $name | |
345 * @return bool | |
346 */ | |
347 public function offsetExists($name) { | |
348 | |
349 if (is_int($name)) return parent::offsetExists($name); | |
350 | |
351 $name = strtoupper($name); | |
352 | |
353 foreach($this->parameters as $parameter) { | |
354 if ($parameter->name == $name) return true; | |
355 } | |
356 return false; | |
357 | |
358 } | |
359 | |
360 /** | |
361 * Returns a parameter. | |
362 * | |
363 * If the parameter does not exist, null is returned. | |
364 * | |
365 * @param string $name | |
366 * @return Node | |
367 */ | |
368 public function offsetGet($name) { | |
369 | |
370 if (is_int($name)) return parent::offsetGet($name); | |
371 $name = strtoupper($name); | |
372 | |
373 if (!isset($this->parameters[$name])) { | |
374 return null; | |
375 } | |
376 | |
377 return $this->parameters[$name]; | |
378 | |
379 } | |
380 | |
381 /** | |
382 * Creates a new parameter | |
383 * | |
384 * @param string $name | |
385 * @param mixed $value | |
386 * @return void | |
387 */ | |
388 public function offsetSet($name, $value) { | |
389 | |
390 if (is_int($name)) { | |
391 parent::offsetSet($name, $value); | |
392 // @codeCoverageIgnoreStart | |
393 // This will never be reached, because an exception is always | |
394 // thrown. | |
395 return; | |
396 // @codeCoverageIgnoreEnd | |
397 } | |
398 | |
399 $param = new Parameter($this->root, $name, $value); | |
400 $this->parameters[$param->name] = $param; | |
401 | |
402 } | |
403 | |
404 /** | |
405 * Removes one or more parameters with the specified name | |
406 * | |
407 * @param string $name | |
408 * @return void | |
409 */ | |
410 public function offsetUnset($name) { | |
411 | |
412 if (is_int($name)) { | |
413 parent::offsetUnset($name); | |
414 // @codeCoverageIgnoreStart | |
415 // This will never be reached, because an exception is always | |
416 // thrown. | |
417 return; | |
418 // @codeCoverageIgnoreEnd | |
419 } | |
420 | |
421 unset($this->parameters[strtoupper($name)]); | |
422 | |
423 } | |
424 /* }}} */ | |
425 | |
426 /** | |
427 * This method is automatically called when the object is cloned. | |
428 * Specifically, this will ensure all child elements are also cloned. | |
429 * | |
430 * @return void | |
431 */ | |
432 public function __clone() { | |
433 | |
434 foreach($this->parameters as $key=>$child) { | |
435 $this->parameters[$key] = clone $child; | |
436 $this->parameters[$key]->parent = $this; | |
437 } | |
438 | |
439 } | |
440 | |
441 /** | |
442 * Validates the node for correctness. | |
443 * | |
444 * The following options are supported: | |
445 * - Node::REPAIR - If something is broken, and automatic repair may | |
446 * be attempted. | |
447 * | |
448 * An array is returned with warnings. | |
449 * | |
450 * Every item in the array has the following properties: | |
451 * * level - (number between 1 and 3 with severity information) | |
452 * * message - (human readable message) | |
453 * * node - (reference to the offending node) | |
454 * | |
455 * @param int $options | |
456 * @return array | |
457 */ | |
458 public function validate($options = 0) { | |
459 | |
460 $warnings = array(); | |
461 | |
462 // Checking if our value is UTF-8 | |
463 if (!StringUtil::isUTF8($this->getRawMimeDirValue())) { | |
464 | |
465 $oldValue = $this->getRawMimeDirValue(); | |
466 $level = 3; | |
467 if ($options & self::REPAIR) { | |
468 $newValue = StringUtil::convertToUTF8($oldValue); | |
469 if (true || StringUtil::isUTF8($newValue)) { | |
470 $this->setRawMimeDirValue($newValue); | |
471 $level = 1; | |
472 } | |
473 | |
474 } | |
475 | |
476 | |
477 if (preg_match('%([\x00-\x08\x0B-\x0C\x0E-\x1F\x7F])%', $oldValue, $matches)) { | |
478 $message = 'Property contained a control character (0x' . bin2hex($matches[1]) . ')'; | |
479 } else { | |
480 $message = 'Property is not valid UTF-8! ' . $oldValue; | |
481 } | |
482 | |
483 $warnings[] = array( | |
484 'level' => $level, | |
485 'message' => $message, | |
486 'node' => $this, | |
487 ); | |
488 } | |
489 | |
490 // Checking if the propertyname does not contain any invalid bytes. | |
491 if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) { | |
492 $warnings[] = array( | |
493 'level' => 1, | |
494 'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed', | |
495 'node' => $this, | |
496 ); | |
497 if ($options & self::REPAIR) { | |
498 // Uppercasing and converting underscores to dashes. | |
499 $this->name = strtoupper( | |
500 str_replace('_', '-', $this->name) | |
501 ); | |
502 // Removing every other invalid character | |
503 $this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name); | |
504 | |
505 } | |
506 | |
507 } | |
508 | |
509 // Validating inner parameters | |
510 foreach($this->parameters as $param) { | |
511 $warnings = array_merge($warnings, $param->validate($options)); | |
512 } | |
513 | |
514 return $warnings; | |
515 | |
516 } | |
517 | |
518 } |