Completed
Push — master ( 91cd10...dc7d5f )
by Brent
09:16
created

Property   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 121
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 1
dl 0
loc 121
rs 10
c 0
b 0
f 0

8 Methods

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