PropertyMetadataBuilder::wrapperOf()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Bdf\Serializer\Metadata\Builder;
4
5
use Bdf\Serializer\Context\DenormalizationContext;
6
use Bdf\Serializer\Context\NormalizationContext;
7
use Bdf\Serializer\Metadata\PropertyMetadata;
8
use Bdf\Serializer\PropertyAccessor\MethodAccessor;
9
use Bdf\Serializer\PropertyAccessor\PropertyAccessorInterface;
10
use Bdf\Serializer\Type\TypeFactory;
11
use Bdf\Serializer\Util\AccessorGuesser;
12
use ReflectionClass;
13
use ReflectionException;
14
15
/**
16
 * PropertyMetadataBuilder
17
 *
18
 * @author  Seb
19
 */
20
class PropertyMetadataBuilder
21
{
22
    /**
23
     * The owner reflection class
24
     *
25
     * @var ReflectionClass
26
     */
27
    private $reflection;
28
29
    /**
30
     * The property name
31
     *
32
     * @var string
33
     */
34
    private $name;
35
36
    /**
37
     * The property alias
38
     *
39
     * @var string|null
40
     */
41
    private $alias;
42
43
    /**
44
     * The property type
45
     *
46
     * @var string|null
47
     */
48
    private $type;
49
50
    /**
51
     * The property groups
52
     *
53
     * @var array
54
     */
55
    private $groups = [];
56
57
    /**
58
     * The property accessor
59
     *
60
     * @var PropertyAccessorInterface|array|null
61
     */
62
    private $customAccessor;
63
64
    /**
65
     * The getter accessor
66
     *
67
     * @var PropertyAccessorInterface|string|null
68
     */
69
    private $getter;
70
71
    /**
72
     * The setter accessor
73
     *
74
     * @var PropertyAccessorInterface|string|null
75
     */
76
    private $setter;
77
78
    /**
79
     * The version when the property has been added.
80
     *
81
     * @var string|null
82
     */
83
    private $since;
84
85
    /**
86
     * The version when the property has been removed.
87
     *
88
     * @var string|null
89
     */
90
    private $until;
91
92
    /**
93
     * The read only state of the property.
94
     *
95
     * @var bool
96
     */
97
    private $readOnly = false;
98
99
    /**
100
     * The inline state of the property.
101
     *
102
     * @var bool
103
     */
104
    private $inline = false;
105
106
    /**
107
     * The context options for normalization.
108
     *
109
     * @var null|array
110
     */
111
    private $normalizationOptions;
112
113
    /**
114
     * The context options for denormalization.
115
     *
116
     * @var null|array
117
     */
118
    private $denormalizationOptions;
119
120
    /**
121
     * PropertyMetadataBuilder constructor.
122
     *
123
     * @param ReflectionClass $reflection
124
     * @param string $name
125
     */
126 240
    public function __construct(ReflectionClass $reflection, string $name)
127
    {
128 240
        $this->reflection = $reflection;
129 240
        $this->name = $name;
130
    }
131
132
    /**
133
     * Build the property metadata
134
     *
135
     * @return PropertyMetadata
136
     */
137 240
    public function build(): PropertyMetadata
138
    {
139 240
        $property = new PropertyMetadata($this->reflection->name, $this->name);
140 240
        $property->setType(TypeFactory::createType($this->type));
141 240
        $property->setAlias($this->alias ?: $this->name);
142 240
        $property->setGroups($this->groups);
143 240
        $property->setAccessor($this->buildAccessor());
144 240
        $property->setSince($this->since);
145 240
        $property->setUntil($this->until);
146 240
        $property->setReadOnly($this->readOnly === true);
147 240
        $property->setInline($this->inline === true);
148 240
        $property->setNormalizationOptions($this->normalizationOptions);
149 240
        $property->setDenormalizationOptions($this->denormalizationOptions);
150
151 240
        $defaultValues = $this->reflection->getDefaultProperties();
152 240
        if (array_key_exists($this->name, $defaultValues)) {
153 214
            $property->setDefaultValue($defaultValues[$this->name]);
154
        }
155
156
        // Tag the property as typed property
157 240
        if (PHP_VERSION_ID >= 70400) {
158
            try {
159 240
                $property->isPhpTyped = $this->reflection->getProperty($this->name)->hasType();
160 10
            } catch (ReflectionException $exception) {
161
                // The property could be virtual.
162
            }
163
        }
164
165 240
        $this->clear();
166
167 240
        return $property;
168
    }
169
170
    /**
171
     * Build the property accessor
172
     *
173
     * @return PropertyAccessorInterface
174
     */
175 240
    private function buildAccessor(): PropertyAccessorInterface
176
    {
177 240
        if ($this->customAccessor instanceof PropertyAccessorInterface) {
178
            return $this->customAccessor;
179
        }
180
181 240
        if ($this->setter !== null || $this->getter !== null) {
182 16
            return AccessorGuesser::getMethodAccessor($this->reflection, $this->name, $this->getter, $this->setter, $this->readOnly === true);
183
        }
184
185 224
        return AccessorGuesser::getPropertyAccessor($this->reflection, $this->name);
186
    }
187
188
    /**
189
     * Set the property type
190
     *
191
     * @param null|string $type
192
     *
193
     * @return $this
194
     */
195 200
    public function type(?string $type)
196
    {
197 200
        $this->type = $type;
198
199 200
        return $this;
200
    }
201
202
    /**
203
     * Set the property type as collection
204
     *
205
     * @return $this
206
     */
207 8
    public function collection()
208
    {
209 8
        $this->type .= '[]';
210
211 8
        return $this;
212
    }
213
214
    /**
215
     * Set the property type as collection of a given type
216
     *
217
     * @param string $subType
218
     *
219
     * @return $this
220
     */
221 4
    public function collectionOf($subType)
222
    {
223 4
        return $this->type($subType)->collection();
224
    }
225
226
    /**
227
     * Set the property type as collection wrapper of a given type
228
     *
229
     * @param string $subType
230
     *
231
     * @return $this
232
     */
233 2
    public function wrapperOf($subType)
234
    {
235 2
        $this->type .= "<$subType>";
236
237 2
        return $this;
238
    }
239
240
    /**
241
     * Set the property alias
242
     *
243
     * @param string|null $alias
244
     *
245
     * @return $this
246
     */
247 54
    public function alias($alias)
248
    {
249 54
        $this->alias = $alias;
250
251 54
        return $this;
252
    }
253
254
    /**
255
     * Get the property alias
256
     *
257
     * @return string|null
258
     */
259 190
    public function getAlias(): ?string
260
    {
261 190
        return $this->alias;
262
    }
263
264
    /**
265
     * Set the property groups
266
     *
267
     * @param array $groups
268
     *
269
     * @return $this
270
     */
271 66
    public function groups(array $groups)
272
    {
273 66
        $this->groups = $groups;
274
275 66
        return $this;
276
    }
277
278
    /**
279
     * Add a property group
280
     *
281
     * @param string $group
282
     *
283
     * @return $this
284
     */
285 2
    public function addGroup(string $group)
286
    {
287 2
        $this->groups[] = $group;
288
289 2
        return $this;
290
    }
291
292
    /**
293
     * Set the property accessor
294
     *
295
     * @param PropertyAccessorInterface $accessor
296
     *
297
     * @return $this
298
     */
299
    public function accessor(PropertyAccessorInterface $accessor)
300
    {
301
        $this->customAccessor = $accessor;
302
303
        return $this;
304
    }
305
306
    /**
307
     * Set the property accessor
308
     *
309
     * A accessor as string will be considered as a method
310
     *
311
     * @param PropertyAccessorInterface|string $getter
312
     *
313
     * @return $this
314
     */
315 12
    public function readWith($getter)
316
    {
317 12
        $this->getter = $getter;
318
319 12
        return $this;
320
    }
321
322
    /**
323
     * Set the property accessor
324
     *
325
     * @param PropertyAccessorInterface|string $setter
326
     *
327
     * @return $this
328
     */
329 10
    public function writeWith($setter)
330
    {
331 10
        $this->setter = $setter;
332
333 10
        return $this;
334
    }
335
336
    /**
337
     * Set a virtual property.
338
     *
339
     * @param string $getter
340
     *
341
     * @return $this
342
     */
343 2
    public function virtual(string $getter)
344
    {
345 2
        $this->readWith($getter);
346 2
        $this->readOnly();
347
348 2
        return $this;
349
    }
350
351
    /**
352
     * Set the version when the property has been added
353
     *
354
     * @param string $version
355
     *
356
     * @return $this
357
     */
358 56
    public function since(string $version)
359
    {
360 56
        $this->since = $version;
361
362 56
        return $this;
363
    }
364
365
    /**
366
     * Set the property read only.
367
     *
368
     * @param bool $flag
369
     *
370
     * @return $this
371
     */
372 10
    public function readOnly(bool $flag = true)
373
    {
374 10
        $this->readOnly = $flag;
375
376 10
        return $this;
377
    }
378
379
    /**
380
     * Set the property inline.
381
     * The properties of this property will be added as the same level.
382
     *
383
     * @param bool $flag
384
     *
385
     * @return $this
386
     */
387 10
    public function inline(bool $flag = true)
388
    {
389 10
        $this->inline = $flag;
390
391 10
        return $this;
392
    }
393
394
    /**
395
     * Set the version when the property has been removed
396
     *
397
     * @param string $version
398
     *
399
     * @return $this
400
     */
401 46
    public function until(string $version)
402
    {
403 46
        $this->until = $version;
404
405 46
        return $this;
406
    }
407
408
    /**
409
     * Add a normalization option
410
     *
411
     * @param string $option
412
     * @param mixed $value
413
     *
414
     * @return $this
415
     */
416 24
    public function normalization(string $option, $value)
417
    {
418 24
        $this->normalizationOptions[$option] = $value;
419
420 24
        return $this;
421
    }
422
423
    /**
424
     * Add a denormalization option
425
     *
426
     * @param string $option
427
     * @param mixed $value
428
     *
429
     * @return $this
430
     */
431 16
    public function denormalization(string $option, $value)
432
    {
433 16
        $this->denormalizationOptions[$option] = $value;
434
435 16
        return $this;
436
    }
437
438
    /**
439
     * Set the date time format
440
     *
441
     * @param string $format
442
     *
443
     * @return $this
444
     */
445 10
    public function dateFormat(string $format)
446
    {
447 10
        $this->normalization(NormalizationContext::DATETIME_FORMAT, $format);
448 10
        $this->denormalization(DenormalizationContext::DATETIME_FORMAT, $format);
449
450 10
        return $this;
451
    }
452
453
    /**
454
     * Set the internal date timezone
455
     *
456
     * @param string $timezone
457
     *
458
     * @return $this
459
     */
460 10
    public function timezone(string $timezone)
461
    {
462 10
        $this->denormalization(DenormalizationContext::TIMEZONE, $timezone);
463
464 10
        return $this;
465
    }
466
467
    /**
468
     * Set the serialized date timezone
469
     *
470
     * @param string $timezone
471
     *
472
     * @return $this
473
     */
474 10
    public function toTimezone(string $timezone)
475
    {
476 10
        $this->normalization(NormalizationContext::TIMEZONE, $timezone);
477 10
        $this->denormalization(DenormalizationContext::TIMEZONE_HINT, $timezone);
478
479 10
        return $this;
480
    }
481
482
    /**
483
     * The property will be normalize if its value is null
484
     *
485
     * @param bool $flag
486
     *
487
     * @return $this
488
     */
489 10
    public function conserveNull(bool $flag = true)
490
    {
491 10
        $this->normalization(NormalizationContext::NULL, $flag);
492
493 10
        return $this;
494
    }
495
496
    /**
497
     * Keep the default value of the property
498
     *
499
     * @param bool $flag
500
     *
501
     * @return $this
502
     */
503 8
    public function conserveDefault(bool $flag = false)
504
    {
505 8
        $this->normalization(NormalizationContext::REMOVE_DEFAULT_VALUE, $flag);
506
507 8
        return $this;
508
    }
509
510
    /**
511
     * Import options in the builder
512
     * Legacy method, should not be used
513
     *
514
     * @param array $values
515
     *
516
     * @return $this
517
     */
518 190
    public function configure(array $values)
519
    {
520 190
        foreach ($values as $property => $value) {
521
            switch ($property) {
522 50
                case 'type':
523 2
                    $this->type($value);
524 2
                    break;
525
526 50
                case 'group':
527 48
                case 'groups':
528 50
                    $this->groups((array)$value);
529 50
                    break;
530
531 48
                case 'alias':
532 48
                case 'serializedName':
533 10
                    $this->alias($value);
534 10
                    break;
535
536 38
                case 'since':
537 38
                    $this->since($value);
538 38
                    break;
539
540
                case 'until':
541
                    $this->until($value);
542
                    break;
543
544
                case 'readOnly':
545
                    $this->readOnly((bool)$value);
546
                    break;
547
548
//                case 'maxDepth':
549
//                    $this->$property = $value;
550
//                    break;
551
//
552
//                case 'inline':
553
//                    $this->$property = (bool)$value;
554
//                    break;
555
            }
556
        }
557
558 190
        return $this;
559
    }
560
561
    /**
562
     * Clear all reference
563
     */
564 240
    private function clear(): void
565
    {
566 240
        $this->customAccessor = $this->setter = $this->getter = null;
567
    }
568
}
569