Completed
Push — master ( a0b37b...b45194 )
by Brent
46:54 queued 37:37
created

Property::assertValidEquals()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
cc 4
nc 3
nop 2
1
<?php
2
3
namespace Spatie\ValueObject;
4
5
use ReflectionProperty;
6
7
class Property extends ReflectionProperty
8
{
9
    /** @var array */
10
    private static $typeMapping = [
11
        'int' => 'integer',
12
        'bool' => 'boolean',
13
    ];
14
15
    /** @var \Spatie\ValueObject\ValueObject */
16
    protected $valueObject;
17
18
    /** @var bool */
19
    private $hasTypeDeclaration = false;
20
21
    /** @var bool */
22
    private $isNullable = false;
23
24
    /** @var bool */
25
    private $isInitialised = false;
26
27
    /** @var array */
28
    private $types = [];
29
30
    public static function fromReflection(ValueObject $valueObject, ReflectionProperty $reflectionProperty)
31
    {
32
        return new self($valueObject, $reflectionProperty);
33
    }
34
35
    public function __construct(ValueObject $valueObject, ReflectionProperty $reflectionProperty)
36
    {
37
        parent::__construct($reflectionProperty->class, $reflectionProperty->getName());
38
39
        $this->valueObject = $valueObject;
40
41
        $this->resolveTypeDefinition();
42
    }
43
44
    public function set($value)
45
    {
46
        if (! $this->isValidType($value)) {
47
            $expectedTypes = implode(', ', $this->types);
48
49
            throw ValueObjectException::invalidType(
50
                $this->getName(),
51
                $this->getDeclaringClass()->getName(),
52
                $expectedTypes,
53
                $value
54
            );
55
        }
56
57
        $this->isInitialised = true;
58
59
        $this->valueObject->{$this->getName()} = $value;
60
    }
61
62
    private function resolveTypeDefinition()
63
    {
64
        $docComment = $this->getDocComment();
65
66
        if (! $docComment) {
67
            return;
68
        }
69
70
        preg_match('/\@var ([\w|\\\\]+)/', $docComment, $matches);
71
72
        if (! count($matches)) {
73
            return;
74
        }
75
76
        $this->hasTypeDeclaration = true;
77
78
        $varDocComment = end($matches);
79
80
        $this->types = explode('|', $varDocComment);
81
82
        $this->isNullable = strpos($varDocComment, 'null') !== false;
83
    }
84
85
    private function isValidType($value): bool
86
    {
87
        if (! $this->hasTypeDeclaration) {
88
            return true;
89
        }
90
91
        if ($this->isNullable && $value === null) {
92
            return true;
93
        }
94
95
        foreach ($this->types as $currentType) {
96
            $isValidType = $this->assertValidEquals($currentType, $value);
97
98
            if ($isValidType) {
99
                return true;
100
            }
101
        }
102
103
        return false;
104
    }
105
106
    private function assertValidEquals(string $type, $value): bool
107
    {
108
        if ($type === 'mixed' && $value !== null) {
109
            return true;
110
        }
111
112
        if (class_exists($type)) {
113
            return $value instanceof $type;
114
        }
115
116
        return gettype($value) === (self::$typeMapping[$type] ?? $type);
117
    }
118
}
119