GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#56)
by Arthur
01:33
created

DataTransferObject::determineImmutability()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 2
nc 2
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spatie\DataTransferObject;
6
7
use ReflectionClass;
8
use ReflectionProperty;
9
use Spatie\DataTransferObject\Contracts\immutable;
10
use Spatie\DataTransferObject\Contracts\DtoContract;
11
use Spatie\DataTransferObject\Contracts\PropertyContract;
12
use Spatie\DataTransferObject\Exceptions\ImmutableDtoException;
13
use Spatie\DataTransferObject\Exceptions\PropertyNotFoundDtoException;
14
use Spatie\DataTransferObject\Exceptions\ImmutablePropertyDtoException;
15
use Spatie\DataTransferObject\Exceptions\UnknownPropertiesDtoException;
16
use Spatie\DataTransferObject\Exceptions\UninitialisedPropertyDtoException;
17
18
/**
19
 * Class DataTransferObject.
20
 */
21
abstract class DataTransferObject implements DtoContract
22
{
23
    /** @var array */
24
    protected $onlyKeys = [];
25
26
    /** @var Property[] | array */
27
    protected $properties = [];
28
29
    /** @var bool */
30
    protected $immutable = false;
31
32
    public function __construct(array $parameters)
33
    {
34
        $this->boot($parameters);
35
    }
36
37
    /**
38
     * Boot the dto and process all parameters.
39
     * @param array $parameters
40
     * @throws \ReflectionException
41
     */
42
    protected function boot(array $parameters): void
43
    {
44
        foreach ($this->getPublicProperties() as $property) {
45
46
            /*
47
             * Do not change the order of the following methods.
48
             * External packages rely on this order.
49
             */
50
51
            $this->setPropertyDefaultValue($property);
52
53
            $property = $this->mutateProperty($property);
54
55
            $this->validateProperty($property, $parameters);
56
57
            $this->setPropertyValue($property, $parameters);
58
59
            /* add the property to an associative array with the name as key */
60
            $this->properties[$property->getName()] = $property;
61
62
            /* remove the property from the value object and parameters array  */
63
            unset($parameters[$property->getName()], $this->{$property->getName()});
64
        }
65
66
        $this->processRemainingProperties($parameters);
67
        $this->determineImmutability();
68
    }
69
70
    protected function determineImmutability()
71
    {
72
        if ($this instanceof immutable) {
73
            $this->setImmutable();
74
        }
75
    }
76
77
    protected function setImmutable()
78
    {
79
        $this->immutable = true;
80
        foreach ($this->properties as $property) {
81
            $this->chainPropertiesImmutable($property);
82
        }
83
    }
84
85
    protected function chainPropertiesImmutable(PropertyContract $property)
86
    {
87
        $dto = $property->getValue();
88
        if ($dto instanceof DtoContract)
89
            $dto->immutable();
90
        elseif (is_iterable($dto)) {
91
            foreach ($dto as $aPotentialDto) {
92
                if ($aPotentialDto instanceof DtoContract)
93
                    $aPotentialDto->immutable();
94
            }
95
        }
96
    }
97
98
    /**
99
     * Get all public properties from the current object through reflection.
100
     * @return Property[]
101
     * @throws \ReflectionException
102
     */
103
    protected function getPublicProperties(): array
104
    {
105
        $class = new ReflectionClass(static::class);
106
107
        $properties = [];
108
        foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $reflectionProperty) {
109
            $properties[$reflectionProperty->getName()] = new Property($reflectionProperty);
110
        }
111
112
        return $properties;
113
    }
114
115
    /**
116
     * Check if property passes the basic conditions.
117
     * @param PropertyContract $property
118
     * @param array $parameters
119
     */
120
    protected function validateProperty(PropertyContract $property, array $parameters): void
121
    {
122
        if (!array_key_exists($property->getName(), $parameters)
123
            && is_null($property->getDefault())
124
            && !$property->nullable()
125
        ) {
126
            throw new UninitialisedPropertyDtoException($property);
127
        }
128
    }
129
130
    /**
131
     * Set the value if it's present in the array.
132
     * @param PropertyContract $property
133
     * @param array $parameters
134
     */
135
    protected function setPropertyValue(PropertyContract $property, array $parameters): void
136
    {
137
        if (array_key_exists($property->getName(), $parameters)) {
138
            $property->set($parameters[$property->getName()]);
139
        }
140
    }
141
142
    /**
143
     * Set the value if it's present in the array.
144
     * @param PropertyContract $property
145
     */
146
    protected function setPropertyDefaultValue(PropertyContract $property): void
147
    {
148
        $property->setDefault($property->getValueFromReflection($this));
149
    }
150
151
    /**
152
     * Allows to mutate the property before it gets processed.
153
     * @param PropertyContract $property
154
     * @return PropertyContract
155
     */
156
    protected function mutateProperty(PropertyContract $property): PropertyContract
157
    {
158
        return $property;
159
    }
160
161
    /**
162
     * Check if there are additional parameters left.
163
     * Throw error if there are.
164
     * Additional properties are not allowed in a dto.
165
     * @param array $parameters
166
     * @throws UnknownPropertiesDtoException
167
     */
168
    protected function processRemainingProperties(array $parameters)
169
    {
170
        if (count($parameters)) {
171
            throw new UnknownPropertiesDtoException(array_keys($parameters), static::class);
172
        }
173
    }
174
175
    /**
176
     * Immutable behavior
177
     * Throw error if a user tries to set a property.
178
     * @param $name
179
     * @param $value
180
     * @throws ImmutableDtoException|ImmutablePropertyDtoException|PropertyNotFoundDtoException
181
     */
182
    public function __set($name, $value)
183
    {
184
        if ($this->immutable) {
185
            throw new ImmutableDtoException($name);
186
        }
187
        if (!isset($this->properties[$name])) {
188
            throw new PropertyNotFoundDtoException($name, get_class($this));
189
        }
190
191
        if ($this->properties[$name]->immutable()) {
192
            throw new ImmutablePropertyDtoException($name);
193
        }
194
        $this->$name = $value;
195
    }
196
197
    /**
198
     * Proxy through to the properties array.
199
     * @param $name
200
     * @return mixed
201
     */
202
    public function __get($name)
203
    {
204
        return $this->properties[$name]->getValue();
205
    }
206
207
    /**
208
     * @return static
209
     */
210
    public function immutable(): DtoContract
211
    {
212
        if (!$this->isImmutable())
213
            $this->setImmutable();
214
        return $this;
215
    }
216
217
    public function isImmutable(): bool
218
    {
219
        return $this->immutable;
220
    }
221
222
    public function all(): array
223
    {
224
        $data = [];
225
226
        foreach ($this->properties as $property) {
227
            $data[$property->getName()] = $property->getValue();
228
        }
229
230
        return $data;
231
    }
232
233
    public function only(string ...$keys): DtoContract
234
    {
235
        $this->onlyKeys = array_merge($this->onlyKeys, $keys);
236
237
        return $this;
238
    }
239
240
    public function except(string ...$keys): DtoContract
241
    {
242
        foreach ($keys as $key) {
243
            $property = $this->properties[$key] ?? null;
244
            if (isset($property)) {
245
                $property->setVisible(false);
246
            }
247
        }
248
249
        return $this;
250
    }
251
252
    public function toArray(): array
253
    {
254
        $data = $this->all();
255
        $array = [];
256
257
        if (count($this->onlyKeys)) {
258
            $array = array_intersect_key($data, array_flip((array)$this->onlyKeys));
259
        } else {
260
            foreach ($data as $key => $propertyValue) {
261
                if ($this->properties[$key]->isVisible()) {
262
                    $array[$key] = $propertyValue;
263
                }
264
            }
265
        }
266
267
        return $this->parseArray($array);
268
    }
269
270
    protected function parseArray(array $array): array
271
    {
272
        foreach ($array as $key => $value) {
273
            if (
274
                $value instanceof DataTransferObject
275
                || $value instanceof DataTransferObjectCollection
276
            ) {
277
                $array[$key] = $value->toArray();
278
279
                continue;
280
            }
281
282
            if (!is_array($value)) {
283
                continue;
284
            }
285
286
            $array[$key] = $this->parseArray($value);
287
        }
288
289
        return $array;
290
    }
291
}
292