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.
Passed
Pull Request — master (#56)
by Arthur
01:00
created

DataTransferObject::__set()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 2
dl 0
loc 13
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
    public function setImmutable(): void
78
    {
79
        if (! $this->isImmutable()) {
80
            $this->immutable = true;
81
            foreach ($this->properties as $property) {
82
                $this->chainPropertiesImmutable($property);
83
            }
84
        }
85
    }
86
87
    protected function chainPropertiesImmutable(PropertyContract $property)
88
    {
89
        $dto = $property->getValue();
90
        if ($dto instanceof DtoContract) {
91
            $dto->setImmutable();
92
        } elseif (is_iterable($dto)) {
93
            foreach ($dto as $aPotentialDto) {
94
                if ($aPotentialDto instanceof DtoContract) {
95
                    $aPotentialDto->setImmutable();
96
                }
97
            }
98
        }
99
    }
100
101
    /**
102
     * Get all public properties from the current object through reflection.
103
     * @return Property[]
104
     * @throws \ReflectionException
105
     */
106
    protected function getPublicProperties(): array
107
    {
108
        $class = new ReflectionClass(static::class);
109
110
        $properties = [];
111
        foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $reflectionProperty) {
112
            $properties[$reflectionProperty->getName()] = new Property($reflectionProperty);
113
        }
114
115
        return $properties;
116
    }
117
118
    /**
119
     * Check if property passes the basic conditions.
120
     * @param PropertyContract $property
121
     * @param array $parameters
122
     */
123
    protected function validateProperty(PropertyContract $property, array $parameters): void
124
    {
125
        if (! array_key_exists($property->getName(), $parameters)
126
            && is_null($property->getDefault())
127
            && ! $property->nullable()
128
        ) {
129
            throw new UninitialisedPropertyDtoException($property);
130
        }
131
    }
132
133
    /**
134
     * Set the value if it's present in the array.
135
     * @param PropertyContract $property
136
     * @param array $parameters
137
     */
138
    protected function setPropertyValue(PropertyContract $property, array $parameters): void
139
    {
140
        if (array_key_exists($property->getName(), $parameters)) {
141
            $property->set($parameters[$property->getName()]);
142
        }
143
    }
144
145
    /**
146
     * Set the value if it's present in the array.
147
     * @param PropertyContract $property
148
     */
149
    protected function setPropertyDefaultValue(PropertyContract $property): void
150
    {
151
        $property->setDefault($property->getValueFromReflection($this));
152
    }
153
154
    /**
155
     * Allows to mutate the property before it gets processed.
156
     * @param PropertyContract $property
157
     * @return PropertyContract
158
     */
159
    protected function mutateProperty(PropertyContract $property): PropertyContract
160
    {
161
        return $property;
162
    }
163
164
    /**
165
     * Check if there are additional parameters left.
166
     * Throw error if there are.
167
     * Additional properties are not allowed in a dto.
168
     * @param array $parameters
169
     * @throws UnknownPropertiesDtoException
170
     */
171
    protected function processRemainingProperties(array $parameters)
172
    {
173
        if (count($parameters)) {
174
            throw new UnknownPropertiesDtoException(array_keys($parameters), static::class);
175
        }
176
    }
177
178
    /**
179
     * Immutable behavior
180
     * Throw error if a user tries to set a property.
181
     * @param $name
182
     * @param $value
183
     * @throws ImmutableDtoException|ImmutablePropertyDtoException|PropertyNotFoundDtoException
184
     */
185
    public function __set($name, $value)
186
    {
187
        if ($this->immutable) {
188
            throw new ImmutableDtoException($name);
189
        }
190
        if (! isset($this->properties[$name])) {
191
            throw new PropertyNotFoundDtoException($name, get_class($this));
192
        }
193
194
        if ($this->properties[$name]->immutable()) {
195
            throw new ImmutablePropertyDtoException($name);
196
        }
197
        $this->$name = $value;
198
    }
199
200
    /**
201
     * Proxy through to the properties array.
202
     * @param $name
203
     * @return mixed
204
     */
205
    public function __get($name)
206
    {
207
        return $this->properties[$name]->getValue();
208
    }
209
210
    /**
211
     * @deprecated
212
     * @param array $data
213
     * @return static
214
     */
215
    public static function immutable(array $data): DtoContract
216
    {
217
        $dto = new static($data);
218
        $dto->setImmutable();
219
220
        return $dto;
221
    }
222
223
    public function isImmutable(): bool
224
    {
225
        return $this->immutable;
226
    }
227
228
    public function all(): array
229
    {
230
        $data = [];
231
232
        foreach ($this->properties as $property) {
233
            $data[$property->getName()] = $property->getValue();
234
        }
235
236
        return $data;
237
    }
238
239
    public function only(string ...$keys): DtoContract
240
    {
241
        $this->onlyKeys = array_merge($this->onlyKeys, $keys);
242
243
        return $this;
244
    }
245
246
    public function except(string ...$keys): DtoContract
247
    {
248
        foreach ($keys as $key) {
249
            $property = $this->properties[$key] ?? null;
250
            if (isset($property)) {
251
                $property->setVisible(false);
252
            }
253
        }
254
255
        return $this;
256
    }
257
258
    public function toArray(): array
259
    {
260
        $data = $this->all();
261
        $array = [];
262
263
        if (count($this->onlyKeys)) {
264
            $array = array_intersect_key($data, array_flip((array) $this->onlyKeys));
265
        } else {
266
            foreach ($data as $key => $propertyValue) {
267
                if ($this->properties[$key]->isVisible()) {
268
                    $array[$key] = $propertyValue;
269
                }
270
            }
271
        }
272
273
        return $this->parseArray($array);
274
    }
275
276
    protected function parseArray(array $array): array
277
    {
278
        foreach ($array as $key => $value) {
279
            if (
280
                $value instanceof DataTransferObject
281
                || $value instanceof DataTransferObjectCollection
282
            ) {
283
                $array[$key] = $value->toArray();
284
285
                continue;
286
            }
287
288
            if (! is_array($value)) {
289
                continue;
290
            }
291
292
            $array[$key] = $this->parseArray($value);
293
        }
294
295
        return $array;
296
    }
297
}
298