Passed
Pull Request — master (#3860)
by Adrien
19:40 queued 09:20
created

Properties::getTitle()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Document;
4
5
use DateTime;
6
use PhpOffice\PhpSpreadsheet\Shared\IntOrFloat;
7
8
class Properties
9
{
10
    /** constants */
11
    public const PROPERTY_TYPE_BOOLEAN = 'b';
12
    public const PROPERTY_TYPE_INTEGER = 'i';
13
    public const PROPERTY_TYPE_FLOAT = 'f';
14
    public const PROPERTY_TYPE_DATE = 'd';
15
    public const PROPERTY_TYPE_STRING = 's';
16
    public const PROPERTY_TYPE_UNKNOWN = 'u';
17
18
    private const VALID_PROPERTY_TYPE_LIST = [
19
        self::PROPERTY_TYPE_BOOLEAN,
20
        self::PROPERTY_TYPE_INTEGER,
21
        self::PROPERTY_TYPE_FLOAT,
22
        self::PROPERTY_TYPE_DATE,
23
        self::PROPERTY_TYPE_STRING,
24
    ];
25
26
    /**
27
     * Creator.
28
     */
29
    private string $creator = 'Unknown Creator';
30
31
    /**
32
     * LastModifiedBy.
33
     */
34
    private string $lastModifiedBy;
35
36
    /**
37
     * Created.
38
     */
39
    private float|int $created;
40
41
    /**
42
     * Modified.
43
     */
44
    private float|int $modified;
45
46
    /**
47
     * Title.
48
     */
49
    private string $title = 'Untitled Spreadsheet';
50
51
    /**
52
     * Description.
53
     */
54
    private string $description = '';
55
56
    /**
57
     * Subject.
58
     */
59
    private string $subject = '';
60
61
    /**
62
     * Keywords.
63
     */
64
    private string $keywords = '';
65
66
    /**
67
     * Category.
68
     */
69
    private string $category = '';
70
71
    /**
72
     * Manager.
73
     */
74
    private string $manager = '';
75
76
    /**
77
     * Company.
78
     */
79
    private string $company = '';
80
81
    /**
82
     * Custom Properties.
83
     *
84
     * @var array{value: null|bool|float|int|string, type: string}[]
85
     */
86
    private array $customProperties = [];
87
88
    private string $hyperlinkBase = '';
89
90
    private string $viewport = '';
91
92
    /**
93
     * Create a new Document Properties instance.
94
     */
95 9919
    public function __construct()
96
    {
97
        // Initialise values
98 9919
        $this->lastModifiedBy = $this->creator;
99 9919
        $this->created = self::intOrFloatTimestamp(null);
100 9919
        $this->modified = $this->created;
101
    }
102
103
    /**
104
     * Get Creator.
105
     */
106 865
    public function getCreator(): string
107
    {
108 865
        return $this->creator;
109
    }
110
111
    /**
112
     * Set Creator.
113
     *
114
     * @return $this
115
     */
116 1168
    public function setCreator(string $creator): self
117
    {
118 1168
        $this->creator = $creator;
119
120 1168
        return $this;
121
    }
122
123
    /**
124
     * Get Last Modified By.
125
     */
126 837
    public function getLastModifiedBy(): string
127
    {
128 837
        return $this->lastModifiedBy;
129
    }
130
131
    /**
132
     * Set Last Modified By.
133
     *
134
     * @return $this
135
     */
136 1167
    public function setLastModifiedBy(string $modifiedBy): self
137
    {
138 1167
        $this->lastModifiedBy = $modifiedBy;
139
140 1167
        return $this;
141
    }
142
143 9919
    private static function intOrFloatTimestamp(null|float|int|string $timestamp): float|int
144
    {
145 9919
        if ($timestamp === null) {
146 9919
            $timestamp = (float) (new DateTime())->format('U');
147 1145
        } elseif (is_string($timestamp)) {
148 1062
            if (is_numeric($timestamp)) {
149 2
                $timestamp = (float) $timestamp;
150
            } else {
151 1060
                $timestamp = (string) preg_replace('/[.][0-9]*$/', '', $timestamp);
152 1060
                $timestamp = (string) preg_replace('/^(\\d{4})- (\\d)/', '$1-0$2', $timestamp);
153 1060
                $timestamp = (string) preg_replace('/^(\\d{4}-\\d{2})- (\\d)/', '$1-0$2', $timestamp);
154 1060
                $timestamp = (float) (new DateTime($timestamp))->format('U');
155
            }
156
        }
157
158 9919
        return IntOrFloat::evaluate($timestamp);
159
    }
160
161
    /**
162
     * Get Created.
163
     */
164 865
    public function getCreated(): float|int
165
    {
166 865
        return $this->created;
167
    }
168
169
    /**
170
     * Set Created.
171
     *
172
     * @return $this
173
     */
174 1139
    public function setCreated(null|float|int|string $timestamp): self
175
    {
176 1139
        $this->created = self::intOrFloatTimestamp($timestamp);
177
178 1139
        return $this;
179
    }
180
181
    /**
182
     * Get Modified.
183
     */
184 865
    public function getModified(): float|int
185
    {
186 865
        return $this->modified;
187
    }
188
189
    /**
190
     * Set Modified.
191
     *
192
     * @return $this
193
     */
194 1139
    public function setModified(null|float|int|string $timestamp): self
195
    {
196 1139
        $this->modified = self::intOrFloatTimestamp($timestamp);
197
198 1139
        return $this;
199
    }
200
201
    /**
202
     * Get Title.
203
     */
204 861
    public function getTitle(): string
205
    {
206 861
        return $this->title;
207
    }
208
209
    /**
210
     * Set Title.
211
     *
212
     * @return $this
213
     */
214 1128
    public function setTitle(string $title): self
215
    {
216 1128
        $this->title = $title;
217
218 1128
        return $this;
219
    }
220
221
    /**
222
     * Get Description.
223
     */
224 857
    public function getDescription(): string
225
    {
226 857
        return $this->description;
227
    }
228
229
    /**
230
     * Set Description.
231
     *
232
     * @return $this
233
     */
234 673
    public function setDescription(string $description): self
235
    {
236 673
        $this->description = $description;
237
238 673
        return $this;
239
    }
240
241
    /**
242
     * Get Subject.
243
     */
244 859
    public function getSubject(): string
245
    {
246 859
        return $this->subject;
247
    }
248
249
    /**
250
     * Set Subject.
251
     *
252
     * @return $this
253
     */
254 672
    public function setSubject(string $subject): self
255
    {
256 672
        $this->subject = $subject;
257
258 672
        return $this;
259
    }
260
261
    /**
262
     * Get Keywords.
263
     */
264 858
    public function getKeywords(): string
265
    {
266 858
        return $this->keywords;
267
    }
268
269
    /**
270
     * Set Keywords.
271
     *
272
     * @return $this
273
     */
274 674
    public function setKeywords(string $keywords): self
275
    {
276 674
        $this->keywords = $keywords;
277
278 674
        return $this;
279
    }
280
281
    /**
282
     * Get Category.
283
     */
284 857
    public function getCategory(): string
285
    {
286 857
        return $this->category;
287
    }
288
289
    /**
290
     * Set Category.
291
     *
292
     * @return $this
293
     */
294 666
    public function setCategory(string $category): self
295
    {
296 666
        $this->category = $category;
297
298 666
        return $this;
299
    }
300
301
    /**
302
     * Get Company.
303
     */
304 813
    public function getCompany(): string
305
    {
306 813
        return $this->company;
307
    }
308
309
    /**
310
     * Set Company.
311
     *
312
     * @return $this
313
     */
314 578
    public function setCompany(string $company): self
315
    {
316 578
        $this->company = $company;
317
318 578
        return $this;
319
    }
320
321
    /**
322
     * Get Manager.
323
     */
324 790
    public function getManager(): string
325
    {
326 790
        return $this->manager;
327
    }
328
329
    /**
330
     * Set Manager.
331
     *
332
     * @return $this
333
     */
334 270
    public function setManager(string $manager): self
335
    {
336 270
        $this->manager = $manager;
337
338 270
        return $this;
339
    }
340
341
    /**
342
     * Get a List of Custom Property Names.
343
     *
344
     * @return string[]
345
     */
346 813
    public function getCustomProperties(): array
347
    {
348 813
        return array_keys($this->customProperties);
349
    }
350
351
    /**
352
     * Check if a Custom Property is defined.
353
     */
354 11
    public function isCustomPropertySet(string $propertyName): bool
355
    {
356 11
        return array_key_exists($propertyName, $this->customProperties);
357
    }
358
359
    /**
360
     * Get a Custom Property Value.
361
     */
362 45
    public function getCustomPropertyValue(string $propertyName): bool|int|float|string|null
363
    {
364 45
        if (isset($this->customProperties[$propertyName])) {
365 44
            return $this->customProperties[$propertyName]['value'];
366
        }
367
368 1
        return null;
369
    }
370
371
    /**
372
     * Get a Custom Property Type.
373
     */
374 42
    public function getCustomPropertyType(string $propertyName): ?string
375
    {
376 42
        return $this->customProperties[$propertyName]['type'] ?? null;
377
    }
378
379 5
    private function identifyPropertyType(bool|int|float|string|null $propertyValue): string
380
    {
381 5
        if (is_float($propertyValue)) {
382 1
            return self::PROPERTY_TYPE_FLOAT;
383
        }
384 4
        if (is_int($propertyValue)) {
385 1
            return self::PROPERTY_TYPE_INTEGER;
386
        }
387 3
        if (is_bool($propertyValue)) {
388 1
            return self::PROPERTY_TYPE_BOOLEAN;
389
        }
390
391 2
        return self::PROPERTY_TYPE_STRING;
392
    }
393
394
    /**
395
     * Set a Custom Property.
396
     *
397
     * @param ?string $propertyType see `self::VALID_PROPERTY_TYPE_LIST`
398
     *
399
     * @return $this
400
     */
401 88
    public function setCustomProperty(string $propertyName, bool|int|float|string|null $propertyValue = '', ?string $propertyType = null): self
402
    {
403 88
        if (($propertyType === null) || (!in_array($propertyType, self::VALID_PROPERTY_TYPE_LIST))) {
404 5
            $propertyType = $this->identifyPropertyType($propertyValue);
405
        }
406
407 88
        $this->customProperties[$propertyName] = [
408 88
            'value' => self::convertProperty($propertyValue, $propertyType),
409 88
            'type' => $propertyType,
410 88
        ];
411
412 88
        return $this;
413
    }
414
415
    private const PROPERTY_TYPE_ARRAY = [
416
        'i' => self::PROPERTY_TYPE_INTEGER,      //    Integer
417
        'i1' => self::PROPERTY_TYPE_INTEGER,     //    1-Byte Signed Integer
418
        'i2' => self::PROPERTY_TYPE_INTEGER,     //    2-Byte Signed Integer
419
        'i4' => self::PROPERTY_TYPE_INTEGER,     //    4-Byte Signed Integer
420
        'i8' => self::PROPERTY_TYPE_INTEGER,     //    8-Byte Signed Integer
421
        'int' => self::PROPERTY_TYPE_INTEGER,    //    Integer
422
        'ui1' => self::PROPERTY_TYPE_INTEGER,    //    1-Byte Unsigned Integer
423
        'ui2' => self::PROPERTY_TYPE_INTEGER,    //    2-Byte Unsigned Integer
424
        'ui4' => self::PROPERTY_TYPE_INTEGER,    //    4-Byte Unsigned Integer
425
        'ui8' => self::PROPERTY_TYPE_INTEGER,    //    8-Byte Unsigned Integer
426
        'uint' => self::PROPERTY_TYPE_INTEGER,   //    Unsigned Integer
427
        'f' => self::PROPERTY_TYPE_FLOAT,        //    Real Number
428
        'r4' => self::PROPERTY_TYPE_FLOAT,       //    4-Byte Real Number
429
        'r8' => self::PROPERTY_TYPE_FLOAT,       //    8-Byte Real Number
430
        'decimal' => self::PROPERTY_TYPE_FLOAT,  //    Decimal
431
        's' => self::PROPERTY_TYPE_STRING,       //    String
432
        'empty' => self::PROPERTY_TYPE_STRING,   //    Empty
433
        'null' => self::PROPERTY_TYPE_STRING,    //    Null
434
        'lpstr' => self::PROPERTY_TYPE_STRING,   //    LPSTR
435
        'lpwstr' => self::PROPERTY_TYPE_STRING,  //    LPWSTR
436
        'bstr' => self::PROPERTY_TYPE_STRING,    //    Basic String
437
        'd' => self::PROPERTY_TYPE_DATE,         //    Date and Time
438
        'date' => self::PROPERTY_TYPE_DATE,      //    Date and Time
439
        'filetime' => self::PROPERTY_TYPE_DATE,  //    File Time
440
        'b' => self::PROPERTY_TYPE_BOOLEAN,      //    Boolean
441
        'bool' => self::PROPERTY_TYPE_BOOLEAN,   //    Boolean
442
    ];
443
444
    private const SPECIAL_TYPES = [
445
        'empty' => '',
446
        'null' => null,
447
    ];
448
449
    /**
450
     * Convert property to form desired by Excel.
451
     */
452 88
    public static function convertProperty(bool|int|float|string|null $propertyValue, string $propertyType): bool|int|float|string|null
453
    {
454 88
        return self::SPECIAL_TYPES[$propertyType] ?? self::convertProperty2($propertyValue, $propertyType);
455
    }
456
457
    /**
458
     * Convert property to form desired by Excel.
459
     */
460 88
    private static function convertProperty2(bool|int|float|string|null $propertyValue, string $type): bool|int|float|string|null
461
    {
462 88
        $propertyType = self::convertPropertyType($type);
463
        switch ($propertyType) {
464
            case self::PROPERTY_TYPE_INTEGER:
465 15
                $intValue = (int) $propertyValue;
466
467 15
                return ($type[0] === 'u') ? abs($intValue) : $intValue;
468
            case self::PROPERTY_TYPE_FLOAT:
469 22
                return (float) $propertyValue;
470
            case self::PROPERTY_TYPE_DATE:
471 31
                return self::intOrFloatTimestamp($propertyValue); // @phpstan-ignore-line
472
            case self::PROPERTY_TYPE_BOOLEAN:
473 29
                return is_bool($propertyValue) ? $propertyValue : ($propertyValue === 'true');
474
            default: // includes string
475 74
                return $propertyValue;
476
        }
477
    }
478
479 88
    public static function convertPropertyType(string $propertyType): string
480
    {
481 88
        return self::PROPERTY_TYPE_ARRAY[$propertyType] ?? self::PROPERTY_TYPE_UNKNOWN;
482
    }
483
484 833
    public function getHyperlinkBase(): string
485
    {
486 833
        return $this->hyperlinkBase;
487
    }
488
489 217
    public function setHyperlinkBase(string $hyperlinkBase): self
490
    {
491 217
        $this->hyperlinkBase = $hyperlinkBase;
492
493 217
        return $this;
494
    }
495
496 477
    public function getViewport(): string
497
    {
498 477
        return $this->viewport;
499
    }
500
501
    public const SUGGESTED_VIEWPORT = 'width=device-width, initial-scale=1';
502
503 1
    public function setViewport(string $viewport): self
504
    {
505 1
        $this->viewport = $viewport;
506
507 1
        return $this;
508
    }
509
}
510