Total Complexity | 92 |
Total Lines | 973 |
Duplicated Lines | 0 % |
Changes | 55 | ||
Bugs | 8 | Features | 10 |
Complex classes like VCard often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use VCard, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
19 | class VCard |
||
20 | { |
||
21 | /** |
||
22 | * definedElements |
||
23 | * |
||
24 | * @var array |
||
25 | */ |
||
26 | private $definedElements; |
||
27 | |||
28 | /** |
||
29 | * Filename |
||
30 | * |
||
31 | * @var string |
||
32 | */ |
||
33 | private $filename; |
||
34 | |||
35 | /** |
||
36 | * Save Path |
||
37 | * |
||
38 | * @var string |
||
39 | */ |
||
40 | private $savePath = null; |
||
41 | |||
42 | /** |
||
43 | * Multiple properties for element allowed |
||
44 | * |
||
45 | * @var array |
||
46 | */ |
||
47 | private $multiplePropertiesForElementAllowed = [ |
||
48 | 'email', |
||
49 | 'address', |
||
50 | 'phoneNumber', |
||
51 | 'url', |
||
52 | 'label', |
||
53 | 'socialmedia' |
||
54 | ]; |
||
55 | |||
56 | /** |
||
57 | * Properties |
||
58 | * |
||
59 | * @var array |
||
60 | */ |
||
61 | private $properties; |
||
62 | |||
63 | /** |
||
64 | * Default Charset |
||
65 | * |
||
66 | * @var string |
||
67 | */ |
||
68 | public $charset = 'utf-8'; |
||
69 | |||
70 | /** |
||
71 | * Add address |
||
72 | * |
||
73 | * @param string [optional] $name |
||
74 | * @param string [optional] $extended |
||
75 | * @param string [optional] $street |
||
76 | * @param string [optional] $city |
||
77 | * @param string [optional] $region |
||
78 | * @param string [optional] $zip |
||
79 | * @param string [optional] $country |
||
80 | * @param string [optional] $type |
||
81 | * $type may be DOM | INTL | POSTAL | PARCEL | HOME | WORK |
||
82 | * or any combination of these: e.g. "WORK;PARCEL;POSTAL" |
||
83 | * @return $this |
||
84 | */ |
||
85 | public function addAddress( |
||
86 | $name = '', |
||
87 | $extended = '', |
||
88 | $street = '', |
||
89 | $city = '', |
||
90 | $region = '', |
||
91 | $zip = '', |
||
92 | $country = '', |
||
93 | $type = 'WORK;POSTAL' |
||
94 | ) { |
||
95 | // init value |
||
96 | $value = $name . ';' . $extended . ';' . $street . ';' . $city . ';' . $region . ';' . $zip . ';' . $country; |
||
97 | |||
98 | // set property |
||
99 | $this->setProperty( |
||
100 | 'address', |
||
101 | 'ADR' . (($type != '') ? ';' . $type : '') . $this->getCharsetString(), |
||
102 | $value |
||
103 | ); |
||
104 | |||
105 | return $this; |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * Add birthday |
||
110 | * |
||
111 | * @param string $date Format is YYYY-MM-DD |
||
112 | * @return $this |
||
113 | */ |
||
114 | public function addBirthday($date) |
||
115 | { |
||
116 | $this->setProperty( |
||
117 | 'birthday', |
||
118 | 'BDAY', |
||
119 | $date |
||
120 | ); |
||
121 | |||
122 | return $this; |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * Add company |
||
127 | * |
||
128 | * @param string $company |
||
129 | * @param string $department |
||
130 | * @return $this |
||
131 | */ |
||
132 | public function addCompany($company, $department = '') |
||
133 | { |
||
134 | $this->setProperty( |
||
135 | 'company', |
||
136 | 'ORG' . $this->getCharsetString(), |
||
137 | $company |
||
138 | . ($department != '' ? ';' . $department : '') |
||
139 | ); |
||
140 | |||
141 | // if filename is empty, add to filename |
||
142 | if ($this->filename === null) { |
||
143 | $this->setFilename($company); |
||
144 | } |
||
145 | |||
146 | return $this; |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Add email |
||
151 | * |
||
152 | * @param string $address The e-mail address |
||
153 | * @param string [optional] $type The type of the email address |
||
154 | * $type may be PREF | WORK | HOME |
||
155 | * or any combination of these: e.g. "PREF;WORK" |
||
156 | * @return $this |
||
157 | */ |
||
158 | public function addEmail($address, $type = '') |
||
159 | { |
||
160 | $this->setProperty( |
||
161 | 'email', |
||
162 | 'EMAIL;INTERNET' . (($type != '') ? ';' . $type : ''), |
||
163 | $address |
||
164 | ); |
||
165 | |||
166 | return $this; |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * Add jobtitle |
||
171 | * |
||
172 | * @param string $jobtitle The jobtitle for the person. |
||
173 | * @return $this |
||
174 | */ |
||
175 | public function addJobtitle($jobtitle) |
||
176 | { |
||
177 | $this->setProperty( |
||
178 | 'jobtitle', |
||
179 | 'TITLE' . $this->getCharsetString(), |
||
180 | $jobtitle |
||
181 | ); |
||
182 | |||
183 | return $this; |
||
184 | } |
||
185 | |||
186 | /** |
||
187 | * Add a label |
||
188 | * |
||
189 | * @param string $label |
||
190 | * @param string $type |
||
191 | * |
||
192 | * @return $this |
||
193 | */ |
||
194 | public function addLabel($label, $type = '') |
||
195 | { |
||
196 | $this->setProperty( |
||
197 | 'label', |
||
198 | 'LABEL' . ($type !== '' ? ';' . $type : ''), |
||
199 | $label |
||
200 | ); |
||
201 | |||
202 | return $this; |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Add role |
||
207 | * |
||
208 | * @param string $role The role for the person. |
||
209 | * @return $this |
||
210 | */ |
||
211 | public function addRole($role) |
||
212 | { |
||
213 | $this->setProperty( |
||
214 | 'role', |
||
215 | 'ROLE' . $this->getCharsetString(), |
||
216 | $role |
||
217 | ); |
||
218 | |||
219 | return $this; |
||
220 | } |
||
221 | |||
222 | /** |
||
223 | * Add socialMedia |
||
224 | * |
||
225 | * @param string $type The type of social media. |
||
226 | * @param string $addr The address of the social media platform. |
||
227 | * @return $this |
||
228 | */ |
||
229 | public function addSocial($type = '', $url) |
||
230 | { |
||
231 | |||
232 | $this->setProperty( |
||
233 | 'socialmedia', |
||
234 | 'X-SOCIALPROFILE;type=' . (($type != '') ? $type : '' ), |
||
235 | $url |
||
236 | ); |
||
237 | |||
238 | return $this; |
||
239 | } |
||
240 | |||
241 | /** |
||
242 | * Add a photo or logo (depending on property name) |
||
243 | * |
||
244 | * @param string $property LOGO|PHOTO |
||
245 | * @param string $url image url or filename |
||
246 | * @param bool $include Do we include the image in our vcard or not? |
||
247 | * @param string $element The name of the element to set |
||
248 | * @throws VCardException |
||
249 | */ |
||
250 | private function addMedia($property, $url, $include = true, $element) |
||
251 | { |
||
252 | $mimeType = null; |
||
253 | |||
254 | //Is this URL for a remote resource? |
||
255 | if (filter_var($url, FILTER_VALIDATE_URL) !== false) { |
||
256 | $headers = get_headers($url, 1); |
||
257 | |||
258 | if (array_key_exists('Content-Type', $headers)) { |
||
259 | $mimeType = $headers['Content-Type']; |
||
260 | if (is_array($mimeType)) { |
||
261 | $mimeType = end($mimeType); |
||
262 | } |
||
263 | } |
||
264 | } else { |
||
265 | //Local file, so inspect it directly |
||
266 | $mimeType = mime_content_type($url); |
||
267 | } |
||
268 | if (strpos($mimeType, ';') !== false) { |
||
269 | $mimeType = strstr($mimeType, ';', true); |
||
270 | } |
||
271 | if (!is_string($mimeType) || substr($mimeType, 0, 6) !== 'image/') { |
||
272 | throw VCardException::invalidImage(); |
||
273 | } |
||
274 | $fileType = strtoupper(substr($mimeType, 6)); |
||
275 | |||
276 | if ($include) { |
||
277 | if ((bool) ini_get('allow_url_fopen') === true) { |
||
278 | $value = file_get_contents($url); |
||
279 | } else { |
||
280 | $curl = curl_init(); |
||
281 | curl_setopt($curl, CURLOPT_URL, $url); |
||
|
|||
282 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); |
||
283 | $value = curl_exec($curl); |
||
284 | curl_close($curl); |
||
285 | } |
||
286 | |||
287 | if (!$value) { |
||
288 | throw VCardException::emptyURL(); |
||
289 | } |
||
290 | |||
291 | $value = base64_encode($value); |
||
292 | $property .= ";ENCODING=b;TYPE=" . $fileType; |
||
293 | } else { |
||
294 | if (filter_var($url, FILTER_VALIDATE_URL) !== false) { |
||
295 | $propertySuffix = ';VALUE=URL'; |
||
296 | $propertySuffix .= ';TYPE=' . strtoupper($fileType); |
||
297 | |||
298 | $property = $property . $propertySuffix; |
||
299 | $value = $url; |
||
300 | } else { |
||
301 | $value = $url; |
||
302 | } |
||
303 | } |
||
304 | |||
305 | $this->setProperty( |
||
306 | $element, |
||
307 | $property, |
||
308 | $value |
||
309 | ); |
||
310 | } |
||
311 | |||
312 | /** |
||
313 | * Add a photo or logo (depending on property name) |
||
314 | * |
||
315 | * @param string $property LOGO|PHOTO |
||
316 | * @param string $content image content |
||
317 | * @param string $element The name of the element to set |
||
318 | */ |
||
319 | private function addMediaContent($property, $content, $element) |
||
320 | { |
||
321 | $finfo = new \finfo(); |
||
322 | $mimeType = $finfo->buffer($content, FILEINFO_MIME_TYPE); |
||
323 | |||
324 | if (strpos($mimeType, ';') !== false) { |
||
325 | $mimeType = strstr($mimeType, ';', true); |
||
326 | } |
||
327 | if (!is_string($mimeType) || substr($mimeType, 0, 6) !== 'image/') { |
||
328 | throw VCardException::invalidImage(); |
||
329 | } |
||
330 | $fileType = strtoupper(substr($mimeType, 6)); |
||
331 | |||
332 | $content = base64_encode($content); |
||
333 | $property .= ";ENCODING=b;TYPE=" . $fileType; |
||
334 | |||
335 | $this->setProperty( |
||
336 | $element, |
||
337 | $property, |
||
338 | $content |
||
339 | ); |
||
340 | } |
||
341 | |||
342 | /** |
||
343 | * Add name |
||
344 | * |
||
345 | * @param string [optional] $lastName |
||
346 | * @param string [optional] $firstName |
||
347 | * @param string [optional] $additional |
||
348 | * @param string [optional] $prefix |
||
349 | * @param string [optional] $suffix |
||
350 | * @return $this |
||
351 | */ |
||
352 | public function addName( |
||
353 | $lastName = '', |
||
354 | $firstName = '', |
||
355 | $additional = '', |
||
356 | $prefix = '', |
||
357 | $suffix = '' |
||
358 | ) { |
||
359 | // define values with non-empty values |
||
360 | $values = array_filter([ |
||
361 | $prefix, |
||
362 | $firstName, |
||
363 | $additional, |
||
364 | $lastName, |
||
365 | $suffix, |
||
366 | ]); |
||
367 | |||
368 | // define filename |
||
369 | $this->setFilename($values); |
||
370 | |||
371 | // set property |
||
372 | $property = $lastName . ';' . $firstName . ';' . $additional . ';' . $prefix . ';' . $suffix; |
||
373 | $this->setProperty( |
||
374 | 'name', |
||
375 | 'N' . $this->getCharsetString(), |
||
376 | $property |
||
377 | ); |
||
378 | |||
379 | // is property FN set? |
||
380 | if (!$this->hasProperty('FN')) { |
||
381 | // set property |
||
382 | $this->setProperty( |
||
383 | 'fullname', |
||
384 | 'FN' . $this->getCharsetString(), |
||
385 | trim(implode(' ', $values)) |
||
386 | ); |
||
387 | } |
||
388 | |||
389 | return $this; |
||
390 | } |
||
391 | |||
392 | /** |
||
393 | * Add note |
||
394 | * |
||
395 | * @param string $note |
||
396 | * @return $this |
||
397 | */ |
||
398 | public function addNote($note) |
||
399 | { |
||
400 | $this->setProperty( |
||
401 | 'note', |
||
402 | 'NOTE' . $this->getCharsetString(), |
||
403 | $note |
||
404 | ); |
||
405 | |||
406 | return $this; |
||
407 | } |
||
408 | |||
409 | /** |
||
410 | * Add categories |
||
411 | * |
||
412 | * @param array $categories |
||
413 | * @return $this |
||
414 | */ |
||
415 | public function addCategories($categories) |
||
416 | { |
||
417 | $this->setProperty( |
||
418 | 'categories', |
||
419 | 'CATEGORIES' . $this->getCharsetString(), |
||
420 | trim(implode(',', $categories)) |
||
421 | ); |
||
422 | |||
423 | return $this; |
||
424 | } |
||
425 | |||
426 | /** |
||
427 | * Add phone number |
||
428 | * |
||
429 | * @param string $number |
||
430 | * @param string [optional] $type |
||
431 | * Type may be PREF | WORK | HOME | VOICE | FAX | MSG | |
||
432 | * CELL | PAGER | BBS | CAR | MODEM | ISDN | VIDEO |
||
433 | * or any senseful combination, e.g. "PREF;WORK;VOICE" |
||
434 | * @return $this |
||
435 | */ |
||
436 | public function addPhoneNumber($number, $type = '') |
||
437 | { |
||
438 | $this->setProperty( |
||
439 | 'phoneNumber', |
||
440 | 'TEL' . (($type != '') ? ';' . $type : ''), |
||
441 | $number |
||
442 | ); |
||
443 | |||
444 | return $this; |
||
445 | } |
||
446 | |||
447 | /** |
||
448 | * Add Logo |
||
449 | * |
||
450 | * @param string $url image url or filename |
||
451 | * @param bool $include Include the image in our vcard? |
||
452 | * @return $this |
||
453 | */ |
||
454 | public function addLogo($url, $include = true) |
||
455 | { |
||
456 | $this->addMedia( |
||
457 | 'LOGO', |
||
458 | $url, |
||
459 | $include, |
||
460 | 'logo' |
||
461 | ); |
||
462 | |||
463 | return $this; |
||
464 | } |
||
465 | |||
466 | /** |
||
467 | * Add Logo content |
||
468 | * |
||
469 | * @param string $content image content |
||
470 | * @return $this |
||
471 | */ |
||
472 | public function addLogoContent($content) |
||
473 | { |
||
474 | $this->addMediaContent( |
||
475 | 'LOGO', |
||
476 | $content, |
||
477 | 'logo' |
||
478 | ); |
||
479 | |||
480 | return $this; |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * Add Photo |
||
485 | * |
||
486 | * @param string $url image url or filename |
||
487 | * @param bool $include Include the image in our vcard? |
||
488 | * @return $this |
||
489 | */ |
||
490 | public function addPhoto($url, $include = true) |
||
491 | { |
||
492 | $this->addMedia( |
||
493 | 'PHOTO', |
||
494 | $url, |
||
495 | $include, |
||
496 | 'photo' |
||
497 | ); |
||
498 | |||
499 | return $this; |
||
500 | } |
||
501 | |||
502 | /** |
||
503 | * Add Photo content |
||
504 | * |
||
505 | * @param string $content image content |
||
506 | * @return $this |
||
507 | */ |
||
508 | public function addPhotoContent($content) |
||
509 | { |
||
510 | $this->addMediaContent( |
||
511 | 'PHOTO', |
||
512 | $content, |
||
513 | 'photo' |
||
514 | ); |
||
515 | |||
516 | return $this; |
||
517 | } |
||
518 | |||
519 | /** |
||
520 | * Add URL |
||
521 | * |
||
522 | * @param string $url |
||
523 | * @param string [optional] $type Type may be WORK | HOME |
||
524 | * @return $this |
||
525 | */ |
||
526 | public function addURL($url, $type = '') |
||
527 | { |
||
528 | $this->setProperty( |
||
529 | 'url', |
||
530 | 'URL' . (($type != '') ? ';' . $type : ''), |
||
531 | $url |
||
532 | ); |
||
533 | |||
534 | return $this; |
||
535 | } |
||
536 | |||
537 | /** |
||
538 | * Build VCard (.vcf) |
||
539 | * |
||
540 | * @return string |
||
541 | */ |
||
542 | public function buildVCard() |
||
543 | { |
||
544 | // init string |
||
545 | $string = "BEGIN:VCARD\r\n"; |
||
546 | $string .= "VERSION:3.0\r\n"; |
||
547 | $string .= "REV:" . date("Y-m-d") . "T" . date("H:i:s") . "Z\r\n"; |
||
548 | |||
549 | // loop all properties |
||
550 | $properties = $this->getProperties(); |
||
551 | foreach ($properties as $property) { |
||
552 | // add to string |
||
553 | $string .= $this->fold($property['key'] . ':' . $this->escape($property['value']) . "\r\n"); |
||
554 | } |
||
555 | |||
556 | // add to string |
||
557 | $string .= "END:VCARD\r\n"; |
||
558 | |||
559 | // return |
||
560 | return $string; |
||
561 | } |
||
562 | |||
563 | /** |
||
564 | * Build VCalender (.ics) - Safari (< iOS 8) can not open .vcf files, so we have build a workaround. |
||
565 | * |
||
566 | * @return string |
||
567 | */ |
||
568 | public function buildVCalendar() |
||
569 | { |
||
570 | // init dates |
||
571 | $dtstart = date("Ymd") . "T" . date("Hi") . "00"; |
||
572 | $dtend = date("Ymd") . "T" . date("Hi") . "01"; |
||
573 | |||
574 | // init string |
||
575 | $string = "BEGIN:VCALENDAR\n"; |
||
576 | $string .= "VERSION:2.0\n"; |
||
577 | $string .= "BEGIN:VEVENT\n"; |
||
578 | $string .= "DTSTART;TZID=Europe/London:" . $dtstart . "\n"; |
||
579 | $string .= "DTEND;TZID=Europe/London:" . $dtend . "\n"; |
||
580 | $string .= "SUMMARY:Click attached contact below to save to your contacts\n"; |
||
581 | $string .= "DTSTAMP:" . $dtstart . "Z\n"; |
||
582 | $string .= "ATTACH;VALUE=BINARY;ENCODING=BASE64;FMTTYPE=text/directory;\n"; |
||
583 | $string .= " X-APPLE-FILENAME=" . $this->getFilename() . "." . $this->getFileExtension() . ":\n"; |
||
584 | |||
585 | // base64 encode it so that it can be used as an attachemnt to the "dummy" calendar appointment |
||
586 | $b64vcard = base64_encode($this->buildVCard()); |
||
587 | |||
588 | // chunk the single long line of b64 text in accordance with RFC2045 |
||
589 | // (and the exact line length determined from the original .ics file exported from Apple calendar |
||
590 | $b64mline = chunk_split($b64vcard, 74, "\n"); |
||
591 | |||
592 | // need to indent all the lines by 1 space for the iphone (yes really?!!) |
||
593 | $b64final = preg_replace('/(.+)/', ' $1', $b64mline); |
||
594 | $string .= $b64final; |
||
595 | |||
596 | // output the correctly formatted encoded text |
||
597 | $string .= "END:VEVENT\n"; |
||
598 | $string .= "END:VCALENDAR\n"; |
||
599 | |||
600 | // return |
||
601 | return $string; |
||
602 | } |
||
603 | |||
604 | /** |
||
605 | * Returns the browser user agent string. |
||
606 | * |
||
607 | * @return string |
||
608 | */ |
||
609 | protected function getUserAgent() |
||
610 | { |
||
611 | if (array_key_exists('HTTP_USER_AGENT', $_SERVER)) { |
||
612 | $browser = strtolower($_SERVER['HTTP_USER_AGENT']); |
||
613 | } else { |
||
614 | $browser = 'unknown'; |
||
615 | } |
||
616 | |||
617 | return $browser; |
||
618 | } |
||
619 | |||
620 | /** |
||
621 | * Decode |
||
622 | * |
||
623 | * @param string $value The value to decode |
||
624 | * @return string decoded |
||
625 | */ |
||
626 | private function decode($value) |
||
627 | { |
||
628 | // convert cyrlic, greek or other caracters to ASCII characters |
||
629 | return Transliterator::transliterate($value); |
||
630 | } |
||
631 | |||
632 | /** |
||
633 | * Download a vcard or vcal file to the browser. |
||
634 | */ |
||
635 | public function download() |
||
636 | { |
||
637 | // define output |
||
638 | $output = $this->getOutput(); |
||
639 | |||
640 | foreach ($this->getHeaders(false) as $header) { |
||
641 | header($header); |
||
642 | } |
||
643 | |||
644 | // echo the output and it will be a download |
||
645 | echo $output; |
||
646 | } |
||
647 | |||
648 | /** |
||
649 | * Fold a line according to RFC2425 section 5.8.1. |
||
650 | * |
||
651 | * @link http://tools.ietf.org/html/rfc2425#section-5.8.1 |
||
652 | * @param string $text |
||
653 | * @return mixed |
||
654 | */ |
||
655 | protected function fold($text) |
||
656 | { |
||
657 | if (strlen($text) <= 75) { |
||
658 | return $text; |
||
659 | } |
||
660 | |||
661 | // split, wrap and trim trailing separator |
||
662 | return substr($this->chunk_split_unicode($text, 75, "\r\n "), 0, -3); |
||
663 | } |
||
664 | |||
665 | /** |
||
666 | * multibyte word chunk split |
||
667 | * @link http://php.net/manual/en/function.chunk-split.php#107711 |
||
668 | * |
||
669 | * @param string $body The string to be chunked. |
||
670 | * @param integer $chunklen The chunk length. |
||
671 | * @param string $end The line ending sequence. |
||
672 | * @return string Chunked string |
||
673 | */ |
||
674 | protected function chunk_split_unicode($body, $chunklen = 76, $end = "\r\n") |
||
675 | { |
||
676 | $array = array_chunk( |
||
677 | preg_split("//u", $body, -1, PREG_SPLIT_NO_EMPTY), $chunklen); |
||
678 | $body = ""; |
||
679 | foreach ($array as $item) { |
||
680 | $body .= join("", $item) . $end; |
||
681 | } |
||
682 | return $body; |
||
683 | } |
||
684 | |||
685 | /** |
||
686 | * Escape newline characters according to RFC2425 section 5.8.4. |
||
687 | * |
||
688 | * @link http://tools.ietf.org/html/rfc2425#section-5.8.4 |
||
689 | * @param string $text |
||
690 | * @return string |
||
691 | */ |
||
692 | protected function escape($text) |
||
693 | { |
||
694 | $text = str_replace("\r\n", "\\n", $text); |
||
695 | $text = str_replace("\n", "\\n", $text); |
||
696 | |||
697 | return $text; |
||
698 | } |
||
699 | |||
700 | /** |
||
701 | * Get output as string |
||
702 | * @deprecated in the future |
||
703 | * |
||
704 | * @return string |
||
705 | */ |
||
706 | public function get() |
||
707 | { |
||
708 | return $this->getOutput(); |
||
709 | } |
||
710 | |||
711 | /** |
||
712 | * Get charset |
||
713 | * |
||
714 | * @return string |
||
715 | */ |
||
716 | public function getCharset() |
||
717 | { |
||
718 | return $this->charset; |
||
719 | } |
||
720 | |||
721 | /** |
||
722 | * Get charset string |
||
723 | * |
||
724 | * @return string |
||
725 | */ |
||
726 | public function getCharsetString() |
||
727 | { |
||
728 | return ';CHARSET=' . $this->charset; |
||
729 | } |
||
730 | |||
731 | /** |
||
732 | * Get content type |
||
733 | * |
||
734 | * @return string |
||
735 | */ |
||
736 | public function getContentType() |
||
737 | { |
||
738 | return ($this->isIOS7()) ? |
||
739 | 'text/x-vcalendar' : 'text/x-vcard'; |
||
740 | } |
||
741 | |||
742 | /** |
||
743 | * Get filename |
||
744 | * |
||
745 | * @return string |
||
746 | */ |
||
747 | public function getFilename() |
||
748 | { |
||
749 | if (!$this->filename) { |
||
750 | return 'unknown'; |
||
751 | } |
||
752 | |||
753 | return $this->filename; |
||
754 | } |
||
755 | |||
756 | /** |
||
757 | * Get file extension |
||
758 | * |
||
759 | * @return string |
||
760 | */ |
||
761 | public function getFileExtension() |
||
762 | { |
||
763 | return ($this->isIOS7()) ? |
||
764 | 'ics' : 'vcf'; |
||
765 | } |
||
766 | |||
767 | /** |
||
768 | * Get headers |
||
769 | * |
||
770 | * @param bool $asAssociative |
||
771 | * @return array |
||
772 | */ |
||
773 | public function getHeaders($asAssociative) |
||
774 | { |
||
775 | $contentType = $this->getContentType() . '; charset=' . $this->getCharset(); |
||
776 | $contentDisposition = 'attachment; filename=' . $this->getFilename() . '.' . $this->getFileExtension(); |
||
777 | $contentLength = mb_strlen($this->getOutput(), '8bit'); |
||
778 | $connection = 'close'; |
||
779 | |||
780 | if ((bool)$asAssociative) { |
||
781 | return [ |
||
782 | 'Content-type' => $contentType, |
||
783 | 'Content-Disposition' => $contentDisposition, |
||
784 | 'Content-Length' => $contentLength, |
||
785 | 'Connection' => $connection, |
||
786 | ]; |
||
787 | } |
||
788 | |||
789 | return [ |
||
790 | 'Content-type: ' . $contentType, |
||
791 | 'Content-Disposition: ' . $contentDisposition, |
||
792 | 'Content-Length: ' . $contentLength, |
||
793 | 'Connection: ' . $connection, |
||
794 | ]; |
||
795 | } |
||
796 | |||
797 | /** |
||
798 | * Get output as string |
||
799 | * iOS devices (and safari < iOS 8 in particular) can not read .vcf (= vcard) files. |
||
800 | * So I build a workaround to build a .ics (= vcalender) file. |
||
801 | * |
||
802 | * @return string |
||
803 | */ |
||
804 | public function getOutput() |
||
810 | } |
||
811 | |||
812 | /** |
||
813 | * Get properties |
||
814 | * |
||
815 | * @return array |
||
816 | */ |
||
817 | public function getProperties() |
||
818 | { |
||
819 | return $this->properties; |
||
820 | } |
||
821 | |||
822 | /** |
||
823 | * Has property |
||
824 | * |
||
825 | * @param string $key |
||
826 | * @return bool |
||
827 | */ |
||
828 | public function hasProperty($key) |
||
829 | { |
||
830 | $properties = $this->getProperties(); |
||
831 | |||
832 | foreach ($properties as $property) { |
||
833 | if ($property['key'] === $key && $property['value'] !== '') { |
||
834 | return true; |
||
835 | } |
||
836 | } |
||
837 | |||
838 | return false; |
||
839 | } |
||
840 | |||
841 | /** |
||
842 | * Is iOS - Check if the user is using an iOS-device |
||
843 | * |
||
844 | * @return bool |
||
845 | */ |
||
846 | public function isIOS() |
||
847 | { |
||
848 | // get user agent |
||
849 | $browser = $this->getUserAgent(); |
||
850 | |||
851 | return (strpos($browser, 'iphone') || strpos($browser, 'ipod') || strpos($browser, 'ipad')); |
||
852 | } |
||
853 | |||
854 | /** |
||
855 | * Is iOS less than 7 (should cal wrapper be returned) |
||
856 | * |
||
857 | * @return bool |
||
858 | */ |
||
859 | public function isIOS7() |
||
860 | { |
||
861 | return ($this->isIOS() && $this->shouldAttachmentBeCal()); |
||
862 | } |
||
863 | |||
864 | /** |
||
865 | * Save to a file |
||
866 | * |
||
867 | * @return void |
||
868 | */ |
||
869 | public function save() |
||
870 | { |
||
871 | $file = $this->getFilename() . '.' . $this->getFileExtension(); |
||
872 | |||
873 | // Add save path if given |
||
874 | if (null !== $this->savePath) { |
||
875 | $file = $this->savePath . $file; |
||
876 | } |
||
877 | |||
878 | file_put_contents( |
||
879 | $file, |
||
880 | $this->getOutput() |
||
881 | ); |
||
882 | } |
||
883 | |||
884 | /** |
||
885 | * Set charset |
||
886 | * |
||
887 | * @param mixed $charset |
||
888 | * @return void |
||
889 | */ |
||
890 | public function setCharset($charset) |
||
891 | { |
||
892 | $this->charset = $charset; |
||
893 | } |
||
894 | |||
895 | /** |
||
896 | * Set filename |
||
897 | * |
||
898 | * @param mixed $value |
||
899 | * @param bool $overwrite [optional] Default overwrite is true |
||
900 | * @param string $separator [optional] Default separator is an underscore '_' |
||
901 | * @return void |
||
902 | */ |
||
903 | public function setFilename($value, $overwrite = true, $separator = '_') |
||
930 | } |
||
931 | |||
932 | /** |
||
933 | * Set the save path directory |
||
934 | * |
||
935 | * @param string $savePath Save Path |
||
936 | * @throws VCardException |
||
937 | */ |
||
938 | public function setSavePath($savePath) |
||
939 | { |
||
940 | if (!is_dir($savePath)) { |
||
941 | throw VCardException::outputDirectoryNotExists(); |
||
942 | } |
||
943 | |||
944 | // Add trailing directory separator the save path |
||
945 | if (substr($savePath, -1) != DIRECTORY_SEPARATOR) { |
||
946 | $savePath .= DIRECTORY_SEPARATOR; |
||
947 | } |
||
948 | |||
949 | $this->savePath = $savePath; |
||
950 | } |
||
951 | |||
952 | /** |
||
953 | * Set property |
||
954 | * |
||
955 | * @param string $element The element name you want to set, f.e.: name, email, phoneNumber, ... |
||
956 | * @param string $key |
||
957 | * @param string $value |
||
958 | * @throws VCardException |
||
959 | */ |
||
960 | protected function setProperty($element, $key, $value) |
||
961 | { |
||
962 | if (!in_array($element, $this->multiplePropertiesForElementAllowed) |
||
963 | && isset($this->definedElements[$element]) |
||
964 | ) { |
||
965 | throw VCardException::elementAlreadyExists($element); |
||
966 | } |
||
967 | |||
968 | // we define that we set this element |
||
969 | $this->definedElements[$element] = true; |
||
970 | |||
971 | // adding property |
||
972 | $this->properties[] = [ |
||
973 | 'key' => $key, |
||
974 | 'value' => $value |
||
975 | ]; |
||
976 | } |
||
977 | |||
978 | /** |
||
979 | * Checks if we should return vcard in cal wrapper |
||
980 | * |
||
981 | * @return bool |
||
982 | */ |
||
983 | protected function shouldAttachmentBeCal() |
||
992 | } |
||
993 | } |
||
994 |