1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Rexlabs\Smokescreen\Resource; |
4
|
|
|
|
5
|
|
|
use Rexlabs\Smokescreen\Exception\InvalidSerializerException; |
6
|
|
|
use Rexlabs\Smokescreen\Exception\InvalidTransformerException; |
7
|
|
|
use Rexlabs\Smokescreen\Serializer\SerializerInterface; |
8
|
|
|
use Rexlabs\Smokescreen\Transformer\TransformerInterface; |
9
|
|
|
|
10
|
|
|
abstract class AbstractResource implements ResourceInterface |
11
|
|
|
{ |
12
|
|
|
/** |
13
|
|
|
* The data to process with the transformer. |
14
|
|
|
* |
15
|
|
|
* @var array|\ArrayIterator|mixed |
16
|
|
|
*/ |
17
|
|
|
protected $data; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Array of meta data. |
21
|
|
|
* |
22
|
|
|
* @var array |
23
|
|
|
*/ |
24
|
|
|
protected $meta = []; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* The resource key. |
28
|
|
|
* |
29
|
|
|
* @var string |
30
|
|
|
*/ |
31
|
|
|
protected $resourceKey; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* A transformer or callable to process the data attached to this resource. |
35
|
|
|
* |
36
|
|
|
* @var callable|TransformerInterface|null |
37
|
|
|
*/ |
38
|
|
|
protected $transformer; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Provide a custom serializer for this resource. |
42
|
|
|
* |
43
|
|
|
* @var callable|SerializerInterface|null |
44
|
|
|
*/ |
45
|
|
|
protected $serializer; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Create a new resource instance. |
49
|
|
|
* |
50
|
|
|
* @param mixed $data |
51
|
|
|
* @param callable|TransformerInterface|null $transformer |
52
|
|
|
* @param string $resourceKey |
53
|
|
|
* |
54
|
|
|
* @throws \Rexlabs\Smokescreen\Exception\InvalidTransformerException |
55
|
|
|
*/ |
56
|
|
|
public function __construct($data = null, $transformer = null, $resourceKey = null) |
57
|
|
|
{ |
58
|
|
|
$this->setData($data); |
59
|
|
|
$this->setTransformer($transformer); |
60
|
|
|
$this->setResourceKey($resourceKey); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Get the data. |
65
|
|
|
* |
66
|
|
|
* @return mixed |
67
|
|
|
*/ |
68
|
|
|
public function getData() |
69
|
|
|
{ |
70
|
|
|
return $this->data; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* Set the data. |
75
|
|
|
* |
76
|
|
|
* @param mixed $data |
77
|
|
|
* |
78
|
|
|
* @return $this |
79
|
|
|
*/ |
80
|
|
|
public function setData($data) |
81
|
|
|
{ |
82
|
|
|
$this->data = $data; |
83
|
|
|
|
84
|
|
|
return $this; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Get the meta data. |
89
|
|
|
* |
90
|
|
|
* @return array |
91
|
|
|
*/ |
92
|
|
|
public function getMeta() |
93
|
|
|
{ |
94
|
|
|
return $this->meta; |
95
|
|
|
} |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Set the meta data. |
99
|
|
|
* |
100
|
|
|
* @param array $meta |
101
|
|
|
* |
102
|
|
|
* @return $this |
103
|
|
|
*/ |
104
|
|
|
public function setMeta(array $meta) |
105
|
|
|
{ |
106
|
|
|
$this->meta = $meta; |
107
|
|
|
|
108
|
|
|
return $this; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Get the meta data. |
113
|
|
|
* |
114
|
|
|
* @param string $metaKey |
115
|
|
|
* |
116
|
|
|
* @return array |
117
|
|
|
*/ |
118
|
|
|
public function getMetaValue($metaKey) |
119
|
|
|
{ |
120
|
|
|
return $this->meta[$metaKey]; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Get the resource key. |
125
|
|
|
* |
126
|
|
|
* @return string|null |
127
|
|
|
*/ |
128
|
|
|
public function getResourceKey() |
129
|
|
|
{ |
130
|
|
|
return $this->resourceKey; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* Set the resource key. |
135
|
|
|
* |
136
|
|
|
* @param string|null $resourceKey |
137
|
|
|
* |
138
|
|
|
* @return $this |
139
|
|
|
*/ |
140
|
|
|
public function setResourceKey($resourceKey) |
141
|
|
|
{ |
142
|
|
|
$this->resourceKey = $resourceKey; |
143
|
|
|
|
144
|
|
|
return $this; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Set the meta data. |
149
|
|
|
* |
150
|
|
|
* @param string $metaKey |
151
|
|
|
* @param mixed $metaValue |
152
|
|
|
* |
153
|
|
|
* @return $this |
154
|
|
|
*/ |
155
|
|
|
public function setMetaValue(string $metaKey, $metaValue) |
156
|
|
|
{ |
157
|
|
|
$this->meta[$metaKey] = $metaValue; |
158
|
|
|
|
159
|
|
|
return $this; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Get a list of relationships from the transformer |
164
|
|
|
* Only applicable when the transformer is an instance of the TransformerInterface (eg. AbstractTransformer) |
165
|
|
|
* @return array |
166
|
|
|
*/ |
167
|
|
|
public function getRelationships(): array |
168
|
|
|
{ |
169
|
|
|
$relationships = []; |
170
|
|
|
if ($this->hasTransformer()) { |
171
|
|
|
if (($transformer = $this->getTransformer()) instanceof TransformerInterface) { |
172
|
|
|
$relationships = $transformer->getRelationships(); |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
return $relationships; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* @return boolean |
181
|
|
|
*/ |
182
|
|
|
public function hasTransformer(): bool |
183
|
|
|
{ |
184
|
|
|
return $this->transformer !== null; |
185
|
|
|
} |
186
|
|
|
|
187
|
|
|
/** |
188
|
|
|
* Get the transformer. |
189
|
|
|
* |
190
|
|
|
* @return callable|TransformerInterface|null |
191
|
|
|
*/ |
192
|
|
|
public function getTransformer() |
193
|
|
|
{ |
194
|
|
|
return $this->transformer; |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Set the transformer. |
199
|
|
|
* |
200
|
|
|
* @param callable|TransformerInterface|null $transformer |
201
|
|
|
* |
202
|
|
|
* @return $this |
203
|
|
|
* @throws InvalidTransformerException |
204
|
|
|
*/ |
205
|
|
|
public function setTransformer($transformer) |
206
|
|
|
{ |
207
|
|
|
if ($transformer !== null) { |
208
|
|
|
if (!$this->isValidTransformer($transformer)) { |
209
|
|
|
throw new InvalidTransformerException('Transformer must be a callable or implement TransformerInterface'); |
210
|
|
|
} |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
$this->transformer = $transformer; |
214
|
|
|
|
215
|
|
|
return $this; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* Determines if the given argument is a valid transformer. |
220
|
|
|
* @param mixed $transformer |
221
|
|
|
* |
222
|
|
|
* @return bool |
223
|
|
|
*/ |
224
|
|
|
public function isValidTransformer($transformer): bool |
225
|
|
|
{ |
226
|
|
|
if ($transformer instanceof TransformerInterface && method_exists($transformer, 'transform')) { |
227
|
|
|
return true; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
if (\is_callable($transformer)) { |
231
|
|
|
return true; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
return false; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
/** |
238
|
|
|
* @return boolean |
239
|
|
|
*/ |
240
|
|
|
public function hasSerializer(): bool |
241
|
|
|
{ |
242
|
|
|
return $this->serializer !== null; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* @return callable|SerializerInterface|false|null |
247
|
|
|
*/ |
248
|
|
|
public function getSerializer() |
249
|
|
|
{ |
250
|
|
|
return $this->serializer; |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* Sets a custom serializer to be used for this resource |
255
|
|
|
* - Usually you will set this to an instance of SerializerInterface. |
256
|
|
|
* - Set to false to force no serialization on this resource. |
257
|
|
|
* - When set to null the default serializer will be used. |
258
|
|
|
* - Optionally set a closure/callback to be used for serialization. |
259
|
|
|
* |
260
|
|
|
* @param callable|SerializerInterface|false|null $serializer |
261
|
|
|
* @throws InvalidSerializerException |
262
|
|
|
* @return $this |
263
|
|
|
*/ |
264
|
|
|
public function setSerializer($serializer) |
265
|
|
|
{ |
266
|
|
|
if ($serializer !== null) { |
267
|
|
|
if (!$this->isValidSerializer($serializer)) { |
268
|
|
|
throw new InvalidSerializerException('Serializer must be one of: callable, SerializerInterface, false or null'); |
269
|
|
|
} |
270
|
|
|
} |
271
|
|
|
$this->serializer = $serializer; |
|
|
|
|
272
|
|
|
|
273
|
|
|
return $this; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* @param mixed $serializer |
278
|
|
|
* |
279
|
|
|
* @return bool |
280
|
|
|
*/ |
281
|
|
|
public function isValidSerializer($serializer): bool |
282
|
|
|
{ |
283
|
|
|
if ($serializer === false) { |
284
|
|
|
// False is a valid value (forces serialization to be off) |
285
|
|
|
return true; |
286
|
|
|
} |
287
|
|
|
if ($serializer instanceof SerializerInterface) { |
288
|
|
|
return true; |
289
|
|
|
} |
290
|
|
|
if (\is_callable($serializer)) { |
291
|
|
|
return true; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
return false; |
295
|
|
|
} |
296
|
|
|
} |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.