1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Spatie\DataTransferObject; |
6
|
|
|
|
7
|
|
|
use ReflectionClass; |
8
|
|
|
use ReflectionProperty; |
9
|
|
|
|
10
|
|
|
abstract class DataTransferObject |
11
|
|
|
{ |
12
|
|
|
/** |
13
|
|
|
* @param array $parameters |
14
|
|
|
* |
15
|
|
|
* @return \Spatie\DataTransferObject\ImmutableDataTransferObject|static |
16
|
|
|
*/ |
17
|
|
|
public static function immutable(array $parameters = []): ImmutableDataTransferObject |
18
|
|
|
{ |
19
|
|
|
return new ImmutableDataTransferObject(new static($parameters)); |
20
|
|
|
} |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @param array $arrayOfParameters |
24
|
|
|
* |
25
|
|
|
* @return \Spatie\DataTransferObject\ImmutableDataTransferObject[]|static[] |
26
|
|
|
*/ |
27
|
|
|
public static function arrayOf(array $arrayOfParameters): array |
28
|
|
|
{ |
29
|
|
|
return array_map( |
30
|
|
|
function ($parameters) { |
31
|
|
|
return new static($parameters); |
32
|
|
|
}, |
33
|
|
|
$arrayOfParameters |
34
|
|
|
); |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
public function __construct(array $parameters = []) |
38
|
|
|
{ |
39
|
|
|
$validators = $this->getFieldValidators(); |
40
|
|
|
|
41
|
|
|
$valueCaster = $this->getValueCaster(); |
42
|
|
|
|
43
|
|
|
/** string[] */ |
44
|
|
|
$invalidTypes = []; |
45
|
|
|
|
46
|
|
|
foreach ($validators as $field => $validator) { |
47
|
|
|
if ( |
48
|
|
|
! isset($parameters[$field]) |
49
|
|
|
&& ! $validator->hasDefaultValue |
50
|
|
|
&& ! $validator->isNullable |
51
|
|
|
) { |
52
|
|
|
throw DataTransferObjectError::uninitialized( |
53
|
|
|
static::class, |
54
|
|
|
$field |
55
|
|
|
); |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
$value = $parameters[$field] ?? $this->{$field} ?? null; |
59
|
|
|
|
60
|
|
|
$value = $this->castValue($valueCaster, $validator, $value); |
61
|
|
|
|
62
|
|
|
if (! $validator->isValidType($value)) { |
63
|
|
|
$invalidTypes[] = DataTransferObjectError::invalidTypeMessage( |
64
|
|
|
static::class, |
65
|
|
|
$field, |
66
|
|
|
$validator->allowedTypes, |
67
|
|
|
$value |
68
|
|
|
); |
69
|
|
|
|
70
|
|
|
continue; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
$this->{$field} = $value; |
74
|
|
|
|
75
|
|
|
unset($parameters[$field]); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
if ($invalidTypes) { |
|
|
|
|
79
|
|
|
DataTransferObjectError::invalidTypes($invalidTypes); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
if (! $this->ignoreMissing() && count($parameters)) { |
83
|
|
|
throw DataTransferObjectError::unknownProperties(array_keys($parameters), static::class); |
84
|
|
|
} |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
protected function ignoreMissing(): bool |
88
|
|
|
{ |
89
|
|
|
return false; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
View Code Duplication |
public function all(): array |
|
|
|
|
93
|
|
|
{ |
94
|
|
|
$data = []; |
95
|
|
|
|
96
|
|
|
$class = new ReflectionClass(static::class); |
97
|
|
|
|
98
|
|
|
$properties = $class->getProperties(ReflectionProperty::IS_PUBLIC); |
99
|
|
|
|
100
|
|
|
foreach ($properties as $reflectionProperty) { |
101
|
|
|
// Skip static properties |
102
|
|
|
if ($reflectionProperty->isStatic()) { |
103
|
|
|
continue; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
$data[$reflectionProperty->getName()] = $reflectionProperty->getValue($this); |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
return $data; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
public function only(string ...$keys): DataTransferObjectArray |
113
|
|
|
{ |
114
|
|
|
return new DataTransferObjectArray(Arr::only($this->toArray(), $keys)); |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
public function except(string ...$keys): DataTransferObjectArray |
118
|
|
|
{ |
119
|
|
|
return new DataTransferObjectArray(Arr::except($this->toArray(), $keys)); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
public function toArray(): array |
123
|
|
|
{ |
124
|
|
|
return $this->parseArray($this->all()); |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
protected function parseArray(array $array): array |
128
|
|
|
{ |
129
|
|
|
foreach ($array as $key => $value) { |
130
|
|
|
if ( |
131
|
|
|
$value instanceof DataTransferObject |
132
|
|
|
|| $value instanceof DataTransferObjectCollection |
|
|
|
|
133
|
|
|
) { |
134
|
|
|
$array[$key] = $value->toArray(); |
135
|
|
|
|
136
|
|
|
continue; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
if (! is_array($value)) { |
140
|
|
|
continue; |
141
|
|
|
} |
142
|
|
|
|
143
|
|
|
$array[$key] = $this->parseArray($value); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
return $array; |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
/** |
150
|
|
|
* @param \ReflectionClass $class |
|
|
|
|
151
|
|
|
* |
152
|
|
|
* @return \Spatie\DataTransferObject\FieldValidator[] |
153
|
|
|
*/ |
154
|
|
|
protected function getFieldValidators(): array |
155
|
|
|
{ |
156
|
|
View Code Duplication |
return DTOCache::resolve(static::class, function () { |
|
|
|
|
157
|
|
|
$class = new ReflectionClass(static::class); |
158
|
|
|
|
159
|
|
|
$properties = []; |
160
|
|
|
|
161
|
|
|
foreach ($class->getProperties(ReflectionProperty::IS_PUBLIC) as $reflectionProperty) { |
162
|
|
|
// Skip static properties |
163
|
|
|
if ($reflectionProperty->isStatic()) { |
164
|
|
|
continue; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
$field = $reflectionProperty->getName(); |
168
|
|
|
|
169
|
|
|
$properties[$field] = FieldValidator::fromReflection($reflectionProperty); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
return $properties; |
173
|
|
|
}); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* @param \Spatie\DataTransferObject\ValueCaster $valueCaster |
178
|
|
|
* @param \Spatie\DataTransferObject\FieldValidator $fieldValidator |
179
|
|
|
* @param mixed $value |
180
|
|
|
* |
181
|
|
|
* @return mixed |
182
|
|
|
*/ |
183
|
|
|
protected function castValue(ValueCaster $valueCaster, FieldValidator $fieldValidator, $value) |
184
|
|
|
{ |
185
|
|
|
if (is_array($value)) { |
186
|
|
|
return $valueCaster->cast($value, $fieldValidator); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
return $value; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
protected function getValueCaster(): ValueCaster |
193
|
|
|
{ |
194
|
|
|
return new ValueCaster(); |
195
|
|
|
} |
196
|
|
|
} |
197
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.