Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.30% covered (success)
96.30%
26 / 27
88.89% covered (warning)
88.89%
8 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
FromProperty
96.30% covered (success)
96.30%
26 / 27
88.89% covered (warning)
88.89%
8 / 9
20
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTypehint
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
3.14
 isOptional
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 fromNative
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 fillField
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fillMissingField
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 isInitialized
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getValue
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 toNative
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2namespace Apie\Core\ValueObjects\Fields;
3
4use Apie\Core\Attributes\Internal;
5use Apie\Core\Attributes\Optional;
6use Apie\Core\Exceptions\InvalidTypeException;
7use Apie\Core\ValueObjects\Interfaces\ValueObjectInterface;
8use Apie\Core\ValueObjects\Utils;
9use ReflectionIntersectionType;
10use ReflectionNamedType;
11use ReflectionProperty;
12use ReflectionUnionType;
13use UnitEnum;
14
15/**
16 * FieldInterface implementation that reads a property from an object with reflection.
17 */
18final class FromProperty implements FieldInterface
19{
20    private ReflectionProperty $property;
21
22    public function __construct(ReflectionProperty $property)
23    {
24        $this->property = $property;
25    }
26
27    public function getTypehint(): string
28    {
29        $type = $this->property->getType();
30        if ($type instanceof ReflectionNamedType) {
31            return ($type->allowsNull() ? ($type->getName() . '|null') : $type->getName());
32        }
33        return (string) $type;
34    }
35
36    public function isOptional(): bool
37    {
38        return $this->property->hasDefaultValue()
39            || !empty($this->property->getAttributes(Optional::class))
40            || !empty($this->property->getAttributes(Internal::class))
41            || $this->property->getType()->allowsNull();
42    }
43
44    public function fromNative(ValueObjectInterface $instance, mixed $value): void
45    {
46        $type = $this->property->getType();
47        if ($type instanceof ReflectionUnionType || $type instanceof ReflectionNamedType) {
48            self::fillField($instance, Utils::toTypehint($type, $value));
49            return;
50        }
51        throw new InvalidTypeException($type, 'ReflectionUnionType|ReflectionNamedType');
52    }
53
54    public function fillField(ValueObjectInterface $instance, mixed $value): void
55    {
56        $this->property->setValue($instance, $value);
57    }
58
59    public function fillMissingField(ValueObjectInterface $instance): void
60    {
61        if (!$this->isOptional()) {
62            $type = $this->property->getType();
63            if (null === $type || $type instanceof ReflectionIntersectionType) {
64                throw new InvalidTypeException($type, 'ReflectionUnionType|ReflectionNamedType');
65            }
66            throw new InvalidTypeException('(missing value)', (string) $type);
67        }
68        if (!empty($this->property->getAttributes(Optional::class))) {
69            return;
70        }
71        $this->property->setValue($instance, $this->property->getDefaultValue());
72    }
73
74    public function isInitialized(ValueObjectInterface $instance): bool
75    {
76        return $this->property->isInitialized($instance);
77    }
78
79    public function getValue(ValueObjectInterface $instance): mixed
80    {
81        return $this->property->getValue($instance);
82    }
83
84    public function toNative(ValueObjectInterface $instance): null|array|string|int|float|bool|UnitEnum
85    {
86        $value = $this->getValue($instance);
87        return Utils::toNative($value);
88    }
89}