19 declare(strict_types=1);
108 public function encode(
string $string): string
116 public function fold(
string $string =
''): string
119 preg_match_all(
'/(.{1,74})/', $string, $matches);
120 for ($i = 0, $iMax = count($matches[1]); $i < $iMax; $i++) {
121 if ($i < (count($matches[1]) - 1)) {
122 $matches[1][$i] .=
"\n";
125 $matches[1][$i] =
' ' . $matches[1][$i];
127 $folded_string .= $matches[1][$i];
129 return $folded_string;
135 public function escape(
string $string): string
137 $string = preg_replace(
'/(?<!\\\\)(\\\\)([^;,n\\\\])/',
'\${1}\${1}\${2}', $string);
138 $string = preg_replace(
'/(?<!\\\\);/',
'\\;', $string);
139 $string = preg_replace(
'/(?<!\\\\),/',
'\\,', $string);
140 $string = preg_replace(
'/\n/',
'\\n', $string);
148 public function explodeVar(
string $variable,
string $separator =
','): array
150 $exploded = explode($separator, $variable);
151 foreach ($exploded as $index => $var) {
152 $exploded[$index] = $this->
escape($var);
162 $fn = $n = $nickname = $photo = $bday = $adr = $label = $tel = $email = $mailer =
163 $tz = $geo = $title = $role = $logo = $agent = $org = $categories = $note = $prodid =
164 $rev = $sortstring = $sound = $uid =
$url = $class = $key = 0;
166 $vcard =
"BEGIN:VCARD\n";
167 $vcard .=
'VERSION:' . $this->types[
'VERSION'] .
"\n";
168 foreach ($this->types as $type => $var) {
173 if (strcmp($this->types[
'FN'],
'') != 0) {
174 $fn = $this->
fold(
'FN:' . $this->types[
'FN']) .
"\n";
180 if (strcmp($this->types[
'N'],
'') != 0) {
181 $n = $this->
fold(
'N:' . $this->types[
'N']) .
"\n";
187 if (strcmp($this->types[
'NICKNAME'],
'') != 0) {
188 $nickname = $this->
fold(
'NICKNAME:' . $this->types[
'NICKNAME']) .
"\n";
195 if (isset($this->types[
'PHOTO'])) {
196 if (strcmp(($this->types[
'PHOTO'][
'VALUE'] ??
''),
'') != 0) {
197 $photo = $this->
fold(
'PHOTO;VALUE=uri:' . $this->types[
'PHOTO'][
'VALUE']) .
"\n";
198 } elseif (strcmp(($this->types[
'PHOTO'][
'ENCODING'] ??
''),
'') != 0) {
199 $photo =
'PHOTO;ENCODING=' . $this->types[
'PHOTO'][
'ENCODING'];
200 if (strcmp($this->types[
'PHOTO'][
'TYPE'],
'') != 0) {
201 $photo .=
';TYPE=' . $this->types[
'PHOTO'][
'TYPE'];
203 $photo .=
':' . $this->types[
'PHOTO'][
'PHOTO'];
204 $photo = $this->
fold($photo) .
"\n";
209 if (strcmp($this->types[
'BDAY'],
'') != 0) {
210 $bday = $this->
fold(
'BDAY:' . $this->types[
'BDAY']) .
"\n";
216 if (count($this->types[
'ADR'])) {
218 foreach ($this->types[
'ADR'] as $key => $address) {
219 $test = implode(
'', $address);
220 if (strcmp($test,
'') != 0) {
223 if ($address[
'TYPE'] > 0) {
225 $adr_types[] =
'dom';
228 $adr_types[] =
'intl';
231 $adr_types[] =
'postal';
234 $adr_types[] =
'parcel';
237 $adr_types[] =
'home';
240 $adr_types[] =
'work';
243 $adr_types[] =
'pref';
245 $adr .=
';TYPE=' . implode(
',', $adr_types);
247 $adr .=
':' . $address[
'POBOX'] .
';' . $address[
'EXTENDED_ADDRESS'] .
248 ';' . $address[
'STREET_ADDRESS'] .
';' . $address[
'LOCALITY'] .
249 ';' . $address[
'REGION'] .
';' . $address[
'POSTAL_CODE'] .
250 ';' . $address[
'COUNTRY'];
251 $adr = $this->
fold($adr) .
"\n";
262 if (isset($this->types[
'LABEL'])) {
263 if (strcmp(($this->types[
'LABEL'][
'LABEL'] ??
''),
'') != 0) {
266 if ($this->types[
'LABEL'][
'TYPE'] > 0) {
267 if (($this->types[
'LABEL'][
'TYPE'] & self::ADR_TYPE_DOM) > 0) {
268 $adr_types[] =
'dom';
270 if (($this->types[
'LABEL'][
'TYPE'] & self::ADR_TYPE_INTL) > 0) {
271 $adr_types[] =
'intl';
273 if (($this->types[
'LABEL'][
'TYPE'] & self::ADR_TYPE_POSTAL) > 0) {
274 $adr_types[] =
'postal';
276 if (($this->types[
'LABEL'][
'TYPE'] & self::ADR_TYPE_PARCEL) > 0) {
277 $adr_types[] =
'parcel';
279 if (($this->types[
'LABEL'][
'TYPE'] & self::ADR_TYPE_HOME) > 0) {
280 $adr_types[] =
'home';
282 if (($this->types[
'LABEL'][
'TYPE'] & self::ADR_TYPE_WORK) > 0) {
283 $adr_types[] =
'work';
285 if (($this->types[
'LABEL'][
'TYPE'] & self::ADR_TYPE_PREF) > 0) {
286 $adr_types[] =
'pref';
288 $label .=
';TYPE=' . implode(
',', $adr_types);
290 $label .=
':' . $this->types[
'LABEL'][
'LABEL'];
291 $label = $this->
fold($label) .
"\n";
296 if (count($this->types[
'TEL'])) {
298 foreach ($this->types[
'TEL'] as $key => $phone) {
299 if (strcmp($phone[
'TEL'],
'') != 0) {
302 if ($phone[
'TYPE'] > 0) {
303 if (($phone[
'TYPE'] & self::TEL_TYPE_HOME) > 0) {
304 $tel_types[] =
'home';
306 if (($phone[
'TYPE'] & self::TEL_TYPE_MSG) > 0) {
307 $tel_types[] =
'msg';
309 if (($phone[
'TYPE'] & self::TEL_TYPE_WORK) > 0) {
310 $tel_types[] =
'work';
312 if (($phone[
'TYPE'] & self::TEL_TYPE_PREF) > 0) {
313 $tel_types[] =
'pref';
315 if (($phone[
'TYPE'] & self::TEL_TYPE_VOICE) > 0) {
316 $tel_types[] =
'voice';
318 if (($phone[
'TYPE'] & self::TEL_TYPE_FAX) > 0) {
319 $tel_types[] =
'fax';
321 if (($phone[
'TYPE'] & self::TEL_TYPE_CELL) > 0) {
322 $tel_types[] =
'cell';
324 if (($phone[
'TYPE'] & self::TEL_TYPE_VIDEO) > 0) {
325 $tel_types[] =
'video';
327 if (($phone[
'TYPE'] & self::TEL_TYPE_PAGER) > 0) {
328 $tel_types[] =
'pager';
330 if (($phone[
'TYPE'] & self::TEL_TYPE_BBS) > 0) {
331 $tel_types[] =
'bbs';
333 if (($phone[
'TYPE'] & self::TEL_TYPE_MODEM) > 0) {
334 $tel_types[] =
'modem';
336 if (($phone[
'TYPE'] & self::TEL_TYPE_CAR) > 0) {
337 $tel_types[] =
'car';
339 if (($phone[
'TYPE'] & self::TEL_TYPE_ISDN) > 0) {
340 $tel_types[] =
'isdn';
342 if (($phone[
'TYPE'] & self::TEL_TYPE_PCS) > 0) {
343 $tel_types[] =
'pcs';
345 $tel .=
';TYPE=' . implode(
',', $tel_types);
347 $tel .=
':' . $phone[
'TEL'];
348 $tel = $this->
fold($tel) .
"\n";
349 $phonenumbers .= $tel;
352 $tel = $phonenumbers;
358 if (count($this->types[
'EMAIL'])) {
360 foreach ($this->types[
'EMAIL'] as $key => $mail) {
361 if (strcmp($mail[
'EMAIL'],
'') != 0) {
364 if ($mail[
'TYPE'] > 0) {
365 if (($mail[
'TYPE'] & self::EMAIL_TYPE_INTERNET) > 0) {
366 $adr_types[] =
'internet';
368 if (($mail[
'TYPE'] & self::EMAIL_TYPE_x400) > 0) {
369 $adr_types[] =
'x400';
371 if (($mail[
'TYPE'] & self::EMAIL_TYPE_PREF) > 0) {
372 $adr_types[] =
'pref';
374 $email .=
';TYPE=' . implode(
',', $adr_types);
376 $email .=
':' . $mail[
'EMAIL'];
377 $email = $this->
fold($email) .
"\n";
387 if (strcmp(($this->types[
'MAILER'] ??
''),
'') != 0) {
388 $mailer = $this->
fold(
'MAILER:' . $this->types[
'MAILER']) .
"\n";
394 if (strcmp(($this->types[
'TZ'] ??
''),
'') != 0) {
395 $tz = $this->
fold(
'TZ:' . $this->types[
'TZ']) .
"\n";
401 if (isset($this->types[
'GEO']) and
402 (strcmp(($this->types[
'GEO'][
'LAT'] ??
''),
'') != 0) and
403 (strcmp(($this->types[
'GEO'][
'LON'] ??
''),
'') != 0)) {
405 'GEO:' . $this->types[
'GEO'][
'LAT'] .
';' . $this->types[
'GEO'][
'LON']
412 if (strcmp(($this->types[
'TITLE'] ??
''),
'') != 0) {
413 $title = $this->
fold(
'TITLE:' . $this->types[
'TITLE']) .
"\n";
419 if (strcmp(($this->types[
'ROLE'] ??
''),
'') != 0) {
420 $role = $this->
fold(
'ROLE:' . $this->types[
'ROLE']) .
"\n";
427 if (isset($this->types[
'LOGO'])) {
428 if (strcmp(($this->types[
'LOGO'][
'VALUE'] ??
''),
'') != 0) {
429 $logo = $this->
fold(
'LOGO;VALUE=uri:' . $this->types[
'LOGO'][
'VALUE']) .
"\n";
430 } elseif (strcmp(($this->types[
'LOGO'][
'ENCODING'] ??
''),
'') != 0) {
431 $logo =
'LOGO;ENCODING=' . $this->types[
'LOGO'][
'ENCODING'];
432 if (strcmp($this->types[
'LOGO'][
'TYPE'],
'') != 0) {
433 $logo .=
';TYPE=' . $this->types[
'LOGO'][
'TYPE'];
435 $logo .=
':' . $this->types[
'LOGO'][
'LOGO'];
436 $logo = $this->
fold($logo) .
"\n";
441 if (strcmp(($this->types[
'AGENT'] ??
''),
'') != 0) {
442 $agent = $this->
fold(
'AGENT:' . $this->types[
'AGENT']) .
"\n";
448 if (strcmp(($this->types[
'ORG'] ??
''),
'') != 0) {
449 $org = $this->
fold(
'ORG:' . $this->types[
'ORG']) .
"\n";
455 if (strcmp(($this->types[
'CATEGORIES'] ??
''),
'') != 0) {
456 $categories = $this->
fold(
'CATEGORIES:' . $this->types[
'CATEGORIES']) .
"\n";
462 if (strcmp(($this->types[
'NOTE'] ??
''),
'') != 0) {
463 $note = $this->
fold(
'NOTE:' . $this->types[
'NOTE']) .
"\n";
469 if (strcmp(($this->types[
'PRODID'] ??
''),
'') != 0) {
470 $prodid = $this->
fold(
'PRODID:' . $this->types[
'PRODID']) .
"\n";
476 if (strcmp(($this->types[
'REV'] ??
''),
'') != 0) {
477 $rev = $this->
fold(
'REV:' . $this->types[
'REV']) .
"\n";
483 if (strcmp(($this->types[
'SORT-STRING'] ??
''),
'') != 0) {
484 $sortstring = $this->
fold(
'SORT-STRING:' . $this->types[
'SORT-STRING']) .
"\n";
491 if (isset($this->types[
'SOUND'])) {
492 if (strcmp(($this->types[
'SOUND'][
'VALUE'] ??
''),
'') != 0) {
493 $sound = $this->
fold(
'SOUND;VALUE=uri:' . $this->types[
'SOUND'][
'VALUE']) .
"\n";
494 } elseif (strcmp(($this->types[
'SOUND'][
'ENCODING'] ??
''),
'') != 0) {
495 $sound =
'SOUND;ENCODING=' . $this->types[
'SOUND'][
'ENCODING'];
496 if (strcmp($this->types[
'SOUND'][
'TYPE'],
'') != 0) {
497 $sound .=
';TYPE=' . $this->types[
'SOUND'][
'TYPE'];
499 $sound .=
':' . $this->types[
'SOUND'][
'SOUND'];
500 $sound = $this->
fold($sound) .
"\n";
506 if (isset($this->types[
'UID'])) {
507 if (strcmp(($this->types[
'UID'][
'UID'] ??
''),
'') != 0) {
509 if (strcmp($this->types[
'UID'][
'TYPE'],
'') != 0) {
510 $uid .=
';TYPE=' . $this->types[
'UID'][
'TYPE'];
512 $uid .=
':' . $this->types[
'UID'][
'UID'];
513 $uid = $this->
fold($uid) .
"\n";
518 if (strcmp(($this->types[
'URL'] ??
''),
'') != 0) {
519 $url = $this->
fold(
'URL:' . $this->types[
'URL']) .
"\n";
526 if (isset($this->types[
'KEY'])) {
527 if (strcmp(($this->types[
'KEY'][
'KEY'] ??
''),
'') != 0) {
529 if (strcmp($this->types[
'KEY'][
'TYPE'],
'') != 0) {
530 $key .=
';TYPE=' . $this->types[
'KEY'][
'TYPE'];
532 if (strcmp($this->types[
'KEY'][
'ENCODING'],
'') != 0) {
533 $key .=
';ENCODING=' . $this->types[
'KEY'][
'ENCODING'];
535 $key .=
':' . $this->types[
'KEY'][
'KEY'];
536 $key = $this->
fold($key) .
"\n";
541 if (strcmp(($this->types[
'CLASS'] ??
''),
'') != 0) {
542 $class = $this->
fold(
'CLASS:' . $this->types[
'CLASS']) .
"\n";
549 $vcard .= $fn . $n . $nickname . $photo . $bday . $adr . $label . $tel . $email . $mailer .
550 $tz . $geo . $title . $role . $logo . $agent . $org . $categories . $note . $prodid .
551 $rev . $sortstring . $sound . $uid .
$url . $class . $key;
552 $vcard .=
"END:vCard\n";
561 $hex = [
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'A',
'B',
'C',
'D',
'E',
'F'];
562 $lines = preg_split(
'/(\r\n|\r|\n)/', $input);
564 $linebreak =
'=0D=0A';
568 for ($j = 0, $jMax = count($lines); $j < $jMax; $j++) {
570 $linlen = strlen($line);
572 for ($i = 0; $i < $linlen; $i++) {
573 $c = substr($line, $i, 1);
575 if (($dec == 32) && ($i == ($linlen - 1))) {
577 } elseif (($dec == 61) || ($dec < 32) || ($dec > 126)) {
578 $h2 = floor($dec / 16);
579 $h1 = floor($dec % 16);
580 $c = $escape . $hex[(string) $h2] . $hex[(
string) $h1];
582 if ((strlen($newline) + strlen(
$c)) >= $line_max) {
583 $output .= $newline . $escape . $eol;
589 if ($j < count($lines) - 1) {
590 $output .= $linebreak;
593 return trim($output);
611 $this->types[
'FN'] = $this->
escape($formatted_name);
638 string $given_name =
'',
639 string $additional_names =
'',
640 string $honorific_prefixes =
'',
641 string $honorific_suffixes =
'' 643 $familynames = $this->
explodeVar($family_name);
645 $addnames = $this->
explodeVar($additional_names);
646 $prefixes = $this->
explodeVar($honorific_prefixes);
647 $suffixes = $this->
explodeVar($honorific_suffixes);
650 implode(
',', $familynames) .
652 implode(
',', $givennames) .
654 implode(
',', $addnames) .
656 implode(
',', $prefixes) .
658 implode(
',', $suffixes);
660 $this->filename = $given_name .
'_' . $family_name .
'.vcf';
661 if (strcmp($this->types[
'FN'],
'') === 0) {
662 $fn = trim(
"$honorific_prefixes $given_name $additional_names $family_name $honorific_suffixes");
663 $fn = preg_replace(
'/\s{2,10}/',
' ', $fn);
684 $this->types[
'NICKNAME'] = implode(
',', $nicknames);
719 if (preg_match(
'/^http/', $photo)) {
720 $value = $this->
encode($photo);
723 $photo = base64_encode($photo);
725 $this->types[
'PHOTO'] = [
728 'ENCODING' => $encoding,
747 public function setBirthday(
int $year,
int $month,
int $day): void
749 if (($year < 1) or ($day < 1) or ($month < 1)) {
750 $this->types[
'BDAY'] =
'';
752 $this->types[
'BDAY'] = sprintf(
'%04d-%02d-%02d', $year, $month, $day);
809 string $extended_address =
'',
810 string $street_address =
'',
811 string $locality =
'',
813 string $postal_code =
'',
814 string $country =
'',
815 int $type = self::ADR_TYPE_NONE
817 if ($type === self::ADR_TYPE_NONE) {
818 $type = self::ADR_TYPE_INTL + self::ADR_TYPE_POSTAL + self::ADR_TYPE_PARCEL + self::ADR_TYPE_WORK;
820 $po_box = implode(
',', $this->
explodeVar($po_box));
821 $extended_address = implode(
',', $this->
explodeVar($extended_address));
822 $street_address = implode(
',', $this->
explodeVar($street_address));
823 $locality = implode(
',', $this->
explodeVar($locality));
824 $region = implode(
',', $this->
explodeVar($region));
825 $postal_code = implode(
',', $this->
explodeVar($postal_code));
826 $country = implode(
',', $this->
explodeVar($country));
827 $this->types[
'ADR'][] = [
829 'EXTENDED_ADDRESS' => $extended_address,
830 'STREET_ADDRESS' => $street_address,
831 'LOCALITY' => $locality,
833 'POSTAL_CODE' => $postal_code,
834 'COUNTRY' => $country,
871 int $type = self::ADR_TYPE_NONE
873 if ($type == self::ADR_TYPE_NONE) {
874 $type = self::ADR_TYPE_INTL + self::ADR_TYPE_POSTAL + self::ADR_TYPE_PARCEL + self::ADR_TYPE_WORK;
876 $this->types[
'LABEL'] = [
877 'LABEL' => $this->
escape($label),
923 int $type = self::TEL_TYPE_VOICE
925 $this->types[
'TEL'][] = [
926 'TEL' => $this->
escape($number),
952 string $address =
'',
953 int $type = self::EMAIL_TYPE_INTERNET
955 $this->types[
'EMAIL'][] = [
956 'EMAIL' => $this->
escape($address),
977 $this->types[
'MAILER'] = $this->
escape($name);
998 $this->types[
'TZ'] = $this->
escape($zone);
1027 public function setPosition(
string $latitude =
'',
string $longitude =
''): void
1029 $this->types[
'GEO'] = [
1052 $this->types[
'TITLE'] = $this->
escape($title);
1071 $this->types[
'ROLE'] = $this->
escape($role);
1099 public function setLogo(
string $logo,
string $type =
''): void
1103 if (preg_match(
'/^http/', $logo)) {
1104 $value = $this->
encode($logo);
1107 $logo = base64_encode($logo);
1109 $this->types[
'LOGO'] = [
1112 'ENCODING' => $encoding,
1139 $this->types[
'AGENT'] = $this->
escape($agent);
1158 $organization = implode(
';', $this->
explodeVar($organization,
';'));
1159 $this->types[
'ORG'] = $organization;
1179 $categories = implode(
',', $this->
explodeVar($categories));
1180 $this->types[
'CATEGORIES'] = $categories;
1197 $this->types[
'NOTE'] = $this->
escape($note);
1214 $this->types[
'PRODID'] = $this->
escape($product_id);
1233 $this->types[
'REV'] = $this->
escape($revision_date);
1270 $this->types[
'SORT-STRING'] = $this->
escape($string);
1299 public function setSound(
string $sound =
'',
string $type =
''): void
1303 if (preg_match(
'/^http/', $sound)) {
1304 $value = $this->
encode($sound);
1307 $sound = base64_encode($sound);
1309 $this->types[
'SOUND'] = [
1312 'ENCODING' => $encoding,
1335 public function setUID(
string $uid =
'',
string $type =
''): void
1337 $this->types[
'UID'] = [
1338 'UID' => $this->
escape($uid),
1353 public function setURL(
string $uri =
''): void
1355 $this->types[
'URL'] = $this->
escape($uri);
1370 $this->types[
'VERSION'] =
$version;
1434 public function setKey(
string $key =
'',
string $type =
''): void
1437 $key = base64_encode($key);
1438 $this->types[
'KEY'] = [
1441 'ENCODING' => $encoding
1447 if (strcmp($this->filename,
'') == 0) {
1456 return 'text/x-vcard';
setNote(string $note='')
Sets the value for the vCard NOTE type.
setCategories(string $categories)
Sets the value for the vCard CATEGORIES type.
encode(string $string)
Encode data with 'b' type encoding according to RFC 2045.
setAgent(string $agent='')
Sets the value for the vCard AGENT type.
setKey(string $key='', string $type='')
Sets the value for the vCard KEY type.
setPhone(string $number='', int $type=self::TEL_TYPE_VOICE)
Sets the value for the vCard TEL type.
setVersion(string $version='3.0')
Sets the value for the vCard VERSION type.
static getLogger(string $a_component_id)
Get component logger.
setRole(string $role='')
Sets the value for the vCard ROLE type.
setBirthday(int $year, int $month, int $day)
Sets the value for the vCard BDAY type.
setRevision(string $revision_date='')
Sets the value for the vCard REV type.
setPosition(string $latitude='', string $longitude='')
Sets the value for the vCard GEO type.
setAddress(string $po_box='', string $extended_address='', string $street_address='', string $locality='', string $region='', string $postal_code='', string $country='', int $type=self::ADR_TYPE_NONE)
Sets the value for the vCard ADR type.
setSortString(string $string='')
Sets the value for the vCard SORT-STRING type.
setPhoto(string $photo, string $type='')
Sets the value for the vCard PHOTO type.
setMailer(string $name='')
Sets the value for the vCard MAILER type.
setEmail(string $address='', int $type=self::EMAIL_TYPE_INTERNET)
Sets the value for the vCard EMAIL type.
__construct(string $version='3.0')
escape(string $string)
Escapes a string according to RFC 2426.
RFC 2426 vCard MIME Directory Profile 3.0 class.
setName(string $family_name, string $given_name='', string $additional_names='', string $honorific_prefixes='', string $honorific_suffixes='')
Sets the value for the vCard N type.
setProductId(string $product_id='')
Sets the value for the vCard PRODID type.
setClassification(string $classification='')
Sets the value for the vCard CLASS type.
setFormattedName(string $formatted_name)
Sets the value for the vCard FN type.
setSound(string $sound='', string $type='')
Sets the value for the vCard SOUND type.
setOrganization(string $organization='')
Sets the value for the vCard ORG type.
buildVCard()
Builds a vCard string out of the attributes of this object.
const EMAIL_TYPE_INTERNET
setUID(string $uid='', string $type='')
Sets the value for the vCard UID type.
setNickname(string $nickname)
Sets the value for the vCard NICKNAME type.
setURL(string $uri='')
Sets the value for the vCard URL type.
setLabel(string $label='', int $type=self::ADR_TYPE_NONE)
Sets the value for the vCard LABEL type.
setTitle(string $title='')
Sets the value for the vCard TITLE type.
setLogo(string $logo, string $type='')
Sets the value for the vCard LOGO type.
setTimezone(string $zone='')
Sets the value for the vCard TZ type.
fold(string $string='')
Fold a string according to RFC 2425.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
quoted_printable_encode(string $input, int $line_max=76)
Creates a quoted printable encoded string according to RFC 2045.
explodeVar(string $variable, string $separator=',')
Splits a variable into an array using a separator and escapes every value.