Issues (1)

src/VCard.php (1 issue)

Labels
Severity
1
<?php 
2
3
namespace Rogersxd\VCard;
4
5
/**
6
*  VCard Class
7
*
8
*  @author Rogers Corrêa
9
*/
10
11
class VCard{
12
13
    /**
14
    * existsElements
15
    *
16
    * @var array
17
    */
18
    private $existsElements;
19
20
    /**
21
    * charset
22
    *
23
    * @var string
24
    */
25
    private $charset = 'UTF-8';
26
27
    /**
28
    * filename
29
    *
30
    * @var string
31
    */
32
    private $filename;
33
34
    /**
35
    * properties
36
    *
37
    * @var array
38
    */
39
    private $properties;
40
41
    /**
42
     * Save Path
43
     *
44
     * @var string
45
     */
46
    private $savePath = null;
47
48
    /**
49
    * multipleAllowed
50
    *
51
    * @var array
52
    */
53
54
    private $multipleAllowed;
55
56
    /**
57
    * Construct method 
58
    *
59
    * Set filename on init and difine the multiple properties allowed
60
    *
61
    * @return string
62
    */
63
    
64
    public function __construct()
65
    {
66
        $this->setFilename(uniqid());
67
68
        $this->multipleAllowed = [
69
            'email',
70
            'address',
71
            'phone',
72
            'url',
73
            'label',
74
            'custom',
75
            'social'
76
        ];
77
    }
78
79
    /**
80
    * Add names method
81
    *
82
    * @param  string [optional] $lastName
83
    * @param  string [optional] $firstName
84
    * @param  string [optional] $additional
85
    * @param  string [optional] $prefix
86
    * @param  string [optional] $suffix
87
    * @return $this
88
    */
89
90
    public function addNames(
91
        $lastName = '',
92
        $firstName = '',
93
        $additional = '',
94
        $prefix = '',
95
        $suffix = '',
96
        $fullName = false
97
    ){
98
        // set property
99
        $property = $lastName . ';' . $firstName . ';' . $additional . ';' . $prefix . ';' . $suffix;
100
        $this->setProperty(
101
            'name',
102
            'N' . $this->getCharsetInVCard(),
103
            $property
104
        );
105
106
        if($fullName === true) {
107
            $values = array_filter([
108
                $prefix,
109
                $firstName,
110
                $additional,
111
                $lastName,
112
                $suffix,
113
            ]);
114
115
            $this->setProperty(
116
                'fullname',
117
                'FN' . $this->getCharsetInVCard(),
118
                trim(implode(' ', $values))
119
            );
120
        }
121
122
        return $this;
123
    }
124
125
    /**
126
    * Add phone number
127
    *
128
    * @param  string $number
129
    * @param  string $type
130
    * TYPES = PREF | WORK | HOME | VOICE | FAX | MSG |
131
    * CELL | PAGER | BBS | CAR | MODEM | ISDN | VIDEO
132
    * @return $this
133
    */
134
    public function addPhone($number, $type = '')
135
    {
136
        $this->setProperty(
137
            'phone',
138
            $type,
139
            $number
140
        );
141
142
        return $this;
143
    }
144
145
    /**
146
    * Add role
147
    *
148
    * @param  string $role
149
    * @return $this
150
    */
151
    public function addRole($role)
152
    {
153
        $this->setProperty(
154
            'role',
155
            'ROLE' . $this->getCharsetInVCard(),
156
            $role
157
        );
158
159
        return $this;
160
    }
161
162
    /**
163
    * Add jobtitle
164
    *
165
    * @param  string $jobtitle
166
    * @return $this
167
    */
168
    public function addJobtitle($jobtitle)
169
    {
170
        $this->setProperty(
171
            'jobtitle',
172
            'TITLE' . $this->getCharsetInVCard(),
173
            $jobtitle
174
        );
175
        return $this;
176
    }
177
178
    /**
179
    * Add birthday
180
    *
181
    * @param  string $date - YYYY-MM-DD
182
    * @return $this
183
    */
184
    public function addBirthday($date)
185
    {
186
        $this->setProperty(
187
            'birthday',
188
            'BDAY',
189
            $date
190
        );
191
        return $this;
192
    }
193
194
    /**
195
     * Add email
196
     *
197
     * @param  string $email E-mail address
198
     * @param  string [optional] $type
199
     * TYPES = PREF | WORK | HOME;
200
     * @return $this
201
     */
202
    public function addEmail($email, $type = '')
203
    {
204
        $this->setProperty(
205
            'email',
206
            'EMAIL;INTERNET' . (($type != '') ? ';' . $type : ''),
207
            $email
208
        );
209
210
        return $this;
211
    }
212
213
    /**
214
    * Add company
215
    *
216
    * @param string $company
217
    * @param string $department
218
    * @return $this
219
    */
220
    public function addCompany($company, $department = '')
221
    {
222
        $this->setProperty(
223
            'company',
224
            'ORG' . $this->getCharsetInVCard(),
225
            $company
226
            . ($department != '' ? ';' . $department : '')
227
        );
228
229
        return $this;
230
    }
231
232
    /**
233
    * Add note
234
    *
235
    * @param  string $note
236
    * @return $this
237
    */
238
    public function addNote($note)
239
    {
240
        $this->setProperty(
241
            'note',
242
            'NOTE' . $this->getCharsetInVCard(),
243
            $note
244
        );
245
246
        return $this;
247
    }
248
249
    /**
250
    * Add Photo
251
    *
252
    * @param  string $path image url or path
253
    * @return $this
254
    */
255
    public function addPhoto($path)
256
    {
257
        $mimeType = null;
258
259
        if (filter_var($path, FILTER_VALIDATE_URL) !== false) {
260
261
            $headers = get_headers($path, 1);
262
263
            if (array_key_exists('Content-Type', $headers)) {
264
                $mimeType = $headers['Content-Type'];
265
                if (is_array($mimeType)) {
266
                    $mimeType = end($mimeType);
267
                }
268
            }
269
        } else {
270
            $mimeType = mime_content_type($path);
271
        }
272
273
        if (strpos($mimeType, ';') !== false) {
274
            $mimeType = strstr($mimeType, ';', true);
275
        }
276
        if (!is_string($mimeType) || substr($mimeType, 0, 6) !== 'image/') {
277
            throw VCardException::invalidImage();
278
        }
279
        $fileType = strtoupper(substr($mimeType, 6));
280
281
        if ((bool) ini_get('allow_url_fopen') === true) {
282
            $value = file_get_contents($path);
283
        } else {
284
            $curl = curl_init();
285
            curl_setopt($curl, CURLOPT_URL, $path);
286
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
287
            $value = curl_exec($curl);
288
            curl_close($curl);
289
        }
290
291
        if (!$value) {
292
            throw VCardException::emptyURL();
293
        }
294
295
        $value = base64_encode($value);
296
297
        $property = "PHOTO;ENCODING=BASE64;TYPE=" . $fileType;
298
299
        $this->setProperty(
300
            'photo',
301
            $property,
302
            $value
303
        );
304
305
        return $this;
306
    }
307
308
    /**
309
    * Add URL
310
    *
311
    * @param  string $url
312
    * @param  string [optional] $type Type may be WORK | HOME
313
    * @return $this
314
    */
315
    public function addURL($url, $type = '')
316
    {
317
        $this->setProperty(
318
            'url',
319
            'URL' . (($type != '') ? ';' . $type : ''),
320
            $url
321
        );
322
323
        return $this;
324
    }
325
326
    /**
327
    * Add address
328
    *
329
    * @param  string [optional] $socialProfile
330
    * @param  string [optional] $type
331
    * TYPES = facebook | twitter | instagram | linkedin
332
    * @return $this
333
    */
334
    public function addSocialProfile($socialProfile, $type)
335
    {
336
        $this->setProperty(
337
            'social',
338
            'X-SOCIALPROFILE;type='. $type,
339
            $socialProfile
340
        );
341
342
        return $this;
343
    }
344
345
    /**
346
    * Add address
347
    *
348
    * @param  string [optional] $name
349
    * @param  string [optional] $extended
350
    * @param  string [optional] $street
351
    * @param  string [optional] $city
352
    * @param  string [optional] $region
353
    * @param  string [optional] $zip
354
    * @param  string [optional] $country
355
    * @param  string [optional] $type
356
    * TYPES = DOM | INTL | POSTAL | PARCEL | HOME | WORK
357
    * @return $this
358
    */
359
    public function addAddress(
360
        $name = '',
361
        $extended = '',
362
        $street = '',
363
        $city = '',
364
        $region = '',
365
        $zip = '',
366
        $country = '',
367
        $type = 'WORK;POSTAL'
368
    ) {
369
370
        $value = $name . ';' . $extended . ';' . $street . ';' . $city . ';' . $region . ';' . $zip . ';' . $country;
371
372
        $this->setProperty(
373
            'address',
374
            'ADR' . (($type != '') ? ';' . $type : '') . $this->getCharsetInVCard(),
375
            $value
376
        );
377
378
        return $this;
379
    }
380
381
    /**
382
    * Add custom
383
    *
384
    * @param  string $param
385
    * @param  string $custom
386
    * @return $this
387
    */
388
    public function addCustom($param, $custom)
389
    {
390
        $this->setProperty(
391
            'custom',
392
            $param,
393
            $custom
394
        );
395
396
        return $this;
397
    }
398
    /**
399
    * Set charset string
400
    */
401
    public function setCharset($charset)
402
    {
403
        $this->charset = $charset;
404
    }
405
406
    /**
407
    * Get charset in vCard
408
    *
409
    * @return string
410
    */
411
    public function getCharsetInVCard()
412
    {
413
        return ';CHARSET=' . $this->charset;
414
    }
415
416
    /**
417
    * Get charset string
418
    *
419
    * @return string
420
    */
421
    public function getCharset()
422
    {
423
        return $this->charset;
424
    }
425
426
    /**
427
    * Set property
428
    *
429
    * @param  string $element
430
    * @param  string $key
431
    * @param  string $value
432
    * @throws VCardException
433
    */
434
    public function setFilename($value, $overwrite = true)
435
    {
436
        $value = trim($value);
437
438
        $value = preg_replace('/\s+/', ' ', $value);
439
440
        if (empty($value)) {
441
            return;
442
        }
443
444
        $value = strtolower($value);
445
446
        $this->filename = ($overwrite) ?
447
        $value : $this->filename . $value;
448
    }
449
450
    /**
451
    * Get filename
452
    *
453
    * @return string
454
    */
455
    public function getFilename()
456
    {
457
        if (!$this->filename) {
458
            return 'unknown';
459
        }
460
461
        return $this->filename;
462
    }
463
464
    /**
465
    * Set property
466
    *
467
    * @param  string $element
468
    * @param  string $key
469
    * @param  string $value
470
    * @throws VCardException
471
    */
472
473
    private function setProperty($element, $key, $value)
474
    {
475
        if (!in_array($element, $this->multipleAllowed)
476
            && isset($this->existsElements[$element])
477
        ) {
478
            throw VCardException::elementExists($element);
479
        }
480
481
            // we define that we set this element
482
        $this->existsElements[$element] = true;
483
484
            // adding property
485
        $this->properties[] = [
486
            'key' => $key,
487
            'value' => $value
488
        ];
489
    }
490
491
    public function getProperties()
492
    {
493
        return $this->properties;
494
    }
495
496
    /**
497
     * Build VCard (.vcf)
498
     *
499
     * @return string
500
     */
501
    public function genVCard()
502
    {
503
        // init string
504
        $string = "BEGIN:VCARD\r\n";
505
        $string .= "VERSION:3.0\r\n";
506
        $string .= "REV:" . date("Y-m-d") . "T" . date("H:i:s") . "Z\r\n";
507
508
        // loop all properties
509
        $properties = $this->getProperties();
510
        foreach ($properties as $property) {
511
            // add to string
512
            $string .= $this->fold($property['key'] . ':' . $this->escape($property['value']) . "\r\n");
513
        }
514
515
        // add to string
516
        $string .= "END:VCARD\r\n";
517
518
        // return
519
        return $string;
520
    }
521
522
    /**
523
    * Set the save path directory
524
    *
525
    * @param  string $savePath Save Path
526
    * @throws VCardException
527
    */
528
    public function setSavePath($savePath)
529
    {
530
        if (!is_dir($savePath)) {
531
            throw VCardException::outputDirectoryNotExists();
532
        }
533
534
        // Add trailing directory separator the save path
535
        if (substr($savePath, -1) != DIRECTORY_SEPARATOR) {
536
            $savePath .= DIRECTORY_SEPARATOR;
537
        }
538
539
        $this->savePath = $savePath;
540
    }
541
542
543
    /**
544
     * Save to a file
545
     *
546
     * @return void
547
     */
548
    public function save()
549
    {
550
        $file = $this->getFilename() . '.vcf';
551
552
        // Add save path if given
553
        if (null !== $this->savePath) {
554
            $file = $this->savePath . $file;
555
        }
556
557
        file_put_contents(
558
            $file,
559
            $this->genVCard()
560
        );
561
    }
562
563
564
    /**
565
    * Fold a line according to RFC2425 section 5.8.1.
566
    *
567
    * @link http://tools.ietf.org/html/rfc2425#section-5.8.1
568
    * @param  string $text
569
    * @return mixed
570
    */
571
    protected function fold($text)
572
    {
573
        if (strlen($text) <= 75) {
574
            return $text;
575
        }
576
577
        // split, wrap and trim trailing separator
578
        return substr($this->chunk_split_unicode($text, 75, "\r\n "), 0, -3);
579
    }
580
581
    /**
582
    * multibyte word chunk split
583
    * @link http://php.net/manual/en/function.chunk-split.php#107711
584
    * 
585
    * @param  string  $body     The string to be chunked.
586
    * @param  integer $chunklen The chunk length.
587
    * @param  string  $end      The line ending sequence.
588
    * @return string            Chunked string
589
    */
590
    protected function chunk_split_unicode($body, $chunklen = 76, $end = "\r\n")
591
    {
592
        $array = array_chunk(
593
            preg_split("//u", $body, -1, PREG_SPLIT_NO_EMPTY), $chunklen);
0 ignored issues
show
It seems like preg_split('//u', $body,...rd\PREG_SPLIT_NO_EMPTY) can also be of type false; however, parameter $input of array_chunk() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

593
            /** @scrutinizer ignore-type */ preg_split("//u", $body, -1, PREG_SPLIT_NO_EMPTY), $chunklen);
Loading history...
594
        $body = "";
595
        foreach ($array as $item) {
596
            $body .= join("", $item) . $end;
597
        }
598
        return $body;
599
    }
600
601
    /**
602
    * Escape newline characters according to RFC2425 section 5.8.4.
603
    *
604
    * @link http://tools.ietf.org/html/rfc2425#section-5.8.4
605
    * @param  string $text
606
    * @return string
607
    */
608
    protected function escape($text)
609
    {
610
        $text = str_replace("\r\n", "\\n", $text);
611
        $text = str_replace("\n", "\\n", $text);
612
613
        return $text;
614
    }
615
}