Passed
Push — master ( 5ec36b...a966a8 )
by Stefan
01:45
created

VCardContact::getHomepageCount()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
declare(strict_types=1);
3
4
namespace SKien\VCard;
5
6
/**
7
 * Class representing all data to one contact.
8
 * Each contact may contains multiple
9
 * - adresses
10
 * - communication numbers
11
 * - e-mail addresses
12
 * - homepages
13
 * - categories
14
 *
15
 * #### Add a contact to a VCard for writing:
16
 * Create a new instance of a `VCardContact`, set all properties and add the contact
17
 * to a vcard using `VCard::addContact()`
18
 *
19
 * #### Retrieve a contact from a read VCard:
20
 * Use `VCard::getContact()` to retrieve existing contact within vcard.
21
 *
22
 * @see VCard::addContact()
23
 * @see VCard::getContact()
24
 *
25
 * @package VCard
26
 * @author Stefanius <[email protected]>
27
 * @copyright MIT License - see the LICENSE file for details
28
 */
29
class VCardContact
30
{
31
    use VCardHelper;
32
33
    /** gender: female (Microsoft specific) */
34
    public const MS_FEMALE    = '1';
35
    /** gender: male (Microsoft specific) */
36
    public const MS_MALE    = '2';
37
    /** Date type: string */
38
    public const DT_STRING = 0;
39
    /** Date type: unix timestamp */
40
    public const DT_UNIX_TIMESTAMP = 1;
41
    /** Date type: DateTime - Object */
42
    public const DT_OBJECT = 2;
43
44
    /** @var string  lastname   */
45
    protected string $strLastName = '';
46
    /** @var string  firstname  */
47
    protected string $strFirstName = '';
48
    /** @var string  prefix (salutation, title,...) */
49
    protected string $strPrefix = '';
50
    /** @var string  suffix (graduation,...)    */
51
    protected string $strSuffix = '';
52
    /** @var string  nickname   */
53
    protected string $strNickName = '';
54
    /** @var string  organisation name  */
55
    protected string $strOrganisation = '';
56
    /** @var string  position within the organisation   */
57
    protected string $strPosition = '';
58
    /** @var string  section within the organisation    */
59
    protected string $strSection = '';
60
    /** @var string  role / profession  */
61
    protected string $strRole = '';
62
    /** @var VCardAddress[] array of VCardAddress objects  */
63
    protected array $aAddress = array();
64
    /** @var array[] array of phone numbers */
65
    protected array $aPhone = array();
66
    /** @var string[] array of email addresses   */
67
    protected array $aEMail = array();
68
    /** @var string[] array of categories    */
69
    protected array $aCategories = array();
70
    /** @var string[] array of homepage URL's    */
71
    protected array $aHomepages = array();
72
    /** @var string  date of birth in format YYYY-MM-DD */
73
    protected string $strDateOfBirth = '';
74
    /** @var int     gender (0: not specified, 1: female, 2: male)  */
75
    protected int $iGender = 0;
76
    /** @var string  note   */
77
    protected string $strNote = '';
78
    /** @var string  binary portrait base64 coded   */
79
    protected string $blobPortrait = '';
80
81
    /**
82
     * Add address.
83
     * Only one address should be marked as preferred.
84
     * @param VCardAddress $oAddress
85
     * @param bool $bPreferred  mark address as preferred.
86
     */
87
    public function addAddress(VCardAddress $oAddress, bool $bPreferred) : void
88
    {
89
        $oAddress->setPreferred($bPreferred);
90
        $this->aAddress[] = $oAddress;
91
    }
92
93
    /**
94
     * Add phone number.
95
     * Use to set a communication number (phone, mobile, FAX, ...). <br/>
96
     * Any combination of the predefined communication number constants plus the definition
97
     * HOME or WORK can be specified as the type. <br/>
98
     * Multiple numbers of the same type can be set within one contact.
99
     * @see VCard::constants VCard communication number constants
100
     * @link https://datatracker.ietf.org/doc/html/rfc2426#section-3.3.1
101
     * @link https://en.wikipedia.org/wiki/E.164
102
     * @link https://www.itu.int/rec/T-REC-X.121-200010-I/en
103
     * @param string $strPhone      the number (SHOULD conform to the semantics of E.164 / X.121)
104
     * @param string|array $type    one single type or an array of multiple types
105
     * @param bool $bPreferred      mark number as preferred
106
     */
107
    public function addPhone(string $strPhone, $type, bool $bPreferred) : void
108
    {
109
        $strType = is_array($type) ? implode(',', $type) : $type;
110
        if ($bPreferred && strpos($strType, 'PREF') === false) {
111
            $strType .= ',PREF';
112
        }
113
        $this->aPhone[] = array('strPhone' => $strPhone, 'strType' => $strType);
114
    }
115
116
    /**
117
     * Add mail address.
118
     * @link https://datatracker.ietf.org/doc/html/rfc2426#section-3.3.2
119
     * @param string $strEMail  valid e-mail address
120
     * @param bool $bPreferred  mark e-mail as preferred
121
     */
122
    public function addEMail(string $strEMail, bool $bPreferred) : void
123
    {
124
        if ($bPreferred) {
125
            // just set preferred mail on top of the list!
126
            array_unshift($this->aEMail, $strEMail);
127
        } else {
128
            $this->aEMail[] = $strEMail;
129
        }
130
    }
131
132
    /**
133
     * Add a category.
134
     * @param string $strCategory
135
     */
136
    public function addCategory(string $strCategory) : void
137
    {
138
        $this->aCategories[] = $strCategory;
139
    }
140
141
    /**
142
     * Set date of birth.
143
     * @param mixed $DateOfBirth    may be string (format YYYY-MM-DD), int (unixtimestamp) or DateTime - object
144
     */
145
    public function setDateOfBirth($DateOfBirth) : void
146
    {
147
        if (is_object($DateOfBirth) && get_class($DateOfBirth) == 'DateTime') {
148
            // DateTime -object
149
            $this->strDateOfBirth = $DateOfBirth->format('Y-m-d');
150
        } else if (is_numeric($DateOfBirth)) {
151
            $this->strDateOfBirth = date('Y-m-d', $DateOfBirth);
152
        } else {
153
            $this->strDateOfBirth = $DateOfBirth;
154
        }
155
    }
156
157
    /**
158
     * Set the gender.
159
     * <b>Note: this is a MS-extension! </b><ul>
160
     * <li> windows contacts: export/import. </li>
161
     * <li> outlook: import only. </li></ul><br/>
162
     * Only the first char of the `$strGender` param (converted to lowercase) is taken into account! <ul>
163
     * <li> male: 'm', '2' </li>
164
     * <li> female: 'f', 'w', '1' </li></ul>
165
     * @param string $strGender
166
     */
167
    public function setGender(string $strGender) : void
168
    {
169
        $chGender = strtolower(substr($strGender, 0, 1));
170
        if (in_array($chGender, array('w', 'f', self::MS_FEMALE))) {
171
            // weibl., female
172
            $this->iGender = 1;
173
        } elseif (in_array($chGender, array('m', self::MS_MALE))) {
174
            // männl., male
175
            $this->iGender = 2;
176
        }
177
    }
178
179
    /**
180
     * Set portrait from image file.
181
     * Supported types are JPG, PNG, GIF and BMP. <br/>
182
     * > <b>Note: </b></br>
183
     * > For transparency the image type itself MUST support transparency (PNG, GIF)
184
     * > and when reading a portrait, it MUST be saved in the same image format!
185
     * @param string $strFilename
186
     */
187
    public function setPortraitFile(string $strFilename) : void
188
    {
189
        if (filter_var($strFilename, FILTER_VALIDATE_URL)) {
190
            // get type from extension
191
            $strType = strtolower((string) pathinfo($strFilename, PATHINFO_EXTENSION));
192
            $this->blobPortrait = 'data:image/' . $strType . ';base64,';
193
194
            // use curl to be independet of [allow_url_fopen] enabled on the system
195
            $curl = curl_init();
196
            curl_setopt($curl, CURLOPT_URL, $strFilename);
197
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
198
199
            $img = curl_exec($curl);
200
            curl_close($curl);
201
202
            if (is_string($img)) {
203
                $this->blobPortrait .= base64_encode($img);
204
            }
205
        } elseif (file_exists($strFilename)) {
206
            switch (exif_imagetype($strFilename)) {
207
                case IMAGETYPE_JPEG:
208
                    $this->blobPortrait = 'data:image/jpg;base64,';
209
                    break;
210
                case IMAGETYPE_PNG:
211
                    $this->blobPortrait = 'data:image/png;base64,';
212
                    break;
213
                case IMAGETYPE_GIF:
214
                    $this->blobPortrait = 'data:image/gif;base64,';
215
                    break;
216
                case IMAGETYPE_BMP:
217
                    $this->blobPortrait = 'data:image/bmp;base64,';
218
                    break;
219
                default:
220
                    break;
221
            }
222
            $img = file_get_contents($strFilename);
223
224
            $this->blobPortrait .= base64_encode($img);
225
        }
226
    }
227
228
    /**
229
     * Set the full name.
230
     * For companies just leave one of the params blank!
231
     * @param string $strLastName
232
     * @param string $strFirstName
233
     */
234
    public function setName(string $strLastName, string $strFirstName) : void
235
    {
236
        $this->strLastName = $strLastName;
237
        $this->strFirstName = $strFirstName;
238
    }
239
240
    /**
241
     * Set (honorific) name prefix.
242
     * i.E. 'Dr.', 'Prof.', ...
243
     * @param string $strPrefix
244
     */
245
    public function setPrefix(string $strPrefix) : void
246
    {
247
        $this->strPrefix = $strPrefix;
248
    }
249
250
    /**
251
     * Set (honorific) name suffix.
252
     * i.E. 'Jr.', 'M.D.', ...
253
     * @param string $strSuffix
254
     */
255
    public function setSuffix(string $strSuffix) : void
256
    {
257
        $this->strSuffix = $strSuffix;
258
    }
259
260
    /**
261
     * Set nickname.
262
     * @param string $strNickName
263
     */
264
    public function setNickName(string $strNickName) : void
265
    {
266
        $this->strNickName = $strNickName;
267
    }
268
269
    /**
270
     * Set name of the organisation.
271
     * @param string $strOrganisation
272
     */
273
    public function setOrganisation(string $strOrganisation) : void
274
    {
275
        $this->strOrganisation = $strOrganisation;
276
    }
277
278
    /**
279
     * Set section or organizational unit within the organisation.
280
     * @param string $strSection
281
     */
282
    public function setSection(string $strSection) : void
283
    {
284
        $this->strSection = $strSection;
285
    }
286
287
    /**
288
     * Set position, job title or function within the organisation.
289
     * @param string $strPosition
290
     */
291
    public function setPosition(string $strPosition) : void
292
    {
293
        $this->strPosition = $strPosition;
294
    }
295
296
    /**
297
     * Set role, occupation or business category within the organisation.
298
     * @param string $strRole
299
     */
300
    public function setRole(string $strRole) : void
301
    {
302
        $this->strPosition = $strRole;
303
    }
304
305
    /**
306
     * Set homepage
307
     * @param string $strHomepage
308
     */
309
    public function setHomepage(string $strHomepage) : void
310
    {
311
        // keep method for backward compatibility!
312
        // just set value on top of the list!
313
        array_unshift($this->aHomepages, $strHomepage);
314
        trigger_error('call of VCardContact::setHomepage() is deprecated - use VCardContact::addtHomepage() instead!', E_USER_DEPRECATED);
315
    }
316
317
    /**
318
     * Add homepage
319
     * @param string $strHomepage
320
     */
321
    public function addHomepage(string $strHomepage) : void
322
    {
323
        $this->aHomepages[] = $strHomepage;
324
    }
325
326
    /**
327
     * Set annotation.
328
     * @param string $strNote
329
     */
330
    public function setNote(string $strNote) : void
331
    {
332
        $this->strNote = $strNote;
333
    }
334
335
    /**
336
     * Set portrait from base64 encoded image data.
337
     * @param string $blobPortrait base64 encoded image
338
     */
339
    public function setPortraitBlob(string $blobPortrait) : void
340
    {
341
        $this->blobPortrait = $blobPortrait;
342
    }
343
344
    /**
345
     * Save portrait as file.
346
     * Supportet types are JPG, PNG, GIF and BMP
347
     * The type depends on the fileextension. If no extensiomnm given, the
348
     * type of the imported image will be used.
349
     * @param string $strFilename
350
     */
351
    public function savePortrait(string $strFilename) : void
352
    {
353
        if (strlen($this->blobPortrait) > 0) {
354
            $strType = '';
355
            $strImage = '';
356
            $this->parseImageData($this->blobPortrait, $strType, $strImage);
357
            if (strlen($strType) > 0 && strlen($strImage) > 0) {
358
                $img = $this->imageFromString($strImage, $strType);
359
                imagealphablending($img, true);
360
                imagesavealpha($img, true);
361
                $strExt = strtolower((string) pathinfo($strFilename, PATHINFO_EXTENSION));
362
                if (strlen($strExt) == 0) {
363
                    $strExt = strtolower($strType);
364
                    $strFilename .= '.' . $strExt;
365
                }
366
                switch ($strExt) {
367
                    case 'jpg':
368
                    case 'jpeg':
369
                        imagejpeg($img, $strFilename);
370
                        break;
371
                    case 'png':
372
                        imagepng($img, $strFilename);
373
                        break;
374
                    case 'gif':
375
                        imagegif($img, $strFilename);
376
                        break;
377
                    case 'bmp':
378
                        imagebmp($img, $strFilename);
379
                        break;
380
                }
381
            }
382
        }
383
    }
384
385
    /**
386
     * Number of addresses the contact contains.
387
     * @return int
388
     */
389
    public function getAddressCount() : int
390
    {
391
        return count($this->aAddress);
392
    }
393
394
    /**
395
     * Get address.
396
     * An address can be referenced by index or by type. <br/>
397
     * For Type requests (=> $i non numeric value): <ul>
398
     * <li> The first address matches specified type is used (contact may contains multiple
399
     *      addresses of same type)  </li>
400
     * <li> If VCard::PREF is requested, the first preferred address in contact used (even
401
     *      if more than one is defined as preferred), if no preferred address found, the
402
     *      first address within the contact will be returned!   </li></ul>
403
     * @param int|string $i     reference to address (int => index, string => type)
404
     * @return VCardAddress|null    valid address object or null, if not found
405
     */
406
    public function getAddress($i) : ?VCardAddress
407
    {
408
        $oAddr = null;
409
        if (is_numeric($i)) {
410
            if ($i >= 0 && $i < count($this->aAddress)) {
411
                $oAddr = $this->aAddress[$i];
412
            }
413
        } else {
414
            foreach ($this->aAddress as $oAddress) {
415
                if (strpos($oAddress->getType(), $i) !== false) {
416
                    $oAddr = $oAddress;
417
                    break;
418
                }
419
            }
420
        }
421
        if (!$oAddr && $i == VCard::PREF && count($this->aAddress) > 0) {
422
            // if preferred item requested and no address in contact defined as prefered, just return first...
423
            $oAddr = $this->aAddress[0];
424
        }
425
        return $oAddr;
426
    }
427
428
    /**
429
     * Count of phone numbers.
430
     * @return int
431
     */
432
    public function getPhoneCount() : int
433
    {
434
        return count($this->aPhone);
435
    }
436
437
    /**
438
     * Get phone number.
439
     * Requested number can be referenced by index or type. <br/>
440
     * For index request: `0 <= $i < self::getPhoneCount()` <br/>
441
     * For type requests (=> $i non numeric value): <ul>
442
     * <li> first phone matches specified type is used (contact may contains multiple phone numbers of same type) </li>
443
     * <li> if VCard::PREF specified, first number in contact used, if no preferred item found </li></ul>
444
     * @param mixed $i     reference to address (int => index, string => type)
445
     * @return array or null
446
     */
447
    public function getPhone($i) : ?array
448
    {
449
        $aPhone = null;
450
        if (is_numeric($i)) {
451
            if ($i >= 0 && $i < count($this->aPhone)) {
452
                $aPhone = $this->aPhone[$i];
453
            }
454
        } else {
455
            foreach ($this->aPhone as $aPhone) {
456
                if (strpos($aPhone['strType'], $i) !== false) {
457
                    return $aPhone;
458
                }
459
                $aPhone = null;
460
            }
461
        }
462
        if (!$aPhone && $i == VCard::PREF && count($this->aPhone) > 0) {
463
            // if preferred item requested and no phone in contact defined as prefered, just return first...
464
            $aPhone = $this->aPhone[0];
465
        }
466
        return $aPhone;
467
    }
468
469
    /**
470
     * Number of email addresses contained.
471
     * @return int
472
     */
473
    public function getEMailCount() : int
474
    {
475
        return count($this->aEMail);
476
    }
477
478
    /**
479
     * Get EMail addres at given index.
480
     * @param int $i    index (`0 <= $i < self::getEMailCount()`)
481
     * @return string
482
     */
483
    public function getEMail(int $i) : string
484
    {
485
        $strEMail = '';
486
        if ($i >= 0 && $i < count($this->aEMail)) {
487
            $strEMail = $this->aEMail[$i];
488
        }
489
        return $strEMail;
490
    }
491
492
    /**
493
     * Number of categories contained.
494
     * @return int
495
     */
496
    public function getCategoriesCount() : int
497
    {
498
        return count($this->aCategories);
499
    }
500
501
    /**
502
     * Get category for given index.
503
     * @param int $i    index (`0 <= $i < self::getCategoriesCount()`)
504
     * @return string
505
     */
506
    public function getCategory(int $i) : string
507
    {
508
        $strCategory = '';
509
        if ($i >= 0 && $i < count($this->aCategories)) {
510
            $strCategory = $this->aCategories[$i];
511
        }
512
        return $strCategory;
513
    }
514
515
    /**
516
     * Return Categories separated by comma.
517
     * @return string
518
     */
519
    public function getCategories() : string
520
    {
521
        $strCategories = '';
522
        $strSep = '';
523
        foreach ($this->aCategories as $strCategory) {
524
            $strCategories .= $strSep . $strCategory;
525
            $strSep = ',';
526
        }
527
        return $strCategories;
528
    }
529
530
    /**
531
     * Get full name.
532
     * `$strFirstName` followed by `$strLastName` separeted by blank.
533
     * @return string
534
     */
535
    public function getName() : string
536
    {
537
        $strSep = (empty($this->strFirstName) || empty($this->strLastName)) ? '' : ' ';
538
        return $this->strFirstName . $strSep . $this->strLastName;
539
    }
540
541
    /**
542
     * Get lastname.
543
     * @return string
544
     */
545
    public function getLastName() : string
546
    {
547
        return $this->strLastName;
548
    }
549
550
    /**
551
     * Get firstname.
552
     * @return string
553
     */
554
    public function getFirstName() : string
555
    {
556
        return $this->strFirstName;
557
    }
558
559
    /**
560
     * Get nickname.
561
     * @return string
562
     */
563
    public function getNickName() : string
564
    {
565
        return $this->strNickName;
566
    }
567
568
    /**
569
     * Get name of the organisation.
570
     * @return string
571
     */
572
    public function getOrganisation() : string
573
    {
574
        return $this->strOrganisation;
575
    }
576
577
    /**
578
     * Get position, job title or function within the organisation.
579
     * @return string
580
     */
581
    public function getPosition() : string
582
    {
583
        return $this->strPosition;
584
    }
585
586
    /**
587
     * Get role, occupation or business category within the organisation.
588
     * @return string
589
     */
590
    public function getRole() : string
591
    {
592
        return $this->strRole;
593
    }
594
595
    /**
596
     * Number of homepages contained.
597
     * @return int
598
     */
599
    public function getHomepageCount() : int
600
    {
601
        return count($this->aHomepages);
602
    }
603
604
    /**
605
     * Get homepage for given index.
606
     * @param int $i    index (`0 <= $i < self::getHomepageCount()`)
607
     * @return string
608
     */
609
    public function getHomepage(int $i = -1) : string
610
    {
611
        $strHomepage = '';
612
        if ($i === -1) {
613
            // default value -1 set for backward compatibility but give chance for a 'deprecated' message!
614
            // version < 1.05 of this package hadn't support for multiple homepages!
615
            trigger_error('call of VCardContact::getHomepage() without index is deprecated!', E_USER_DEPRECATED);
616
            $i = 0;
617
        }
618
        if ($i >= 0 && $i < count($this->aHomepages)) {
619
            $strHomepage = $this->aHomepages[$i];
620
        }
621
        return $strHomepage;
622
    }
623
624
    /**
625
     * Get date of birth.
626
     * The return type can be specified in the `$iType`parameter: <ul>
627
     * <li><b> self::DT_STRING (default):</b> Date as String in f´the format set with `$strFormat`param (default = 'Y-m-d') </li>
628
     * <li><b> self::DT_UNIX_TIMESTAMP:</b> Date as unix timestamp</li>
629
     * <li><b> self::DT_OBJECT:</b> Date as DateTime object </li></ul>
630
     *
631
     * if the property is not set in the contact method returns: <ul>
632
     * <li><b> self::DT_STRING:</b> empty string </li>
633
     * <li><b> self::DT_UNIX_TIMESTAMP:</b> integer 0</li>
634
     * <li><b> self::DT_OBJECT:</b> null </li></ul>
635
     *
636
     * @link https://datatracker.ietf.org/doc/html/rfc2426#section-3.1.5
637
     * @link https://www.php.net/manual/en/datetime.format.php
638
     * @param int $iType    self::DT_STRING (default), self::DT_UNIX_TIMESTAMP or self::DT_OBJECT
639
     * @param string $strFormat Date format compliant to DateTime::format() (default 'Y-m-d')
640
     * @return string|int|\DateTime
641
     */
642
    public function getDateOfBirth(int $iType = self::DT_STRING, string $strFormat = 'Y-m-d')
643
    {
644
        $dtBirth = new \DateTime($this->strDateOfBirth);
645
        switch ($iType) {
646
            case self::DT_UNIX_TIMESTAMP:
647
                return (empty($this->strDateOfBirth) ? 0 : $dtBirth->getTimestamp());
648
            case self::DT_OBJECT:
649
                return (empty($this->strDateOfBirth) ? null : $dtBirth);
650
            default:
651
                return (empty($this->strDateOfBirth) ? '' : $dtBirth->format($strFormat));
652
        }
653
    }
654
655
    /**
656
     * Get gender (Microsoft only).
657
     * @return int  0: not set, 1: female, 2: male
658
     */
659
    public function getGender() : int
660
    {
661
        return $this->iGender;
662
    }
663
664
    /**
665
     * Get section or organizational unit within the organisation.
666
     * @return string
667
     */
668
    public function getSection() : string
669
    {
670
        return $this->strSection;
671
    }
672
673
    /**
674
     * Get annotation.
675
     * @return string
676
     */
677
    public function getNote() : string
678
    {
679
        return $this->strNote;
680
    }
681
682
    /**
683
     * Get (honorific) name prefix.
684
     * i.E. 'Dr.', 'Prof.', ...
685
     * @return string
686
     */
687
    public function getPrefix() : string
688
    {
689
        return $this->strPrefix;
690
    }
691
692
    /**
693
     * Get (honorific) name suffix.
694
     * i.E. 'Jr.', 'M.D.', ...
695
     * @return string
696
     */
697
    public function getSuffix() : string
698
    {
699
        return $this->strSuffix;
700
    }
701
702
    /**
703
     * Get the image as base64 encoded string.
704
     * @return string base64 encoded image
705
     */
706
    public function getPortraitBlob() : string
707
    {
708
        return $this->blobPortrait;
709
    }
710
}
711