1<?php 2 3namespace Sabre\VObject; 4 5use 6 ArrayObject; 7 8/** 9 * VObject Parameter 10 * 11 * This class represents a parameter. A parameter is always tied to a property. 12 * In the case of: 13 * DTSTART;VALUE=DATE:20101108 14 * VALUE=DATE would be the parameter name and value. 15 * 16 * @copyright Copyright (C) fruux GmbH (https://fruux.com/) 17 * @author Evert Pot (http://evertpot.com/) 18 * @license http://sabre.io/license/ Modified BSD License 19 */ 20class Parameter extends Node { 21 22 /** 23 * Parameter name 24 * 25 * @var string 26 */ 27 public $name; 28 29 /** 30 * vCard 2.1 allows parameters to be encoded without a name. 31 * 32 * We can deduce the parameter name based on it's value. 33 * 34 * @var bool 35 */ 36 public $noName = false; 37 38 /** 39 * Parameter value 40 * 41 * @var string 42 */ 43 protected $value; 44 45 /** 46 * Sets up the object. 47 * 48 * It's recommended to use the create:: factory method instead. 49 * 50 * @param string $name 51 * @param string $value 52 */ 53 public function __construct(Document $root, $name, $value = null) { 54 55 $this->name = strtoupper($name); 56 $this->root = $root; 57 if (is_null($name)) { 58 $this->noName = true; 59 $this->name = static::guessParameterNameByValue($value); 60 } 61 62 // If guessParameterNameByValue() returns an empty string 63 // above, we're actually dealing with a parameter that has no value. 64 // In that case we have to move the value to the name. 65 if ($this->name === '') { 66 $this->noName = false; 67 $this->name = strtoupper($value); 68 } else { 69 $this->setValue($value); 70 } 71 72 } 73 74 /** 75 * Try to guess property name by value, can be used for vCard 2.1 nameless parameters. 76 * 77 * Figuring out what the name should have been. Note that a ton of 78 * these are rather silly in 2014 and would probably rarely be 79 * used, but we like to be complete. 80 * 81 * @param string $value 82 * @return string 83 */ 84 public static function guessParameterNameByValue($value) { 85 switch(strtoupper($value)) { 86 87 // Encodings 88 case '7-BIT' : 89 case 'QUOTED-PRINTABLE' : 90 case 'BASE64' : 91 $name = 'ENCODING'; 92 break; 93 94 // Common types 95 case 'WORK' : 96 case 'HOME' : 97 case 'PREF' : 98 99 // Delivery Label Type 100 case 'DOM' : 101 case 'INTL' : 102 case 'POSTAL' : 103 case 'PARCEL' : 104 105 // Telephone types 106 case 'VOICE' : 107 case 'FAX' : 108 case 'MSG' : 109 case 'CELL' : 110 case 'PAGER' : 111 case 'BBS' : 112 case 'MODEM' : 113 case 'CAR' : 114 case 'ISDN' : 115 case 'VIDEO' : 116 117 // EMAIL types (lol) 118 case 'AOL' : 119 case 'APPLELINK' : 120 case 'ATTMAIL' : 121 case 'CIS' : 122 case 'EWORLD' : 123 case 'INTERNET' : 124 case 'IBMMAIL' : 125 case 'MCIMAIL' : 126 case 'POWERSHARE' : 127 case 'PRODIGY' : 128 case 'TLX' : 129 case 'X400' : 130 131 // Photo / Logo format types 132 case 'GIF' : 133 case 'CGM' : 134 case 'WMF' : 135 case 'BMP' : 136 case 'DIB' : 137 case 'PICT' : 138 case 'TIFF' : 139 case 'PDF ': 140 case 'PS' : 141 case 'JPEG' : 142 case 'MPEG' : 143 case 'MPEG2' : 144 case 'AVI' : 145 case 'QTIME' : 146 147 // Sound Digital Audio Type 148 case 'WAVE' : 149 case 'PCM' : 150 case 'AIFF' : 151 152 // Key types 153 case 'X509' : 154 case 'PGP' : 155 $name = 'TYPE'; 156 break; 157 158 // Value types 159 case 'INLINE' : 160 case 'URL' : 161 case 'CONTENT-ID' : 162 case 'CID' : 163 $name = 'VALUE'; 164 break; 165 166 default: 167 $name = ''; 168 } 169 170 return $name; 171 } 172 173 /** 174 * Updates the current value. 175 * 176 * This may be either a single, or multiple strings in an array. 177 * 178 * @param string|array $value 179 * @return void 180 */ 181 public function setValue($value) { 182 183 $this->value = $value; 184 185 } 186 187 /** 188 * Returns the current value 189 * 190 * This method will always return a string, or null. If there were multiple 191 * values, it will automatically concatinate them (separated by comma). 192 * 193 * @return string|null 194 */ 195 public function getValue() { 196 197 if (is_array($this->value)) { 198 return implode(',' , $this->value); 199 } else { 200 return $this->value; 201 } 202 203 } 204 205 /** 206 * Sets multiple values for this parameter. 207 * 208 * @param array $value 209 * @return void 210 */ 211 public function setParts(array $value) { 212 213 $this->value = $value; 214 215 } 216 217 /** 218 * Returns all values for this parameter. 219 * 220 * If there were no values, an empty array will be returned. 221 * 222 * @return array 223 */ 224 public function getParts() { 225 226 if (is_array($this->value)) { 227 return $this->value; 228 } elseif (is_null($this->value)) { 229 return array(); 230 } else { 231 return array($this->value); 232 } 233 234 } 235 236 /** 237 * Adds a value to this parameter 238 * 239 * If the argument is specified as an array, all items will be added to the 240 * parameter value list. 241 * 242 * @param string|array $part 243 * @return void 244 */ 245 public function addValue($part) { 246 247 if (is_null($this->value)) { 248 $this->value = $part; 249 } else { 250 $this->value = array_merge((array)$this->value, (array)$part); 251 } 252 253 } 254 255 /** 256 * Checks if this parameter contains the specified value. 257 * 258 * This is a case-insensitive match. It makes sense to call this for for 259 * instance the TYPE parameter, to see if it contains a keyword such as 260 * 'WORK' or 'FAX'. 261 * 262 * @param string $value 263 * @return bool 264 */ 265 public function has($value) { 266 267 return in_array( 268 strtolower($value), 269 array_map('strtolower', (array)$this->value) 270 ); 271 272 } 273 274 /** 275 * Turns the object back into a serialized blob. 276 * 277 * @return string 278 */ 279 public function serialize() { 280 281 $value = $this->getParts(); 282 283 if (count($value)===0) { 284 return $this->name . '='; 285 } 286 287 if ($this->root->getDocumentType() === Document::VCARD21 && $this->noName) { 288 289 return implode(';', $value); 290 291 } 292 293 return $this->name . '=' . array_reduce( 294 $value, 295 function($out, $item) { 296 297 if (!is_null($out)) $out.=','; 298 299 // If there's no special characters in the string, we'll use the simple 300 // format. 301 // 302 // The list of special characters is defined as: 303 // 304 // Any character except CONTROL, DQUOTE, ";", ":", "," 305 // 306 // by the iCalendar spec: 307 // https://tools.ietf.org/html/rfc5545#section-3.1 308 // 309 // And we add ^ to that because of: 310 // https://tools.ietf.org/html/rfc6868 311 // 312 // But we've found that iCal (7.0, shipped with OSX 10.9) 313 // severaly trips on + characters not being quoted, so we 314 // added + as well. 315 if (!preg_match('#(?: [\n":;\^,\+] )#x', $item)) { 316 return $out.$item; 317 } else { 318 // Enclosing in double-quotes, and using RFC6868 for encoding any 319 // special characters 320 $out.='"' . strtr( 321 $item, 322 array( 323 '^' => '^^', 324 "\n" => '^n', 325 '"' => '^\'', 326 ) 327 ) . '"'; 328 return $out; 329 } 330 331 } 332 ); 333 334 } 335 336 /** 337 * This method returns an array, with the representation as it should be 338 * encoded in json. This is used to create jCard or jCal documents. 339 * 340 * @return array 341 */ 342 public function jsonSerialize() { 343 344 return $this->value; 345 346 } 347 348 /** 349 * Called when this object is being cast to a string 350 * 351 * @return string 352 */ 353 public function __toString() { 354 355 return (string)$this->getValue(); 356 357 } 358 359 /** 360 * Returns the iterator for this object 361 * 362 * @return ElementList 363 */ 364 public function getIterator() { 365 366 if (!is_null($this->iterator)) 367 return $this->iterator; 368 369 return $this->iterator = new ArrayObject((array)$this->value); 370 371 } 372 373} 374