Passed
Push — master ( cbecdb...34e2d9 )
by Arthur
07:30
created

PropertyFactory::buildProperties()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 20
c 0
b 0
f 0
rs 9.9332
cc 4
nc 5
nop 0
1
<?php
2
3
namespace Larapie\DataTransferObject\Factories;
4
5
use Larapie\DataTransferObject\Contracts\AdditionalProperties;
6
use Larapie\DataTransferObject\Contracts\DtoContract;
7
use Larapie\DataTransferObject\Contracts\WithAdditionalProperties;
8
use Larapie\DataTransferObject\Exceptions\UnknownPropertiesDtoException;
9
use Larapie\DataTransferObject\Property\Property;
10
use ReflectionClass;
11
use ReflectionProperty;
12
13
class PropertyFactory
14
{
15
    /**
16
     * @var DtoContract
17
     */
18
    protected $dto;
19
20
    /**
21
     * @var Property[]
22
     */
23
    private static $cache = [];
24
25
    /**
26
     * PropertyValueFactory constructor.
27
     * @param DtoContract $dto
28
     */
29
    public function __construct(DtoContract &$dto)
30
    {
31
        $this->dto = $dto;
32
    }
33
34
    public function build(array $parameters)
35
    {
36
        $properties = [];
37
        foreach ($this->getProperties() as $property) {
38
            if (array_key_exists($property->getName(), $parameters)) {
39
                $property->set($parameters[$property->getName()]);
40
            }
41
42
            /* add the property to an associative array with the name as key */
43
            $properties[$property->getName()] = $property;
44
45
            /* remove the property from the value object and parameters array  */
46
            unset($parameters[$property->getName()], $this->dto->{$property->getName()});
47
        }
48
49
        $this->checkRemainingProperties($parameters);
50
51
        return $properties;
52
    }
53
54
    protected function getDtoClass(): string
55
    {
56
        return get_class($this->dto);
57
    }
58
59
    protected function buildProperties()
60
    {
61
        $class = new ReflectionClass($this->dto);
62
63
        $properties = [];
64
        foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $reflectionProperty) {
65
            $property = new Property($reflectionProperty);
66
67
            //Set default value
68
            if (($default = $reflectionProperty->getValue($this->dto)) !== null) {
0 ignored issues
show
Unused Code introduced by
The assignment to $default is dead and can be removed.
Loading history...
69
                $property->set($reflectionProperty->getValue($this->dto));
70
            }
71
72
            //If property is immutable make sure all nested values are also immutable
73
            if ($property->isImmutable())
74
                $property->chainImmutable(true);
75
76
            $properties[$reflectionProperty->getName()] = $property;
77
        }
78
        return $properties;
79
    }
80
81
    protected function dtoIsCached()
82
    {
83
        return isset(self::$cache[$this->getDtoClass()]);
84
    }
85
86
    protected function getProperties(): array
87
    {
88
        if (!$this->dtoIsCached()) {
89
            $properties = $this->buildProperties();
90
91
            self::$cache[$this->getDtoClass()] = $properties;
92
93
            return $properties;
94
        }
95
96
        return $this->getFreshProperties();
97
    }
98
99
    protected function getFreshProperties()
100
    {
101
        $properties = [];
102
        foreach (self::$cache[$this->getDtoClass()] as $key => $property) {
103
            $property = clone $property;
104
            $property->reset();
105
            $properties[$key] = $property;
106
        }
107
108
        return $properties;
109
    }
110
111
    protected function checkRemainingProperties(array $parameters)
112
    {
113
        if (empty($parameters)) {
114
            return;
115
        } elseif ($this->dto instanceof WithAdditionalProperties) {
116
            foreach ($parameters as $name => $parameter) {
117
                $this->dto->with($name, $parameter);
118
            }
119
120
            return;
121
        } elseif ($this->dto instanceof AdditionalProperties) {
122
            return;
123
        }
124
        throw new UnknownPropertiesDtoException($parameters, $this->getDtoClass());
125
    }
126
}
127